Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions jstests/aggregation/bugs/arrayadd.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
c = db.arrayadd;

// Test array add with implicit array creation
c.drop();
c.save({a:[{b: 1}, {b: 2}, {b: 3}]});

assert.eq(c.aggregate({$project: {total: {$add: "$a.b"}}}).result[0].total, 6);

// Test nested array add
c.drop();
c.save({a: [1, 2, [1, 1, 1]]});

assert.eq(c.aggregate({$project: {total: {$add: "$a"}}}).result[0].total, 6);

// Test null aborts sum (SERVER-7932)
c.drop();
c.save({a:[{b: 1}, {b: 2}, {b: null}]});

assert.eq(c.aggregate({$project: {total: {$add: "$a.b"}}}).result[0].total, null);
65 changes: 43 additions & 22 deletions src/mongo/db/pipeline/expression.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -345,28 +345,10 @@ namespace mongo {
for (size_t i = 0; i < n; ++i) {
Value val = vpOperand[i]->evaluate(pDocument);

if (val.numeric()) {
totalType = Value::getWidestNumeric(totalType, val.getType());

doubleTotal += val.coerceToDouble();
longTotal += val.coerceToLong();
}
else if (val.getType() == Date) {
uassert(16612, "only one Date allowed in an $add expression",
!haveDate);
haveDate = true;

// We don't manipulate totalType here.

longTotal += val.getDate();
doubleTotal += val.getDate();
}
else if (val.nullish()) {
return Value(BSONNULL);
}
else {
uasserted(16554, str::stream() << "$add only supports numeric or date types, not "
<< typeName(val.getType()));
boost::optional<Value> earlyReturnValue = addToRunningTotal(
val, totalType, doubleTotal, longTotal, haveDate);
if (earlyReturnValue) {
return *earlyReturnValue;
}
}

Expand All @@ -389,6 +371,45 @@ namespace mongo {
}
}

boost::optional<Value> ExpressionAdd::addToRunningTotal(const Value& val, BSONType& totalType,
double& doubleTotal, long long & longTotal, bool& haveDate) const {
if (val.numeric()) {
totalType = Value::getWidestNumeric(totalType, val.getType());

doubleTotal += val.coerceToDouble();
longTotal += val.coerceToLong();
}
else if (val.getType() == Date) {
uassert(16612, "only one Date allowed in an $add expression",
!haveDate);
haveDate = true;

// We don't manipulate totalType here.

longTotal += val.getDate();
doubleTotal += val.getDate();
}
else if (val.getType() == Array) {
// Sum numeric array values if supplied
for (size_t i = 0; i < val.getArrayLength(); i++) {
boost::optional<Value> earlyReturnValue = addToRunningTotal(val.getArray()[i],
totalType, doubleTotal, longTotal, haveDate);
if (earlyReturnValue) {
return earlyReturnValue;
}
}
}
else if (val.nullish()) {
return boost::optional<Value>(Value(BSONNULL));
}
else {
uasserted(16554, str::stream() << "$add only supports numeric or date types, not "
<< typeName(val.getType()));
}

return boost::optional<Value>();
}

const char *ExpressionAdd::getOpName() const {
return "$add";
}
Expand Down
6 changes: 5 additions & 1 deletion src/mongo/db/pipeline/expression.h
Original file line number Diff line number Diff line change
Expand Up @@ -325,7 +325,11 @@ namespace mongo {
@returns addition expression
*/
static intrusive_ptr<ExpressionNary> create();
};

private:
boost::optional<Value> addToRunningTotal(const Value& val, BSONType& totalType,
double& doubleTotal, long long & longTotal, bool& haveDate) const;
};


class ExpressionAnd :
Expand Down