diff options
-rw-r--r-- | jstests/aggregation/bugs/server14691.js | 52 | ||||
-rw-r--r-- | jstests/aggregation/testshard1.js | 4 | ||||
-rw-r--r-- | src/mongo/db/pipeline/accumulator_avg.cpp | 2 | ||||
-rw-r--r-- | src/mongo/db/pipeline/accumulator_test.cpp | 2 |
4 files changed, 58 insertions, 2 deletions
diff --git a/jstests/aggregation/bugs/server14691.js b/jstests/aggregation/bugs/server14691.js new file mode 100644 index 00000000000..0ba010ac41a --- /dev/null +++ b/jstests/aggregation/bugs/server14691.js @@ -0,0 +1,52 @@ +// SERVER-14691: $avg aggregator should return null when it receives no input. +(function() { + 'use strict'; + + var coll = db.accumulate_avg_sum_null; + + // Test the $avg aggregator. + coll.drop(); + + // Null cases. + assert.writeOK(coll.insert({a: 1, b: 2, c: 'string', d: null})); + + // Missing field. + var pipeline = [{$group: {_id: '$a', avg: {$avg: '$missing'}}}]; + assert.eq(coll.aggregate(pipeline).toArray(), [{_id: 1, avg: null}]); + + // Non-numeric field. + pipeline = [{$group: {_id: '$a', avg: {$avg: '$c'}}}]; + assert.eq(coll.aggregate(pipeline).toArray(), [{_id: 1, avg: null}]); + + // Field with value of null. + pipeline = [{$group: {_id: '$a', avg: {$avg: '$d'}}}]; + assert.eq(coll.aggregate(pipeline).toArray(), [{_id: 1, avg: null}]); + + // All three. + coll.insert({a: 1, d: 'string'}); + coll.insert({a: 1}); + pipeline = [{$group: {_id: '$a', avg: {$avg: '$d'}}}]; + assert.eq(coll.aggregate(pipeline).toArray(), [{_id: 1, avg: null}]); + + // Non-null cases. + coll.drop(); + assert.writeOK(coll.insert({a: 1, b: 2})); + pipeline = [{$group: {_id: '$a', avg: {$avg: '$b'}}}]; + + // One field. + assert.eq(coll.aggregate(pipeline).toArray(), [{_id: 1, avg: 2}]); + + // Two fields. + assert.writeOK(coll.insert({a: 1, b: 4})); + assert.eq(coll.aggregate(pipeline).toArray(), [{_id: 1, avg: 3}]); + + // Average of zero should still work. + assert.writeOK(coll.insert({a: 1, b: -6})); + assert.eq(coll.aggregate(pipeline).toArray(), [{_id: 1, avg: 0}]); + + // Missing, null, or non-numeric fields should not error or affect the average. + assert.writeOK(coll.insert({a: 1})); + assert.writeOK(coll.insert({a: 1, b: 'string'})); + assert.writeOK(coll.insert({a: 1, b: null})); + assert.eq(coll.aggregate(pipeline).toArray(), [{_id: 1, avg: 0}]); +}()); diff --git a/jstests/aggregation/testshard1.js b/jstests/aggregation/testshard1.js index 81f27e51830..71271ca0af5 100644 --- a/jstests/aggregation/testshard1.js +++ b/jstests/aggregation/testshard1.js @@ -129,6 +129,10 @@ jsTestLog('sum of an arithmetic progression S(n) = (n/2)(a(1) + a(n));'); assert.eq(a2[0].total, (nItems/2)*(1 + nItems), 'agg sharded test counter sum failed'); +jsTestLog('A group combining all documents into one, averaging a null field.'); +assert.eq(aggregateOrdered(db.ts1, [{$group: {_id: null, avg: {$avg: "$missing"}}}]), + [{_id: null, avg: null}]); + jsTestLog('an initial group starts the group in the shards, and combines them in mongos'); var a3 = aggregateOrdered(db.ts1, [ { $group: { diff --git a/src/mongo/db/pipeline/accumulator_avg.cpp b/src/mongo/db/pipeline/accumulator_avg.cpp index cf2d5c9b677..06dd585bdf7 100644 --- a/src/mongo/db/pipeline/accumulator_avg.cpp +++ b/src/mongo/db/pipeline/accumulator_avg.cpp @@ -72,7 +72,7 @@ intrusive_ptr<Accumulator> AccumulatorAvg::create() { Value AccumulatorAvg::getValue(bool toBeMerged) const { if (!toBeMerged) { if (_count == 0) - return Value(0.0); + return Value(BSONNULL); return Value(_total / static_cast<double>(_count)); } else { diff --git a/src/mongo/db/pipeline/accumulator_test.cpp b/src/mongo/db/pipeline/accumulator_test.cpp index e3e6bd04d87..9bef088316d 100644 --- a/src/mongo/db/pipeline/accumulator_test.cpp +++ b/src/mongo/db/pipeline/accumulator_test.cpp @@ -84,7 +84,7 @@ class None : public Base { public: void run() { createAccumulator(); - ASSERT_EQUALS(0, accumulator()->getValue(false).getDouble()); + ASSERT_EQUALS(Value(BSONNULL), accumulator()->getValue(false)); } }; |