diff options
Diffstat (limited to 'jstests/aggregation/bugs')
56 files changed, 3186 insertions, 3109 deletions
diff --git a/jstests/aggregation/bugs/cond.js b/jstests/aggregation/bugs/cond.js index 313316f4418..84831ca11a7 100644 --- a/jstests/aggregation/bugs/cond.js +++ b/jstests/aggregation/bugs/cond.js @@ -1,88 +1,87 @@ // $cond returns the evaluated second argument if the first evaluates to true but the evaluated // third argument if the first evaluates to false. (function() { - "use strict"; - load('jstests/aggregation/extras/utils.js'); +"use strict"; +load('jstests/aggregation/extras/utils.js'); - const coll = db.jstests_aggregation_cond; - coll.drop(); +const coll = db.jstests_aggregation_cond; +coll.drop(); - coll.save({}); +coll.save({}); - function assertError(expectedErrorCode, condSpec) { - assertErrorCode(coll, {$project: {a: {$cond: condSpec}}}, expectedErrorCode); - } +function assertError(expectedErrorCode, condSpec) { + assertErrorCode(coll, {$project: {a: {$cond: condSpec}}}, expectedErrorCode); +} - function assertResult(expectedResult, arg) { - assert.eq(expectedResult, coll.aggregate({$project: {a: {$cond: arg}}}).toArray()[0].a); - } +function assertResult(expectedResult, arg) { + assert.eq(expectedResult, coll.aggregate({$project: {a: {$cond: arg}}}).toArray()[0].a); +} - // Wrong number of args. - assertError(16020, []); - assertError(16020, [1]); - assertError(16020, [false]); - assertError(16020, [1, 1]); - assertError(16020, [1, 1, null, 1]); - assertError(16020, [1, 1, 1, undefined]); +// Wrong number of args. +assertError(16020, []); +assertError(16020, [1]); +assertError(16020, [false]); +assertError(16020, [1, 1]); +assertError(16020, [1, 1, null, 1]); +assertError(16020, [1, 1, 1, undefined]); - // Bad object cases. - assertError(17080, {"else": 1, then: 1}); - assertError(17081, {"if": 1, "else": 1}); - assertError(17082, {"if": 1, then: 1}); - assertError(17083, {asdf: 1, then: 1}); +// Bad object cases. +assertError(17080, {"else": 1, then: 1}); +assertError(17081, {"if": 1, "else": 1}); +assertError(17082, {"if": 1, then: 1}); +assertError(17083, {asdf: 1, then: 1}); - // Literal expressions. - assertResult(1, [true, 1, 2]); - assertResult(2, [false, 1, 2]); +// Literal expressions. +assertResult(1, [true, 1, 2]); +assertResult(2, [false, 1, 2]); - // Order independence for object case. - assertResult(1, {"if": true, "then": 1, "else": 2}); - assertResult(1, {"if": true, "else": 2, "then": 1}); - assertResult(1, {"then": 1, "if": true, "else": 2}); - assertResult(1, {"then": 1, "else": 2, "if": true}); - assertResult(1, {"else": 2, "then": 1, "if": true}); - assertResult(1, {"else": 2, "if": true, "then": 1}); +// Order independence for object case. +assertResult(1, {"if": true, "then": 1, "else": 2}); +assertResult(1, {"if": true, "else": 2, "then": 1}); +assertResult(1, {"then": 1, "if": true, "else": 2}); +assertResult(1, {"then": 1, "else": 2, "if": true}); +assertResult(1, {"else": 2, "then": 1, "if": true}); +assertResult(1, {"else": 2, "if": true, "then": 1}); - // Computed expressions. - assertResult(1, [{$and: []}, {$add: [1]}, {$add: [1, 1]}]); - assertResult(2, [{$or: []}, {$add: [1]}, {$add: [1, 1]}]); +// Computed expressions. +assertResult(1, [{$and: []}, {$add: [1]}, {$add: [1, 1]}]); +assertResult(2, [{$or: []}, {$add: [1]}, {$add: [1, 1]}]); - assert(coll.drop()); - assert.writeOK(coll.insert({t: true, f: false, x: 'foo', y: 'bar'})); +assert(coll.drop()); +assert.writeOK(coll.insert({t: true, f: false, x: 'foo', y: 'bar'})); - // Field path expressions. - assertResult('foo', ['$t', '$x', '$y']); - assertResult('bar', ['$f', '$x', '$y']); +// Field path expressions. +assertResult('foo', ['$t', '$x', '$y']); +assertResult('bar', ['$f', '$x', '$y']); - assert(coll.drop()); - assert.writeOK(coll.insert({})); +assert(coll.drop()); +assert.writeOK(coll.insert({})); - // Coerce to bool. - assertResult('a', [1, 'a', 'b']); - assertResult('a', ['', 'a', 'b']); - assertResult('b', [0, 'a', 'b']); +// Coerce to bool. +assertResult('a', [1, 'a', 'b']); +assertResult('a', ['', 'a', 'b']); +assertResult('b', [0, 'a', 'b']); - // Nested. - assert(coll.drop()); - assert.writeOK(coll.insert({noonSense: 'am', mealCombined: 'no'})); - assert.writeOK(coll.insert({noonSense: 'am', mealCombined: 'yes'})); - assert.writeOK(coll.insert({noonSense: 'pm', mealCombined: 'yes'})); - assert.writeOK(coll.insert({noonSense: 'pm', mealCombined: 'no'})); - assert.eq( - ['breakfast', 'brunch', 'dinner', 'linner'], - coll.aggregate([ - { - $project: { - meal: { - $cond: [ - {$eq: ['$noonSense', 'am']}, - {$cond: [{$eq: ['$mealCombined', 'yes']}, 'brunch', 'breakfast']}, - {$cond: [{$eq: ['$mealCombined', 'yes']}, 'linner', 'dinner']} - ] +// Nested. +assert(coll.drop()); +assert.writeOK(coll.insert({noonSense: 'am', mealCombined: 'no'})); +assert.writeOK(coll.insert({noonSense: 'am', mealCombined: 'yes'})); +assert.writeOK(coll.insert({noonSense: 'pm', mealCombined: 'yes'})); +assert.writeOK(coll.insert({noonSense: 'pm', mealCombined: 'no'})); +assert.eq(['breakfast', 'brunch', 'dinner', 'linner'], + coll.aggregate([ + { + $project: { + meal: { + $cond: [ + {$eq: ['$noonSense', 'am']}, + {$cond: [{$eq: ['$mealCombined', 'yes']}, 'brunch', 'breakfast']}, + {$cond: [{$eq: ['$mealCombined', 'yes']}, 'linner', 'dinner']} + ] + } } - } - }, - {$sort: {meal: 1}} - ]) - .map(doc => doc.meal)); + }, + {$sort: {meal: 1}} + ]) + .map(doc => doc.meal)); }()); diff --git a/jstests/aggregation/bugs/cursor_timeout.js b/jstests/aggregation/bugs/cursor_timeout.js index f579fba407d..21260074d26 100644 --- a/jstests/aggregation/bugs/cursor_timeout.js +++ b/jstests/aggregation/bugs/cursor_timeout.js @@ -7,83 +7,82 @@ * ] */ (function() { - 'use strict'; +'use strict'; - // Cursor timeout on mongod is handled by a single thread/timer that will sleep for - // "clientCursorMonitorFrequencySecs" and add the sleep value to each operation's duration when - // it wakes up, timing out those whose "now() - last accessed since" time exceeds. A cursor - // timeout of 2 seconds with a monitor frequency of 1 second means an effective timeout period - // of 1 to 2 seconds. - const cursorTimeoutMs = 2000; - const cursorMonitorFrequencySecs = 1; +// Cursor timeout on mongod is handled by a single thread/timer that will sleep for +// "clientCursorMonitorFrequencySecs" and add the sleep value to each operation's duration when +// it wakes up, timing out those whose "now() - last accessed since" time exceeds. A cursor +// timeout of 2 seconds with a monitor frequency of 1 second means an effective timeout period +// of 1 to 2 seconds. +const cursorTimeoutMs = 2000; +const cursorMonitorFrequencySecs = 1; - const options = { - setParameter: { - internalDocumentSourceCursorBatchSizeBytes: 1, - // We use the "cursorTimeoutMillis" server parameter to decrease how long it takes for a - // non-exhausted cursor to time out. We use the "clientCursorMonitorFrequencySecs" - // server parameter to make the ClientCursorMonitor that cleans up the timed out cursors - // run more often. The combination of these server parameters reduces the amount of time - // we need to wait within this test. - cursorTimeoutMillis: cursorTimeoutMs, - clientCursorMonitorFrequencySecs: cursorMonitorFrequencySecs, - } - }; - const conn = MongoRunner.runMongod(options); - assert.neq(null, conn, 'mongod was unable to start up with options: ' + tojson(options)); +const options = { + setParameter: { + internalDocumentSourceCursorBatchSizeBytes: 1, + // We use the "cursorTimeoutMillis" server parameter to decrease how long it takes for a + // non-exhausted cursor to time out. We use the "clientCursorMonitorFrequencySecs" + // server parameter to make the ClientCursorMonitor that cleans up the timed out cursors + // run more often. The combination of these server parameters reduces the amount of time + // we need to wait within this test. + cursorTimeoutMillis: cursorTimeoutMs, + clientCursorMonitorFrequencySecs: cursorMonitorFrequencySecs, + } +}; +const conn = MongoRunner.runMongod(options); +assert.neq(null, conn, 'mongod was unable to start up with options: ' + tojson(options)); - const testDB = conn.getDB('test'); +const testDB = conn.getDB('test'); - // We use a batch size of 2 to ensure that the mongo shell does not exhaust the cursor on its - // first batch. - const batchSize = 2; - const numMatches = 5; +// We use a batch size of 2 to ensure that the mongo shell does not exhaust the cursor on its +// first batch. +const batchSize = 2; +const numMatches = 5; - function assertCursorTimesOut(collName, pipeline) { - const res = assert.commandWorked(testDB.runCommand({ - aggregate: collName, - pipeline: pipeline, - cursor: { - batchSize: batchSize, - }, - })); +function assertCursorTimesOut(collName, pipeline) { + const res = assert.commandWorked(testDB.runCommand({ + aggregate: collName, + pipeline: pipeline, + cursor: { + batchSize: batchSize, + }, + })); - let serverStatus = assert.commandWorked(testDB.serverStatus()); - const expectedNumTimedOutCursors = serverStatus.metrics.cursor.timedOut + 1; + let serverStatus = assert.commandWorked(testDB.serverStatus()); + const expectedNumTimedOutCursors = serverStatus.metrics.cursor.timedOut + 1; - const cursor = new DBCommandCursor(testDB, res, batchSize); + const cursor = new DBCommandCursor(testDB, res, batchSize); - // Wait until the idle cursor background job has killed the aggregation cursor. - assert.soon( - function() { - serverStatus = assert.commandWorked(testDB.serverStatus()); - return +serverStatus.metrics.cursor.timedOut === expectedNumTimedOutCursors; - }, - function() { - return "aggregation cursor failed to time out: " + - tojson(serverStatus.metrics.cursor); - }); + // Wait until the idle cursor background job has killed the aggregation cursor. + assert.soon( + function() { + serverStatus = assert.commandWorked(testDB.serverStatus()); + return +serverStatus.metrics.cursor.timedOut === expectedNumTimedOutCursors; + }, + function() { + return "aggregation cursor failed to time out: " + tojson(serverStatus.metrics.cursor); + }); - assert.eq(0, serverStatus.metrics.cursor.open.total, tojson(serverStatus)); + assert.eq(0, serverStatus.metrics.cursor.open.total, tojson(serverStatus)); - // We attempt to exhaust the aggregation cursor to verify that sending a getMore returns an - // error due to the cursor being killed. - let err = assert.throws(function() { - cursor.itcount(); - }); - assert.eq(ErrorCodes.CursorNotFound, err.code, tojson(err)); - } + // We attempt to exhaust the aggregation cursor to verify that sending a getMore returns an + // error due to the cursor being killed. + let err = assert.throws(function() { + cursor.itcount(); + }); + assert.eq(ErrorCodes.CursorNotFound, err.code, tojson(err)); +} - assert.writeOK(testDB.source.insert({local: 1})); - for (let i = 0; i < numMatches; ++i) { - assert.writeOK(testDB.dest.insert({foreign: 1})); - } +assert.writeOK(testDB.source.insert({local: 1})); +for (let i = 0; i < numMatches; ++i) { + assert.writeOK(testDB.dest.insert({foreign: 1})); +} - // Test that a regular aggregation cursor is killed when the timeout is reached. - assertCursorTimesOut('dest', []); +// Test that a regular aggregation cursor is killed when the timeout is reached. +assertCursorTimesOut('dest', []); - // Test that an aggregation cursor with a $lookup stage is killed when the timeout is reached. - assertCursorTimesOut('source', [ +// Test that an aggregation cursor with a $lookup stage is killed when the timeout is reached. +assertCursorTimesOut('source', [ { $lookup: { from: 'dest', @@ -97,9 +96,9 @@ }, ]); - // Test that an aggregation cursor with nested $lookup stages is killed when the timeout is - // reached. - assertCursorTimesOut('source', [ +// Test that an aggregation cursor with nested $lookup stages is killed when the timeout is +// reached. +assertCursorTimesOut('source', [ { $lookup: { from: 'dest', @@ -126,5 +125,5 @@ }, ]); - MongoRunner.stopMongod(conn); +MongoRunner.stopMongod(conn); })(); diff --git a/jstests/aggregation/bugs/explain_options_helper.js b/jstests/aggregation/bugs/explain_options_helper.js index 17360acab73..0834d56e469 100644 --- a/jstests/aggregation/bugs/explain_options_helper.js +++ b/jstests/aggregation/bugs/explain_options_helper.js @@ -2,23 +2,25 @@ // This test was designed to reproduce SERVER-32300". (function() { - "use strict"; +"use strict"; - const coll = db.explain_options; - coll.drop(); +const coll = db.explain_options; +coll.drop(); - for (let i = 0; i < 10; ++i) { - assert.writeOK(coll.insert({_id: i})); - } +for (let i = 0; i < 10; ++i) { + assert.writeOK(coll.insert({_id: i})); +} - const collation = {collation: {locale: "zh", backwards: false}}; +const collation = { + collation: {locale: "zh", backwards: false} +}; - const firstResults = coll.aggregate([{$sort: {_id: 1}}], collation).toArray(); - // Issue an explain in order to verify that 'collation' is not modified to include the explain - // flag. - assert.commandWorked(coll.explain().aggregate([], collation)); +const firstResults = coll.aggregate([{$sort: {_id: 1}}], collation).toArray(); +// Issue an explain in order to verify that 'collation' is not modified to include the explain +// flag. +assert.commandWorked(coll.explain().aggregate([], collation)); - const secondResults = coll.aggregate([{$sort: {_id: 1}}], collation).toArray(); - // Assert that the result didn't change after an explain helper is issued. - assert.eq(firstResults, secondResults); +const secondResults = coll.aggregate([{$sort: {_id: 1}}], collation).toArray(); +// Assert that the result didn't change after an explain helper is issued. +assert.eq(firstResults, secondResults); }()); diff --git a/jstests/aggregation/bugs/firstlast.js b/jstests/aggregation/bugs/firstlast.js index aa360a25b7e..8ab83fe30b7 100644 --- a/jstests/aggregation/bugs/firstlast.js +++ b/jstests/aggregation/bugs/firstlast.js @@ -2,120 +2,119 @@ * Tests the $first and $last accumulators in $group. */ (function() { - 'use strict'; - const coll = db.jstests_aggregation_firstlast; - coll.drop(); - - /** Check expected $first and $last result values. */ - function assertFirstLast(expectedFirst, expectedLast, stages, expression) { - let pipeline = [{$sort: {_id: 1}}]; - if (stages) { - pipeline = pipeline.concat(stages); - } +'use strict'; +const coll = db.jstests_aggregation_firstlast; +coll.drop(); + +/** Check expected $first and $last result values. */ +function assertFirstLast(expectedFirst, expectedLast, stages, expression) { + let pipeline = [{$sort: {_id: 1}}]; + if (stages) { + pipeline = pipeline.concat(stages); + } - expression = expression || '$b'; - pipeline.push( - {$group: {_id: '$a', first: {$first: expression}, last: {$last: expression}}}); - - const result = coll.aggregate(pipeline).toArray(); - for (let i = 0; i < result.length; ++i) { - if (result[i]._id === 1) { - // Check results for group _id 1. - assert.eq(expectedFirst, result[i].first); - assert.eq(expectedLast, result[i].last); - return; - } + expression = expression || '$b'; + pipeline.push({$group: {_id: '$a', first: {$first: expression}, last: {$last: expression}}}); + + const result = coll.aggregate(pipeline).toArray(); + for (let i = 0; i < result.length; ++i) { + if (result[i]._id === 1) { + // Check results for group _id 1. + assert.eq(expectedFirst, result[i].first); + assert.eq(expectedLast, result[i].last); + return; } - throw new Error('Expected $group _id "1" is missing'); } - - // One document. - assert.writeOK(coll.insert({a: 1, b: 1})); - assertFirstLast(1, 1); - - // Two documents. - assert.writeOK(coll.insert({a: 1, b: 2})); - assertFirstLast(1, 2); - - // Three documents. - assert.writeOK(coll.insert({a: 1, b: 3})); - assertFirstLast(1, 3); - - // Another 'a' key value does not affect outcome. - assert(coll.drop()); - assert.writeOK(coll.insert({a: 3, b: 0})); - assert.writeOK(coll.insert({a: 1, b: 1})); - assert.writeOK(coll.insert({a: 1, b: 2})); - assert.writeOK(coll.insert({a: 1, b: 3})); - assert.writeOK(coll.insert({a: 2, b: 0})); - assertFirstLast(1, 3); - - // Additional pipeline stages do not affect outcome if order is maintained. - assertFirstLast(1, 3, [{$project: {x: '$a', y: '$b'}}, {$project: {a: '$x', b: '$y'}}]); - - // Additional pipeline stages affect outcome if order is modified. - assertFirstLast(3, 1, [{$sort: {b: -1}}]); - - // Skip and limit affect the results seen. - assert(coll.drop()); - assert.writeOK(coll.insert({a: 1, b: 1})); - assert.writeOK(coll.insert({a: 1, b: 2})); - assert.writeOK(coll.insert({a: 1, b: 3})); - assertFirstLast(1, 2, [{$limit: 2}]); - assertFirstLast(2, 3, [{$skip: 1}, {$limit: 2}]); - assertFirstLast(2, 2, [{$skip: 1}, {$limit: 1}]); - - // Mixed type values. - assert.writeOK(coll.insert({a: 1, b: 'foo'})); - assertFirstLast(1, 'foo'); - - assert(coll.drop()); - assert.writeOK(coll.insert({a: 1, b: 'bar'})); - assert.writeOK(coll.insert({a: 1, b: true})); - assertFirstLast('bar', true); - - // Value null. - assert(coll.drop()); - assert.writeOK(coll.insert({a: 1, b: null})); - assert.writeOK(coll.insert({a: 1, b: 2})); - assertFirstLast(null, 2); - - assert(coll.drop()); - assert.writeOK(coll.insert({a: 1, b: 2})); - assert.writeOK(coll.insert({a: 1, b: null})); - assertFirstLast(2, null); - - assert(coll.drop()); - assert.writeOK(coll.insert({a: 1, b: null})); - assert.writeOK(coll.insert({a: 1, b: null})); - assertFirstLast(null, null); - - // Value missing. - assert(coll.drop()); - assert.writeOK(coll.insert({a: 1})); - assert.writeOK(coll.insert({a: 1, b: 2})); - assertFirstLast(undefined, 2); - - assert(coll.drop()); - assert.writeOK(coll.insert({a: 1, b: 2})); - assert.writeOK(coll.insert({a: 1})); - assertFirstLast(2, undefined); - - assert(coll.drop()); - assert.writeOK(coll.insert({a: 1})); - assert.writeOK(coll.insert({a: 1})); - assertFirstLast(undefined, undefined); - - // Dotted field. - assert(coll.drop()); - assert.writeOK(coll.insert({a: 1, b: [{c: 1}, {c: 2}]})); - assert.writeOK(coll.insert({a: 1, b: [{c: 6}, {}]})); - assertFirstLast([1, 2], [6], [], '$b.c'); - - // Computed expressions. - assert(coll.drop()); - assert.writeOK(coll.insert({a: 1, b: 1})); - assert.writeOK(coll.insert({a: 1, b: 2})); - assertFirstLast(1, 0, [], {$mod: ['$b', 2]}); - assertFirstLast(0, 1, [], {$mod: [{$add: ['$b', 1]}, 2]}); + throw new Error('Expected $group _id "1" is missing'); +} + +// One document. +assert.writeOK(coll.insert({a: 1, b: 1})); +assertFirstLast(1, 1); + +// Two documents. +assert.writeOK(coll.insert({a: 1, b: 2})); +assertFirstLast(1, 2); + +// Three documents. +assert.writeOK(coll.insert({a: 1, b: 3})); +assertFirstLast(1, 3); + +// Another 'a' key value does not affect outcome. +assert(coll.drop()); +assert.writeOK(coll.insert({a: 3, b: 0})); +assert.writeOK(coll.insert({a: 1, b: 1})); +assert.writeOK(coll.insert({a: 1, b: 2})); +assert.writeOK(coll.insert({a: 1, b: 3})); +assert.writeOK(coll.insert({a: 2, b: 0})); +assertFirstLast(1, 3); + +// Additional pipeline stages do not affect outcome if order is maintained. +assertFirstLast(1, 3, [{$project: {x: '$a', y: '$b'}}, {$project: {a: '$x', b: '$y'}}]); + +// Additional pipeline stages affect outcome if order is modified. +assertFirstLast(3, 1, [{$sort: {b: -1}}]); + +// Skip and limit affect the results seen. +assert(coll.drop()); +assert.writeOK(coll.insert({a: 1, b: 1})); +assert.writeOK(coll.insert({a: 1, b: 2})); +assert.writeOK(coll.insert({a: 1, b: 3})); +assertFirstLast(1, 2, [{$limit: 2}]); +assertFirstLast(2, 3, [{$skip: 1}, {$limit: 2}]); +assertFirstLast(2, 2, [{$skip: 1}, {$limit: 1}]); + +// Mixed type values. +assert.writeOK(coll.insert({a: 1, b: 'foo'})); +assertFirstLast(1, 'foo'); + +assert(coll.drop()); +assert.writeOK(coll.insert({a: 1, b: 'bar'})); +assert.writeOK(coll.insert({a: 1, b: true})); +assertFirstLast('bar', true); + +// Value null. +assert(coll.drop()); +assert.writeOK(coll.insert({a: 1, b: null})); +assert.writeOK(coll.insert({a: 1, b: 2})); +assertFirstLast(null, 2); + +assert(coll.drop()); +assert.writeOK(coll.insert({a: 1, b: 2})); +assert.writeOK(coll.insert({a: 1, b: null})); +assertFirstLast(2, null); + +assert(coll.drop()); +assert.writeOK(coll.insert({a: 1, b: null})); +assert.writeOK(coll.insert({a: 1, b: null})); +assertFirstLast(null, null); + +// Value missing. +assert(coll.drop()); +assert.writeOK(coll.insert({a: 1})); +assert.writeOK(coll.insert({a: 1, b: 2})); +assertFirstLast(undefined, 2); + +assert(coll.drop()); +assert.writeOK(coll.insert({a: 1, b: 2})); +assert.writeOK(coll.insert({a: 1})); +assertFirstLast(2, undefined); + +assert(coll.drop()); +assert.writeOK(coll.insert({a: 1})); +assert.writeOK(coll.insert({a: 1})); +assertFirstLast(undefined, undefined); + +// Dotted field. +assert(coll.drop()); +assert.writeOK(coll.insert({a: 1, b: [{c: 1}, {c: 2}]})); +assert.writeOK(coll.insert({a: 1, b: [{c: 6}, {}]})); +assertFirstLast([1, 2], [6], [], '$b.c'); + +// Computed expressions. +assert(coll.drop()); +assert.writeOK(coll.insert({a: 1, b: 1})); +assert.writeOK(coll.insert({a: 1, b: 2})); +assertFirstLast(1, 0, [], {$mod: ['$b', 2]}); +assertFirstLast(0, 1, [], {$mod: [{$add: ['$b', 1]}, 2]}); }()); diff --git a/jstests/aggregation/bugs/groupMissing.js b/jstests/aggregation/bugs/groupMissing.js index c08e70185b1..5f734abbee5 100644 --- a/jstests/aggregation/bugs/groupMissing.js +++ b/jstests/aggregation/bugs/groupMissing.js @@ -8,68 +8,68 @@ load('jstests/aggregation/extras/utils.js'); // For resultsEq. (function() { - "use strict"; +"use strict"; - var coll = db.groupMissing; - coll.drop(); +var coll = db.groupMissing; +coll.drop(); - coll.insert({a: null}); - coll.insert({}); +coll.insert({a: null}); +coll.insert({}); - var res = coll.aggregate({$group: {_id: "$a"}}); - var arr = res.toArray(); - assert.eq(arr.length, 1); - assert.eq(arr[0]._id, null); +var res = coll.aggregate({$group: {_id: "$a"}}); +var arr = res.toArray(); +assert.eq(arr.length, 1); +assert.eq(arr[0]._id, null); - coll.createIndex({a: 1}); - res = coll.aggregate({$sort: {a: 1}}, {$group: {_id: "$a"}}); - arr = res.toArray(); - assert.eq(arr.length, 1); - assert.eq(arr[0]._id, null); +coll.createIndex({a: 1}); +res = coll.aggregate({$sort: {a: 1}}, {$group: {_id: "$a"}}); +arr = res.toArray(); +assert.eq(arr.length, 1); +assert.eq(arr[0]._id, null); - coll.drop(); +coll.drop(); - coll.insert({a: null}); - coll.insert({}); +coll.insert({a: null}); +coll.insert({}); - // Bug, see SERVER-21992. +// Bug, see SERVER-21992. +res = coll.aggregate({$group: {_id: {a: "$a"}}}); +assert(resultsEq(res.toArray(), [{_id: {a: null}}])); + +// Correct behavior after SERVER-21992 is fixed. +if (0) { res = coll.aggregate({$group: {_id: {a: "$a"}}}); - assert(resultsEq(res.toArray(), [{_id: {a: null}}])); + assert(resultsEq(res.toArray(), [{_id: {a: null}}, {_id: {a: {}}}])); +} - // Correct behavior after SERVER-21992 is fixed. - if (0) { - res = coll.aggregate({$group: {_id: {a: "$a"}}}); - assert(resultsEq(res.toArray(), [{_id: {a: null}}, {_id: {a: {}}}])); - } +// Bug, see SERVER-21992. +coll.createIndex({a: 1}); +res = coll.aggregate({$group: {_id: {a: "$a"}}}); +assert(resultsEq(res.toArray(), [{_id: {a: null}}])); - // Bug, see SERVER-21992. - coll.createIndex({a: 1}); +// Correct behavior after SERVER-21992 is fixed. +if (0) { res = coll.aggregate({$group: {_id: {a: "$a"}}}); - assert(resultsEq(res.toArray(), [{_id: {a: null}}])); + assert(resultsEq(res.toArray(), [{_id: {a: null}}, {_id: {a: {}}}])); +} - // Correct behavior after SERVER-21992 is fixed. - if (0) { - res = coll.aggregate({$group: {_id: {a: "$a"}}}); - assert(resultsEq(res.toArray(), [{_id: {a: null}}, {_id: {a: {}}}])); - } +coll.drop(); +coll.insert({a: null, b: 1}); +coll.insert({b: 1}); +coll.insert({a: null, b: 1}); - coll.drop(); - coll.insert({a: null, b: 1}); - coll.insert({b: 1}); - coll.insert({a: null, b: 1}); +res = coll.aggregate({$group: {_id: {a: "$a", b: "$b"}}}); +assert(resultsEq(res.toArray(), [{_id: {b: 1}}, {_id: {a: null, b: 1}}])); - res = coll.aggregate({$group: {_id: {a: "$a", b: "$b"}}}); - assert(resultsEq(res.toArray(), [{_id: {b: 1}}, {_id: {a: null, b: 1}}])); +// Bug, see SERVER-23229. +coll.createIndex({a: 1, b: 1}); +res = coll.aggregate({$sort: {a: 1, b: 1}}, {$group: {_id: {a: "$a", b: "$b"}}}); +assert(resultsEq(res.toArray(), [{_id: {a: null, b: 1}}])); - // Bug, see SERVER-23229. +// Correct behavior after SERVER-23229 is fixed. +if (0) { coll.createIndex({a: 1, b: 1}); res = coll.aggregate({$sort: {a: 1, b: 1}}, {$group: {_id: {a: "$a", b: "$b"}}}); - assert(resultsEq(res.toArray(), [{_id: {a: null, b: 1}}])); - - // Correct behavior after SERVER-23229 is fixed. - if (0) { - coll.createIndex({a: 1, b: 1}); - res = coll.aggregate({$sort: {a: 1, b: 1}}, {$group: {_id: {a: "$a", b: "$b"}}}); - assert(resultsEq(res.toArray(), [{_id: {b: 1}}, {_id: {a: null, b: 1}}])); - } + assert(resultsEq(res.toArray(), [{_id: {b: 1}}, {_id: {a: null, b: 1}}])); +} }()); diff --git a/jstests/aggregation/bugs/lookup_unwind_getmore.js b/jstests/aggregation/bugs/lookup_unwind_getmore.js index 3ba7dbf4007..67b970de820 100644 --- a/jstests/aggregation/bugs/lookup_unwind_getmore.js +++ b/jstests/aggregation/bugs/lookup_unwind_getmore.js @@ -8,45 +8,47 @@ * ] */ (function() { - 'use strict'; +'use strict'; - const options = {setParameter: 'internalDocumentSourceCursorBatchSizeBytes=1'}; - const conn = MongoRunner.runMongod(options); - assert.neq(null, conn, 'mongod was unable to start up with options: ' + tojson(options)); +const options = { + setParameter: 'internalDocumentSourceCursorBatchSizeBytes=1' +}; +const conn = MongoRunner.runMongod(options); +assert.neq(null, conn, 'mongod was unable to start up with options: ' + tojson(options)); - const testDB = conn.getDB('test'); +const testDB = conn.getDB('test'); - /** - * Executes an aggregrate with 'options.pipeline' and confirms that 'options.numResults' were - * returned. - */ - function runTest(options) { - // The batchSize must be smaller than the number of documents returned by the $lookup. This - // ensures that the mongo shell will issue a getMore when unwinding the $lookup results for - // the same document in the 'source' collection, under a different OperationContext. - const batchSize = 2; +/** + * Executes an aggregrate with 'options.pipeline' and confirms that 'options.numResults' were + * returned. + */ +function runTest(options) { + // The batchSize must be smaller than the number of documents returned by the $lookup. This + // ensures that the mongo shell will issue a getMore when unwinding the $lookup results for + // the same document in the 'source' collection, under a different OperationContext. + const batchSize = 2; - testDB.source.drop(); - assert.writeOK(testDB.source.insert({x: 1})); + testDB.source.drop(); + assert.writeOK(testDB.source.insert({x: 1})); - testDB.dest.drop(); - for (let i = 0; i < 5; ++i) { - assert.writeOK(testDB.dest.insert({x: 1})); - } + testDB.dest.drop(); + for (let i = 0; i < 5; ++i) { + assert.writeOK(testDB.dest.insert({x: 1})); + } - const res = assert.commandWorked(testDB.runCommand({ - aggregate: 'source', - pipeline: options.pipeline, - cursor: { - batchSize: batchSize, - }, - })); + const res = assert.commandWorked(testDB.runCommand({ + aggregate: 'source', + pipeline: options.pipeline, + cursor: { + batchSize: batchSize, + }, + })); - const cursor = new DBCommandCursor(testDB, res, batchSize); - assert.eq(options.numResults, cursor.itcount()); - } + const cursor = new DBCommandCursor(testDB, res, batchSize); + assert.eq(options.numResults, cursor.itcount()); +} - runTest({ +runTest({ pipeline: [ { $lookup: { @@ -65,7 +67,7 @@ numResults: 5 }); - runTest({ +runTest({ pipeline: [ { $lookup: { @@ -99,5 +101,5 @@ numResults: 25 }); - MongoRunner.stopMongod(conn); +MongoRunner.stopMongod(conn); })(); diff --git a/jstests/aggregation/bugs/lookup_unwind_killcursor.js b/jstests/aggregation/bugs/lookup_unwind_killcursor.js index 45da6350c2f..eab9d05c591 100644 --- a/jstests/aggregation/bugs/lookup_unwind_killcursor.js +++ b/jstests/aggregation/bugs/lookup_unwind_killcursor.js @@ -8,43 +8,45 @@ * ] */ (function() { - 'use strict'; +'use strict'; - const options = {setParameter: 'internalDocumentSourceCursorBatchSizeBytes=1'}; - const conn = MongoRunner.runMongod(options); - assert.neq(null, conn, 'mongod was unable to start up with options: ' + tojson(options)); +const options = { + setParameter: 'internalDocumentSourceCursorBatchSizeBytes=1' +}; +const conn = MongoRunner.runMongod(options); +assert.neq(null, conn, 'mongod was unable to start up with options: ' + tojson(options)); - const testDB = conn.getDB('test'); +const testDB = conn.getDB('test'); - function runTest(pipeline) { - // We use a batch size of 2 to ensure that the mongo shell does not exhaust the cursor on - // its first batch. - const batchSize = 2; +function runTest(pipeline) { + // We use a batch size of 2 to ensure that the mongo shell does not exhaust the cursor on + // its first batch. + const batchSize = 2; - testDB.source.drop(); - assert.writeOK(testDB.source.insert({x: 1})); + testDB.source.drop(); + assert.writeOK(testDB.source.insert({x: 1})); - testDB.dest.drop(); - for (let i = 0; i < 5; ++i) { - assert.writeOK(testDB.dest.insert({x: 1})); - } + testDB.dest.drop(); + for (let i = 0; i < 5; ++i) { + assert.writeOK(testDB.dest.insert({x: 1})); + } - const res = assert.commandWorked(testDB.runCommand({ - aggregate: 'source', - pipeline: pipeline, - cursor: { - batchSize: batchSize, - }, - })); + const res = assert.commandWorked(testDB.runCommand({ + aggregate: 'source', + pipeline: pipeline, + cursor: { + batchSize: batchSize, + }, + })); - const cursor = new DBCommandCursor(testDB, res, batchSize); - cursor.close(); // Closing the cursor will issue the "killCursors" command. + const cursor = new DBCommandCursor(testDB, res, batchSize); + cursor.close(); // Closing the cursor will issue the "killCursors" command. - const serverStatus = assert.commandWorked(testDB.adminCommand({serverStatus: 1})); - assert.eq(0, serverStatus.metrics.cursor.open.total, tojson(serverStatus.metrics.cursor)); - } + const serverStatus = assert.commandWorked(testDB.adminCommand({serverStatus: 1})); + assert.eq(0, serverStatus.metrics.cursor.open.total, tojson(serverStatus.metrics.cursor)); +} - runTest([ +runTest([ { $lookup: { from: 'dest', @@ -60,7 +62,7 @@ }, ]); - runTest([ +runTest([ { $lookup: { from: 'dest', @@ -91,5 +93,5 @@ }, ]); - MongoRunner.stopMongod(conn); +MongoRunner.stopMongod(conn); })(); diff --git a/jstests/aggregation/bugs/match.js b/jstests/aggregation/bugs/match.js index 8cb4519a861..6a545ed60c1 100644 --- a/jstests/aggregation/bugs/match.js +++ b/jstests/aggregation/bugs/match.js @@ -2,166 +2,169 @@ // - Filtering behavior equivalent to a mongo query. // - $where and geo operators are not allowed (function() { - "use strict"; - - load('jstests/aggregation/extras/utils.js'); - - const coll = db.jstests_aggregation_match; - coll.drop(); - - const identityProjection = {_id: '$_id', a: '$a'}; - - /** Assert that an aggregation generated the expected error. */ - function assertError(expectedCode, matchSpec) { - const matchStage = {$match: matchSpec}; - // Check where matching is folded in to DocumentSourceCursor. - assertErrorCode(coll, [matchStage], expectedCode); - // Check where matching is not folded in to DocumentSourceCursor. - assertErrorCode(coll, [{$project: identityProjection}, matchStage], expectedCode); +"use strict"; + +load('jstests/aggregation/extras/utils.js'); + +const coll = db.jstests_aggregation_match; +coll.drop(); + +const identityProjection = { + _id: '$_id', + a: '$a' +}; + +/** Assert that an aggregation generated the expected error. */ +function assertError(expectedCode, matchSpec) { + const matchStage = {$match: matchSpec}; + // Check where matching is folded in to DocumentSourceCursor. + assertErrorCode(coll, [matchStage], expectedCode); + // Check where matching is not folded in to DocumentSourceCursor. + assertErrorCode(coll, [{$project: identityProjection}, matchStage], expectedCode); +} + +/** Assert that the contents of two arrays are equal, ignoring element ordering. */ +function assertEqualResultsUnordered(one, two) { + let oneStr = one.map(function(x) { + return tojson(x); + }); + let twoStr = two.map(function(x) { + return tojson(x); + }); + oneStr.sort(); + twoStr.sort(); + assert.eq(oneStr, twoStr); +} + +/** Assert that an aggregation result is as expected. */ +function assertResults(expectedResults, matchSpec) { + const findResults = coll.find(matchSpec).toArray(); + if (expectedResults) { + assertEqualResultsUnordered(expectedResults, findResults); } - - /** Assert that the contents of two arrays are equal, ignoring element ordering. */ - function assertEqualResultsUnordered(one, two) { - let oneStr = one.map(function(x) { - return tojson(x); - }); - let twoStr = two.map(function(x) { - return tojson(x); - }); - oneStr.sort(); - twoStr.sort(); - assert.eq(oneStr, twoStr); - } - - /** Assert that an aggregation result is as expected. */ - function assertResults(expectedResults, matchSpec) { - const findResults = coll.find(matchSpec).toArray(); - if (expectedResults) { - assertEqualResultsUnordered(expectedResults, findResults); - } - const matchStage = {$match: matchSpec}; - // Check where matching is folded in to DocumentSourceCursor. - assertEqualResultsUnordered(findResults, coll.aggregate(matchStage).toArray()); - // Check where matching is not folded in to DocumentSourceCursor. - assertEqualResultsUnordered( - findResults, coll.aggregate({$project: identityProjection}, matchStage).toArray()); - } - - // Invalid matcher syntax. - assertError(2, {a: {$mod: [0 /* invalid */, 0]}}); - - // $where not allowed. - assertError(ErrorCodes.BadValue, {$where: 'true'}); - - // Geo not allowed. - assertError(ErrorCodes.BadValue, {$match: {a: {$near: [0, 0]}}}); - - function checkMatchResults(indexed) { - // No results. - coll.remove({}); - assertResults([], {}); - - assert.writeOK(coll.insert({_id: 0, a: 1})); - assert.writeOK(coll.insert({_id: 1, a: 2})); - assert.writeOK(coll.insert({_id: 2, a: 3})); - - // Empty query. - assertResults([{_id: 0, a: 1}, {_id: 1, a: 2}, {_id: 2, a: 3}], {}); - - // Simple queries. - assertResults([{_id: 0, a: 1}], {a: 1}); - assertResults([{_id: 1, a: 2}], {a: 2}); - assertResults([{_id: 1, a: 2}, {_id: 2, a: 3}], {a: {$gt: 1}}); - assertResults([{_id: 0, a: 1}, {_id: 1, a: 2}], {a: {$lte: 2}}); - assertResults([{_id: 0, a: 1}, {_id: 2, a: 3}], {a: {$in: [1, 3]}}); - - // Regular expression. - coll.remove({}); - assert.writeOK(coll.insert({_id: 0, a: 'x'})); - assert.writeOK(coll.insert({_id: 1, a: 'yx'})); - assertResults([{_id: 0, a: 'x'}], {a: /^x/}); - assertResults([{_id: 0, a: 'x'}, {_id: 1, a: 'yx'}], {a: /x/}); - - // Dotted field. - coll.remove({}); - assert.writeOK(coll.insert({_id: 0, a: {b: 4}})); - assert.writeOK(coll.insert({_id: 1, a: 2})); - assertResults([{_id: 0, a: {b: 4}}], {'a.b': 4}); - - // Value within an array. - coll.remove({}); - assert.writeOK(coll.insert({_id: 0, a: [1, 2, 3]})); - assert.writeOK(coll.insert({_id: 1, a: [2, 2, 3]})); - assert.writeOK(coll.insert({_id: 2, a: [2, 2, 2]})); - assertResults([{_id: 0, a: [1, 2, 3]}, {_id: 1, a: [2, 2, 3]}], {a: 3}); - - // Missing, null, $exists matching. - coll.remove({}); - assert.writeOK(coll.insert({_id: 0})); - assert.writeOK(coll.insert({_id: 1, a: null})); - assert.writeOK(coll.insert({_id: 3, a: 0})); - assertResults([{_id: 0}, {_id: 1, a: null}], {a: null}); - assertResults(null, {a: {$exists: true}}); - assertResults(null, {a: {$exists: false}}); - - // $elemMatch - coll.remove({}); - assert.writeOK(coll.insert({_id: 0, a: [1, 2]})); - assert.writeOK(coll.insert({_id: 1, a: [1, 2, 3]})); - assertResults([{_id: 1, a: [1, 2, 3]}], {a: {$elemMatch: {$gt: 1, $mod: [2, 1]}}}); - - coll.remove({}); - assert.writeOK(coll.insert({_id: 0, a: [{b: 1}, {c: 2}]})); - assert.writeOK(coll.insert({_id: 1, a: [{b: 1, c: 2}]})); - assertResults([{_id: 1, a: [{b: 1, c: 2}]}], {a: {$elemMatch: {b: 1, c: 2}}}); - - // $size - coll.remove({}); - assert.writeOK(coll.insert({})); - assert.writeOK(coll.insert({a: null})); - assert.writeOK(coll.insert({a: []})); - assert.writeOK(coll.insert({a: [1]})); - assert.writeOK(coll.insert({a: [1, 2]})); - assertResults(null, {a: {$size: 0}}); - assertResults(null, {a: {$size: 1}}); - assertResults(null, {a: {$size: 2}}); - - // $type - coll.remove({}); - assert.writeOK(coll.insert({})); - assert.writeOK(coll.insert({a: null})); - assert.writeOK(coll.insert({a: NumberInt(1)})); - assert.writeOK(coll.insert({a: NumberLong(2)})); - assert.writeOK(coll.insert({a: 66.6})); - assert.writeOK(coll.insert({a: 'abc'})); - assert.writeOK(coll.insert({a: /xyz/})); - assert.writeOK(coll.insert({a: {q: 1}})); - assert.writeOK(coll.insert({a: true})); - assert.writeOK(coll.insert({a: new Date()})); - assert.writeOK(coll.insert({a: new ObjectId()})); - for (let type = 1; type <= 18; ++type) { - assertResults(null, {a: {$type: type}}); - } - - coll.remove({}); - assert.writeOK(coll.insert({_id: 0, a: 1})); - assert.writeOK(coll.insert({_id: 1, a: 2})); - assert.writeOK(coll.insert({_id: 2, a: 3})); - - // $and - assertResults([{_id: 1, a: 2}], {$and: [{a: 2}, {_id: 1}]}); - assertResults([], {$and: [{a: 1}, {_id: 1}]}); - assertResults([{_id: 1, a: 2}, {_id: 2, a: 3}], - {$and: [{$or: [{_id: 1}, {a: 3}]}, {$or: [{_id: 2}, {a: 2}]}]}); - - // $or - assertResults([{_id: 0, a: 1}, {_id: 2, a: 3}], {$or: [{_id: 0}, {a: 3}]}); + const matchStage = {$match: matchSpec}; + // Check where matching is folded in to DocumentSourceCursor. + assertEqualResultsUnordered(findResults, coll.aggregate(matchStage).toArray()); + // Check where matching is not folded in to DocumentSourceCursor. + assertEqualResultsUnordered( + findResults, coll.aggregate({$project: identityProjection}, matchStage).toArray()); +} + +// Invalid matcher syntax. +assertError(2, {a: {$mod: [0 /* invalid */, 0]}}); + +// $where not allowed. +assertError(ErrorCodes.BadValue, {$where: 'true'}); + +// Geo not allowed. +assertError(ErrorCodes.BadValue, {$match: {a: {$near: [0, 0]}}}); + +function checkMatchResults(indexed) { + // No results. + coll.remove({}); + assertResults([], {}); + + assert.writeOK(coll.insert({_id: 0, a: 1})); + assert.writeOK(coll.insert({_id: 1, a: 2})); + assert.writeOK(coll.insert({_id: 2, a: 3})); + + // Empty query. + assertResults([{_id: 0, a: 1}, {_id: 1, a: 2}, {_id: 2, a: 3}], {}); + + // Simple queries. + assertResults([{_id: 0, a: 1}], {a: 1}); + assertResults([{_id: 1, a: 2}], {a: 2}); + assertResults([{_id: 1, a: 2}, {_id: 2, a: 3}], {a: {$gt: 1}}); + assertResults([{_id: 0, a: 1}, {_id: 1, a: 2}], {a: {$lte: 2}}); + assertResults([{_id: 0, a: 1}, {_id: 2, a: 3}], {a: {$in: [1, 3]}}); + + // Regular expression. + coll.remove({}); + assert.writeOK(coll.insert({_id: 0, a: 'x'})); + assert.writeOK(coll.insert({_id: 1, a: 'yx'})); + assertResults([{_id: 0, a: 'x'}], {a: /^x/}); + assertResults([{_id: 0, a: 'x'}, {_id: 1, a: 'yx'}], {a: /x/}); + + // Dotted field. + coll.remove({}); + assert.writeOK(coll.insert({_id: 0, a: {b: 4}})); + assert.writeOK(coll.insert({_id: 1, a: 2})); + assertResults([{_id: 0, a: {b: 4}}], {'a.b': 4}); + + // Value within an array. + coll.remove({}); + assert.writeOK(coll.insert({_id: 0, a: [1, 2, 3]})); + assert.writeOK(coll.insert({_id: 1, a: [2, 2, 3]})); + assert.writeOK(coll.insert({_id: 2, a: [2, 2, 2]})); + assertResults([{_id: 0, a: [1, 2, 3]}, {_id: 1, a: [2, 2, 3]}], {a: 3}); + + // Missing, null, $exists matching. + coll.remove({}); + assert.writeOK(coll.insert({_id: 0})); + assert.writeOK(coll.insert({_id: 1, a: null})); + assert.writeOK(coll.insert({_id: 3, a: 0})); + assertResults([{_id: 0}, {_id: 1, a: null}], {a: null}); + assertResults(null, {a: {$exists: true}}); + assertResults(null, {a: {$exists: false}}); + + // $elemMatch + coll.remove({}); + assert.writeOK(coll.insert({_id: 0, a: [1, 2]})); + assert.writeOK(coll.insert({_id: 1, a: [1, 2, 3]})); + assertResults([{_id: 1, a: [1, 2, 3]}], {a: {$elemMatch: {$gt: 1, $mod: [2, 1]}}}); + + coll.remove({}); + assert.writeOK(coll.insert({_id: 0, a: [{b: 1}, {c: 2}]})); + assert.writeOK(coll.insert({_id: 1, a: [{b: 1, c: 2}]})); + assertResults([{_id: 1, a: [{b: 1, c: 2}]}], {a: {$elemMatch: {b: 1, c: 2}}}); + + // $size + coll.remove({}); + assert.writeOK(coll.insert({})); + assert.writeOK(coll.insert({a: null})); + assert.writeOK(coll.insert({a: []})); + assert.writeOK(coll.insert({a: [1]})); + assert.writeOK(coll.insert({a: [1, 2]})); + assertResults(null, {a: {$size: 0}}); + assertResults(null, {a: {$size: 1}}); + assertResults(null, {a: {$size: 2}}); + + // $type + coll.remove({}); + assert.writeOK(coll.insert({})); + assert.writeOK(coll.insert({a: null})); + assert.writeOK(coll.insert({a: NumberInt(1)})); + assert.writeOK(coll.insert({a: NumberLong(2)})); + assert.writeOK(coll.insert({a: 66.6})); + assert.writeOK(coll.insert({a: 'abc'})); + assert.writeOK(coll.insert({a: /xyz/})); + assert.writeOK(coll.insert({a: {q: 1}})); + assert.writeOK(coll.insert({a: true})); + assert.writeOK(coll.insert({a: new Date()})); + assert.writeOK(coll.insert({a: new ObjectId()})); + for (let type = 1; type <= 18; ++type) { + assertResults(null, {a: {$type: type}}); } - checkMatchResults(false); - coll.createIndex({a: 1}); - checkMatchResults(true); - coll.createIndex({'a.b': 1}); - coll.createIndex({'a.c': 1}); - checkMatchResults(true); + coll.remove({}); + assert.writeOK(coll.insert({_id: 0, a: 1})); + assert.writeOK(coll.insert({_id: 1, a: 2})); + assert.writeOK(coll.insert({_id: 2, a: 3})); + + // $and + assertResults([{_id: 1, a: 2}], {$and: [{a: 2}, {_id: 1}]}); + assertResults([], {$and: [{a: 1}, {_id: 1}]}); + assertResults([{_id: 1, a: 2}, {_id: 2, a: 3}], + {$and: [{$or: [{_id: 1}, {a: 3}]}, {$or: [{_id: 2}, {a: 2}]}]}); + + // $or + assertResults([{_id: 0, a: 1}, {_id: 2, a: 3}], {$or: [{_id: 0}, {a: 3}]}); +} + +checkMatchResults(false); +coll.createIndex({a: 1}); +checkMatchResults(true); +coll.createIndex({'a.b': 1}); +coll.createIndex({'a.c': 1}); +checkMatchResults(true); })(); diff --git a/jstests/aggregation/bugs/match_swap_limit.js b/jstests/aggregation/bugs/match_swap_limit.js index 3de26d6f4b5..7dabc7130ca 100644 --- a/jstests/aggregation/bugs/match_swap_limit.js +++ b/jstests/aggregation/bugs/match_swap_limit.js @@ -2,19 +2,19 @@ * Ensure that $match is always applied after $limit. */ (function() { - "use strict"; +"use strict"; - let coll = db.jstests_match_swap_limit; - coll.drop(); +let coll = db.jstests_match_swap_limit; +coll.drop(); - assert.writeOK(coll.insert({_id: 0, x: 1, y: 3})); - assert.writeOK(coll.insert({_id: 1, x: 2, y: 2})); - assert.writeOK(coll.insert({_id: 2, x: 3, y: 1})); +assert.writeOK(coll.insert({_id: 0, x: 1, y: 3})); +assert.writeOK(coll.insert({_id: 1, x: 2, y: 2})); +assert.writeOK(coll.insert({_id: 2, x: 3, y: 1})); - assert.eq([{_id: 1, x: 2, y: 2}], - coll.aggregate([{$sort: {x: -1}}, {$limit: 2}, {$match: {y: {$gte: 2}}}]).toArray()); +assert.eq([{_id: 1, x: 2, y: 2}], + coll.aggregate([{$sort: {x: -1}}, {$limit: 2}, {$match: {y: {$gte: 2}}}]).toArray()); - assert.writeOK(coll.createIndex({x: 1})); - assert.eq([{_id: 1, x: 2, y: 2}], - coll.aggregate([{$sort: {x: -1}}, {$limit: 2}, {$match: {y: {$gte: 2}}}]).toArray()); +assert.writeOK(coll.createIndex({x: 1})); +assert.eq([{_id: 1, x: 2, y: 2}], + coll.aggregate([{$sort: {x: -1}}, {$limit: 2}, {$match: {y: {$gte: 2}}}]).toArray()); }()); diff --git a/jstests/aggregation/bugs/reverseArray.js b/jstests/aggregation/bugs/reverseArray.js index 0fa4010654b..cf80c040171 100644 --- a/jstests/aggregation/bugs/reverseArray.js +++ b/jstests/aggregation/bugs/reverseArray.js @@ -4,29 +4,29 @@ load("jstests/aggregation/extras/utils.js"); // For assertErrorCode. (function() { - "use strict"; +"use strict"; - var coll = db.reverseArray; - coll.drop(); +var coll = db.reverseArray; +coll.drop(); - // We need a document to flow through the pipeline, even though we don't care what fields it - // has. - coll.insert({}); +// We need a document to flow through the pipeline, even though we don't care what fields it +// has. +coll.insert({}); - assertErrorCode(coll, [{$project: {reversed: {$reverseArray: 1}}}], 34435); +assertErrorCode(coll, [{$project: {reversed: {$reverseArray: 1}}}], 34435); - var res = coll.aggregate([{$project: {reversed: {$reverseArray: {$literal: [1, 2]}}}}]); - var output = res.toArray(); - assert.eq(1, output.length); - assert.eq(output[0].reversed, [2, 1]); +var res = coll.aggregate([{$project: {reversed: {$reverseArray: {$literal: [1, 2]}}}}]); +var output = res.toArray(); +assert.eq(1, output.length); +assert.eq(output[0].reversed, [2, 1]); - var res = coll.aggregate([{$project: {reversed: {$reverseArray: {$literal: [[1, 2]]}}}}]); - var output = res.toArray(); - assert.eq(1, output.length); - assert.eq(output[0].reversed, [[1, 2]]); +var res = coll.aggregate([{$project: {reversed: {$reverseArray: {$literal: [[1, 2]]}}}}]); +var output = res.toArray(); +assert.eq(1, output.length); +assert.eq(output[0].reversed, [[1, 2]]); - var res = coll.aggregate([{$project: {reversed: {$reverseArray: "$notAField"}}}]); - var output = res.toArray(); - assert.eq(1, output.length); - assert.eq(output[0].reversed, null); +var res = coll.aggregate([{$project: {reversed: {$reverseArray: "$notAField"}}}]); +var output = res.toArray(); +assert.eq(1, output.length); +assert.eq(output[0].reversed, null); }()); diff --git a/jstests/aggregation/bugs/server10176.js b/jstests/aggregation/bugs/server10176.js index 988beb24f13..9283c819342 100644 --- a/jstests/aggregation/bugs/server10176.js +++ b/jstests/aggregation/bugs/server10176.js @@ -4,61 +4,61 @@ load('jstests/aggregation/extras/utils.js'); (function() { - var coll = db.abs_expr; - coll.drop(); +var coll = db.abs_expr; +coll.drop(); - // valid types (numeric and null) - assert.writeOK(coll.insert({_id: 0, a: 5})); - assert.writeOK(coll.insert({_id: 1, a: -5})); - assert.writeOK(coll.insert({_id: 2, a: 5.5})); - assert.writeOK(coll.insert({_id: 3, a: -5.5})); - assert.writeOK(coll.insert({_id: 4, a: NumberInt("5")})); - assert.writeOK(coll.insert({_id: 5, a: NumberInt("-5")})); - assert.writeOK(coll.insert({_id: 6, a: NumberLong("5")})); - assert.writeOK(coll.insert({_id: 7, a: NumberLong("-5")})); - assert.writeOK(coll.insert({_id: 8, a: 0.0})); - assert.writeOK(coll.insert({_id: 9, a: -0.0})); - assert.writeOK(coll.insert({_id: 10, a: NumberInt("0")})); - // INT_MIN is -(2 ^ 31) - assert.writeOK(coll.insert({_id: 11, a: NumberInt(-Math.pow(2, 31))})); - assert.writeOK(coll.insert({_id: 12, a: -Math.pow(2, 31)})); - // 1152921504606846977 is 2^60 + 1, an integer that can't be represented precisely as a double - assert.writeOK(coll.insert({_id: 13, a: NumberLong("1152921504606846977")})); - assert.writeOK(coll.insert({_id: 14, a: NumberLong("-1152921504606846977")})); - assert.writeOK(coll.insert({_id: 15, a: null})); - assert.writeOK(coll.insert({_id: 16, a: undefined})); - assert.writeOK(coll.insert({_id: 17, a: NaN})); - assert.writeOK(coll.insert({_id: 18})); +// valid types (numeric and null) +assert.writeOK(coll.insert({_id: 0, a: 5})); +assert.writeOK(coll.insert({_id: 1, a: -5})); +assert.writeOK(coll.insert({_id: 2, a: 5.5})); +assert.writeOK(coll.insert({_id: 3, a: -5.5})); +assert.writeOK(coll.insert({_id: 4, a: NumberInt("5")})); +assert.writeOK(coll.insert({_id: 5, a: NumberInt("-5")})); +assert.writeOK(coll.insert({_id: 6, a: NumberLong("5")})); +assert.writeOK(coll.insert({_id: 7, a: NumberLong("-5")})); +assert.writeOK(coll.insert({_id: 8, a: 0.0})); +assert.writeOK(coll.insert({_id: 9, a: -0.0})); +assert.writeOK(coll.insert({_id: 10, a: NumberInt("0")})); +// INT_MIN is -(2 ^ 31) +assert.writeOK(coll.insert({_id: 11, a: NumberInt(-Math.pow(2, 31))})); +assert.writeOK(coll.insert({_id: 12, a: -Math.pow(2, 31)})); +// 1152921504606846977 is 2^60 + 1, an integer that can't be represented precisely as a double +assert.writeOK(coll.insert({_id: 13, a: NumberLong("1152921504606846977")})); +assert.writeOK(coll.insert({_id: 14, a: NumberLong("-1152921504606846977")})); +assert.writeOK(coll.insert({_id: 15, a: null})); +assert.writeOK(coll.insert({_id: 16, a: undefined})); +assert.writeOK(coll.insert({_id: 17, a: NaN})); +assert.writeOK(coll.insert({_id: 18})); - // valid use of $abs: numbers become positive, null/undefined/nonexistent become null +// valid use of $abs: numbers become positive, null/undefined/nonexistent become null - var results = coll.aggregate([{$project: {a: {$abs: "$a"}}}, {$sort: {_id: 1}}]).toArray(); - assert.eq(results, [ - {_id: 0, a: 5}, - {_id: 1, a: 5}, - {_id: 2, a: 5.5}, - {_id: 3, a: 5.5}, - {_id: 4, a: 5}, - {_id: 5, a: 5}, - {_id: 6, a: NumberLong("5")}, - {_id: 7, a: NumberLong("5")}, - {_id: 8, a: 0}, - {_id: 9, a: 0}, - {_id: 10, a: 0}, - {_id: 11, a: NumberLong(Math.pow(2, 31))}, - {_id: 12, a: Math.pow(2, 31)}, - {_id: 13, a: NumberLong("1152921504606846977")}, - {_id: 14, a: NumberLong("1152921504606846977")}, - {_id: 15, a: null}, - {_id: 16, a: null}, - {_id: 17, a: NaN}, - {_id: 18, a: null}, - ]); - // Invalid +var results = coll.aggregate([{$project: {a: {$abs: "$a"}}}, {$sort: {_id: 1}}]).toArray(); +assert.eq(results, [ + {_id: 0, a: 5}, + {_id: 1, a: 5}, + {_id: 2, a: 5.5}, + {_id: 3, a: 5.5}, + {_id: 4, a: 5}, + {_id: 5, a: 5}, + {_id: 6, a: NumberLong("5")}, + {_id: 7, a: NumberLong("5")}, + {_id: 8, a: 0}, + {_id: 9, a: 0}, + {_id: 10, a: 0}, + {_id: 11, a: NumberLong(Math.pow(2, 31))}, + {_id: 12, a: Math.pow(2, 31)}, + {_id: 13, a: NumberLong("1152921504606846977")}, + {_id: 14, a: NumberLong("1152921504606846977")}, + {_id: 15, a: null}, + {_id: 16, a: null}, + {_id: 17, a: NaN}, + {_id: 18, a: null}, +]); +// Invalid - // using $abs on string - assertErrorCode(coll, [{$project: {a: {$abs: "string"}}}], 28765); +// using $abs on string +assertErrorCode(coll, [{$project: {a: {$abs: "string"}}}], 28765); - // using $abs on LLONG_MIN (-2 ^ 63) - assertErrorCode(coll, [{$project: {a: {$abs: NumberLong("-9223372036854775808")}}}], 28680); +// using $abs on LLONG_MIN (-2 ^ 63) +assertErrorCode(coll, [{$project: {a: {$abs: NumberLong("-9223372036854775808")}}}], 28680); }()); diff --git a/jstests/aggregation/bugs/server11118.js b/jstests/aggregation/bugs/server11118.js index 27b3fa7597e..46e79c3a7cc 100644 --- a/jstests/aggregation/bugs/server11118.js +++ b/jstests/aggregation/bugs/server11118.js @@ -1,154 +1,153 @@ // SERVER-11118 Tests for $dateToString (function() { - "use strict"; - - load('jstests/aggregation/extras/utils.js'); - - const coll = db.server11118; - - // Used to verify expected output format - function testFormat(date, formatStr, expectedStr) { - coll.drop(); - assert.writeOK(coll.insert({date: date})); - - const res = - coll.aggregate([{ - $project: - {_id: 0, formatted: {$dateToString: {format: formatStr, date: "$date"}}} - }]) - .toArray(); - - assert.eq(res[0].formatted, expectedStr); - } - - // Used to verify that server recognizes bad formats - function testFormatError(formatObj, errCode) { - coll.drop(); - assert.writeOK(coll.insert({date: ISODate()})); - - assertErrorCode(coll, {$project: {_id: 0, formatted: {$dateToString: formatObj}}}, errCode); - } - - // Used to verify that only date values are accepted for date parameter - function testDateValueError(dateVal, errCode) { - coll.drop(); - assert.writeOK(coll.insert({date: dateVal})); - - assertErrorCode( - coll, {$project: {formatted: {$dateToString: {format: "%Y", date: "$date"}}}}, errCode); - } - - const now = ISODate(); - - // Use all modifiers we can test with js provided function - testFormat(now, "%%-%Y-%m-%d-%H-%M-%S-%L", [ - "%", - now.getUTCFullYear().zeroPad(4), - (now.getUTCMonth() + 1).zeroPad(2), - now.getUTCDate().zeroPad(2), - now.getUTCHours().zeroPad(2), - now.getUTCMinutes().zeroPad(2), - now.getUTCSeconds().zeroPad(2), - now.getUTCMilliseconds().zeroPad(3) - ].join("-")); - - // Padding tests - const padme = ISODate("2001-02-03T04:05:06.007Z"); - - testFormat(padme, "%%", "%"); - testFormat(padme, "%Y", padme.getUTCFullYear().zeroPad(4)); - testFormat(padme, "%m", (padme.getUTCMonth() + 1).zeroPad(2)); - testFormat(padme, "%d", padme.getUTCDate().zeroPad(2)); - testFormat(padme, "%H", padme.getUTCHours().zeroPad(2)); - testFormat(padme, "%M", padme.getUTCMinutes().zeroPad(2)); - testFormat(padme, "%S", padme.getUTCSeconds().zeroPad(2)); - testFormat(padme, "%L", padme.getUTCMilliseconds().zeroPad(3)); - - // no space and multiple characters between modifiers - testFormat(now, "%d%d***%d***%d**%d*%d", [ - now.getUTCDate().zeroPad(2), - now.getUTCDate().zeroPad(2), - "***", - now.getUTCDate().zeroPad(2), - "***", - now.getUTCDate().zeroPad(2), - "**", - now.getUTCDate().zeroPad(2), - "*", - now.getUTCDate().zeroPad(2) - ].join("")); - - // JS doesn't have equivalents of these format specifiers - testFormat(ISODate('1999-01-02 03:04:05.006Z'), "%U-%w-%j", "00-7-002"); - - // Missing date - testFormatError({format: "%Y"}, 18628); - - // Extra field - testFormatError({format: "%Y", date: "$date", extra: "whyamIhere"}, 18534); - - // Not an object - testFormatError(["%Y", "$date"], 18629); - - // Use invalid modifier at middle of string - testFormatError({format: "%Y-%q", date: "$date"}, 18536); - - // Odd number of percent signs at end - testFormatError({format: "%U-%w-%j-%%%", date: "$date"}, 18535); - - // Odd number of percent signs at middle - // will get interpreted as an invalid modifier since it will try to use '%A' - testFormatError({format: "AAAAA%%%AAAAAA", date: "$date"}, 18536); - - // Format parameter not a string - testFormatError({format: {iamalion: "roar"}, date: "$date"}, 18533); - - /// - /// Additional Tests - /// - - // Test document - const date = ISODate("1999-08-29"); - - testFormat(date, "%%d", "%d"); - - // A very long string of "%"s - const longstr = Array(1000).join("%%"); - const halfstr = Array(1000).join("%"); - testFormat(date, longstr, halfstr); - - // Dates as null (should return a null) - testFormat(null, "%Y", null); - - /// - /// Using non-date fields as date parameter *should fail* - /// - - // Array - testDateValueError([], 16006); - testDateValueError([1, 2, 3], 16006); - - // Sub-object - testDateValueError({}, 16006); - testDateValueError({a: 1}, 16006); - - // String - testDateValueError("blahblahblah", 16006); - - // Integer - testDateValueError(1234, 16006); - - /// - /// Using non-string fields as format strings - /// - - // Array - testFormatError({format: [], date: "$date"}, 18533); - testFormatError({format: [1, 2, 3], date: "$date"}, 18533); - - // Integer - testFormatError({format: 1, date: "$date"}, 18533); +"use strict"; + +load('jstests/aggregation/extras/utils.js'); + +const coll = db.server11118; + +// Used to verify expected output format +function testFormat(date, formatStr, expectedStr) { + coll.drop(); + assert.writeOK(coll.insert({date: date})); + + const res = + coll.aggregate([ + {$project: {_id: 0, formatted: {$dateToString: {format: formatStr, date: "$date"}}}} + ]) + .toArray(); + + assert.eq(res[0].formatted, expectedStr); +} + +// Used to verify that server recognizes bad formats +function testFormatError(formatObj, errCode) { + coll.drop(); + assert.writeOK(coll.insert({date: ISODate()})); + + assertErrorCode(coll, {$project: {_id: 0, formatted: {$dateToString: formatObj}}}, errCode); +} + +// Used to verify that only date values are accepted for date parameter +function testDateValueError(dateVal, errCode) { + coll.drop(); + assert.writeOK(coll.insert({date: dateVal})); + + assertErrorCode( + coll, {$project: {formatted: {$dateToString: {format: "%Y", date: "$date"}}}}, errCode); +} + +const now = ISODate(); + +// Use all modifiers we can test with js provided function +testFormat(now, "%%-%Y-%m-%d-%H-%M-%S-%L", [ + "%", + now.getUTCFullYear().zeroPad(4), + (now.getUTCMonth() + 1).zeroPad(2), + now.getUTCDate().zeroPad(2), + now.getUTCHours().zeroPad(2), + now.getUTCMinutes().zeroPad(2), + now.getUTCSeconds().zeroPad(2), + now.getUTCMilliseconds().zeroPad(3) +].join("-")); + +// Padding tests +const padme = ISODate("2001-02-03T04:05:06.007Z"); + +testFormat(padme, "%%", "%"); +testFormat(padme, "%Y", padme.getUTCFullYear().zeroPad(4)); +testFormat(padme, "%m", (padme.getUTCMonth() + 1).zeroPad(2)); +testFormat(padme, "%d", padme.getUTCDate().zeroPad(2)); +testFormat(padme, "%H", padme.getUTCHours().zeroPad(2)); +testFormat(padme, "%M", padme.getUTCMinutes().zeroPad(2)); +testFormat(padme, "%S", padme.getUTCSeconds().zeroPad(2)); +testFormat(padme, "%L", padme.getUTCMilliseconds().zeroPad(3)); + +// no space and multiple characters between modifiers +testFormat(now, "%d%d***%d***%d**%d*%d", [ + now.getUTCDate().zeroPad(2), + now.getUTCDate().zeroPad(2), + "***", + now.getUTCDate().zeroPad(2), + "***", + now.getUTCDate().zeroPad(2), + "**", + now.getUTCDate().zeroPad(2), + "*", + now.getUTCDate().zeroPad(2) +].join("")); + +// JS doesn't have equivalents of these format specifiers +testFormat(ISODate('1999-01-02 03:04:05.006Z'), "%U-%w-%j", "00-7-002"); + +// Missing date +testFormatError({format: "%Y"}, 18628); + +// Extra field +testFormatError({format: "%Y", date: "$date", extra: "whyamIhere"}, 18534); + +// Not an object +testFormatError(["%Y", "$date"], 18629); + +// Use invalid modifier at middle of string +testFormatError({format: "%Y-%q", date: "$date"}, 18536); + +// Odd number of percent signs at end +testFormatError({format: "%U-%w-%j-%%%", date: "$date"}, 18535); + +// Odd number of percent signs at middle +// will get interpreted as an invalid modifier since it will try to use '%A' +testFormatError({format: "AAAAA%%%AAAAAA", date: "$date"}, 18536); + +// Format parameter not a string +testFormatError({format: {iamalion: "roar"}, date: "$date"}, 18533); + +/// +/// Additional Tests +/// + +// Test document +const date = ISODate("1999-08-29"); + +testFormat(date, "%%d", "%d"); + +// A very long string of "%"s +const longstr = Array(1000).join("%%"); +const halfstr = Array(1000).join("%"); +testFormat(date, longstr, halfstr); + +// Dates as null (should return a null) +testFormat(null, "%Y", null); + +/// +/// Using non-date fields as date parameter *should fail* +/// + +// Array +testDateValueError([], 16006); +testDateValueError([1, 2, 3], 16006); + +// Sub-object +testDateValueError({}, 16006); +testDateValueError({a: 1}, 16006); + +// String +testDateValueError("blahblahblah", 16006); + +// Integer +testDateValueError(1234, 16006); + +/// +/// Using non-string fields as format strings +/// + +// Array +testFormatError({format: [], date: "$date"}, 18533); +testFormatError({format: [1, 2, 3], date: "$date"}, 18533); + +// Integer +testFormatError({format: 1, date: "$date"}, 18533); - // Date - testFormatError({format: ISODate(), date: "$date"}, 18533); +// Date +testFormatError({format: ISODate(), date: "$date"}, 18533); })(); diff --git a/jstests/aggregation/bugs/server11675.js b/jstests/aggregation/bugs/server11675.js index 759b4393b30..2d02a1ff53e 100644 --- a/jstests/aggregation/bugs/server11675.js +++ b/jstests/aggregation/bugs/server11675.js @@ -1,227 +1,224 @@ // SERVER-11675 Text search integration with aggregation (function() { - load('jstests/aggregation/extras/utils.js'); // For 'assertErrorCode'. - load('jstests/libs/fixture_helpers.js'); // For 'FixtureHelpers' - - const coll = db.server11675; - coll.drop(); - - assert.writeOK(coll.insert({_id: 1, text: "apple", words: 1})); - assert.writeOK(coll.insert({_id: 2, text: "banana", words: 1})); - assert.writeOK(coll.insert({_id: 3, text: "apple banana", words: 2})); - assert.writeOK(coll.insert({_id: 4, text: "cantaloupe", words: 1})); - - assert.commandWorked(coll.createIndex({text: "text"})); - - // query should have subfields query, project, sort, skip and limit. All but query are optional. - const assertSameAsFind = function(query) { - let cursor = coll.find(query.query); - const pipeline = [{$match: query.query}]; - - if ('project' in query) { - cursor = coll.find(query.query, query.project); // no way to add to constructed cursor - pipeline.push({$project: query.project}); - } - - if ('sort' in query) { - cursor = cursor.sort(query.sort); - pipeline.push({$sort: query.sort}); - } - - if ('skip' in query) { - cursor = cursor.skip(query.skip); - pipeline.push({$skip: query.skip}); - } - - if ('limit' in query) { - cursor = cursor.limit(query.limit); - pipeline.push({$limit: query.limit}); - } - - const findRes = cursor.toArray(); - const aggRes = coll.aggregate(pipeline).toArray(); - - // If the query doesn't specify its own sort, there is a possibility that find() and - // aggregate() will return the same results in different orders. We sort by _id on the - // client side, so that the results still count as equal. - if (!query.hasOwnProperty("sort")) { - findRes.sort(function(a, b) { - return a._id - b._id; - }); - aggRes.sort(function(a, b) { - return a._id - b._id; - }); - } - - assert.docEq(aggRes, findRes); - }; - - assertSameAsFind({query: {}}); // sanity check - assertSameAsFind({query: {$text: {$search: "apple"}}}); - assertSameAsFind({query: {_id: 1, $text: {$search: "apple"}}}); - assertSameAsFind( - {query: {$text: {$search: "apple"}}, project: {_id: 1, score: {$meta: "textScore"}}}); - assertSameAsFind({ - query: {$text: {$search: "apple banana"}}, - project: {_id: 1, score: {$meta: "textScore"}} - }); - assertSameAsFind({ - query: {$text: {$search: "apple banana"}}, - project: {_id: 1, score: {$meta: "textScore"}}, - sort: {score: {$meta: "textScore"}} - }); - assertSameAsFind({ - query: {$text: {$search: "apple banana"}}, - project: {_id: 1, score: {$meta: "textScore"}}, - sort: {score: {$meta: "textScore"}}, - limit: 1 - }); - assertSameAsFind({ - query: {$text: {$search: "apple banana"}}, - project: {_id: 1, score: {$meta: "textScore"}}, - sort: {score: {$meta: "textScore"}}, - skip: 1 - }); - assertSameAsFind({ - query: {$text: {$search: "apple banana"}}, - project: {_id: 1, score: {$meta: "textScore"}}, - sort: {score: {$meta: "textScore"}}, - skip: 1, - limit: 1 - }); - - // $meta sort specification should be rejected if it has additional keys. - assert.throws(function() { - coll.aggregate([ - {$match: {$text: {$search: 'apple banana'}}}, - {$sort: {textScore: {$meta: 'textScore', extra: 1}}} - ]) - .itcount(); - }); - - // $meta sort specification should be rejected if the type of meta sort is not known. - assert.throws(function() { - coll.aggregate([ - {$match: {$text: {$search: 'apple banana'}}}, - {$sort: {textScore: {$meta: 'unknown'}}} - ]) - .itcount(); - }); - - // Sort specification should be rejected if a $-keyword other than $meta is used. - assert.throws(function() { - coll.aggregate([ - {$match: {$text: {$search: 'apple banana'}}}, - {$sort: {textScore: {$notMeta: 'textScore'}}} - ]) - .itcount(); - }); - - // Sort specification should be rejected if it is a string, not an object with $meta. - assert.throws(function() { - coll.aggregate( - [{$match: {$text: {$search: 'apple banana'}}}, {$sort: {textScore: 'textScore'}}]) - .itcount(); - }); - - // sharded find requires projecting the score to sort, but sharded agg does not. - var findRes = coll.find({$text: {$search: "apple banana"}}, {textScore: {$meta: 'textScore'}}) - .sort({textScore: {$meta: 'textScore'}}) - .map(function(obj) { - delete obj.textScore; // remove it to match agg output - return obj; - }); - let res = coll.aggregate([ - {$match: {$text: {$search: 'apple banana'}}}, - {$sort: {textScore: {$meta: 'textScore'}}} - ]) - .toArray(); - assert.eq(res, findRes); - - // Make sure {$meta: 'textScore'} can be used as a sub-expression - res = coll.aggregate([ - {$match: {_id: 1, $text: {$search: 'apple'}}}, - { - $project: { - words: 1, - score: {$meta: 'textScore'}, - wordsTimesScore: {$multiply: ['$words', {$meta: 'textScore'}]} - } - } - ]) - .toArray(); - assert.eq(res[0].wordsTimesScore, res[0].words * res[0].score, tojson(res)); - - // And can be used in $group - res = coll.aggregate([ - {$match: {_id: 1, $text: {$search: 'apple banana'}}}, - {$group: {_id: {$meta: 'textScore'}, score: {$first: {$meta: 'textScore'}}}} - ]) - .toArray(); - assert.eq(res[0]._id, res[0].score, tojson(res)); - - // Make sure metadata crosses shard -> merger boundary - res = coll.aggregate([ - {$match: {_id: 1, $text: {$search: 'apple'}}}, - {$project: {scoreOnShard: {$meta: 'textScore'}}}, - {$limit: 1}, // force a split. later stages run on merger - {$project: {scoreOnShard: 1, scoreOnMerger: {$meta: 'textScore'}}} - ]) - .toArray(); - assert.eq(res[0].scoreOnMerger, res[0].scoreOnShard); - let score = res[0].scoreOnMerger; // save for later tests - - // Make sure metadata crosses shard -> merger boundary even if not used on shard - res = coll.aggregate([ - {$match: {_id: 1, $text: {$search: 'apple'}}}, - {$limit: 1}, // force a split. later stages run on merger - {$project: {scoreOnShard: 1, scoreOnMerger: {$meta: 'textScore'}}} +load('jstests/aggregation/extras/utils.js'); // For 'assertErrorCode'. +load('jstests/libs/fixture_helpers.js'); // For 'FixtureHelpers' + +const coll = db.server11675; +coll.drop(); + +assert.writeOK(coll.insert({_id: 1, text: "apple", words: 1})); +assert.writeOK(coll.insert({_id: 2, text: "banana", words: 1})); +assert.writeOK(coll.insert({_id: 3, text: "apple banana", words: 2})); +assert.writeOK(coll.insert({_id: 4, text: "cantaloupe", words: 1})); + +assert.commandWorked(coll.createIndex({text: "text"})); + +// query should have subfields query, project, sort, skip and limit. All but query are optional. +const assertSameAsFind = function(query) { + let cursor = coll.find(query.query); + const pipeline = [{$match: query.query}]; + + if ('project' in query) { + cursor = coll.find(query.query, query.project); // no way to add to constructed cursor + pipeline.push({$project: query.project}); + } + + if ('sort' in query) { + cursor = cursor.sort(query.sort); + pipeline.push({$sort: query.sort}); + } + + if ('skip' in query) { + cursor = cursor.skip(query.skip); + pipeline.push({$skip: query.skip}); + } + + if ('limit' in query) { + cursor = cursor.limit(query.limit); + pipeline.push({$limit: query.limit}); + } + + const findRes = cursor.toArray(); + const aggRes = coll.aggregate(pipeline).toArray(); + + // If the query doesn't specify its own sort, there is a possibility that find() and + // aggregate() will return the same results in different orders. We sort by _id on the + // client side, so that the results still count as equal. + if (!query.hasOwnProperty("sort")) { + findRes.sort(function(a, b) { + return a._id - b._id; + }); + aggRes.sort(function(a, b) { + return a._id - b._id; + }); + } + + assert.docEq(aggRes, findRes); +}; + +assertSameAsFind({query: {}}); // sanity check +assertSameAsFind({query: {$text: {$search: "apple"}}}); +assertSameAsFind({query: {_id: 1, $text: {$search: "apple"}}}); +assertSameAsFind( + {query: {$text: {$search: "apple"}}, project: {_id: 1, score: {$meta: "textScore"}}}); +assertSameAsFind( + {query: {$text: {$search: "apple banana"}}, project: {_id: 1, score: {$meta: "textScore"}}}); +assertSameAsFind({ + query: {$text: {$search: "apple banana"}}, + project: {_id: 1, score: {$meta: "textScore"}}, + sort: {score: {$meta: "textScore"}} +}); +assertSameAsFind({ + query: {$text: {$search: "apple banana"}}, + project: {_id: 1, score: {$meta: "textScore"}}, + sort: {score: {$meta: "textScore"}}, + limit: 1 +}); +assertSameAsFind({ + query: {$text: {$search: "apple banana"}}, + project: {_id: 1, score: {$meta: "textScore"}}, + sort: {score: {$meta: "textScore"}}, + skip: 1 +}); +assertSameAsFind({ + query: {$text: {$search: "apple banana"}}, + project: {_id: 1, score: {$meta: "textScore"}}, + sort: {score: {$meta: "textScore"}}, + skip: 1, + limit: 1 +}); + +// $meta sort specification should be rejected if it has additional keys. +assert.throws(function() { + coll.aggregate([ + {$match: {$text: {$search: 'apple banana'}}}, + {$sort: {textScore: {$meta: 'textScore', extra: 1}}} + ]) + .itcount(); +}); + +// $meta sort specification should be rejected if the type of meta sort is not known. +assert.throws(function() { + coll.aggregate([ + {$match: {$text: {$search: 'apple banana'}}}, + {$sort: {textScore: {$meta: 'unknown'}}} + ]) + .itcount(); +}); + +// Sort specification should be rejected if a $-keyword other than $meta is used. +assert.throws(function() { + coll.aggregate([ + {$match: {$text: {$search: 'apple banana'}}}, + {$sort: {textScore: {$notMeta: 'textScore'}}} + ]) + .itcount(); +}); + +// Sort specification should be rejected if it is a string, not an object with $meta. +assert.throws(function() { + coll.aggregate( + [{$match: {$text: {$search: 'apple banana'}}}, {$sort: {textScore: 'textScore'}}]) + .itcount(); +}); + +// sharded find requires projecting the score to sort, but sharded agg does not. +var findRes = coll.find({$text: {$search: "apple banana"}}, {textScore: {$meta: 'textScore'}}) + .sort({textScore: {$meta: 'textScore'}}) + .map(function(obj) { + delete obj.textScore; // remove it to match agg output + return obj; + }); +let res = coll.aggregate([ + {$match: {$text: {$search: 'apple banana'}}}, + {$sort: {textScore: {$meta: 'textScore'}}} ]) .toArray(); - assert.eq(res[0].scoreOnMerger, score); - - // Make sure metadata works if first $project doesn't use it. - res = coll.aggregate([ - {$match: {_id: 1, $text: {$search: 'apple'}}}, - {$project: {_id: 1}}, - {$project: {_id: 1, score: {$meta: 'textScore'}}} - ]) - .toArray(); - assert.eq(res[0].score, score); - - // Make sure the pipeline fails if it tries to reference the text score and it doesn't exist. - res = coll.runCommand( - {aggregate: coll.getName(), pipeline: [{$project: {_id: 1, score: {$meta: 'textScore'}}}]}); - assert.commandFailed(res); - - // Make sure the metadata is 'missing()' when it doesn't exist because the document changed - res = coll.aggregate([ - {$match: {_id: 1, $text: {$search: 'apple banana'}}}, - {$group: {_id: 1, score: {$first: {$meta: 'textScore'}}}}, - {$project: {_id: 1, scoreAgain: {$meta: 'textScore'}}}, - ]) - .toArray(); - assert(!("scoreAgain" in res[0])); - - // Make sure metadata works after a $unwind - assert.writeOK(coll.insert({_id: 5, text: 'mango', words: [1, 2, 3]})); - res = coll.aggregate([ - {$match: {$text: {$search: 'mango'}}}, - {$project: {score: {$meta: "textScore"}, _id: 1, words: 1}}, - {$unwind: '$words'}, - {$project: {scoreAgain: {$meta: "textScore"}, score: 1}} - ]) - .toArray(); - assert.eq(res[0].scoreAgain, res[0].score); - - // Error checking - // $match, but wrong position - assertErrorCode( - coll, [{$sort: {text: 1}}, {$match: {$text: {$search: 'apple banana'}}}], 17313); - - // wrong $stage, but correct position - assertErrorCode(coll, - [{$project: {searchValue: {$text: {$search: 'apple banana'}}}}], - ErrorCodes.InvalidPipelineOperator); - assertErrorCode(coll, [{$sort: {$text: {$search: 'apple banana'}}}], 17312); +assert.eq(res, findRes); + +// Make sure {$meta: 'textScore'} can be used as a sub-expression +res = coll.aggregate([ + {$match: {_id: 1, $text: {$search: 'apple'}}}, + { + $project: { + words: 1, + score: {$meta: 'textScore'}, + wordsTimesScore: {$multiply: ['$words', {$meta: 'textScore'}]} + } + } + ]) + .toArray(); +assert.eq(res[0].wordsTimesScore, res[0].words * res[0].score, tojson(res)); + +// And can be used in $group +res = coll.aggregate([ + {$match: {_id: 1, $text: {$search: 'apple banana'}}}, + {$group: {_id: {$meta: 'textScore'}, score: {$first: {$meta: 'textScore'}}}} + ]) + .toArray(); +assert.eq(res[0]._id, res[0].score, tojson(res)); + +// Make sure metadata crosses shard -> merger boundary +res = coll.aggregate([ + {$match: {_id: 1, $text: {$search: 'apple'}}}, + {$project: {scoreOnShard: {$meta: 'textScore'}}}, + {$limit: 1}, // force a split. later stages run on merger + {$project: {scoreOnShard: 1, scoreOnMerger: {$meta: 'textScore'}}} + ]) + .toArray(); +assert.eq(res[0].scoreOnMerger, res[0].scoreOnShard); +let score = res[0].scoreOnMerger; // save for later tests + +// Make sure metadata crosses shard -> merger boundary even if not used on shard +res = coll.aggregate([ + {$match: {_id: 1, $text: {$search: 'apple'}}}, + {$limit: 1}, // force a split. later stages run on merger + {$project: {scoreOnShard: 1, scoreOnMerger: {$meta: 'textScore'}}} + ]) + .toArray(); +assert.eq(res[0].scoreOnMerger, score); + +// Make sure metadata works if first $project doesn't use it. +res = coll.aggregate([ + {$match: {_id: 1, $text: {$search: 'apple'}}}, + {$project: {_id: 1}}, + {$project: {_id: 1, score: {$meta: 'textScore'}}} + ]) + .toArray(); +assert.eq(res[0].score, score); + +// Make sure the pipeline fails if it tries to reference the text score and it doesn't exist. +res = coll.runCommand( + {aggregate: coll.getName(), pipeline: [{$project: {_id: 1, score: {$meta: 'textScore'}}}]}); +assert.commandFailed(res); + +// Make sure the metadata is 'missing()' when it doesn't exist because the document changed +res = coll.aggregate([ + {$match: {_id: 1, $text: {$search: 'apple banana'}}}, + {$group: {_id: 1, score: {$first: {$meta: 'textScore'}}}}, + {$project: {_id: 1, scoreAgain: {$meta: 'textScore'}}}, + ]) + .toArray(); +assert(!("scoreAgain" in res[0])); + +// Make sure metadata works after a $unwind +assert.writeOK(coll.insert({_id: 5, text: 'mango', words: [1, 2, 3]})); +res = coll.aggregate([ + {$match: {$text: {$search: 'mango'}}}, + {$project: {score: {$meta: "textScore"}, _id: 1, words: 1}}, + {$unwind: '$words'}, + {$project: {scoreAgain: {$meta: "textScore"}, score: 1}} + ]) + .toArray(); +assert.eq(res[0].scoreAgain, res[0].score); + +// Error checking +// $match, but wrong position +assertErrorCode(coll, [{$sort: {text: 1}}, {$match: {$text: {$search: 'apple banana'}}}], 17313); + +// wrong $stage, but correct position +assertErrorCode(coll, + [{$project: {searchValue: {$text: {$search: 'apple banana'}}}}], + ErrorCodes.InvalidPipelineOperator); +assertErrorCode(coll, [{$sort: {$text: {$search: 'apple banana'}}}], 17312); })(); diff --git a/jstests/aggregation/bugs/server12015.js b/jstests/aggregation/bugs/server12015.js index 1b59a59545c..2c2b34d126c 100644 --- a/jstests/aggregation/bugs/server12015.js +++ b/jstests/aggregation/bugs/server12015.js @@ -9,77 +9,79 @@ load("jstests/aggregation/extras/utils.js"); // For orderedArrayEq. (function() { - "use strict"; - const coll = db.server12015; - coll.drop(); - const indexSpec = {a: 1, b: 1}; +"use strict"; +const coll = db.server12015; +coll.drop(); +const indexSpec = { + a: 1, + b: 1 +}; - assert.writeOK(coll.insert({_id: 0, a: 0, b: 0})); - assert.writeOK(coll.insert({_id: 1, a: 0, b: 1})); - assert.writeOK(coll.insert({_id: 2, a: 1, b: 0})); - assert.writeOK(coll.insert({_id: 3, a: 1, b: 1})); +assert.writeOK(coll.insert({_id: 0, a: 0, b: 0})); +assert.writeOK(coll.insert({_id: 1, a: 0, b: 1})); +assert.writeOK(coll.insert({_id: 2, a: 1, b: 0})); +assert.writeOK(coll.insert({_id: 3, a: 1, b: 1})); - /** - * Helper to test that for a given pipeline, the same results are returned whether or not an - * index is present. If 'ignoreSortOrder' is present, test for result parity without assuming - * the order of results. - */ - function assertResultsMatch(pipeline, ignoreSortOrder) { - // Add a match stage to ensure index scans are considered for planning (workaround for - // SERVER-20066). - pipeline = [{$match: {a: {$gte: 0}}}].concat(pipeline); +/** + * Helper to test that for a given pipeline, the same results are returned whether or not an + * index is present. If 'ignoreSortOrder' is present, test for result parity without assuming + * the order of results. + */ +function assertResultsMatch(pipeline, ignoreSortOrder) { + // Add a match stage to ensure index scans are considered for planning (workaround for + // SERVER-20066). + pipeline = [{$match: {a: {$gte: 0}}}].concat(pipeline); - // Once with an index. - assert.commandWorked(coll.ensureIndex(indexSpec)); - var resultsWithIndex = coll.aggregate(pipeline).toArray(); + // Once with an index. + assert.commandWorked(coll.ensureIndex(indexSpec)); + var resultsWithIndex = coll.aggregate(pipeline).toArray(); - // Again without an index. - assert.commandWorked(coll.dropIndex(indexSpec)); - var resultsWithoutIndex = coll.aggregate(pipeline).toArray(); + // Again without an index. + assert.commandWorked(coll.dropIndex(indexSpec)); + var resultsWithoutIndex = coll.aggregate(pipeline).toArray(); - if (ignoreSortOrder) { - assert(arrayEq(resultsWithIndex, resultsWithoutIndex), tojson({ - resultsWithIndex: resultsWithIndex, - resultsWithoutIndex: resultsWithoutIndex - })); - } else { - assert.eq(resultsWithIndex, resultsWithoutIndex); - } + if (ignoreSortOrder) { + assert( + arrayEq(resultsWithIndex, resultsWithoutIndex), + tojson({resultsWithIndex: resultsWithIndex, resultsWithoutIndex: resultsWithoutIndex})); + } else { + assert.eq(resultsWithIndex, resultsWithoutIndex); } +} - // Uncovered $project, no $sort. - const ignoreSortOrder = true; - assertResultsMatch([{$project: {_id: 1, a: 1, b: 1}}], ignoreSortOrder); +// Uncovered $project, no $sort. +const ignoreSortOrder = true; +assertResultsMatch([{$project: {_id: 1, a: 1, b: 1}}], ignoreSortOrder); - // Covered $project, no $sort. - assertResultsMatch([{$project: {_id: 0, a: 1}}], ignoreSortOrder); - assertResultsMatch([{$project: {_id: 0, a: 1, b: 1}}], ignoreSortOrder); - assertResultsMatch([{$project: {_id: 0, a: 1, b: 1, c: {$literal: 1}}}], ignoreSortOrder); - assertResultsMatch([{$project: {_id: 0, a: 1, b: 1}}, {$project: {a: 1}}], ignoreSortOrder); - assertResultsMatch([{$project: {_id: 0, a: 1, b: 1}}, {$group: {_id: null, a: {$sum: "$a"}}}], - ignoreSortOrder); +// Covered $project, no $sort. +assertResultsMatch([{$project: {_id: 0, a: 1}}], ignoreSortOrder); +assertResultsMatch([{$project: {_id: 0, a: 1, b: 1}}], ignoreSortOrder); +assertResultsMatch([{$project: {_id: 0, a: 1, b: 1, c: {$literal: 1}}}], ignoreSortOrder); +assertResultsMatch([{$project: {_id: 0, a: 1, b: 1}}, {$project: {a: 1}}], ignoreSortOrder); +assertResultsMatch([{$project: {_id: 0, a: 1, b: 1}}, {$group: {_id: null, a: {$sum: "$a"}}}], + ignoreSortOrder); - // Non-blocking $sort, uncovered $project. - assertResultsMatch([{$sort: {a: -1, b: -1}}, {$project: {_id: 1, a: 1, b: 1}}]); - assertResultsMatch([{$sort: {a: 1, b: 1}}, {$project: {_id: 1, a: 1, b: 1}}]); - assertResultsMatch( - [{$sort: {a: 1, b: 1}}, {$group: {_id: "$_id", arr: {$push: "$a"}, sum: {$sum: "$b"}}}], - ignoreSortOrder); +// Non-blocking $sort, uncovered $project. +assertResultsMatch([{$sort: {a: -1, b: -1}}, {$project: {_id: 1, a: 1, b: 1}}]); +assertResultsMatch([{$sort: {a: 1, b: 1}}, {$project: {_id: 1, a: 1, b: 1}}]); +assertResultsMatch( + [{$sort: {a: 1, b: 1}}, {$group: {_id: "$_id", arr: {$push: "$a"}, sum: {$sum: "$b"}}}], + ignoreSortOrder); - // Non-blocking $sort, covered $project. - assertResultsMatch([{$sort: {a: -1, b: -1}}, {$project: {_id: 0, a: 1, b: 1}}]); - assertResultsMatch([{$sort: {a: 1, b: 1}}, {$project: {_id: 0, a: 1, b: 1}}]); - assertResultsMatch([{$sort: {a: 1, b: 1}}, {$group: {_id: "$b", arr: {$push: "$a"}}}], - ignoreSortOrder); +// Non-blocking $sort, covered $project. +assertResultsMatch([{$sort: {a: -1, b: -1}}, {$project: {_id: 0, a: 1, b: 1}}]); +assertResultsMatch([{$sort: {a: 1, b: 1}}, {$project: {_id: 0, a: 1, b: 1}}]); +assertResultsMatch([{$sort: {a: 1, b: 1}}, {$group: {_id: "$b", arr: {$push: "$a"}}}], + ignoreSortOrder); - // Blocking $sort, uncovered $project. - assertResultsMatch([{$sort: {b: 1, a: -1}}, {$project: {_id: 1, a: 1, b: 1}}]); - assertResultsMatch( - [{$sort: {b: 1, a: -1}}, {$group: {_id: "$_id", arr: {$push: "$a"}, sum: {$sum: "$b"}}}], - ignoreSortOrder); +// Blocking $sort, uncovered $project. +assertResultsMatch([{$sort: {b: 1, a: -1}}, {$project: {_id: 1, a: 1, b: 1}}]); +assertResultsMatch( + [{$sort: {b: 1, a: -1}}, {$group: {_id: "$_id", arr: {$push: "$a"}, sum: {$sum: "$b"}}}], + ignoreSortOrder); - // Blocking $sort, covered $project. - assertResultsMatch([{$sort: {b: 1, a: -1}}, {$project: {_id: 0, a: 1, b: 1}}]); - assertResultsMatch([{$sort: {b: 1, a: -1}}, {$group: {_id: "$b", arr: {$push: "$a"}}}], - ignoreSortOrder); +// Blocking $sort, covered $project. +assertResultsMatch([{$sort: {b: 1, a: -1}}, {$project: {_id: 0, a: 1, b: 1}}]); +assertResultsMatch([{$sort: {b: 1, a: -1}}, {$group: {_id: "$b", arr: {$push: "$a"}}}], + ignoreSortOrder); }()); diff --git a/jstests/aggregation/bugs/server14421.js b/jstests/aggregation/bugs/server14421.js index 3201e20a81a..b6701546e3d 100644 --- a/jstests/aggregation/bugs/server14421.js +++ b/jstests/aggregation/bugs/server14421.js @@ -1,40 +1,40 @@ // SERVER-14421 minDistance for $geoNear aggregation operator (function() { - 'use strict'; - var coll = db.mindistance; - coll.drop(); - assert.writeOK(coll.insert([ - {_id: 0, loc: {type: "Point", coordinates: [0, 0]}}, - {_id: 1, loc: {type: "Point", coordinates: [0, 0.01]}} - ])); - var response = coll.createIndex({loc: "2dsphere"}); - assert.eq(response.ok, 1, "Could not create 2dsphere index"); - var results = coll.aggregate([{ - $geoNear: { - minDistance: 10000, - spherical: true, - distanceField: "distance", - near: {type: "Point", coordinates: [0, 0]} - } - }]); - assert.eq(results.itcount(), 0); - results = coll.aggregate([{ - $geoNear: { - minDistance: 1, - spherical: true, - distanceField: "distance", - near: {type: "Point", coordinates: [0, 0]} - } - }]); - assert.eq(results.itcount(), 1); - results = coll.aggregate([{ - $geoNear: { - minDistance: 0, - spherical: true, - distanceField: "distance", - near: {type: "Point", coordinates: [0, 0]} - } - }]); - assert.eq(results.itcount(), 2); - coll.drop(); +'use strict'; +var coll = db.mindistance; +coll.drop(); +assert.writeOK(coll.insert([ + {_id: 0, loc: {type: "Point", coordinates: [0, 0]}}, + {_id: 1, loc: {type: "Point", coordinates: [0, 0.01]}} +])); +var response = coll.createIndex({loc: "2dsphere"}); +assert.eq(response.ok, 1, "Could not create 2dsphere index"); +var results = coll.aggregate([{ + $geoNear: { + minDistance: 10000, + spherical: true, + distanceField: "distance", + near: {type: "Point", coordinates: [0, 0]} + } +}]); +assert.eq(results.itcount(), 0); +results = coll.aggregate([{ + $geoNear: { + minDistance: 1, + spherical: true, + distanceField: "distance", + near: {type: "Point", coordinates: [0, 0]} + } +}]); +assert.eq(results.itcount(), 1); +results = coll.aggregate([{ + $geoNear: { + minDistance: 0, + spherical: true, + distanceField: "distance", + near: {type: "Point", coordinates: [0, 0]} + } +}]); +assert.eq(results.itcount(), 2); +coll.drop(); }());
\ No newline at end of file diff --git a/jstests/aggregation/bugs/server14670.js b/jstests/aggregation/bugs/server14670.js index dc8a750e9db..adadb154da0 100644 --- a/jstests/aggregation/bugs/server14670.js +++ b/jstests/aggregation/bugs/server14670.js @@ -3,21 +3,19 @@ load("jstests/aggregation/extras/utils.js"); // For assertErrorCode. (function() { - "use strict"; +"use strict"; - var coll = db.substr; - coll.drop(); +var coll = db.substr; +coll.drop(); - // Need an empty document for the pipeline. - coll.insert({}); +// Need an empty document for the pipeline. +coll.insert({}); - assertErrorCode(coll, - [{$project: {strLen: {$strLenBytes: 1}}}], - 34473, - "$strLenBytes requires a string argument."); +assertErrorCode(coll, + [{$project: {strLen: {$strLenBytes: 1}}}], + 34473, + "$strLenBytes requires a string argument."); - assertErrorCode(coll, - [{$project: {strLen: {$strLenCP: 1}}}], - 34471, - "$strLenCP requires a string argument."); +assertErrorCode( + coll, [{$project: {strLen: {$strLenCP: 1}}}], 34471, "$strLenCP requires a string argument."); }()); diff --git a/jstests/aggregation/bugs/server14691.js b/jstests/aggregation/bugs/server14691.js index 0ba010ac41a..2703f2dead9 100644 --- a/jstests/aggregation/bugs/server14691.js +++ b/jstests/aggregation/bugs/server14691.js @@ -1,52 +1,52 @@ // SERVER-14691: $avg aggregator should return null when it receives no input. (function() { - 'use strict'; +'use strict'; - var coll = db.accumulate_avg_sum_null; +var coll = db.accumulate_avg_sum_null; - // Test the $avg aggregator. - coll.drop(); +// Test the $avg aggregator. +coll.drop(); - // Null cases. - assert.writeOK(coll.insert({a: 1, b: 2, c: 'string', d: null})); +// 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}]); +// 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}]); +// 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}]); +// 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}]); +// 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'}}}]; +// 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}]); +// 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}]); +// 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}]); +// 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}]); +// 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/bugs/server14872.js b/jstests/aggregation/bugs/server14872.js index 3be4018ac21..4787df5259b 100644 --- a/jstests/aggregation/bugs/server14872.js +++ b/jstests/aggregation/bugs/server14872.js @@ -4,36 +4,36 @@ load('jstests/aggregation/extras/utils.js'); (function() { - 'use strict'; +'use strict'; - var coll = db.agg_concat_arrays_expr; - coll.drop(); +var coll = db.agg_concat_arrays_expr; +coll.drop(); - assert.writeOK(coll.insert({a: [1, 2], b: ['three'], c: [], d: [[3], 4], e: null, str: 'x'})); +assert.writeOK(coll.insert({a: [1, 2], b: ['three'], c: [], d: [[3], 4], e: null, str: 'x'})); - // Basic concatenation. - var pipeline = [{$project: {_id: 0, all: {$concatArrays: ['$a', '$b', '$c']}}}]; - assert.eq(coll.aggregate(pipeline).toArray(), [{all: [1, 2, 'three']}]); +// Basic concatenation. +var pipeline = [{$project: {_id: 0, all: {$concatArrays: ['$a', '$b', '$c']}}}]; +assert.eq(coll.aggregate(pipeline).toArray(), [{all: [1, 2, 'three']}]); - // Concatenation with nested arrays. - pipeline = [{$project: {_id: 0, all: {$concatArrays: ['$a', '$d']}}}]; - assert.eq(coll.aggregate(pipeline).toArray(), [{all: [1, 2, [3], 4]}]); +// Concatenation with nested arrays. +pipeline = [{$project: {_id: 0, all: {$concatArrays: ['$a', '$d']}}}]; +assert.eq(coll.aggregate(pipeline).toArray(), [{all: [1, 2, [3], 4]}]); - // Concatenation with 1 argument. - pipeline = [{$project: {_id: 0, all: {$concatArrays: ['$a']}}}]; - assert.eq(coll.aggregate(pipeline).toArray(), [{all: [1, 2]}]); +// Concatenation with 1 argument. +pipeline = [{$project: {_id: 0, all: {$concatArrays: ['$a']}}}]; +assert.eq(coll.aggregate(pipeline).toArray(), [{all: [1, 2]}]); - // Concatenation with no arguments. - pipeline = [{$project: {_id: 0, all: {$concatArrays: []}}}]; - assert.eq(coll.aggregate(pipeline).toArray(), [{all: []}]); +// Concatenation with no arguments. +pipeline = [{$project: {_id: 0, all: {$concatArrays: []}}}]; +assert.eq(coll.aggregate(pipeline).toArray(), [{all: []}]); - // Any nullish inputs will result in null. - pipeline = [{$project: {_id: 0, all: {$concatArrays: ['$a', '$e']}}}]; - assert.eq(coll.aggregate(pipeline).toArray(), [{all: null}]); - pipeline = [{$project: {_id: 0, all: {$concatArrays: ['$a', '$f']}}}]; - assert.eq(coll.aggregate(pipeline).toArray(), [{all: null}]); +// Any nullish inputs will result in null. +pipeline = [{$project: {_id: 0, all: {$concatArrays: ['$a', '$e']}}}]; +assert.eq(coll.aggregate(pipeline).toArray(), [{all: null}]); +pipeline = [{$project: {_id: 0, all: {$concatArrays: ['$a', '$f']}}}]; +assert.eq(coll.aggregate(pipeline).toArray(), [{all: null}]); - // Error on any non-array, non-null inputs. - pipeline = [{$project: {_id: 0, all: {$concatArrays: ['$a', '$str']}}}]; - assertErrorCode(coll, pipeline, 28664); +// Error on any non-array, non-null inputs. +pipeline = [{$project: {_id: 0, all: {$concatArrays: ['$a', '$str']}}}]; +assertErrorCode(coll, pipeline, 28664); }()); diff --git a/jstests/aggregation/bugs/server17224.js b/jstests/aggregation/bugs/server17224.js index 888c99b808c..31d0e889b8c 100644 --- a/jstests/aggregation/bugs/server17224.js +++ b/jstests/aggregation/bugs/server17224.js @@ -1,25 +1,25 @@ // SERVER-17224 An aggregation result with exactly the right size could crash the server rather than // returning an error. (function() { - 'use strict'; +'use strict'; - var t = db.server17224; - t.drop(); +var t = db.server17224; +t.drop(); - // first 63MB - for (var i = 0; i < 63; i++) { - t.insert({a: new Array(1024 * 1024 + 1).join('a')}); - } +// first 63MB +for (var i = 0; i < 63; i++) { + t.insert({a: new Array(1024 * 1024 + 1).join('a')}); +} - // the remaining ~1MB with room for field names and other overhead - t.insert({a: new Array(1024 * 1024 - 1105).join('a')}); +// the remaining ~1MB with room for field names and other overhead +t.insert({a: new Array(1024 * 1024 - 1105).join('a')}); - // do not use cursor form, since it has a different workaroud for this issue. - assert.commandFailed(db.runCommand({ - aggregate: t.getName(), - pipeline: [{$match: {}}, {$group: {_id: null, arr: {$push: {a: '$a'}}}}] - })); +// do not use cursor form, since it has a different workaroud for this issue. +assert.commandFailed(db.runCommand({ + aggregate: t.getName(), + pipeline: [{$match: {}}, {$group: {_id: null, arr: {$push: {a: '$a'}}}}] +})); - // Make sure the server is still up. - assert.commandWorked(db.runCommand('ping')); +// Make sure the server is still up. +assert.commandWorked(db.runCommand('ping')); }()); diff --git a/jstests/aggregation/bugs/server17943.js b/jstests/aggregation/bugs/server17943.js index 075623c705d..6b510e2ddbc 100644 --- a/jstests/aggregation/bugs/server17943.js +++ b/jstests/aggregation/bugs/server17943.js @@ -4,80 +4,88 @@ load('jstests/aggregation/extras/utils.js'); (function() { - 'use strict'; +'use strict'; - var coll = db.agg_filter_expr; - coll.drop(); +var coll = db.agg_filter_expr; +coll.drop(); - assert.writeOK(coll.insert({_id: 0, a: [1, 2, 3, 4, 5]})); - assert.writeOK(coll.insert({_id: 1, a: [2, 4]})); - assert.writeOK(coll.insert({_id: 2, a: []})); - assert.writeOK(coll.insert({_id: 3, a: [1]})); - assert.writeOK(coll.insert({_id: 4, a: null})); - assert.writeOK(coll.insert({_id: 5, a: undefined})); - assert.writeOK(coll.insert({_id: 6})); +assert.writeOK(coll.insert({_id: 0, a: [1, 2, 3, 4, 5]})); +assert.writeOK(coll.insert({_id: 1, a: [2, 4]})); +assert.writeOK(coll.insert({_id: 2, a: []})); +assert.writeOK(coll.insert({_id: 3, a: [1]})); +assert.writeOK(coll.insert({_id: 4, a: null})); +assert.writeOK(coll.insert({_id: 5, a: undefined})); +assert.writeOK(coll.insert({_id: 6})); - // Create filter to only accept odd numbers. - filterDoc = {input: '$a', as: 'x', cond: {$eq: [1, {$mod: ['$$x', 2]}]}}; - var expectedResults = [ - {_id: 0, b: [1, 3, 5]}, - {_id: 1, b: []}, - {_id: 2, b: []}, - {_id: 3, b: [1]}, - {_id: 4, b: null}, - {_id: 5, b: null}, - {_id: 6, b: null}, - ]; - var results = - coll.aggregate([{$project: {b: {$filter: filterDoc}}}, {$sort: {_id: 1}}]).toArray(); - assert.eq(results, expectedResults); +// Create filter to only accept odd numbers. +filterDoc = {input: '$a', as: 'x', cond: {$eq: [1, {$mod: ['$$x', 2]}]}}; +var expectedResults = [ + {_id: 0, b: [1, 3, 5]}, + {_id: 1, b: []}, + {_id: 2, b: []}, + {_id: 3, b: [1]}, + {_id: 4, b: null}, + {_id: 5, b: null}, + {_id: 6, b: null}, +]; +var results = coll.aggregate([{$project: {b: {$filter: filterDoc}}}, {$sort: {_id: 1}}]).toArray(); +assert.eq(results, expectedResults); - // create filter that uses the default variable name in 'cond' - filterDoc = {input: '$a', cond: {$eq: [2, '$$this']}}; - expectedResults = [ - {_id: 0, b: [2]}, - {_id: 1, b: [2]}, - {_id: 2, b: []}, - {_id: 3, b: []}, - {_id: 4, b: null}, - {_id: 5, b: null}, - {_id: 6, b: null}, - ]; - results = coll.aggregate([{$project: {b: {$filter: filterDoc}}}, {$sort: {_id: 1}}]).toArray(); - assert.eq(results, expectedResults); +// create filter that uses the default variable name in 'cond' +filterDoc = { + input: '$a', + cond: {$eq: [2, '$$this']} +}; +expectedResults = [ + {_id: 0, b: [2]}, + {_id: 1, b: [2]}, + {_id: 2, b: []}, + {_id: 3, b: []}, + {_id: 4, b: null}, + {_id: 5, b: null}, + {_id: 6, b: null}, +]; +results = coll.aggregate([{$project: {b: {$filter: filterDoc}}}, {$sort: {_id: 1}}]).toArray(); +assert.eq(results, expectedResults); - // Invalid filter expressions. +// Invalid filter expressions. - // '$filter' is not a document. - var filterDoc = 'string'; - assertErrorCode(coll, [{$project: {b: {$filter: filterDoc}}}], 28646); +// '$filter' is not a document. +var filterDoc = 'string'; +assertErrorCode(coll, [{$project: {b: {$filter: filterDoc}}}], 28646); - // Extra field(s). - filterDoc = {input: '$a', as: 'x', cond: true, extra: 1}; - assertErrorCode(coll, [{$project: {b: {$filter: filterDoc}}}], 28647); +// Extra field(s). +filterDoc = {input: '$a', as: 'x', cond: true, extra: 1}; +assertErrorCode(coll, [{$project: {b: {$filter: filterDoc}}}], 28647); - // Missing 'input'. - filterDoc = {as: 'x', cond: true}; - assertErrorCode(coll, [{$project: {b: {$filter: filterDoc}}}], 28648); +// Missing 'input'. +filterDoc = { + as: 'x', + cond: true +}; +assertErrorCode(coll, [{$project: {b: {$filter: filterDoc}}}], 28648); - // Missing 'cond'. - filterDoc = {input: '$a', as: 'x'}; - assertErrorCode(coll, [{$project: {b: {$filter: filterDoc}}}], 28650); +// Missing 'cond'. +filterDoc = {input: '$a', as: 'x'}; +assertErrorCode(coll, [{$project: {b: {$filter: filterDoc}}}], 28650); - // 'as' is not a valid variable name. - filterDoc = {input: '$a', as: '$x', cond: true}; - assertErrorCode(coll, [{$project: {b: {$filter: filterDoc}}}], 16867); +// 'as' is not a valid variable name. +filterDoc = {input: '$a', as: '$x', cond: true}; +assertErrorCode(coll, [{$project: {b: {$filter: filterDoc}}}], 16867); - // 'input' is not an array. - filterDoc = {input: 'string', as: 'x', cond: true}; - assertErrorCode(coll, [{$project: {b: {$filter: filterDoc}}}], 28651); +// 'input' is not an array. +filterDoc = {input: 'string', as: 'x', cond: true}; +assertErrorCode(coll, [{$project: {b: {$filter: filterDoc}}}], 28651); - // 'cond' uses undefined variable name. - filterDoc = {input: '$a', cond: {$eq: [1, '$$var']}}; - assertErrorCode(coll, [{$project: {b: {$filter: filterDoc}}}], 17276); +// 'cond' uses undefined variable name. +filterDoc = { + input: '$a', + cond: {$eq: [1, '$$var']} +}; +assertErrorCode(coll, [{$project: {b: {$filter: filterDoc}}}], 17276); - assert(coll.drop()); - assert.writeOK(coll.insert({a: 'string'})); - filterDoc = {input: '$a', as: 'x', cond: true}; - assertErrorCode(coll, [{$project: {b: {$filter: filterDoc}}}], 28651); +assert(coll.drop()); +assert.writeOK(coll.insert({a: 'string'})); +filterDoc = {input: '$a', as: 'x', cond: true}; +assertErrorCode(coll, [{$project: {b: {$filter: filterDoc}}}], 28651); }()); diff --git a/jstests/aggregation/bugs/server18198.js b/jstests/aggregation/bugs/server18198.js index a182195a864..9aa26451161 100644 --- a/jstests/aggregation/bugs/server18198.js +++ b/jstests/aggregation/bugs/server18198.js @@ -1,67 +1,67 @@ // SERVER-18198 check read pref is only applied when there is no $out stage // in aggregate shell helper (function() { - "use strict"; - var t = db.server18198; - t.drop(); +"use strict"; +var t = db.server18198; +t.drop(); - var mongo = db.getMongo(); +var mongo = db.getMongo(); - try { - var commandsRan = []; - // hook in our patched mongo - var mockMongo = { - getSlaveOk: function() { - return true; - }, - runCommand: function(db, cmd, opts) { - commandsRan.push({db: db, cmd: cmd, opts: opts}); - return {ok: 1.0}; - }, - getReadPref: function() { - return {mode: "secondaryPreferred"}; - }, - getReadPrefMode: function() { - return "secondaryPreferred"; - }, - getMinWireVersion: function() { - return mongo.getMinWireVersion(); - }, - getMaxWireVersion: function() { - return mongo.getMaxWireVersion(); - }, - isReplicaSetMember: function() { - return mongo.isReplicaSetMember(); - }, - isMongos: function() { - return mongo.isMongos(); - }, - isCausalConsistency: function() { - return false; - }, - getClusterTime: function() { - return mongo.getClusterTime(); - }, - }; +try { + var commandsRan = []; + // hook in our patched mongo + var mockMongo = { + getSlaveOk: function() { + return true; + }, + runCommand: function(db, cmd, opts) { + commandsRan.push({db: db, cmd: cmd, opts: opts}); + return {ok: 1.0}; + }, + getReadPref: function() { + return {mode: "secondaryPreferred"}; + }, + getReadPrefMode: function() { + return "secondaryPreferred"; + }, + getMinWireVersion: function() { + return mongo.getMinWireVersion(); + }, + getMaxWireVersion: function() { + return mongo.getMaxWireVersion(); + }, + isReplicaSetMember: function() { + return mongo.isReplicaSetMember(); + }, + isMongos: function() { + return mongo.isMongos(); + }, + isCausalConsistency: function() { + return false; + }, + getClusterTime: function() { + return mongo.getClusterTime(); + }, + }; - db._mongo = mockMongo; - db._session = new _DummyDriverSession(mockMongo); + db._mongo = mockMongo; + db._session = new _DummyDriverSession(mockMongo); - // this query should not get a read pref - t.aggregate([{$sort: {"x": 1}}, {$out: "foo"}]); - assert.eq(commandsRan.length, 1); - // check that it doesn't have a read preference - assert(!commandsRan[0].cmd.hasOwnProperty("$readPreference")); + // this query should not get a read pref + t.aggregate([{$sort: {"x": 1}}, {$out: "foo"}]); + assert.eq(commandsRan.length, 1); + // check that it doesn't have a read preference + assert(!commandsRan[0].cmd.hasOwnProperty("$readPreference")); - commandsRan = []; + commandsRan = []; - t.aggregate([{$sort: {"x": 1}}]); - // check another command was run - assert.eq(commandsRan.length, 1); - // check that it has a read preference - assert(commandsRan[0].cmd.hasOwnProperty("$readPreference")); - } finally { - db._mongo = mongo; - db._session = new _DummyDriverSession(mongo); - } + t.aggregate([{$sort: {"x": 1}}]); + // check another command was run + assert.eq(commandsRan.length, 1); + // check that it has a read preference + assert(commandsRan[0].cmd.hasOwnProperty("$readPreference")); +} finally { + db._mongo = mongo; + db._session = new _DummyDriverSession(mongo); +} })(); diff --git a/jstests/aggregation/bugs/server18222.js b/jstests/aggregation/bugs/server18222.js index cea52b3970d..d27188bbb2b 100644 --- a/jstests/aggregation/bugs/server18222.js +++ b/jstests/aggregation/bugs/server18222.js @@ -1,43 +1,43 @@ // SERVER-18222: Add $isArray aggregation expression. (function() { - 'use strict'; - var coll = db.is_array_expr; - coll.drop(); +'use strict'; +var coll = db.is_array_expr; +coll.drop(); - // Non-array types. - assert.writeOK(coll.insert({_id: 0, x: 0})); - assert.writeOK(coll.insert({_id: 1, x: '0'})); - assert.writeOK(coll.insert({_id: 2, x: new ObjectId()})); - assert.writeOK(coll.insert({_id: 3, x: new NumberLong(0)})); - assert.writeOK(coll.insert({_id: 4, x: {y: []}})); - assert.writeOK(coll.insert({_id: 5, x: null})); - assert.writeOK(coll.insert({_id: 6, x: NaN})); - assert.writeOK(coll.insert({_id: 7, x: undefined})); +// Non-array types. +assert.writeOK(coll.insert({_id: 0, x: 0})); +assert.writeOK(coll.insert({_id: 1, x: '0'})); +assert.writeOK(coll.insert({_id: 2, x: new ObjectId()})); +assert.writeOK(coll.insert({_id: 3, x: new NumberLong(0)})); +assert.writeOK(coll.insert({_id: 4, x: {y: []}})); +assert.writeOK(coll.insert({_id: 5, x: null})); +assert.writeOK(coll.insert({_id: 6, x: NaN})); +assert.writeOK(coll.insert({_id: 7, x: undefined})); - // Array types. - assert.writeOK(coll.insert({_id: 8, x: []})); - assert.writeOK(coll.insert({_id: 9, x: [0]})); - assert.writeOK(coll.insert({_id: 10, x: ['0']})); +// Array types. +assert.writeOK(coll.insert({_id: 8, x: []})); +assert.writeOK(coll.insert({_id: 9, x: [0]})); +assert.writeOK(coll.insert({_id: 10, x: ['0']})); - // Project field is_array to represent whether the field x was an array. - var results = coll.aggregate([ - {$sort: {_id: 1}}, - {$project: {isArray: {$isArray: '$x'}}}, - ]) - .toArray(); - var expectedResults = [ - {_id: 0, isArray: false}, - {_id: 1, isArray: false}, - {_id: 2, isArray: false}, - {_id: 3, isArray: false}, - {_id: 4, isArray: false}, - {_id: 5, isArray: false}, - {_id: 6, isArray: false}, - {_id: 7, isArray: false}, - {_id: 8, isArray: true}, - {_id: 9, isArray: true}, - {_id: 10, isArray: true}, - ]; +// Project field is_array to represent whether the field x was an array. +var results = coll.aggregate([ + {$sort: {_id: 1}}, + {$project: {isArray: {$isArray: '$x'}}}, + ]) + .toArray(); +var expectedResults = [ + {_id: 0, isArray: false}, + {_id: 1, isArray: false}, + {_id: 2, isArray: false}, + {_id: 3, isArray: false}, + {_id: 4, isArray: false}, + {_id: 5, isArray: false}, + {_id: 6, isArray: false}, + {_id: 7, isArray: false}, + {_id: 8, isArray: true}, + {_id: 9, isArray: true}, + {_id: 10, isArray: true}, +]; - assert.eq(results, expectedResults); +assert.eq(results, expectedResults); }()); diff --git a/jstests/aggregation/bugs/server18427.js b/jstests/aggregation/bugs/server18427.js index f15c1f9e23e..fffbc51ef64 100644 --- a/jstests/aggregation/bugs/server18427.js +++ b/jstests/aggregation/bugs/server18427.js @@ -4,156 +4,151 @@ load('jstests/aggregation/extras/utils.js'); (function() { - 'use strict'; - var coll = db.log_exponential_expressions; - coll.drop(); - assert.writeOK(coll.insert({_id: 0})); - - var decimalE = NumberDecimal("2.718281828459045235360287471352662"); - var decimal1overE = NumberDecimal("0.3678794411714423215955237701614609"); - - // Helper for testing that op returns expResult. - function testOp(op, expResult) { - var pipeline = [{$project: {_id: 0, result: op}}]; - assert.eq(coll.aggregate(pipeline).toArray(), [{result: expResult}]); - } - - // $log, $log10, $ln. - - // Valid input: numeric/null/NaN, base positive and not equal to 1, arg positive. - // - NumberDouble - testOp({$log: [10, 10]}, 1); - testOp({$log10: [10]}, 1); - testOp({$ln: [Math.E]}, 1); - // - NumberDecimal - testOp({$log: [NumberDecimal("10"), NumberDecimal("10")]}, NumberDecimal("1")); - testOp({$log10: [NumberDecimal("10")]}, NumberDecimal("1")); - // The below answer is actually correct: the input is an approximation of E - testOp({$ln: [decimalE]}, NumberDecimal("0.9999999999999999999999999999999998")); - // All types converted to doubles. - testOp({$log: [NumberLong("10"), NumberLong("10")]}, 1); - testOp({$log10: [NumberLong("10")]}, 1); - testOp({$ln: [NumberLong("1")]}, 0); - // LLONG_MAX is converted to a double. - testOp({$log: [NumberLong("9223372036854775807"), 10]}, 18.964889726830812); - // Null inputs result in null. - testOp({$log: [null, 10]}, null); - testOp({$log: [10, null]}, null); - testOp({$log: [null, NumberDecimal(10)]}, null); - testOp({$log: [NumberDecimal(10), null]}, null); - testOp({$log10: [null]}, null); - testOp({$ln: [null]}, null); - // NaN inputs result in NaN. - testOp({$log: [NaN, 10]}, NaN); - testOp({$log: [10, NaN]}, NaN); - testOp({$log: [NaN, NumberDecimal(10)]}, NaN); - testOp({$log: [NumberDecimal(10), NaN]}, NaN); - testOp({$log10: [NaN]}, NaN); - testOp({$ln: [NaN]}, NaN); - - // Invalid input: non-numeric/non-null, bases not positive or equal to 1, args not positive. - - // Args/bases must be numeric or null. - assertErrorCode(coll, [{$project: {log: {$log: ["string", 5]}}}], 28756); - assertErrorCode(coll, [{$project: {log: {$log: [5, "string"]}}}], 28757); - assertErrorCode(coll, [{$project: {log10: {$log10: ["string"]}}}], 28765); - assertErrorCode(coll, [{$project: {ln: {$ln: ["string"]}}}], 28765); - // Args/bases cannot equal 0. - assertErrorCode(coll, [{$project: {log: {$log: [0, 5]}}}], 28758); - assertErrorCode(coll, [{$project: {log: {$log: [5, 0]}}}], 28759); - assertErrorCode(coll, [{$project: {log10: {$log10: [0]}}}], 28761); - assertErrorCode(coll, [{$project: {ln: {$ln: [0]}}}], 28766); - assertErrorCode(coll, [{$project: {log: {$log: [NumberDecimal(0), NumberDecimal(5)]}}}], 28758); - assertErrorCode(coll, [{$project: {log: {$log: [NumberDecimal(5), NumberDecimal(0)]}}}], 28759); - assertErrorCode(coll, [{$project: {log10: {$log10: [NumberDecimal(0)]}}}], 28761); - assertErrorCode(coll, [{$project: {ln: {$ln: [NumberDecimal(0)]}}}], 28766); - // Args/bases cannot be negative. - assertErrorCode(coll, [{$project: {log: {$log: [-1, 5]}}}], 28758); - assertErrorCode(coll, [{$project: {log: {$log: [5, -1]}}}], 28759); - assertErrorCode(coll, [{$project: {log10: {$log10: [-1]}}}], 28761); - assertErrorCode(coll, [{$project: {ln: {$ln: [-1]}}}], 28766); - assertErrorCode( - coll, [{$project: {log: {$log: [NumberDecimal(-1), NumberDecimal(5)]}}}], 28758); - assertErrorCode( - coll, [{$project: {log: {$log: [NumberDecimal(5), NumberDecimal(-1)]}}}], 28759); - assertErrorCode(coll, [{$project: {log10: {$log10: [NumberDecimal(-1)]}}}], 28761); - assertErrorCode(coll, [{$project: {ln: {$ln: [NumberDecimal(-1)]}}}], 28766); - // Base can't equal 1. - assertErrorCode(coll, [{$project: {log: {$log: [5, 1]}}}], 28759); - assertErrorCode(coll, [{$project: {log: {$log: [NumberDecimal(5), NumberDecimal(1)]}}}], 28759); - - // $pow, $exp. - - // Valid input - numeric/null/NaN. - - // $pow -- if either input is a double return a double. - testOp({$pow: [10, 2]}, 100); - testOp({$pow: [1 / 2, -1]}, 2); - testOp({$pow: [-2, 2]}, 4); - testOp({$pow: [NumberInt("2"), 2]}, 4); - testOp({$pow: [-2, NumberInt("2")]}, 4); - // $pow -- if either input is a NumberDecimal, return a NumberDecimal - testOp({$pow: [NumberDecimal("10.0"), -2]}, - NumberDecimal("0.01000000000000000000000000000000000")); - testOp({$pow: [0.5, NumberDecimal("-1")]}, - NumberDecimal("2.000000000000000000000000000000000")); - testOp({$pow: [-2, NumberDecimal("2")]}, NumberDecimal("4.000000000000000000000000000000000")); - testOp({$pow: [NumberInt("2"), NumberDecimal("2")]}, - NumberDecimal("4.000000000000000000000000000000000")); - testOp({$pow: [NumberDecimal("-2.0"), NumberInt("2")]}, - NumberDecimal("4.000000000000000000000000000000000")); - testOp({$pow: [NumberDecimal("10.0"), 2]}, - NumberDecimal("100.0000000000000000000000000000000")); - - // If exponent is negative and base not -1, 0, or 1, return a double. - testOp({$pow: [NumberLong("2"), NumberLong("-1")]}, 1 / 2); - testOp({$pow: [NumberInt("4"), NumberInt("-1")]}, 1 / 4); - testOp({$pow: [NumberInt("4"), NumberLong("-1")]}, 1 / 4); - testOp({$pow: [NumberInt("1"), NumberLong("-2")]}, NumberLong("1")); - testOp({$pow: [NumberInt("-1"), NumberLong("-2")]}, NumberLong("1")); - testOp({$pow: [NumberLong("-1"), NumberLong("-3")]}, NumberLong("-1")); - // If result would overflow a long, return a double. - testOp({$pow: [NumberInt("2"), NumberLong("63")]}, 9223372036854776000); - // Exact decimal result - testOp({$pow: [NumberInt("5"), NumberDecimal("-112")]}, - NumberDecimal("5192296858534827628530496329220096E-112")); - - // Result would be incorrect if double were returned. - testOp({$pow: [NumberInt("3"), NumberInt("35")]}, NumberLong("50031545098999707")); - - // Else if either input is a long, return a long. - testOp({$pow: [NumberInt("-2"), NumberLong("63")]}, NumberLong("-9223372036854775808")); - testOp({$pow: [NumberInt("4"), NumberLong("2")]}, NumberLong("16")); - testOp({$pow: [NumberLong("4"), NumberInt("2")]}, NumberLong("16")); - testOp({$pow: [NumberLong("4"), NumberLong("2")]}, NumberLong("16")); - - // Else return an int if it fits. - testOp({$pow: [NumberInt("4"), NumberInt("2")]}, 16); - - // $exp always returns doubles for non-zero non-decimal inputs, since e is a double. - testOp({$exp: [NumberInt("-1")]}, 1 / Math.E); - testOp({$exp: [NumberLong("1")]}, Math.E); - // $exp returns decimal results for decimal inputs - testOp({$exp: [NumberDecimal("-1")]}, decimal1overE); - testOp({$exp: [NumberDecimal("1")]}, decimalE); - // Null input results in null. - testOp({$pow: [null, 2]}, null); - testOp({$pow: [1 / 2, null]}, null); - testOp({$pow: [null, NumberDecimal(2)]}, null); - testOp({$pow: [NumberDecimal("0.5"), null]}, null); - testOp({$exp: [null]}, null); - // NaN input results in NaN. - testOp({$pow: [NaN, 2]}, NaN); - testOp({$pow: [1 / 2, NaN]}, NaN); - testOp({$pow: [NaN, NumberDecimal(2)]}, NumberDecimal("NaN")); - testOp({$pow: [NumberDecimal("0.5"), NaN]}, NumberDecimal("NaN")); - testOp({$exp: [NaN]}, NaN); - - // Invalid inputs - non-numeric/non-null types, or 0 to a negative exponent. - assertErrorCode(coll, [{$project: {pow: {$pow: [0, NumberLong("-1")]}}}], 28764); - assertErrorCode(coll, [{$project: {pow: {$pow: ["string", 5]}}}], 28762); - assertErrorCode(coll, [{$project: {pow: {$pow: [5, "string"]}}}], 28763); - assertErrorCode(coll, [{$project: {exp: {$exp: ["string"]}}}], 28765); - assertErrorCode(coll, [{$project: {pow: {$pow: [NumberDecimal(0), NumberLong("-1")]}}}], 28764); - assertErrorCode(coll, [{$project: {pow: {$pow: ["string", NumberDecimal(5)]}}}], 28762); +'use strict'; +var coll = db.log_exponential_expressions; +coll.drop(); +assert.writeOK(coll.insert({_id: 0})); + +var decimalE = NumberDecimal("2.718281828459045235360287471352662"); +var decimal1overE = NumberDecimal("0.3678794411714423215955237701614609"); + +// Helper for testing that op returns expResult. +function testOp(op, expResult) { + var pipeline = [{$project: {_id: 0, result: op}}]; + assert.eq(coll.aggregate(pipeline).toArray(), [{result: expResult}]); +} + +// $log, $log10, $ln. + +// Valid input: numeric/null/NaN, base positive and not equal to 1, arg positive. +// - NumberDouble +testOp({$log: [10, 10]}, 1); +testOp({$log10: [10]}, 1); +testOp({$ln: [Math.E]}, 1); +// - NumberDecimal +testOp({$log: [NumberDecimal("10"), NumberDecimal("10")]}, NumberDecimal("1")); +testOp({$log10: [NumberDecimal("10")]}, NumberDecimal("1")); +// The below answer is actually correct: the input is an approximation of E +testOp({$ln: [decimalE]}, NumberDecimal("0.9999999999999999999999999999999998")); +// All types converted to doubles. +testOp({$log: [NumberLong("10"), NumberLong("10")]}, 1); +testOp({$log10: [NumberLong("10")]}, 1); +testOp({$ln: [NumberLong("1")]}, 0); +// LLONG_MAX is converted to a double. +testOp({$log: [NumberLong("9223372036854775807"), 10]}, 18.964889726830812); +// Null inputs result in null. +testOp({$log: [null, 10]}, null); +testOp({$log: [10, null]}, null); +testOp({$log: [null, NumberDecimal(10)]}, null); +testOp({$log: [NumberDecimal(10), null]}, null); +testOp({$log10: [null]}, null); +testOp({$ln: [null]}, null); +// NaN inputs result in NaN. +testOp({$log: [NaN, 10]}, NaN); +testOp({$log: [10, NaN]}, NaN); +testOp({$log: [NaN, NumberDecimal(10)]}, NaN); +testOp({$log: [NumberDecimal(10), NaN]}, NaN); +testOp({$log10: [NaN]}, NaN); +testOp({$ln: [NaN]}, NaN); + +// Invalid input: non-numeric/non-null, bases not positive or equal to 1, args not positive. + +// Args/bases must be numeric or null. +assertErrorCode(coll, [{$project: {log: {$log: ["string", 5]}}}], 28756); +assertErrorCode(coll, [{$project: {log: {$log: [5, "string"]}}}], 28757); +assertErrorCode(coll, [{$project: {log10: {$log10: ["string"]}}}], 28765); +assertErrorCode(coll, [{$project: {ln: {$ln: ["string"]}}}], 28765); +// Args/bases cannot equal 0. +assertErrorCode(coll, [{$project: {log: {$log: [0, 5]}}}], 28758); +assertErrorCode(coll, [{$project: {log: {$log: [5, 0]}}}], 28759); +assertErrorCode(coll, [{$project: {log10: {$log10: [0]}}}], 28761); +assertErrorCode(coll, [{$project: {ln: {$ln: [0]}}}], 28766); +assertErrorCode(coll, [{$project: {log: {$log: [NumberDecimal(0), NumberDecimal(5)]}}}], 28758); +assertErrorCode(coll, [{$project: {log: {$log: [NumberDecimal(5), NumberDecimal(0)]}}}], 28759); +assertErrorCode(coll, [{$project: {log10: {$log10: [NumberDecimal(0)]}}}], 28761); +assertErrorCode(coll, [{$project: {ln: {$ln: [NumberDecimal(0)]}}}], 28766); +// Args/bases cannot be negative. +assertErrorCode(coll, [{$project: {log: {$log: [-1, 5]}}}], 28758); +assertErrorCode(coll, [{$project: {log: {$log: [5, -1]}}}], 28759); +assertErrorCode(coll, [{$project: {log10: {$log10: [-1]}}}], 28761); +assertErrorCode(coll, [{$project: {ln: {$ln: [-1]}}}], 28766); +assertErrorCode(coll, [{$project: {log: {$log: [NumberDecimal(-1), NumberDecimal(5)]}}}], 28758); +assertErrorCode(coll, [{$project: {log: {$log: [NumberDecimal(5), NumberDecimal(-1)]}}}], 28759); +assertErrorCode(coll, [{$project: {log10: {$log10: [NumberDecimal(-1)]}}}], 28761); +assertErrorCode(coll, [{$project: {ln: {$ln: [NumberDecimal(-1)]}}}], 28766); +// Base can't equal 1. +assertErrorCode(coll, [{$project: {log: {$log: [5, 1]}}}], 28759); +assertErrorCode(coll, [{$project: {log: {$log: [NumberDecimal(5), NumberDecimal(1)]}}}], 28759); + +// $pow, $exp. + +// Valid input - numeric/null/NaN. + +// $pow -- if either input is a double return a double. +testOp({$pow: [10, 2]}, 100); +testOp({$pow: [1 / 2, -1]}, 2); +testOp({$pow: [-2, 2]}, 4); +testOp({$pow: [NumberInt("2"), 2]}, 4); +testOp({$pow: [-2, NumberInt("2")]}, 4); +// $pow -- if either input is a NumberDecimal, return a NumberDecimal +testOp({$pow: [NumberDecimal("10.0"), -2]}, NumberDecimal("0.01000000000000000000000000000000000")); +testOp({$pow: [0.5, NumberDecimal("-1")]}, NumberDecimal("2.000000000000000000000000000000000")); +testOp({$pow: [-2, NumberDecimal("2")]}, NumberDecimal("4.000000000000000000000000000000000")); +testOp({$pow: [NumberInt("2"), NumberDecimal("2")]}, + NumberDecimal("4.000000000000000000000000000000000")); +testOp({$pow: [NumberDecimal("-2.0"), NumberInt("2")]}, + NumberDecimal("4.000000000000000000000000000000000")); +testOp({$pow: [NumberDecimal("10.0"), 2]}, NumberDecimal("100.0000000000000000000000000000000")); + +// If exponent is negative and base not -1, 0, or 1, return a double. +testOp({$pow: [NumberLong("2"), NumberLong("-1")]}, 1 / 2); +testOp({$pow: [NumberInt("4"), NumberInt("-1")]}, 1 / 4); +testOp({$pow: [NumberInt("4"), NumberLong("-1")]}, 1 / 4); +testOp({$pow: [NumberInt("1"), NumberLong("-2")]}, NumberLong("1")); +testOp({$pow: [NumberInt("-1"), NumberLong("-2")]}, NumberLong("1")); +testOp({$pow: [NumberLong("-1"), NumberLong("-3")]}, NumberLong("-1")); +// If result would overflow a long, return a double. +testOp({$pow: [NumberInt("2"), NumberLong("63")]}, 9223372036854776000); +// Exact decimal result +testOp({$pow: [NumberInt("5"), NumberDecimal("-112")]}, + NumberDecimal("5192296858534827628530496329220096E-112")); + +// Result would be incorrect if double were returned. +testOp({$pow: [NumberInt("3"), NumberInt("35")]}, NumberLong("50031545098999707")); + +// Else if either input is a long, return a long. +testOp({$pow: [NumberInt("-2"), NumberLong("63")]}, NumberLong("-9223372036854775808")); +testOp({$pow: [NumberInt("4"), NumberLong("2")]}, NumberLong("16")); +testOp({$pow: [NumberLong("4"), NumberInt("2")]}, NumberLong("16")); +testOp({$pow: [NumberLong("4"), NumberLong("2")]}, NumberLong("16")); + +// Else return an int if it fits. +testOp({$pow: [NumberInt("4"), NumberInt("2")]}, 16); + +// $exp always returns doubles for non-zero non-decimal inputs, since e is a double. +testOp({$exp: [NumberInt("-1")]}, 1 / Math.E); +testOp({$exp: [NumberLong("1")]}, Math.E); +// $exp returns decimal results for decimal inputs +testOp({$exp: [NumberDecimal("-1")]}, decimal1overE); +testOp({$exp: [NumberDecimal("1")]}, decimalE); +// Null input results in null. +testOp({$pow: [null, 2]}, null); +testOp({$pow: [1 / 2, null]}, null); +testOp({$pow: [null, NumberDecimal(2)]}, null); +testOp({$pow: [NumberDecimal("0.5"), null]}, null); +testOp({$exp: [null]}, null); +// NaN input results in NaN. +testOp({$pow: [NaN, 2]}, NaN); +testOp({$pow: [1 / 2, NaN]}, NaN); +testOp({$pow: [NaN, NumberDecimal(2)]}, NumberDecimal("NaN")); +testOp({$pow: [NumberDecimal("0.5"), NaN]}, NumberDecimal("NaN")); +testOp({$exp: [NaN]}, NaN); + +// Invalid inputs - non-numeric/non-null types, or 0 to a negative exponent. +assertErrorCode(coll, [{$project: {pow: {$pow: [0, NumberLong("-1")]}}}], 28764); +assertErrorCode(coll, [{$project: {pow: {$pow: ["string", 5]}}}], 28762); +assertErrorCode(coll, [{$project: {pow: {$pow: [5, "string"]}}}], 28763); +assertErrorCode(coll, [{$project: {exp: {$exp: ["string"]}}}], 28765); +assertErrorCode(coll, [{$project: {pow: {$pow: [NumberDecimal(0), NumberLong("-1")]}}}], 28764); +assertErrorCode(coll, [{$project: {pow: {$pow: ["string", NumberDecimal(5)]}}}], 28762); }()); diff --git a/jstests/aggregation/bugs/server20163.js b/jstests/aggregation/bugs/server20163.js index e61ba606c24..a03e3c70fbe 100644 --- a/jstests/aggregation/bugs/server20163.js +++ b/jstests/aggregation/bugs/server20163.js @@ -3,138 +3,204 @@ load("jstests/aggregation/extras/utils.js"); // For assertErrorCode. (function() { - "use strict"; - - var coll = db.zip; - coll.drop(); - - coll.insert({'long': [1, 2, 3], 'short': ['x', 'y']}); - - var zipObj = 3; - assertErrorCode(coll, - [{$project: {zipped: {$zip: zipObj}}}], - 34460, - "$zip requires an object" + " as an argument."); - - zipObj = {inputs: []}; - assertErrorCode(coll, - [{$project: {zipped: {$zip: zipObj}}}], - 34465, - "$zip requires at least" + " one input array"); - - zipObj = {inputs: {"a": "b"}}; - assertErrorCode(coll, [{$project: {zipped: {$zip: zipObj}}}], 34461, "inputs is not an array"); - - zipObj = {inputs: ["$a"], defaults: ["A"]}; - assertErrorCode(coll, - [{$project: {zipped: {$zip: zipObj}}}], - 34466, - "cannot specify defaults" + " unless useLongestLength is true."); - - zipObj = {inputs: ["$a"], defaults: ["A", "B"], useLongestLength: true}; - assertErrorCode(coll, - [{$project: {zipped: {$zip: zipObj}}}], - 34467, - "inputs and defaults" + " must be the same length."); - - zipObj = {inputs: ["$a"], defaults: {"a": "b"}}; - assertErrorCode( - coll, [{$project: {zipped: {$zip: zipObj}}}], 34462, "defaults is not an" + " array"); - - zipObj = {inputs: ["$a"], defaults: ["A"], useLongestLength: 1}; - assertErrorCode( - coll, [{$project: {zipped: {$zip: zipObj}}}], 34463, "useLongestLength is not" + " a bool"); - - zipObj = {inputs: ["$a", "$b"], defaults: ["A"], notAField: 1}; - assertErrorCode(coll, [{$project: {zipped: {$zip: zipObj}}}], 34464, "unknown argument"); - - zipObj = {inputs: ["A", "B"]}; - assertErrorCode(coll, - [{$project: {zipped: {$zip: zipObj}}}], - 34468, - "an element of inputs" + " was not an array."); - - zipObj = {inputs: [[1, 2, 3], ["A", "B", "C"]]}; - var res = coll.aggregate([{$project: {zipped: {$zip: zipObj}}}]); - var output = res.toArray(); - assert.eq(1, output.length); - assert.eq(output[0].zipped, [[1, "A"], [2, "B"], [3, "C"]]); - - zipObj = {inputs: [[1, 2, 3], null]}; - res = coll.aggregate([{$project: {zipped: {$zip: zipObj}}}]); - output = res.toArray(); - assert.eq(1, output.length); - assert.eq(output[0].zipped, null); - - zipObj = {inputs: [null, [1, 2, 3]]}; - res = coll.aggregate([{$project: {zipped: {$zip: zipObj}}}]); - output = res.toArray(); - assert.eq(1, output.length); - assert.eq(output[0].zipped, null); - - zipObj = {inputs: ["$missing", [1, 2, 3]]}; - res = coll.aggregate([{$project: {zipped: {$zip: zipObj}}}]); - output = res.toArray(); - assert.eq(1, output.length); - assert.eq(output[0].zipped, null); - - zipObj = {inputs: [undefined, [1, 2, 3]]}; - res = coll.aggregate([{$project: {zipped: {$zip: zipObj}}}]); - output = res.toArray(); - assert.eq(1, output.length); - assert.eq(output[0].zipped, null); - - zipObj = {inputs: [[1, 2, 3], ["A", "B"]]}; - res = coll.aggregate([{$project: {zipped: {$zip: zipObj}}}]); - output = res.toArray(); - assert.eq(1, output.length); - assert.eq(output[0].zipped, [[1, "A"], [2, "B"]]); - - zipObj = {inputs: [["A", "B"], [1, 2, 3]]}; - res = coll.aggregate([{$project: {zipped: {$zip: zipObj}}}]); - output = res.toArray(); - assert.eq(1, output.length); - assert.eq(output[0].zipped, [["A", 1], ["B", 2]]); - - zipObj = {inputs: [[], []]}; - res = coll.aggregate([{$project: {zipped: {$zip: zipObj}}}]); - output = res.toArray(); - assert.eq(1, output.length); - assert.eq(output[0].zipped, []); - - zipObj = {inputs: [["$short"], ["$long"]]}; - res = coll.aggregate([{$project: {zipped: {$zip: zipObj}}}]); - output = res.toArray(); - assert.eq(1, output.length); - assert.eq(output[0].zipped, [[['x', 'y'], [1, 2, 3]]]); - - zipObj = {inputs: ["$short", "$long"]}; - res = coll.aggregate([{$project: {zipped: {$zip: zipObj}}}]); - output = res.toArray(); - assert.eq(1, output.length); - assert.eq(output[0].zipped, [['x', 1], ['y', 2]]); - - zipObj = {inputs: [["$long"]]}; - res = coll.aggregate([{$project: {zipped: {$zip: zipObj}}}]); - output = res.toArray(); - assert.eq(1, output.length); - assert.eq(output[0].zipped, [[[1, 2, 3]]]); - - zipObj = {inputs: [[1, 2, 3], ['a', 'b', 'c'], ['c', 'b', 'a']]}; - res = coll.aggregate([{$project: {zipped: {$zip: zipObj}}}]); - output = res.toArray(); - assert.eq(1, output.length); - assert.eq(output[0].zipped, [[1, 'a', 'c'], [2, 'b', 'b'], [3, 'c', 'a']]); - - zipObj = {inputs: [[1, 2, 3], ["A", "B"]], defaults: ["C", "D"], useLongestLength: true}; - res = coll.aggregate([{$project: {zipped: {$zip: zipObj}}}]); - output = res.toArray(); - assert.eq(1, output.length); - assert.eq(output[0].zipped, [[1, "A"], [2, "B"], [3, "D"]]); - - zipObj = {inputs: [[1, 2, 3], ["A", "B"]], useLongestLength: true}; - res = coll.aggregate([{$project: {zipped: {$zip: zipObj}}}]); - output = res.toArray(); - assert.eq(1, output.length); - assert.eq(output[0].zipped, [[1, "A"], [2, "B"], [3, null]]); +"use strict"; + +var coll = db.zip; +coll.drop(); + +coll.insert({'long': [1, 2, 3], 'short': ['x', 'y']}); + +var zipObj = 3; +assertErrorCode(coll, + [{$project: {zipped: {$zip: zipObj}}}], + 34460, + "$zip requires an object" + + " as an argument."); + +zipObj = { + inputs: [] +}; +assertErrorCode(coll, + [{$project: {zipped: {$zip: zipObj}}}], + 34465, + "$zip requires at least" + + " one input array"); + +zipObj = { + inputs: {"a": "b"} +}; +assertErrorCode(coll, [{$project: {zipped: {$zip: zipObj}}}], 34461, "inputs is not an array"); + +zipObj = { + inputs: ["$a"], + defaults: ["A"] +}; +assertErrorCode(coll, + [{$project: {zipped: {$zip: zipObj}}}], + 34466, + "cannot specify defaults" + + " unless useLongestLength is true."); + +zipObj = { + inputs: ["$a"], + defaults: ["A", "B"], + useLongestLength: true +}; +assertErrorCode(coll, + [{$project: {zipped: {$zip: zipObj}}}], + 34467, + "inputs and defaults" + + " must be the same length."); + +zipObj = { + inputs: ["$a"], + defaults: {"a": "b"} +}; +assertErrorCode(coll, + [{$project: {zipped: {$zip: zipObj}}}], + 34462, + "defaults is not an" + + " array"); + +zipObj = { + inputs: ["$a"], + defaults: ["A"], + useLongestLength: 1 +}; +assertErrorCode(coll, + [{$project: {zipped: {$zip: zipObj}}}], + 34463, + "useLongestLength is not" + + " a bool"); + +zipObj = { + inputs: ["$a", "$b"], + defaults: ["A"], + notAField: 1 +}; +assertErrorCode(coll, [{$project: {zipped: {$zip: zipObj}}}], 34464, "unknown argument"); + +zipObj = { + inputs: ["A", "B"] +}; +assertErrorCode(coll, + [{$project: {zipped: {$zip: zipObj}}}], + 34468, + "an element of inputs" + + " was not an array."); + +zipObj = { + inputs: [[1, 2, 3], ["A", "B", "C"]] +}; +var res = coll.aggregate([{$project: {zipped: {$zip: zipObj}}}]); +var output = res.toArray(); +assert.eq(1, output.length); +assert.eq(output[0].zipped, [[1, "A"], [2, "B"], [3, "C"]]); + +zipObj = { + inputs: [[1, 2, 3], null] +}; +res = coll.aggregate([{$project: {zipped: {$zip: zipObj}}}]); +output = res.toArray(); +assert.eq(1, output.length); +assert.eq(output[0].zipped, null); + +zipObj = { + inputs: [null, [1, 2, 3]] +}; +res = coll.aggregate([{$project: {zipped: {$zip: zipObj}}}]); +output = res.toArray(); +assert.eq(1, output.length); +assert.eq(output[0].zipped, null); + +zipObj = { + inputs: ["$missing", [1, 2, 3]] +}; +res = coll.aggregate([{$project: {zipped: {$zip: zipObj}}}]); +output = res.toArray(); +assert.eq(1, output.length); +assert.eq(output[0].zipped, null); + +zipObj = { + inputs: [undefined, [1, 2, 3]] +}; +res = coll.aggregate([{$project: {zipped: {$zip: zipObj}}}]); +output = res.toArray(); +assert.eq(1, output.length); +assert.eq(output[0].zipped, null); + +zipObj = { + inputs: [[1, 2, 3], ["A", "B"]] +}; +res = coll.aggregate([{$project: {zipped: {$zip: zipObj}}}]); +output = res.toArray(); +assert.eq(1, output.length); +assert.eq(output[0].zipped, [[1, "A"], [2, "B"]]); + +zipObj = { + inputs: [["A", "B"], [1, 2, 3]] +}; +res = coll.aggregate([{$project: {zipped: {$zip: zipObj}}}]); +output = res.toArray(); +assert.eq(1, output.length); +assert.eq(output[0].zipped, [["A", 1], ["B", 2]]); + +zipObj = { + inputs: [[], []] +}; +res = coll.aggregate([{$project: {zipped: {$zip: zipObj}}}]); +output = res.toArray(); +assert.eq(1, output.length); +assert.eq(output[0].zipped, []); + +zipObj = { + inputs: [["$short"], ["$long"]] +}; +res = coll.aggregate([{$project: {zipped: {$zip: zipObj}}}]); +output = res.toArray(); +assert.eq(1, output.length); +assert.eq(output[0].zipped, [[['x', 'y'], [1, 2, 3]]]); + +zipObj = { + inputs: ["$short", "$long"] +}; +res = coll.aggregate([{$project: {zipped: {$zip: zipObj}}}]); +output = res.toArray(); +assert.eq(1, output.length); +assert.eq(output[0].zipped, [['x', 1], ['y', 2]]); + +zipObj = { + inputs: [["$long"]] +}; +res = coll.aggregate([{$project: {zipped: {$zip: zipObj}}}]); +output = res.toArray(); +assert.eq(1, output.length); +assert.eq(output[0].zipped, [[[1, 2, 3]]]); + +zipObj = { + inputs: [[1, 2, 3], ['a', 'b', 'c'], ['c', 'b', 'a']] +}; +res = coll.aggregate([{$project: {zipped: {$zip: zipObj}}}]); +output = res.toArray(); +assert.eq(1, output.length); +assert.eq(output[0].zipped, [[1, 'a', 'c'], [2, 'b', 'b'], [3, 'c', 'a']]); + +zipObj = { + inputs: [[1, 2, 3], ["A", "B"]], + defaults: ["C", "D"], + useLongestLength: true +}; +res = coll.aggregate([{$project: {zipped: {$zip: zipObj}}}]); +output = res.toArray(); +assert.eq(1, output.length); +assert.eq(output[0].zipped, [[1, "A"], [2, "B"], [3, "D"]]); + +zipObj = { + inputs: [[1, 2, 3], ["A", "B"]], + useLongestLength: true +}; +res = coll.aggregate([{$project: {zipped: {$zip: zipObj}}}]); +output = res.toArray(); +assert.eq(1, output.length); +assert.eq(output[0].zipped, [[1, "A"], [2, "B"], [3, null]]); }()); diff --git a/jstests/aggregation/bugs/server20168.js b/jstests/aggregation/bugs/server20168.js index 2ff8c6e53cd..9a886bbc279 100644 --- a/jstests/aggregation/bugs/server20168.js +++ b/jstests/aggregation/bugs/server20168.js @@ -1,39 +1,38 @@ // SERVER-20168: Add option to $unwind to output a null result for empty arrays. (function() { - "use strict"; +"use strict"; - var coll = db.server20168; - coll.drop(); +var coll = db.server20168; +coll.drop(); - // Should return no results on a non-existent collection. - var results = coll.aggregate([{$unwind: {path: "$x"}}]).toArray(); - assert.eq(0, results.length, "$unwind returned the wrong number of results"); +// Should return no results on a non-existent collection. +var results = coll.aggregate([{$unwind: {path: "$x"}}]).toArray(); +assert.eq(0, results.length, "$unwind returned the wrong number of results"); - /** - * Asserts that with the input 'inputDoc', an $unwind stage on 'unwindPath' should produce no - * results if preserveNullAndEmptyArrays is not specified, and produces one result, equal to - * 'outputDoc', if it is specified. - */ - function testPreserveNullAndEmptyArraysParam(inputDoc, unwindPath, outputDoc) { - coll.drop(); - assert.writeOK(coll.insert(inputDoc)); +/** + * Asserts that with the input 'inputDoc', an $unwind stage on 'unwindPath' should produce no + * results if preserveNullAndEmptyArrays is not specified, and produces one result, equal to + * 'outputDoc', if it is specified. + */ +function testPreserveNullAndEmptyArraysParam(inputDoc, unwindPath, outputDoc) { + coll.drop(); + assert.writeOK(coll.insert(inputDoc)); - // If preserveNullAndEmptyArrays is passed, we should get an output document. - var preservedResults = - coll.aggregate([{$unwind: {path: unwindPath, preserveNullAndEmptyArrays: true}}]) - .toArray(); - assert.eq(1, preservedResults.length, "$unwind returned the wrong number of results"); - assert.eq(preservedResults[0], - outputDoc, - "Unexpected result for an $unwind with preserveNullAndEmptyArrays " + - "(input was " + tojson(inputDoc) + ")"); + // If preserveNullAndEmptyArrays is passed, we should get an output document. + var preservedResults = + coll.aggregate([{$unwind: {path: unwindPath, preserveNullAndEmptyArrays: true}}]).toArray(); + assert.eq(1, preservedResults.length, "$unwind returned the wrong number of results"); + assert.eq(preservedResults[0], + outputDoc, + "Unexpected result for an $unwind with preserveNullAndEmptyArrays " + + "(input was " + tojson(inputDoc) + ")"); - // If not, we should get no outputs. - var defaultResults = coll.aggregate([{$unwind: {path: unwindPath}}]).toArray(); - assert.eq(0, defaultResults.length, "$unwind returned the wrong number of results"); - } + // If not, we should get no outputs. + var defaultResults = coll.aggregate([{$unwind: {path: unwindPath}}]).toArray(); + assert.eq(0, defaultResults.length, "$unwind returned the wrong number of results"); +} - testPreserveNullAndEmptyArraysParam({_id: 0}, "$x", {_id: 0}); - testPreserveNullAndEmptyArraysParam({_id: 0, x: null}, "$x", {_id: 0, x: null}); - testPreserveNullAndEmptyArraysParam({_id: 0, x: []}, "$x", {_id: 0}); +testPreserveNullAndEmptyArraysParam({_id: 0}, "$x", {_id: 0}); +testPreserveNullAndEmptyArraysParam({_id: 0, x: null}, "$x", {_id: 0, x: null}); +testPreserveNullAndEmptyArraysParam({_id: 0, x: []}, "$x", {_id: 0}); }()); diff --git a/jstests/aggregation/bugs/server20169.js b/jstests/aggregation/bugs/server20169.js index 27995b8030c..2b5a969f803 100644 --- a/jstests/aggregation/bugs/server20169.js +++ b/jstests/aggregation/bugs/server20169.js @@ -3,59 +3,67 @@ load("jstests/aggregation/extras/utils.js"); // For assertErrorCode. (function() { - "use strict"; - - var coll = db.range; - coll.drop(); - - // We need an input document to receive an output document. - coll.insert({}); - - var rangeObj = [1]; - assertErrorCode(coll, - [{$project: {range: {$range: rangeObj}}}], - 28667, - "range requires two" + " or three arguments"); - - rangeObj = ["a", 1]; - assertErrorCode(coll, - [{$project: {range: {$range: rangeObj}}}], - 34443, - "range requires a" + " numeric starting value"); - - rangeObj = [1.1, 1]; - assertErrorCode(coll, - [{$project: {range: {$range: rangeObj}}}], - 34444, - "range requires an" + " integral starting value"); - - rangeObj = [1, "a"]; - assertErrorCode(coll, - [{$project: {range: {$range: rangeObj}}}], - 34445, - "range requires a" + " numeric ending value"); - - rangeObj = [1, 1.1]; - assertErrorCode(coll, - [{$project: {range: {$range: rangeObj}}}], - 34446, - "range requires an" + " integral ending value"); - - rangeObj = [1, 3, "a"]; - assertErrorCode(coll, - [{$project: {range: {$range: rangeObj}}}], - 34447, - "range requires a" + " numeric step value"); - - rangeObj = [1, 3, 1.1]; - assertErrorCode(coll, - [{$project: {range: {$range: rangeObj}}}], - 34448, - "range requires an" + " integral step value"); - - rangeObj = [1, 3, 0]; - assertErrorCode(coll, - [{$project: {range: {$range: rangeObj}}}], - 34449, - "range requires a" + " non-zero step value"); +"use strict"; + +var coll = db.range; +coll.drop(); + +// We need an input document to receive an output document. +coll.insert({}); + +var rangeObj = [1]; +assertErrorCode(coll, + [{$project: {range: {$range: rangeObj}}}], + 28667, + "range requires two" + + " or three arguments"); + +rangeObj = ["a", 1]; +assertErrorCode(coll, + [{$project: {range: {$range: rangeObj}}}], + 34443, + "range requires a" + + " numeric starting value"); + +rangeObj = [1.1, 1]; +assertErrorCode(coll, + [{$project: {range: {$range: rangeObj}}}], + 34444, + "range requires an" + + " integral starting value"); + +rangeObj = [1, "a"]; +assertErrorCode(coll, + [{$project: {range: {$range: rangeObj}}}], + 34445, + "range requires a" + + " numeric ending value"); + +rangeObj = [1, 1.1]; +assertErrorCode(coll, + [{$project: {range: {$range: rangeObj}}}], + 34446, + "range requires an" + + " integral ending value"); + +rangeObj = [1, 3, "a"]; +assertErrorCode(coll, + [{$project: {range: {$range: rangeObj}}}], + 34447, + "range requires a" + + " numeric step value"); + +rangeObj = [1, 3, 1.1]; +assertErrorCode(coll, + [{$project: {range: {$range: rangeObj}}}], + 34448, + "range requires an" + + " integral step value"); + +rangeObj = [1, 3, 0]; +assertErrorCode(coll, + [{$project: {range: {$range: rangeObj}}}], + 34449, + "range requires a" + + " non-zero step value"); }()); diff --git a/jstests/aggregation/bugs/server21632.js b/jstests/aggregation/bugs/server21632.js index 944ca114ab6..c23d8836bea 100644 --- a/jstests/aggregation/bugs/server21632.js +++ b/jstests/aggregation/bugs/server21632.js @@ -11,77 +11,76 @@ // 2. We should not see any duplicate documents in any one $sample (this is only guaranteed if // there are no ongoing write operations). (function() { - "use strict"; - - var coll = db.server21632; - coll.drop(); - - // If there is no collection, or no documents in the collection, we should not get any results - // from a sample. - assert.eq([], coll.aggregate([{$sample: {size: 1}}]).toArray()); - assert.eq([], coll.aggregate([{$sample: {size: 10}}]).toArray()); - - db.createCollection(coll.getName()); - - // Test if we are running WT + LSM and if so, skip the test. - // WiredTiger LSM random cursor implementation doesn't currently give random enough - // distribution to pass this test case, so disable the test when checking an LSM - // configuration for now. We will need revisit this before releasing WiredTiger LSM - // as a supported file type. (See: WT-2403 for details on forthcoming changes) - - var storageEngine = jsTest.options().storageEngine || "wiredTiger"; - - if (storageEngine == "wiredTiger" && coll.stats().wiredTiger.type == 'lsm') { - return; - } - - assert.eq([], coll.aggregate([{$sample: {size: 1}}]).toArray()); - assert.eq([], coll.aggregate([{$sample: {size: 10}}]).toArray()); - - // If there is only one document, we should get that document. - var paddingStr = "abcdefghijklmnopqrstuvwxyz"; - var firstDoc = {_id: 0, paddingStr: paddingStr}; - assert.writeOK(coll.insert(firstDoc)); - assert.eq([firstDoc], coll.aggregate([{$sample: {size: 1}}]).toArray()); - assert.eq([firstDoc], coll.aggregate([{$sample: {size: 10}}]).toArray()); - - // Insert a bunch of documents. - var bulk = coll.initializeUnorderedBulkOp(); - var nDocs = 1000; - for (var id = 1; id < nDocs; id++) { - bulk.insert({_id: id, paddingStr: paddingStr}); - } - bulk.execute(); - - // Will contain a document's _id as a key if we've ever seen that document. - var cumulativeSeenIds = {}; - var sampleSize = 10; - - jsTestLog("About to do repeated samples, explain output: " + - tojson(coll.explain().aggregate([{$sample: {size: sampleSize}}]))); - - // Repeatedly ask for small samples of documents to get a cumulative sample of size 'nDocs'. - for (var i = 0; i < nDocs / sampleSize; i++) { - var results = coll.aggregate([{$sample: {size: sampleSize}}]).toArray(); - - assert.eq( - results.length, sampleSize, "$sample did not return the expected number of results"); - - // Check that there are no duplicate documents in the result of any single sample. - var idsThisSample = {}; - results.forEach(function recordId(result) { - assert.lte(result._id, nDocs, "$sample returned an unknown document"); - assert(!idsThisSample[result._id], - "A single $sample returned the same document twice: " + result._id); - - cumulativeSeenIds[result._id] = true; - idsThisSample[result._id] = true; - }); - } - - // An implementation would have to be very broken for this assertion to fail. - assert.gte(Object.keys(cumulativeSeenIds).length, nDocs / 4); - - // Make sure we can return all documents in the collection. - assert.eq(coll.aggregate([{$sample: {size: nDocs}}]).toArray().length, nDocs); +"use strict"; + +var coll = db.server21632; +coll.drop(); + +// If there is no collection, or no documents in the collection, we should not get any results +// from a sample. +assert.eq([], coll.aggregate([{$sample: {size: 1}}]).toArray()); +assert.eq([], coll.aggregate([{$sample: {size: 10}}]).toArray()); + +db.createCollection(coll.getName()); + +// Test if we are running WT + LSM and if so, skip the test. +// WiredTiger LSM random cursor implementation doesn't currently give random enough +// distribution to pass this test case, so disable the test when checking an LSM +// configuration for now. We will need revisit this before releasing WiredTiger LSM +// as a supported file type. (See: WT-2403 for details on forthcoming changes) + +var storageEngine = jsTest.options().storageEngine || "wiredTiger"; + +if (storageEngine == "wiredTiger" && coll.stats().wiredTiger.type == 'lsm') { + return; +} + +assert.eq([], coll.aggregate([{$sample: {size: 1}}]).toArray()); +assert.eq([], coll.aggregate([{$sample: {size: 10}}]).toArray()); + +// If there is only one document, we should get that document. +var paddingStr = "abcdefghijklmnopqrstuvwxyz"; +var firstDoc = {_id: 0, paddingStr: paddingStr}; +assert.writeOK(coll.insert(firstDoc)); +assert.eq([firstDoc], coll.aggregate([{$sample: {size: 1}}]).toArray()); +assert.eq([firstDoc], coll.aggregate([{$sample: {size: 10}}]).toArray()); + +// Insert a bunch of documents. +var bulk = coll.initializeUnorderedBulkOp(); +var nDocs = 1000; +for (var id = 1; id < nDocs; id++) { + bulk.insert({_id: id, paddingStr: paddingStr}); +} +bulk.execute(); + +// Will contain a document's _id as a key if we've ever seen that document. +var cumulativeSeenIds = {}; +var sampleSize = 10; + +jsTestLog("About to do repeated samples, explain output: " + + tojson(coll.explain().aggregate([{$sample: {size: sampleSize}}]))); + +// Repeatedly ask for small samples of documents to get a cumulative sample of size 'nDocs'. +for (var i = 0; i < nDocs / sampleSize; i++) { + var results = coll.aggregate([{$sample: {size: sampleSize}}]).toArray(); + + assert.eq(results.length, sampleSize, "$sample did not return the expected number of results"); + + // Check that there are no duplicate documents in the result of any single sample. + var idsThisSample = {}; + results.forEach(function recordId(result) { + assert.lte(result._id, nDocs, "$sample returned an unknown document"); + assert(!idsThisSample[result._id], + "A single $sample returned the same document twice: " + result._id); + + cumulativeSeenIds[result._id] = true; + idsThisSample[result._id] = true; + }); +} + +// An implementation would have to be very broken for this assertion to fail. +assert.gte(Object.keys(cumulativeSeenIds).length, nDocs / 4); + +// Make sure we can return all documents in the collection. +assert.eq(coll.aggregate([{$sample: {size: nDocs}}]).toArray().length, nDocs); })(); diff --git a/jstests/aggregation/bugs/server22093.js b/jstests/aggregation/bugs/server22093.js index 61068e38493..618c65f85b7 100644 --- a/jstests/aggregation/bugs/server22093.js +++ b/jstests/aggregation/bugs/server22093.js @@ -11,42 +11,42 @@ load('jstests/libs/analyze_plan.js'); (function() { - "use strict"; +"use strict"; - var coll = db.countscan; - coll.drop(); +var coll = db.countscan; +coll.drop(); - for (var i = 0; i < 3; i++) { - for (var j = 0; j < 10; j += 2) { - coll.insert({foo: i, bar: j}); - } +for (var i = 0; i < 3; i++) { + for (var j = 0; j < 10; j += 2) { + coll.insert({foo: i, bar: j}); } +} - coll.ensureIndex({foo: 1}); +coll.ensureIndex({foo: 1}); - var simpleGroup = coll.aggregate([{$group: {_id: null, count: {$sum: 1}}}]).toArray(); +var simpleGroup = coll.aggregate([{$group: {_id: null, count: {$sum: 1}}}]).toArray(); - assert.eq(simpleGroup.length, 1); - assert.eq(simpleGroup[0]["count"], 15); +assert.eq(simpleGroup.length, 1); +assert.eq(simpleGroup[0]["count"], 15); - var explained = coll.explain().aggregate( - [{$match: {foo: {$gt: 0}}}, {$group: {_id: null, count: {$sum: 1}}}]); +var explained = + coll.explain().aggregate([{$match: {foo: {$gt: 0}}}, {$group: {_id: null, count: {$sum: 1}}}]); - assert(planHasStage(db, explained.stages[0].$cursor.queryPlanner.winningPlan, "COUNT_SCAN")); +assert(planHasStage(db, explained.stages[0].$cursor.queryPlanner.winningPlan, "COUNT_SCAN")); - explained = coll.explain().aggregate([ - {$match: {foo: {$gt: 0}}}, - {$project: {_id: 0, a: {$literal: null}}}, - {$group: {_id: null, count: {$sum: 1}}} - ]); +explained = coll.explain().aggregate([ + {$match: {foo: {$gt: 0}}}, + {$project: {_id: 0, a: {$literal: null}}}, + {$group: {_id: null, count: {$sum: 1}}} +]); - assert(planHasStage(db, explained.stages[0].$cursor.queryPlanner.winningPlan, "COUNT_SCAN")); +assert(planHasStage(db, explained.stages[0].$cursor.queryPlanner.winningPlan, "COUNT_SCAN")); - // Make sure a $count stage can use the COUNT_SCAN optimization. - explained = coll.explain().aggregate([{$match: {foo: {$gt: 0}}}, {$count: "count"}]); - assert(planHasStage(db, explained.stages[0].$cursor.queryPlanner.winningPlan, "COUNT_SCAN")); +// Make sure a $count stage can use the COUNT_SCAN optimization. +explained = coll.explain().aggregate([{$match: {foo: {$gt: 0}}}, {$count: "count"}]); +assert(planHasStage(db, explained.stages[0].$cursor.queryPlanner.winningPlan, "COUNT_SCAN")); - // A $match that is not a single range cannot use the COUNT_SCAN optimization. - explained = coll.explain().aggregate([{$match: {foo: {$in: [0, 1]}}}, {$count: "count"}]); - assert(!planHasStage(db, explained.stages[0].$cursor.queryPlanner.winningPlan, "COUNT_SCAN")); +// A $match that is not a single range cannot use the COUNT_SCAN optimization. +explained = coll.explain().aggregate([{$match: {foo: {$in: [0, 1]}}}, {$count: "count"}]); +assert(!planHasStage(db, explained.stages[0].$cursor.queryPlanner.winningPlan, "COUNT_SCAN")); }()); diff --git a/jstests/aggregation/bugs/server22580.js b/jstests/aggregation/bugs/server22580.js index 3a448173875..3b9f81dbcfc 100644 --- a/jstests/aggregation/bugs/server22580.js +++ b/jstests/aggregation/bugs/server22580.js @@ -3,41 +3,46 @@ load("jstests/aggregation/extras/utils.js"); // For assertErrorCode. (function() { - "use strict"; - - var coll = db.substrCP; - coll.drop(); - - // Need an empty document for pipeline. - coll.insert({}); - - assertErrorCode(coll, - [{$project: {substr: {$substrCP: ["abc", 0, "a"]}}}], - 34452, - "$substrCP" + " does not accept non-numeric types as a length."); - - assertErrorCode(coll, - [{$project: {substr: {$substrCP: ["abc", 0, NaN]}}}], - 34453, - "$substrCP" + " does not accept non-integers as a length."); - - assertErrorCode(coll, - [{$project: {substr: {$substrCP: ["abc", "abc", 3]}}}], - 34450, - "$substrCP does not accept non-numeric types as a starting index."); - - assertErrorCode(coll, - [{$project: {substr: {$substrCP: ["abc", 2.2, 3]}}}], - 34451, - "$substrCP" + " does not accept non-integers as a starting index."); - - assertErrorCode(coll, - [{$project: {substr: {$substrCP: ["abc", -1, 3]}}}], - 34455, - "$substrCP " + "does not accept negative integers as inputs."); - - assertErrorCode(coll, - [{$project: {substr: {$substrCP: ["abc", 1, -3]}}}], - 34454, - "$substrCP " + "does not accept negative integers as inputs."); +"use strict"; + +var coll = db.substrCP; +coll.drop(); + +// Need an empty document for pipeline. +coll.insert({}); + +assertErrorCode(coll, + [{$project: {substr: {$substrCP: ["abc", 0, "a"]}}}], + 34452, + "$substrCP" + + " does not accept non-numeric types as a length."); + +assertErrorCode(coll, + [{$project: {substr: {$substrCP: ["abc", 0, NaN]}}}], + 34453, + "$substrCP" + + " does not accept non-integers as a length."); + +assertErrorCode(coll, + [{$project: {substr: {$substrCP: ["abc", "abc", 3]}}}], + 34450, + "$substrCP does not accept non-numeric types as a starting index."); + +assertErrorCode(coll, + [{$project: {substr: {$substrCP: ["abc", 2.2, 3]}}}], + 34451, + "$substrCP" + + " does not accept non-integers as a starting index."); + +assertErrorCode(coll, + [{$project: {substr: {$substrCP: ["abc", -1, 3]}}}], + 34455, + "$substrCP " + + "does not accept negative integers as inputs."); + +assertErrorCode(coll, + [{$project: {substr: {$substrCP: ["abc", 1, -3]}}}], + 34454, + "$substrCP " + + "does not accept negative integers as inputs."); }()); diff --git a/jstests/aggregation/bugs/server25590.js b/jstests/aggregation/bugs/server25590.js index 329ae808a2c..b478f806029 100644 --- a/jstests/aggregation/bugs/server25590.js +++ b/jstests/aggregation/bugs/server25590.js @@ -1,19 +1,19 @@ // Test that an aggregate command where the "pipeline" field has the wrong type fails with a // TypeMismatch error. (function() { - "use strict"; +"use strict"; - const coll = db.server25590; - coll.drop(); +const coll = db.server25590; +coll.drop(); - assert.writeOK(coll.insert({})); +assert.writeOK(coll.insert({})); - assert.commandFailedWithCode(db.runCommand({aggregate: coll.getName(), pipeline: 1}), - ErrorCodes.TypeMismatch); - assert.commandFailedWithCode(db.runCommand({aggregate: coll.getName(), pipeline: {}}), - ErrorCodes.TypeMismatch); - assert.commandFailedWithCode(db.runCommand({aggregate: coll.getName(), pipeline: [1, 2]}), - ErrorCodes.TypeMismatch); - assert.commandFailedWithCode(db.runCommand({aggregate: coll.getName(), pipeline: [1, null]}), - ErrorCodes.TypeMismatch); +assert.commandFailedWithCode(db.runCommand({aggregate: coll.getName(), pipeline: 1}), + ErrorCodes.TypeMismatch); +assert.commandFailedWithCode(db.runCommand({aggregate: coll.getName(), pipeline: {}}), + ErrorCodes.TypeMismatch); +assert.commandFailedWithCode(db.runCommand({aggregate: coll.getName(), pipeline: [1, 2]}), + ErrorCodes.TypeMismatch); +assert.commandFailedWithCode(db.runCommand({aggregate: coll.getName(), pipeline: [1, null]}), + ErrorCodes.TypeMismatch); })(); diff --git a/jstests/aggregation/bugs/server26462.js b/jstests/aggregation/bugs/server26462.js index b0ef33ae35b..08225e54ce3 100644 --- a/jstests/aggregation/bugs/server26462.js +++ b/jstests/aggregation/bugs/server26462.js @@ -1,29 +1,29 @@ // Tests that adding a field that only contains metadata does not cause a segmentation fault when // grouping on the added field. (function() { - "use strict"; +"use strict"; - // Drop the old test collection, if any. - db.server26462.drop(); +// Drop the old test collection, if any. +db.server26462.drop(); - // Insert some test documents into the collection. - assert.writeOK(db.server26462.insert({"_id": 1, "title": "cakes and ale"})); - assert.writeOK(db.server26462.insert({"_id": 2, "title": "more cakes"})); - assert.writeOK(db.server26462.insert({"_id": 3, "title": "bread"})); - assert.writeOK(db.server26462.insert({"_id": 4, "title": "some cakes"})); +// Insert some test documents into the collection. +assert.writeOK(db.server26462.insert({"_id": 1, "title": "cakes and ale"})); +assert.writeOK(db.server26462.insert({"_id": 2, "title": "more cakes"})); +assert.writeOK(db.server26462.insert({"_id": 3, "title": "bread"})); +assert.writeOK(db.server26462.insert({"_id": 4, "title": "some cakes"})); - // Create a text index on the documents. - assert.commandWorked(db.server26462.createIndex({title: "text"})); +// Create a text index on the documents. +assert.commandWorked(db.server26462.createIndex({title: "text"})); - // Add a metadata only field in the aggregation pipeline and use that field in the $group _id. - let res = db.server26462 - .aggregate([ - {$match: {$text: {$search: "cake"}}}, - {$addFields: {fooScore: {$meta: "textScore"}}}, - {$group: {_id: "$fooScore", count: {$sum: 1}}} - ]) - .itcount(); +// Add a metadata only field in the aggregation pipeline and use that field in the $group _id. +let res = db.server26462 + .aggregate([ + {$match: {$text: {$search: "cake"}}}, + {$addFields: {fooScore: {$meta: "textScore"}}}, + {$group: {_id: "$fooScore", count: {$sum: 1}}} + ]) + .itcount(); - // Assert that the command worked. - assert.eq(2, res); +// Assert that the command worked. +assert.eq(2, res); })(); diff --git a/jstests/aggregation/bugs/server37750.js b/jstests/aggregation/bugs/server37750.js index cdfd098d87d..902c427c292 100644 --- a/jstests/aggregation/bugs/server37750.js +++ b/jstests/aggregation/bugs/server37750.js @@ -6,75 +6,75 @@ * requires_sharding] */ (function() { - "use strict"; +"use strict"; - load("jstests/libs/fixture_helpers.js"); // For FixtureHelpers. +load("jstests/libs/fixture_helpers.js"); // For FixtureHelpers. - // Set up a 2-shard cluster. Configure 'internalQueryExecYieldIterations' on both shards such - // that operations will yield on each PlanExecuter iteration. - const st = new ShardingTest({ - name: jsTestName(), - shards: 2, - rs: {nodes: 1, setParameter: {internalQueryExecYieldIterations: 1}} - }); +// Set up a 2-shard cluster. Configure 'internalQueryExecYieldIterations' on both shards such +// that operations will yield on each PlanExecuter iteration. +const st = new ShardingTest({ + name: jsTestName(), + shards: 2, + rs: {nodes: 1, setParameter: {internalQueryExecYieldIterations: 1}} +}); - const mongosDB = st.s.getDB(jsTestName()); - const mongosColl = mongosDB.test; +const mongosDB = st.s.getDB(jsTestName()); +const mongosColl = mongosDB.test; - // Shard the test collection, split it at {_id: 0}, and move the upper chunk to shard1. - st.shardColl(mongosColl, {_id: 1}, {_id: 0}, {_id: 0}); +// Shard the test collection, split it at {_id: 0}, and move the upper chunk to shard1. +st.shardColl(mongosColl, {_id: 1}, {_id: 0}, {_id: 0}); - // Insert enough documents on each shard to induce the $sample random-cursor optimization. - for (let i = (-150); i < 150; ++i) { - assert.commandWorked(mongosColl.insert({_id: i})); - } +// Insert enough documents on each shard to induce the $sample random-cursor optimization. +for (let i = (-150); i < 150; ++i) { + assert.commandWorked(mongosColl.insert({_id: i})); +} - // Run the initial aggregate for the $sample stage. - const cmdRes = assert.commandWorked(mongosDB.runCommand({ - aggregate: mongosColl.getName(), - pipeline: [{$sample: {size: 3}}], - comment: "$sample random", - cursor: {batchSize: 0} - })); - assert.eq(cmdRes.cursor.firstBatch.length, 0); +// Run the initial aggregate for the $sample stage. +const cmdRes = assert.commandWorked(mongosDB.runCommand({ + aggregate: mongosColl.getName(), + pipeline: [{$sample: {size: 3}}], + comment: "$sample random", + cursor: {batchSize: 0} +})); +assert.eq(cmdRes.cursor.firstBatch.length, 0); - // Force each shard to hang on yield to allow for currentOp capture. - FixtureHelpers.runCommandOnEachPrimary({ - db: mongosDB.getSiblingDB("admin"), - cmdObj: { - configureFailPoint: "setYieldAllLocksHang", - mode: "alwaysOn", - data: {namespace: mongosColl.getFullName()} - } - }); +// Force each shard to hang on yield to allow for currentOp capture. +FixtureHelpers.runCommandOnEachPrimary({ + db: mongosDB.getSiblingDB("admin"), + cmdObj: { + configureFailPoint: "setYieldAllLocksHang", + mode: "alwaysOn", + data: {namespace: mongosColl.getFullName()} + } +}); - // Run $currentOp to confirm that the $sample getMore yields on both shards. - const awaitShell = startParallelShell(() => { - load("jstests/libs/fixture_helpers.js"); - assert.soon(() => db.getSiblingDB("admin") - .aggregate([ - {$currentOp: {}}, - { - $match: { - "cursor.originatingCommand.comment": "$sample random", - planSummary: "QUEUED_DATA, MULTI_ITERATOR", - numYields: {$gt: 0} - } +// Run $currentOp to confirm that the $sample getMore yields on both shards. +const awaitShell = startParallelShell(() => { + load("jstests/libs/fixture_helpers.js"); + assert.soon(() => db.getSiblingDB("admin") + .aggregate([ + {$currentOp: {}}, + { + $match: { + "cursor.originatingCommand.comment": "$sample random", + planSummary: "QUEUED_DATA, MULTI_ITERATOR", + numYields: {$gt: 0} } - ]) - .itcount() === 2); - // Release the failpoint and allow the getMores to complete. - FixtureHelpers.runCommandOnEachPrimary({ - db: db.getSiblingDB("admin"), - cmdObj: {configureFailPoint: "setYieldAllLocksHang", mode: "off"} - }); - }, mongosDB.getMongo().port); + } + ]) + .itcount() === 2); + // Release the failpoint and allow the getMores to complete. + FixtureHelpers.runCommandOnEachPrimary({ + db: db.getSiblingDB("admin"), + cmdObj: {configureFailPoint: "setYieldAllLocksHang", mode: "off"} + }); +}, mongosDB.getMongo().port); - // Retrieve the results for the $sample aggregation. - const sampleCursor = new DBCommandCursor(mongosDB, cmdRes); - assert.eq(sampleCursor.toArray().length, 3); +// Retrieve the results for the $sample aggregation. +const sampleCursor = new DBCommandCursor(mongosDB, cmdRes); +assert.eq(sampleCursor.toArray().length, 3); - // Confirm that the parallel shell completes successfully, and tear down the cluster. - awaitShell(); - st.stop(); +// Confirm that the parallel shell completes successfully, and tear down the cluster. +awaitShell(); +st.stop(); })();
\ No newline at end of file diff --git a/jstests/aggregation/bugs/server4588.js b/jstests/aggregation/bugs/server4588.js index 000cc8f0231..be04773c0ff 100644 --- a/jstests/aggregation/bugs/server4588.js +++ b/jstests/aggregation/bugs/server4588.js @@ -1,60 +1,55 @@ // SERVER-4588 Add option to $unwind to emit array index. (function() { - "use strict"; +"use strict"; - const coll = db.server4588; - coll.drop(); +const coll = db.server4588; +coll.drop(); - assert.writeOK(coll.insert({_id: 0})); - assert.writeOK(coll.insert({_id: 1, x: null})); - assert.writeOK(coll.insert({_id: 2, x: []})); - assert.writeOK(coll.insert({_id: 3, x: [1, 2, 3]})); - assert.writeOK(coll.insert({_id: 4, x: 5})); +assert.writeOK(coll.insert({_id: 0})); +assert.writeOK(coll.insert({_id: 1, x: null})); +assert.writeOK(coll.insert({_id: 2, x: []})); +assert.writeOK(coll.insert({_id: 3, x: [1, 2, 3]})); +assert.writeOK(coll.insert({_id: 4, x: 5})); - // Without includeArrayIndex. - let actualResults = - coll.aggregate([{$unwind: {path: "$x"}}, {$sort: {_id: 1, x: 1}}]).toArray(); - let expectedResults = [ - {_id: 3, x: 1}, - {_id: 3, x: 2}, - {_id: 3, x: 3}, - {_id: 4, x: 5}, - ]; - assert.eq(expectedResults, actualResults, "Incorrect results for normal $unwind"); +// Without includeArrayIndex. +let actualResults = coll.aggregate([{$unwind: {path: "$x"}}, {$sort: {_id: 1, x: 1}}]).toArray(); +let expectedResults = [ + {_id: 3, x: 1}, + {_id: 3, x: 2}, + {_id: 3, x: 3}, + {_id: 4, x: 5}, +]; +assert.eq(expectedResults, actualResults, "Incorrect results for normal $unwind"); - // With includeArrayIndex, index inserted into a new field. - actualResults = - coll.aggregate( - [{$unwind: {path: "$x", includeArrayIndex: "index"}}, {$sort: {_id: 1, x: 1}}]) - .toArray(); - expectedResults = [ - {_id: 3, x: 1, index: NumberLong(0)}, - {_id: 3, x: 2, index: NumberLong(1)}, - {_id: 3, x: 3, index: NumberLong(2)}, - {_id: 4, x: 5, index: null}, - ]; - assert.eq(expectedResults, actualResults, "Incorrect results $unwind with includeArrayIndex"); +// With includeArrayIndex, index inserted into a new field. +actualResults = + coll.aggregate([{$unwind: {path: "$x", includeArrayIndex: "index"}}, {$sort: {_id: 1, x: 1}}]) + .toArray(); +expectedResults = [ + {_id: 3, x: 1, index: NumberLong(0)}, + {_id: 3, x: 2, index: NumberLong(1)}, + {_id: 3, x: 3, index: NumberLong(2)}, + {_id: 4, x: 5, index: null}, +]; +assert.eq(expectedResults, actualResults, "Incorrect results $unwind with includeArrayIndex"); - // With both includeArrayIndex and preserveNullAndEmptyArrays. - actualResults = - coll.aggregate([ - { - $unwind: - {path: "$x", includeArrayIndex: "index", preserveNullAndEmptyArrays: true} - }, - {$sort: {_id: 1, x: 1}} - ]) - .toArray(); - expectedResults = [ - {_id: 0, index: null}, - {_id: 1, x: null, index: null}, - {_id: 2, index: null}, - {_id: 3, x: 1, index: NumberLong(0)}, - {_id: 3, x: 2, index: NumberLong(1)}, - {_id: 3, x: 3, index: NumberLong(2)}, - {_id: 4, x: 5, index: null}, - ]; - assert.eq(expectedResults, - actualResults, - "Incorrect results $unwind with includeArrayIndex and preserveNullAndEmptyArrays"); +// With both includeArrayIndex and preserveNullAndEmptyArrays. +actualResults = + coll.aggregate([ + {$unwind: {path: "$x", includeArrayIndex: "index", preserveNullAndEmptyArrays: true}}, + {$sort: {_id: 1, x: 1}} + ]) + .toArray(); +expectedResults = [ + {_id: 0, index: null}, + {_id: 1, x: null, index: null}, + {_id: 2, index: null}, + {_id: 3, x: 1, index: NumberLong(0)}, + {_id: 3, x: 2, index: NumberLong(1)}, + {_id: 3, x: 3, index: NumberLong(2)}, + {_id: 4, x: 5, index: null}, +]; +assert.eq(expectedResults, + actualResults, + "Incorrect results $unwind with includeArrayIndex and preserveNullAndEmptyArrays"); }()); diff --git a/jstests/aggregation/bugs/server4589.js b/jstests/aggregation/bugs/server4589.js index e7f2e1b9746..efa7254e4d9 100644 --- a/jstests/aggregation/bugs/server4589.js +++ b/jstests/aggregation/bugs/server4589.js @@ -4,67 +4,67 @@ load('jstests/aggregation/extras/utils.js'); (function() { - 'use strict'; - - var coll = db.agg_array_elem_at_expr; - coll.drop(); - - assert.writeOK(coll.insert({a: [1, 2, 3, 4, 5]})); - - // Normal indexing. - var pipeline = [{$project: {_id: 0, x: {$arrayElemAt: ['$a', 2]}}}]; - assert.eq(coll.aggregate(pipeline).toArray(), [{x: 3}]); - - // Indexing with a float. - pipeline = [{$project: {_id: 0, x: {$arrayElemAt: ['$a', 1.0]}}}]; - assert.eq(coll.aggregate(pipeline).toArray(), [{x: 2}]); - - // Indexing with a decimal - pipeline = [{$project: {_id: 0, x: {$arrayElemAt: ['$a', NumberDecimal('2.0')]}}}]; - assert.eq(coll.aggregate(pipeline).toArray(), [{x: 3}]); - - // Negative indexing. - pipeline = [{$project: {_id: 0, x: {$arrayElemAt: ['$a', -1]}}}]; - assert.eq(coll.aggregate(pipeline).toArray(), [{x: 5}]); - pipeline = [{$project: {_id: 0, x: {$arrayElemAt: ['$a', -5]}}}]; - assert.eq(coll.aggregate(pipeline).toArray(), [{x: 1}]); - - // Out of bounds positive. - pipeline = [{$project: {_id: 0, x: {$arrayElemAt: ['$a', 5]}}}]; - assert.eq(coll.aggregate(pipeline).toArray(), [{}]); - pipeline = [{$project: {_id: 0, x: {$arrayElemAt: ['$a', Math.pow(2, 31) - 1]}}}]; - assert.eq(coll.aggregate(pipeline).toArray(), [{}]); - pipeline = [{$project: {_id: 0, x: {$arrayElemAt: ['$a', NumberLong(Math.pow(2, 31) - 1)]}}}]; - assert.eq(coll.aggregate(pipeline).toArray(), [{}]); - - // Out of bounds negative. - pipeline = [{$project: {_id: 0, x: {$arrayElemAt: ['$a', -6]}}}]; - assert.eq(coll.aggregate(pipeline).toArray(), [{}]); - pipeline = [{$project: {_id: 0, x: {$arrayElemAt: ['$a', -Math.pow(2, 31)]}}}]; - assert.eq(coll.aggregate(pipeline).toArray(), [{}]); - pipeline = [{$project: {_id: 0, x: {$arrayElemAt: ['$a', NumberLong(-Math.pow(2, 31))]}}}]; - assert.eq(coll.aggregate(pipeline).toArray(), [{}]); - - // Null inputs. - pipeline = [{$project: {_id: 0, x: {$arrayElemAt: ['$a', null]}}}]; - assert.eq(coll.aggregate(pipeline).toArray(), [{x: null}]); - pipeline = [{$project: {_id: 0, x: {$arrayElemAt: [null, 4]}}}]; - assert.eq(coll.aggregate(pipeline).toArray(), [{x: null}]); - - // Error cases. - - // Wrong number of arguments. - assertErrorCode(coll, [{$project: {x: {$arrayElemAt: [['one', 'arg']]}}}], 16020); - - // First argument is not an array. - assertErrorCode(coll, [{$project: {x: {$arrayElemAt: ['one', 2]}}}], 28689); - - // Second argument is not numeric. - assertErrorCode(coll, [{$project: {x: {$arrayElemAt: [[1, 2], '2']}}}], 28690); - - // Second argument is not integral. - assertErrorCode(coll, [{$project: {x: {$arrayElemAt: [[1, 2], 1.5]}}}], 28691); - assertErrorCode(coll, [{$project: {x: {$arrayElemAt: [[1, 2], NumberDecimal('1.5')]}}}], 28691); - assertErrorCode(coll, [{$project: {x: {$arrayElemAt: [[1, 2], Math.pow(2, 32)]}}}], 28691); - assertErrorCode(coll, [{$project: {x: {$arrayElemAt: [[1, 2], -Math.pow(2, 31) - 1]}}}], 28691); +'use strict'; + +var coll = db.agg_array_elem_at_expr; +coll.drop(); + +assert.writeOK(coll.insert({a: [1, 2, 3, 4, 5]})); + +// Normal indexing. +var pipeline = [{$project: {_id: 0, x: {$arrayElemAt: ['$a', 2]}}}]; +assert.eq(coll.aggregate(pipeline).toArray(), [{x: 3}]); + +// Indexing with a float. +pipeline = [{$project: {_id: 0, x: {$arrayElemAt: ['$a', 1.0]}}}]; +assert.eq(coll.aggregate(pipeline).toArray(), [{x: 2}]); + +// Indexing with a decimal +pipeline = [{$project: {_id: 0, x: {$arrayElemAt: ['$a', NumberDecimal('2.0')]}}}]; +assert.eq(coll.aggregate(pipeline).toArray(), [{x: 3}]); + +// Negative indexing. +pipeline = [{$project: {_id: 0, x: {$arrayElemAt: ['$a', -1]}}}]; +assert.eq(coll.aggregate(pipeline).toArray(), [{x: 5}]); +pipeline = [{$project: {_id: 0, x: {$arrayElemAt: ['$a', -5]}}}]; +assert.eq(coll.aggregate(pipeline).toArray(), [{x: 1}]); + +// Out of bounds positive. +pipeline = [{$project: {_id: 0, x: {$arrayElemAt: ['$a', 5]}}}]; +assert.eq(coll.aggregate(pipeline).toArray(), [{}]); +pipeline = [{$project: {_id: 0, x: {$arrayElemAt: ['$a', Math.pow(2, 31) - 1]}}}]; +assert.eq(coll.aggregate(pipeline).toArray(), [{}]); +pipeline = [{$project: {_id: 0, x: {$arrayElemAt: ['$a', NumberLong(Math.pow(2, 31) - 1)]}}}]; +assert.eq(coll.aggregate(pipeline).toArray(), [{}]); + +// Out of bounds negative. +pipeline = [{$project: {_id: 0, x: {$arrayElemAt: ['$a', -6]}}}]; +assert.eq(coll.aggregate(pipeline).toArray(), [{}]); +pipeline = [{$project: {_id: 0, x: {$arrayElemAt: ['$a', -Math.pow(2, 31)]}}}]; +assert.eq(coll.aggregate(pipeline).toArray(), [{}]); +pipeline = [{$project: {_id: 0, x: {$arrayElemAt: ['$a', NumberLong(-Math.pow(2, 31))]}}}]; +assert.eq(coll.aggregate(pipeline).toArray(), [{}]); + +// Null inputs. +pipeline = [{$project: {_id: 0, x: {$arrayElemAt: ['$a', null]}}}]; +assert.eq(coll.aggregate(pipeline).toArray(), [{x: null}]); +pipeline = [{$project: {_id: 0, x: {$arrayElemAt: [null, 4]}}}]; +assert.eq(coll.aggregate(pipeline).toArray(), [{x: null}]); + +// Error cases. + +// Wrong number of arguments. +assertErrorCode(coll, [{$project: {x: {$arrayElemAt: [['one', 'arg']]}}}], 16020); + +// First argument is not an array. +assertErrorCode(coll, [{$project: {x: {$arrayElemAt: ['one', 2]}}}], 28689); + +// Second argument is not numeric. +assertErrorCode(coll, [{$project: {x: {$arrayElemAt: [[1, 2], '2']}}}], 28690); + +// Second argument is not integral. +assertErrorCode(coll, [{$project: {x: {$arrayElemAt: [[1, 2], 1.5]}}}], 28691); +assertErrorCode(coll, [{$project: {x: {$arrayElemAt: [[1, 2], NumberDecimal('1.5')]}}}], 28691); +assertErrorCode(coll, [{$project: {x: {$arrayElemAt: [[1, 2], Math.pow(2, 32)]}}}], 28691); +assertErrorCode(coll, [{$project: {x: {$arrayElemAt: [[1, 2], -Math.pow(2, 31) - 1]}}}], 28691); }()); diff --git a/jstests/aggregation/bugs/server4638.js b/jstests/aggregation/bugs/server4638.js index 4934da94a34..ee6f7cfd6df 100644 --- a/jstests/aggregation/bugs/server4638.js +++ b/jstests/aggregation/bugs/server4638.js @@ -13,4 +13,4 @@ assert.eq(res[0].x, 0); // Make sure having an undefined doesn't break pipelines that do use the field res = t.aggregate({$project: {undef: 1}}).toArray(); assert.eq(res[0].undef, undefined); -assert.eq(typeof(res[0].undef), "undefined"); +assert.eq(typeof (res[0].undef), "undefined"); diff --git a/jstests/aggregation/bugs/server5012.js b/jstests/aggregation/bugs/server5012.js index a9955349490..14dfe914b52 100644 --- a/jstests/aggregation/bugs/server5012.js +++ b/jstests/aggregation/bugs/server5012.js @@ -1,11 +1,11 @@ (function() { - "use strict"; - load('jstests/aggregation/data/articles.js'); +"use strict"; +load('jstests/aggregation/data/articles.js'); - const article = db.getSiblingDB("aggdb").getCollection("article"); - const cursor = article.aggregate( - [{$sort: {_id: 1}}, {$project: {author: 1, _id: 0}}, {$project: {Writer: "$author"}}]); - const expected = [{Writer: "bob"}, {Writer: "dave"}, {Writer: "jane"}]; +const article = db.getSiblingDB("aggdb").getCollection("article"); +const cursor = article.aggregate( + [{$sort: {_id: 1}}, {$project: {author: 1, _id: 0}}, {$project: {Writer: "$author"}}]); +const expected = [{Writer: "bob"}, {Writer: "dave"}, {Writer: "jane"}]; - assert.eq(cursor.toArray(), expected); +assert.eq(cursor.toArray(), expected); }()); diff --git a/jstests/aggregation/bugs/server533.js b/jstests/aggregation/bugs/server533.js index b64ddc9669f..d66c5d27ad8 100644 --- a/jstests/aggregation/bugs/server533.js +++ b/jstests/aggregation/bugs/server533.js @@ -4,32 +4,32 @@ load('jstests/aggregation/extras/utils.js'); (function() { - 'use strict'; - - var coll = db.agg_sample; - coll.drop(); - - // Should return no results on a collection that doesn't exist. Should not crash. - assert.eq(coll.aggregate([{$sample: {size: 10}}]).toArray(), []); - - var nItems = 3; - for (var i = 0; i < nItems; i++) { - assert.writeOK(coll.insert({_id: i})); - } - - [0, 1, nItems, nItems + 1].forEach(function(size) { - var results = coll.aggregate([{$sample: {size: size}}]).toArray(); - assert.eq(results.length, Math.min(size, nItems)); - }); - - // Multiple $sample stages are allowed. - var results = coll.aggregate([{$sample: {size: nItems}}, {$sample: {size: 1}}]).toArray(); - assert.eq(results.length, 1); - - // Invalid options. - assertErrorCode(coll, [{$sample: 'string'}], 28745); - assertErrorCode(coll, [{$sample: {size: 'string'}}], 28746); - assertErrorCode(coll, [{$sample: {size: -1}}], 28747); - assertErrorCode(coll, [{$sample: {unknownOpt: true}}], 28748); - assertErrorCode(coll, [{$sample: {/* no size */}}], 28749); +'use strict'; + +var coll = db.agg_sample; +coll.drop(); + +// Should return no results on a collection that doesn't exist. Should not crash. +assert.eq(coll.aggregate([{$sample: {size: 10}}]).toArray(), []); + +var nItems = 3; +for (var i = 0; i < nItems; i++) { + assert.writeOK(coll.insert({_id: i})); +} + +[0, 1, nItems, nItems + 1].forEach(function(size) { + var results = coll.aggregate([{$sample: {size: size}}]).toArray(); + assert.eq(results.length, Math.min(size, nItems)); +}); + +// Multiple $sample stages are allowed. +var results = coll.aggregate([{$sample: {size: nItems}}, {$sample: {size: 1}}]).toArray(); +assert.eq(results.length, 1); + +// Invalid options. +assertErrorCode(coll, [{$sample: 'string'}], 28745); +assertErrorCode(coll, [{$sample: {size: 'string'}}], 28746); +assertErrorCode(coll, [{$sample: {size: -1}}], 28747); +assertErrorCode(coll, [{$sample: {unknownOpt: true}}], 28748); +assertErrorCode(coll, [{$sample: {/* no size */}}], 28749); }()); diff --git a/jstests/aggregation/bugs/server6074.js b/jstests/aggregation/bugs/server6074.js index 8adf6b7eca8..8e53459ba9e 100644 --- a/jstests/aggregation/bugs/server6074.js +++ b/jstests/aggregation/bugs/server6074.js @@ -4,78 +4,78 @@ load('jstests/aggregation/extras/utils.js'); (function() { - 'use strict'; - - var coll = db.agg_slice_expr; - coll.drop(); - - // Need to have at least one document to ensure the pipeline executes. - assert.writeOK(coll.insert({})); - - function testSlice(sliceArgs, expArray) { - var pipeline = [{$project: {_id: 0, slice: {$slice: sliceArgs}}}]; - assert.eq(coll.aggregate(pipeline).toArray(), [{slice: expArray}]); - } - - // Two argument form. - - testSlice([[0, 1, 2, 3, 4], 2], [0, 1]); - testSlice([[0, 1, 2, 3, 4], 2.0], [0, 1]); - // Negative count - testSlice([[0, 1, 2, 3, 4], -2], [3, 4]); - testSlice([[0, 1, 2, 3, 4], -2.0], [3, 4]); - // Zero count. - testSlice([[0, 1, 2, 3, 4], 0], []); - // Out of bounds positive. - testSlice([[0, 1, 2, 3, 4], 10], [0, 1, 2, 3, 4]); - // Out of bounds negative. - testSlice([[0, 1, 2, 3, 4], -10], [0, 1, 2, 3, 4]); - // Null arguments - testSlice([null, -10], null); - testSlice([[0, 1, 2, 3, 4], null], null); - - // Three argument form. - - testSlice([[0, 1, 2, 3, 4], 1, 2], [1, 2]); - testSlice([[0, 1, 2, 3, 4], 1.0, 2.0], [1, 2]); - // Negative start index. - testSlice([[0, 1, 2, 3, 4], -3, 2], [2, 3]); - testSlice([[0, 1, 2, 3, 4], -5, 2], [0, 1]); - // Slice starts out of bounds. - testSlice([[0, 1, 2, 3, 4], -10, 2], [0, 1]); - testSlice([[0, 1, 2, 3, 4], 10, 2], []); - // Slice ends out of bounds. - testSlice([[0, 1, 2, 3, 4], 4, 3], [4]); - testSlice([[0, 1, 2, 3, 4], -1, 3], [4]); - // Null arguments - testSlice([[0, 1, 2, 3, 4], -1, null], null); - - // Error cases. - - // Wrong number of arguments. - assertErrorCode(coll, [{$project: {x: {$slice: [[1, 2, 3]]}}}], 28667); - assertErrorCode(coll, [{$project: {x: {$slice: [[1, 2, 3], 4, 5, 6]}}}], 28667); - - // First argument is not an array. - assertErrorCode(coll, [{$project: {x: {$slice: ['one', 2]}}}], 28724); - - // Second argument is not numeric. - assertErrorCode(coll, [{$project: {x: {$slice: [[1, 2], '2']}}}], 28725); - - // Second argument is not integral. - assertErrorCode(coll, [{$project: {x: {$slice: [[1, 2], 1.5]}}}], 28726); - assertErrorCode(coll, [{$project: {x: {$slice: [[1, 2], Math.pow(2, 32)]}}}], 28726); - assertErrorCode(coll, [{$project: {x: {$slice: [[1, 2], -Math.pow(2, 31) - 1]}}}], 28726); - - // Third argument is not numeric. - assertErrorCode(coll, [{$project: {x: {$slice: [[1, 2], 0, '2']}}}], 28727); - - // Third argument is not integral. - assertErrorCode(coll, [{$project: {x: {$slice: [[1, 2], 0, 1.5]}}}], 28728); - assertErrorCode(coll, [{$project: {x: {$slice: [[1, 2], 0, Math.pow(2, 32)]}}}], 28728); - assertErrorCode(coll, [{$project: {x: {$slice: [[1, 2], 0, -Math.pow(2, 31) - 1]}}}], 28728); - - // Third argument is not positive. - assertErrorCode(coll, [{$project: {x: {$slice: [[1, 2], 0, 0]}}}], 28729); - assertErrorCode(coll, [{$project: {x: {$slice: [[1, 2], 0, -1]}}}], 28729); +'use strict'; + +var coll = db.agg_slice_expr; +coll.drop(); + +// Need to have at least one document to ensure the pipeline executes. +assert.writeOK(coll.insert({})); + +function testSlice(sliceArgs, expArray) { + var pipeline = [{$project: {_id: 0, slice: {$slice: sliceArgs}}}]; + assert.eq(coll.aggregate(pipeline).toArray(), [{slice: expArray}]); +} + +// Two argument form. + +testSlice([[0, 1, 2, 3, 4], 2], [0, 1]); +testSlice([[0, 1, 2, 3, 4], 2.0], [0, 1]); +// Negative count +testSlice([[0, 1, 2, 3, 4], -2], [3, 4]); +testSlice([[0, 1, 2, 3, 4], -2.0], [3, 4]); +// Zero count. +testSlice([[0, 1, 2, 3, 4], 0], []); +// Out of bounds positive. +testSlice([[0, 1, 2, 3, 4], 10], [0, 1, 2, 3, 4]); +// Out of bounds negative. +testSlice([[0, 1, 2, 3, 4], -10], [0, 1, 2, 3, 4]); +// Null arguments +testSlice([null, -10], null); +testSlice([[0, 1, 2, 3, 4], null], null); + +// Three argument form. + +testSlice([[0, 1, 2, 3, 4], 1, 2], [1, 2]); +testSlice([[0, 1, 2, 3, 4], 1.0, 2.0], [1, 2]); +// Negative start index. +testSlice([[0, 1, 2, 3, 4], -3, 2], [2, 3]); +testSlice([[0, 1, 2, 3, 4], -5, 2], [0, 1]); +// Slice starts out of bounds. +testSlice([[0, 1, 2, 3, 4], -10, 2], [0, 1]); +testSlice([[0, 1, 2, 3, 4], 10, 2], []); +// Slice ends out of bounds. +testSlice([[0, 1, 2, 3, 4], 4, 3], [4]); +testSlice([[0, 1, 2, 3, 4], -1, 3], [4]); +// Null arguments +testSlice([[0, 1, 2, 3, 4], -1, null], null); + +// Error cases. + +// Wrong number of arguments. +assertErrorCode(coll, [{$project: {x: {$slice: [[1, 2, 3]]}}}], 28667); +assertErrorCode(coll, [{$project: {x: {$slice: [[1, 2, 3], 4, 5, 6]}}}], 28667); + +// First argument is not an array. +assertErrorCode(coll, [{$project: {x: {$slice: ['one', 2]}}}], 28724); + +// Second argument is not numeric. +assertErrorCode(coll, [{$project: {x: {$slice: [[1, 2], '2']}}}], 28725); + +// Second argument is not integral. +assertErrorCode(coll, [{$project: {x: {$slice: [[1, 2], 1.5]}}}], 28726); +assertErrorCode(coll, [{$project: {x: {$slice: [[1, 2], Math.pow(2, 32)]}}}], 28726); +assertErrorCode(coll, [{$project: {x: {$slice: [[1, 2], -Math.pow(2, 31) - 1]}}}], 28726); + +// Third argument is not numeric. +assertErrorCode(coll, [{$project: {x: {$slice: [[1, 2], 0, '2']}}}], 28727); + +// Third argument is not integral. +assertErrorCode(coll, [{$project: {x: {$slice: [[1, 2], 0, 1.5]}}}], 28728); +assertErrorCode(coll, [{$project: {x: {$slice: [[1, 2], 0, Math.pow(2, 32)]}}}], 28728); +assertErrorCode(coll, [{$project: {x: {$slice: [[1, 2], 0, -Math.pow(2, 31) - 1]}}}], 28728); + +// Third argument is not positive. +assertErrorCode(coll, [{$project: {x: {$slice: [[1, 2], 0, 0]}}}], 28729); +assertErrorCode(coll, [{$project: {x: {$slice: [[1, 2], 0, -1]}}}], 28729); }()); diff --git a/jstests/aggregation/bugs/server6125.js b/jstests/aggregation/bugs/server6125.js index 592a560312a..bd4ab4ce220 100644 --- a/jstests/aggregation/bugs/server6125.js +++ b/jstests/aggregation/bugs/server6125.js @@ -65,9 +65,9 @@ function setupArray() { {_id: 13, a: new Timestamp(1 / 1000, 1), ty: "Timestamp"}, {_id: 14, a: /regex/, ty: "RegExp"}, { - _id: 15, - a: new DBPointer("test.s6125", new ObjectId("0102030405060708090A0B0C")), - ty: "DBPointer" + _id: 15, + a: new DBPointer("test.s6125", new ObjectId("0102030405060708090A0B0C")), + ty: "DBPointer" }, {_id: 16, a: function() {}, ty: "Code"}, // Code with Scope not implemented in JS diff --git a/jstests/aggregation/bugs/server6127.js b/jstests/aggregation/bugs/server6127.js index 26585c87d21..1f11d858c83 100644 --- a/jstests/aggregation/bugs/server6127.js +++ b/jstests/aggregation/bugs/server6127.js @@ -7,21 +7,21 @@ * is no path). Previous it would uassert causing the aggregation to end. */ (function() { - "use strict"; - db.s6127.drop(); +"use strict"; +db.s6127.drop(); - assert.writeOK(db.s6127.insert({_id: 0, a: 1})); - assert.writeOK(db.s6127.insert({_id: 1, foo: 2})); - assert.writeOK(db.s6127.insert({_id: 2, foo: {bar: 3}})); +assert.writeOK(db.s6127.insert({_id: 0, a: 1})); +assert.writeOK(db.s6127.insert({_id: 1, foo: 2})); +assert.writeOK(db.s6127.insert({_id: 2, foo: {bar: 3}})); - // Aggregate checking the field foo and the path foo.bar. - const cursor = db.s6127.aggregate( - [{$sort: {_id: 1}}, {$project: {_id: 0, "foo.bar": 1, field: "$foo", path: "$foo.bar"}}]); +// Aggregate checking the field foo and the path foo.bar. +const cursor = db.s6127.aggregate( + [{$sort: {_id: 1}}, {$project: {_id: 0, "foo.bar": 1, field: "$foo", path: "$foo.bar"}}]); - // The first document should contain nothing as neither field exists, the second document should - // contain only field as it has a value in foo, but foo does not have a field bar so it cannot - // walk that path, the third document should have both the field and path as foo is an object - // which has a field bar. - const expected = [{}, {field: 2}, {foo: {bar: 3}, field: {bar: 3}, path: 3}]; - assert.eq(cursor.toArray(), expected); +// The first document should contain nothing as neither field exists, the second document should +// contain only field as it has a value in foo, but foo does not have a field bar so it cannot +// walk that path, the third document should have both the field and path as foo is an object +// which has a field bar. +const expected = [{}, {field: 2}, {foo: {bar: 3}, field: {bar: 3}, path: 3}]; +assert.eq(cursor.toArray(), expected); }()); diff --git a/jstests/aggregation/bugs/server6147.js b/jstests/aggregation/bugs/server6147.js index 0969b366636..c74e1848512 100644 --- a/jstests/aggregation/bugs/server6147.js +++ b/jstests/aggregation/bugs/server6147.js @@ -6,44 +6,44 @@ * constant and a field regardless of whether they were equal or not. */ (function() { - "use strict"; - db.s6147.drop(); +"use strict"; +db.s6147.drop(); - assert.writeOK(db.s6147.insert({a: 1})); - assert.writeOK(db.s6147.insert({a: 2})); +assert.writeOK(db.s6147.insert({a: 1})); +assert.writeOK(db.s6147.insert({a: 2})); - // Aggregate checking various combinations of the constant and the field. - const cursor = db.s6147.aggregate([ - {$sort: {a: 1}}, - { - $project: { - _id: 0, - constantAndField: {$ne: [1, "$a"]}, - fieldAndConstant: {$ne: ["$a", 1]}, - constantAndConstant: {$ne: [1, 1]}, - fieldAndField: {$ne: ["$a", "$a"]} - } +// Aggregate checking various combinations of the constant and the field. +const cursor = db.s6147.aggregate([ + {$sort: {a: 1}}, + { + $project: { + _id: 0, + constantAndField: {$ne: [1, "$a"]}, + fieldAndConstant: {$ne: ["$a", 1]}, + constantAndConstant: {$ne: [1, 1]}, + fieldAndField: {$ne: ["$a", "$a"]} } - ]); + } +]); - // In both documents, the constantAndConstant and fieldAndField should be false since they - // compare something with itself. However, the constantAndField and fieldAndConstant should be - // different as document one contains 1 which should return false and document 2 contains - // something different so should return true. - const expected = [ - { - constantAndField: false, - fieldAndConstant: false, - constantAndConstant: false, - fieldAndField: false - }, - { - constantAndField: true, - fieldAndConstant: true, - constantAndConstant: false, - fieldAndField: false - } - ]; +// In both documents, the constantAndConstant and fieldAndField should be false since they +// compare something with itself. However, the constantAndField and fieldAndConstant should be +// different as document one contains 1 which should return false and document 2 contains +// something different so should return true. +const expected = [ + { + constantAndField: false, + fieldAndConstant: false, + constantAndConstant: false, + fieldAndField: false + }, + { + constantAndField: true, + fieldAndConstant: true, + constantAndConstant: false, + fieldAndField: false + } +]; - assert.eq(cursor.toArray(), expected); +assert.eq(cursor.toArray(), expected); }()); diff --git a/jstests/aggregation/bugs/server6179.js b/jstests/aggregation/bugs/server6179.js index a5e934a9e89..065f5b261ee 100644 --- a/jstests/aggregation/bugs/server6179.js +++ b/jstests/aggregation/bugs/server6179.js @@ -4,53 +4,53 @@ // requires_spawning_own_processes, // ] (function() { - 'use strict'; - - var s = new ShardingTest({shards: 2}); - - assert.commandWorked(s.s0.adminCommand({enablesharding: "test"})); - s.ensurePrimaryShard('test', s.shard1.shardName); - assert.commandWorked(s.s0.adminCommand({shardcollection: "test.data", key: {_id: 1}})); - - var d = s.getDB("test"); - - // Insert _id values 0 - 99 - var N = 100; - - var bulkOp = d.data.initializeOrderedBulkOp(); - for (var i = 0; i < N; ++i) { - bulkOp.insert({_id: i, i: i % 10}); - } - bulkOp.execute(); - - // Split the data into 3 chunks - assert.commandWorked(s.s0.adminCommand({split: "test.data", middle: {_id: 33}})); - assert.commandWorked(s.s0.adminCommand({split: "test.data", middle: {_id: 66}})); - - // Migrate the middle chunk to another shard - assert.commandWorked(s.s0.adminCommand( - {movechunk: "test.data", find: {_id: 50}, to: s.getOther(s.getPrimaryShard("test")).name})); - - // Check that we get results rather than an error - var result = d.data - .aggregate({$group: {_id: '$_id', i: {$first: '$i'}}}, - {$group: {_id: '$i', avg_id: {$avg: '$_id'}}}, - {$sort: {_id: 1}}) - .toArray(); - var expected = [ - {"_id": 0, "avg_id": 45}, - {"_id": 1, "avg_id": 46}, - {"_id": 2, "avg_id": 47}, - {"_id": 3, "avg_id": 48}, - {"_id": 4, "avg_id": 49}, - {"_id": 5, "avg_id": 50}, - {"_id": 6, "avg_id": 51}, - {"_id": 7, "avg_id": 52}, - {"_id": 8, "avg_id": 53}, - {"_id": 9, "avg_id": 54} - ]; - - assert.eq(result, expected); - - s.stop(); +'use strict'; + +var s = new ShardingTest({shards: 2}); + +assert.commandWorked(s.s0.adminCommand({enablesharding: "test"})); +s.ensurePrimaryShard('test', s.shard1.shardName); +assert.commandWorked(s.s0.adminCommand({shardcollection: "test.data", key: {_id: 1}})); + +var d = s.getDB("test"); + +// Insert _id values 0 - 99 +var N = 100; + +var bulkOp = d.data.initializeOrderedBulkOp(); +for (var i = 0; i < N; ++i) { + bulkOp.insert({_id: i, i: i % 10}); +} +bulkOp.execute(); + +// Split the data into 3 chunks +assert.commandWorked(s.s0.adminCommand({split: "test.data", middle: {_id: 33}})); +assert.commandWorked(s.s0.adminCommand({split: "test.data", middle: {_id: 66}})); + +// Migrate the middle chunk to another shard +assert.commandWorked(s.s0.adminCommand( + {movechunk: "test.data", find: {_id: 50}, to: s.getOther(s.getPrimaryShard("test")).name})); + +// Check that we get results rather than an error +var result = d.data + .aggregate({$group: {_id: '$_id', i: {$first: '$i'}}}, + {$group: {_id: '$i', avg_id: {$avg: '$_id'}}}, + {$sort: {_id: 1}}) + .toArray(); +var expected = [ + {"_id": 0, "avg_id": 45}, + {"_id": 1, "avg_id": 46}, + {"_id": 2, "avg_id": 47}, + {"_id": 3, "avg_id": 48}, + {"_id": 4, "avg_id": 49}, + {"_id": 5, "avg_id": 50}, + {"_id": 6, "avg_id": 51}, + {"_id": 7, "avg_id": 52}, + {"_id": 8, "avg_id": 53}, + {"_id": 9, "avg_id": 54} +]; + +assert.eq(result, expected); + +s.stop(); })(); diff --git a/jstests/aggregation/bugs/server6185.js b/jstests/aggregation/bugs/server6185.js index cf084d4b371..06eacdf791d 100644 --- a/jstests/aggregation/bugs/server6185.js +++ b/jstests/aggregation/bugs/server6185.js @@ -2,16 +2,16 @@ * Tests that projecting a non-existent subfield behaves identically in both query and aggregation. */ (function() { - "use strict"; - const coll = db.c; - coll.drop(); +"use strict"; +const coll = db.c; +coll.drop(); - assert.writeOK(coll.insert({a: [1]})); - assert.writeOK(coll.insert({a: {c: 1}})); - assert.writeOK(coll.insert({a: [{c: 1}, {b: 1, c: 1}, {c: 1}]})); - assert.writeOK(coll.insert({a: 1})); - assert.writeOK(coll.insert({b: 1})); +assert.writeOK(coll.insert({a: [1]})); +assert.writeOK(coll.insert({a: {c: 1}})); +assert.writeOK(coll.insert({a: [{c: 1}, {b: 1, c: 1}, {c: 1}]})); +assert.writeOK(coll.insert({a: 1})); +assert.writeOK(coll.insert({b: 1})); - assert.eq(coll.aggregate([{$project: {'a.b': 1}}, {$sort: {_id: 1}}]).toArray(), - coll.find({}, {'a.b': 1}).sort({_id: 1}).toArray()); +assert.eq(coll.aggregate([{$project: {'a.b': 1}}, {$sort: {_id: 1}}]).toArray(), + coll.find({}, {'a.b': 1}).sort({_id: 1}).toArray()); }()); diff --git a/jstests/aggregation/bugs/server6530.js b/jstests/aggregation/bugs/server6530.js index 36a5d3deb3f..77dfcd703fb 100644 --- a/jstests/aggregation/bugs/server6530.js +++ b/jstests/aggregation/bugs/server6530.js @@ -2,31 +2,37 @@ * Test that $near queries are disallowed in $match stages. */ (function() { - "use strict"; - load("jstests/aggregation/extras/utils.js"); +"use strict"; +load("jstests/aggregation/extras/utils.js"); - const coll = db.getCollection("no_near_in_match"); - coll.drop(); +const coll = db.getCollection("no_near_in_match"); +coll.drop(); - // Create indexes that could satisfy various $near queries. - assert.commandWorked(coll.createIndex({point2d: "2d"})); - assert.commandWorked(coll.createIndex({point2dsphere: "2dsphere"})); +// Create indexes that could satisfy various $near queries. +assert.commandWorked(coll.createIndex({point2d: "2d"})); +assert.commandWorked(coll.createIndex({point2dsphere: "2dsphere"})); - // Populate the collection so that successful queries can return at least one result. - assert.writeOK(coll.insert({point2d: [0.25, 0.35]})); - assert.writeOK(coll.insert({point2dsphere: [0.25, 0.35]})); +// Populate the collection so that successful queries can return at least one result. +assert.writeOK(coll.insert({point2d: [0.25, 0.35]})); +assert.writeOK(coll.insert({point2dsphere: [0.25, 0.35]})); - const nearQuery = {point2d: {$near: [0, 0]}}; - const nearSphereQuery = {point2dsphere: {$nearSphere: [0, 0]}}; - const geoNearQuery = {point2d: {$geoNear: [0, 0]}}; +const nearQuery = { + point2d: {$near: [0, 0]} +}; +const nearSphereQuery = { + point2dsphere: {$nearSphere: [0, 0]} +}; +const geoNearQuery = { + point2d: {$geoNear: [0, 0]} +}; - // Test that normal finds return a result. - assert.eq(1, coll.find(nearQuery).count()); - assert.eq(1, coll.find(nearSphereQuery).count()); - assert.eq(1, coll.find(geoNearQuery).count()); +// Test that normal finds return a result. +assert.eq(1, coll.find(nearQuery).count()); +assert.eq(1, coll.find(nearSphereQuery).count()); +assert.eq(1, coll.find(geoNearQuery).count()); - // Test that we refuse to run $match with a near query. - assertErrorCode(coll, {$match: nearQuery}, ErrorCodes.BadValue); - assertErrorCode(coll, {$match: nearSphereQuery}, ErrorCodes.BadValue); - assertErrorCode(coll, {$match: geoNearQuery}, ErrorCodes.BadValue); +// Test that we refuse to run $match with a near query. +assertErrorCode(coll, {$match: nearQuery}, ErrorCodes.BadValue); +assertErrorCode(coll, {$match: nearSphereQuery}, ErrorCodes.BadValue); +assertErrorCode(coll, {$match: geoNearQuery}, ErrorCodes.BadValue); }()); diff --git a/jstests/aggregation/bugs/server6779.js b/jstests/aggregation/bugs/server6779.js index 44f641ea15d..d9d48898068 100644 --- a/jstests/aggregation/bugs/server6779.js +++ b/jstests/aggregation/bugs/server6779.js @@ -1,20 +1,20 @@ // server 6779: serializing ExpressionCoerceToBool // This test only fails in debug mode with the bug since that tests round-tripping (function() { - "use strict"; +"use strict"; - function test(op, val) { - const coll = db.server6779; - coll.drop(); - assert.writeOK(coll.insert({a: true})); - assert.writeOK(coll.insert({a: false})); +function test(op, val) { + const coll = db.server6779; + coll.drop(); + assert.writeOK(coll.insert({a: true})); + assert.writeOK(coll.insert({a: false})); - const obj = {}; - obj[op] = ['$a', val]; - const result = coll.aggregate([{$project: {_id: 0, bool: obj}}, {$sort: {bool: -1}}]); + const obj = {}; + obj[op] = ['$a', val]; + const result = coll.aggregate([{$project: {_id: 0, bool: obj}}, {$sort: {bool: -1}}]); - assert.eq(result.toArray(), [{bool: true}, {bool: false}]); - } - test('$and', true); - test('$or', false); + assert.eq(result.toArray(), [{bool: true}, {bool: false}]); +} +test('$and', true); +test('$or', false); }()); diff --git a/jstests/aggregation/bugs/server7695_isodates.js b/jstests/aggregation/bugs/server7695_isodates.js index 4d969bf80f8..ca90c47f0fe 100644 --- a/jstests/aggregation/bugs/server7695_isodates.js +++ b/jstests/aggregation/bugs/server7695_isodates.js @@ -1,254 +1,251 @@ // SERVER-7695: Add $isoWeek, $isoWeekYear, and $isoDayOfWeek aggregation expressions. (function() { - "use strict"; - const coll = db.server7695; - let testOpCount = 0; - - load('jstests/libs/dateutil.js'); - - coll.drop(); - - // Seed collection so that the pipeline will execute. - assert.writeOK(coll.insert({})); - - /** - * Helper for testing that 'op' returns 'expResult'. - */ - function testOp(op, value, expResult) { - testOpCount++; - let pipeline = [{$project: {_id: 0, result: {}}}]; - pipeline[0].$project.result[op] = value; - let msg = "Exptected {" + op + ": " + value + "} to equal: " + expResult; - let res = coll.runCommand('aggregate', {pipeline: pipeline, cursor: {}}); - - // in the case of $dateToString the date is on property date - let date = value.date || value; - if (date.valueOf() < 0 && _isWindows() && res.code === 16422) { - // some versions of windows (but not all) fail with dates before 1970 - print("skipping test of " + date.tojson() + - " because system doesn't support old dates"); - return; - } - - if (date.valueOf() / 1000 < -2 * 1024 * 1024 * 1024 && res.code == 16421) { - // we correctly detected that we are outside of the range of a 32-bit time_t - print("skipping test of " + date.tojson() + " because it is outside of time_t range"); - return; - } +"use strict"; +const coll = db.server7695; +let testOpCount = 0; + +load('jstests/libs/dateutil.js'); + +coll.drop(); + +// Seed collection so that the pipeline will execute. +assert.writeOK(coll.insert({})); + +/** + * Helper for testing that 'op' returns 'expResult'. + */ +function testOp(op, value, expResult) { + testOpCount++; + let pipeline = [{$project: {_id: 0, result: {}}}]; + pipeline[0].$project.result[op] = value; + let msg = "Exptected {" + op + ": " + value + "} to equal: " + expResult; + let res = coll.runCommand('aggregate', {pipeline: pipeline, cursor: {}}); + + // in the case of $dateToString the date is on property date + let date = value.date || value; + if (date.valueOf() < 0 && _isWindows() && res.code === 16422) { + // some versions of windows (but not all) fail with dates before 1970 + print("skipping test of " + date.tojson() + " because system doesn't support old dates"); + return; + } - assert.eq(res.cursor.firstBatch[0].result, expResult, tojson(pipeline)); + if (date.valueOf() / 1000 < -2 * 1024 * 1024 * 1024 && res.code == 16421) { + // we correctly detected that we are outside of the range of a 32-bit time_t + print("skipping test of " + date.tojson() + " because it is outside of time_t range"); + return; } - // While development, there was a bug which caused an error with $dateToString if the order of - // %V and %G changed, so I added this test to prevent regression. - testOp('$dateToString', {date: new Date("1900-12-31T23:59:59Z"), format: "%V-%G"}, "01-1901"); - // This was failing, but it shouldn't as it is the same as above, only rotated. - testOp('$dateToString', {date: new Date("1900-12-31T23:59:59Z"), format: "%G-%V"}, "1901-01"); - - // 1900 is special because it's devisible by 4 and by 100 but not 400 so it's not a leap year. - // 2000 is special, because it's devisible by 4, 100, 400 and so it is a leap year. - const years = { - common: [ - 1900, // Starting and ending on Monday (special). - 2002, // Starting and ending on Tuesday. - 2014, // Starting and ending on Wednesday. - 2015, // Starting and ending on Thursday. - 2010, // Starting and ending on Friday. - 2011, // Starting and ending on Saturday. - 2006, // Starting and ending on Sunday. - ], - leap: [ - 1996, // Starting on Monday, ending on Tuesday. - 2008, // Starting on Tuesday, ending on Wednesday. - 1992, // Starting on Wednesday, ending on Thursday. - 2004, // Starting on Thursday, ending on Friday. - 2016, // Starting on Friday, ending on Saturday. - 2000, // Starting on Saturday, ending on Sunday (special). - 2012, // Starting on Sunday, ending on Monday. - ], - commonAfterLeap: [ - 2001, // Starting and ending on Monday. - 2013, // Starting and ending on Tuesday. - 1997, // Starting and ending on Wednesday. - 2009, // Starting and ending on Thursday. - 1993, // Starting and ending on Friday. - 2005, // Starting and ending on Saturday. - 2017, // Starting and ending on Sunday. - ], - }; - - const MONDAY = 1; - const TUESDAY = 2; - const WEDNESDAY = 3; - const THURSDAY = 4; - const FRIDAY = 5; - const SATURDAY = 6; - const SUNDAY = 7; - - ['common', 'leap', 'commonAfterLeap'].forEach(function(type) { - years[type].forEach(function(year, day) { - // forEach starts indexing at zero but weekdays start with Monday on 1 so we add +1. - day = day + 1; - let newYear = DateUtil.getNewYear(year); - let endOfFirstWeekInYear = DateUtil.getEndOfFirstWeekInYear(year, day); - let startOfSecondWeekInYear = DateUtil.getStartOfSecondWeekInYear(year, day); - let birthday = DateUtil.getBirthday(year); - let endOfSecondToLastWeekInYear = - DateUtil.getEndOfSecondToLastWeekInYear(year, day, type); - let startOfLastWeekInYear = DateUtil.getStartOfLastWeekInYear(year, day, type); - let newYearsEve = DateUtil.getNewYearsEve(year); - - testOp('$isoDayOfWeek', newYear, day); - testOp('$isoDayOfWeek', endOfFirstWeekInYear, SUNDAY); - testOp('$isoDayOfWeek', startOfSecondWeekInYear, MONDAY); - testOp('$isoDayOfWeek', endOfSecondToLastWeekInYear, SUNDAY); - testOp('$isoDayOfWeek', startOfLastWeekInYear, MONDAY); - if (type === 'leap') { - testOp('$isoDayOfWeek', newYearsEve, DateUtil.shiftWeekday(day, 1)); - } else { - testOp('$isoDayOfWeek', newYearsEve, day); - } + assert.eq(res.cursor.firstBatch[0].result, expResult, tojson(pipeline)); +} + +// While development, there was a bug which caused an error with $dateToString if the order of +// %V and %G changed, so I added this test to prevent regression. +testOp('$dateToString', {date: new Date("1900-12-31T23:59:59Z"), format: "%V-%G"}, "01-1901"); +// This was failing, but it shouldn't as it is the same as above, only rotated. +testOp('$dateToString', {date: new Date("1900-12-31T23:59:59Z"), format: "%G-%V"}, "1901-01"); + +// 1900 is special because it's devisible by 4 and by 100 but not 400 so it's not a leap year. +// 2000 is special, because it's devisible by 4, 100, 400 and so it is a leap year. +const years = { + common: [ + 1900, // Starting and ending on Monday (special). + 2002, // Starting and ending on Tuesday. + 2014, // Starting and ending on Wednesday. + 2015, // Starting and ending on Thursday. + 2010, // Starting and ending on Friday. + 2011, // Starting and ending on Saturday. + 2006, // Starting and ending on Sunday. + ], + leap: [ + 1996, // Starting on Monday, ending on Tuesday. + 2008, // Starting on Tuesday, ending on Wednesday. + 1992, // Starting on Wednesday, ending on Thursday. + 2004, // Starting on Thursday, ending on Friday. + 2016, // Starting on Friday, ending on Saturday. + 2000, // Starting on Saturday, ending on Sunday (special). + 2012, // Starting on Sunday, ending on Monday. + ], + commonAfterLeap: [ + 2001, // Starting and ending on Monday. + 2013, // Starting and ending on Tuesday. + 1997, // Starting and ending on Wednesday. + 2009, // Starting and ending on Thursday. + 1993, // Starting and ending on Friday. + 2005, // Starting and ending on Saturday. + 2017, // Starting and ending on Sunday. + ], +}; + +const MONDAY = 1; +const TUESDAY = 2; +const WEDNESDAY = 3; +const THURSDAY = 4; +const FRIDAY = 5; +const SATURDAY = 6; +const SUNDAY = 7; + +['common', 'leap', 'commonAfterLeap'].forEach(function(type) { + years[type].forEach(function(year, day) { + // forEach starts indexing at zero but weekdays start with Monday on 1 so we add +1. + day = day + 1; + let newYear = DateUtil.getNewYear(year); + let endOfFirstWeekInYear = DateUtil.getEndOfFirstWeekInYear(year, day); + let startOfSecondWeekInYear = DateUtil.getStartOfSecondWeekInYear(year, day); + let birthday = DateUtil.getBirthday(year); + let endOfSecondToLastWeekInYear = DateUtil.getEndOfSecondToLastWeekInYear(year, day, type); + let startOfLastWeekInYear = DateUtil.getStartOfLastWeekInYear(year, day, type); + let newYearsEve = DateUtil.getNewYearsEve(year); + + testOp('$isoDayOfWeek', newYear, day); + testOp('$isoDayOfWeek', endOfFirstWeekInYear, SUNDAY); + testOp('$isoDayOfWeek', startOfSecondWeekInYear, MONDAY); + testOp('$isoDayOfWeek', endOfSecondToLastWeekInYear, SUNDAY); + testOp('$isoDayOfWeek', startOfLastWeekInYear, MONDAY); + if (type === 'leap') { + testOp('$isoDayOfWeek', newYearsEve, DateUtil.shiftWeekday(day, 1)); + } else { + testOp('$isoDayOfWeek', newYearsEve, day); + } - if (type === 'leap') { - testOp('$isoDayOfWeek', birthday, DateUtil.shiftWeekday(day, 4)); - } else { - testOp('$isoDayOfWeek', birthday, DateUtil.shiftWeekday(day, 3)); - } + if (type === 'leap') { + testOp('$isoDayOfWeek', birthday, DateUtil.shiftWeekday(day, 4)); + } else { + testOp('$isoDayOfWeek', birthday, DateUtil.shiftWeekday(day, 3)); + } - testOp('$isoWeekYear', birthday, year); - // In leap years staring on Thursday, the birthday is in week 28, every year else it is - // in week 27. - if (type === 'leap' && day === THURSDAY) { - testOp('$isoWeek', birthday, 28); - } else { - testOp('$isoWeek', birthday, 27); - } + testOp('$isoWeekYear', birthday, year); + // In leap years staring on Thursday, the birthday is in week 28, every year else it is + // in week 27. + if (type === 'leap' && day === THURSDAY) { + testOp('$isoWeek', birthday, 28); + } else { + testOp('$isoWeek', birthday, 27); + } - if (day <= THURSDAY) { - // A year starting between Monday and Thursday will always start in week 1. - testOp('$isoWeek', newYear, 1); - testOp('$isoWeekYear', newYear, year); - testOp('$isoWeek', endOfFirstWeekInYear, 1); - testOp('$isoWeekYear', endOfFirstWeekInYear, year); - testOp('$isoWeek', startOfSecondWeekInYear, 2); - testOp('$isoWeekYear', startOfSecondWeekInYear, year); + if (day <= THURSDAY) { + // A year starting between Monday and Thursday will always start in week 1. + testOp('$isoWeek', newYear, 1); + testOp('$isoWeekYear', newYear, year); + testOp('$isoWeek', endOfFirstWeekInYear, 1); + testOp('$isoWeekYear', endOfFirstWeekInYear, year); + testOp('$isoWeek', startOfSecondWeekInYear, 2); + testOp('$isoWeekYear', startOfSecondWeekInYear, year); + testOp( + '$dateToString', {format: '%G-W%V-%u', date: newYear}, "" + year + "-W01-" + day); + } else if (day == FRIDAY || (day == SATURDAY && type === 'commonAfterLeap')) { + // A year starting on Friday will always start with week 53 of the previous year. + // A common year starting on a Saturday and after a leap year will also start with + // week 53 of the previous year. + testOp('$isoWeek', newYear, 53); + testOp('$isoWeekYear', newYear, year - 1); + testOp('$isoWeek', endOfFirstWeekInYear, 53); + testOp('$isoWeekYear', endOfFirstWeekInYear, year - 1); + testOp('$isoWeek', startOfSecondWeekInYear, 1); + testOp('$isoWeekYear', startOfSecondWeekInYear, year); + testOp('$dateToString', + {format: '%G-W%V-%u', date: newYear}, + "" + (year - 1) + "-W53-" + day); + } else { + // A year starting on Saturday (except after a leap year) or Sunday will always + // start with week 52 of the previous year. + testOp('$isoWeek', newYear, 52); + testOp('$isoWeekYear', newYear, year - 1); + testOp('$isoWeek', endOfFirstWeekInYear, 52); + testOp('$isoWeekYear', endOfFirstWeekInYear, year - 1); + testOp('$isoWeek', startOfSecondWeekInYear, 1); + testOp('$isoWeekYear', startOfSecondWeekInYear, year); + testOp('$dateToString', + {format: '%G-W%V-%u', date: newYear}, + "" + (year - 1) + "-W52-" + day); + } + + if (type === 'leap') { + if (day <= TUESDAY) { + // A leap year starting between Monday and Tuesday will always end in week 1 of + // the next year. + testOp('$isoWeek', newYearsEve, 1); + testOp('$isoWeekYear', newYearsEve, year + 1); + testOp('$isoWeek', endOfSecondToLastWeekInYear, 52); + testOp('$isoWeekYear', endOfSecondToLastWeekInYear, year); + testOp('$isoWeek', startOfLastWeekInYear, 1); + testOp('$isoWeekYear', startOfLastWeekInYear, year + 1); + testOp('$dateToString', + {format: '%G-W%V-%u', date: newYearsEve}, + "" + (year + 1) + "-W01-" + DateUtil.shiftWeekday(day, 1)); + } else if (day <= THURSDAY) { + // A leap year starting on Wednesday or Thursday will always end with week 53. + testOp('$isoWeek', newYearsEve, 53); + testOp('$isoWeekYear', newYearsEve, year); + testOp('$isoWeek', endOfSecondToLastWeekInYear, 52); + testOp('$isoWeekYear', endOfSecondToLastWeekInYear, year); + testOp('$isoWeek', startOfLastWeekInYear, 53); + testOp('$isoWeekYear', startOfLastWeekInYear, year); testOp('$dateToString', - {format: '%G-W%V-%u', date: newYear}, - "" + year + "-W01-" + day); - } else if (day == FRIDAY || (day == SATURDAY && type === 'commonAfterLeap')) { - // A year starting on Friday will always start with week 53 of the previous year. - // A common year starting on a Saturday and after a leap year will also start with - // week 53 of the previous year. - testOp('$isoWeek', newYear, 53); - testOp('$isoWeekYear', newYear, year - 1); - testOp('$isoWeek', endOfFirstWeekInYear, 53); - testOp('$isoWeekYear', endOfFirstWeekInYear, year - 1); - testOp('$isoWeek', startOfSecondWeekInYear, 1); - testOp('$isoWeekYear', startOfSecondWeekInYear, year); + {format: '%G-W%V-%u', date: newYearsEve}, + "" + (year) + "-W53-" + DateUtil.shiftWeekday(day, 1)); + } else if (day <= SATURDAY) { + // A leap year starting on Friday or Sarturday will always and with week 52 + testOp('$isoWeek', newYearsEve, 52); + testOp('$isoWeekYear', newYearsEve, year); + testOp('$isoWeek', endOfSecondToLastWeekInYear, 51); + testOp('$isoWeekYear', endOfSecondToLastWeekInYear, year); + testOp('$isoWeek', startOfLastWeekInYear, 52); + testOp('$isoWeekYear', startOfLastWeekInYear, year); testOp('$dateToString', - {format: '%G-W%V-%u', date: newYear}, - "" + (year - 1) + "-W53-" + day); + {format: '%G-W%V-%u', date: newYearsEve}, + "" + (year) + "-W52-" + DateUtil.shiftWeekday(day, 1)); } else { - // A year starting on Saturday (except after a leap year) or Sunday will always - // start with week 52 of the previous year. - testOp('$isoWeek', newYear, 52); - testOp('$isoWeekYear', newYear, year - 1); - testOp('$isoWeek', endOfFirstWeekInYear, 52); - testOp('$isoWeekYear', endOfFirstWeekInYear, year - 1); - testOp('$isoWeek', startOfSecondWeekInYear, 1); - testOp('$isoWeekYear', startOfSecondWeekInYear, year); + // A leap year starting on Sunday will always end with week 1 + testOp('$isoWeek', newYearsEve, 1); + testOp('$isoWeekYear', newYearsEve, year + 1); + testOp('$isoWeek', endOfSecondToLastWeekInYear, 51); + testOp('$isoWeekYear', endOfSecondToLastWeekInYear, year); + testOp('$isoWeek', startOfLastWeekInYear, 52); + testOp('$isoWeekYear', startOfLastWeekInYear, year); testOp('$dateToString', - {format: '%G-W%V-%u', date: newYear}, - "" + (year - 1) + "-W52-" + day); + {format: '%G-W%V-%u', date: newYearsEve}, + "" + (year + 1) + "-W01-" + DateUtil.shiftWeekday(day, 1)); } - - if (type === 'leap') { - if (day <= TUESDAY) { - // A leap year starting between Monday and Tuesday will always end in week 1 of - // the next year. - testOp('$isoWeek', newYearsEve, 1); - testOp('$isoWeekYear', newYearsEve, year + 1); - testOp('$isoWeek', endOfSecondToLastWeekInYear, 52); - testOp('$isoWeekYear', endOfSecondToLastWeekInYear, year); - testOp('$isoWeek', startOfLastWeekInYear, 1); - testOp('$isoWeekYear', startOfLastWeekInYear, year + 1); - testOp('$dateToString', - {format: '%G-W%V-%u', date: newYearsEve}, - "" + (year + 1) + "-W01-" + DateUtil.shiftWeekday(day, 1)); - } else if (day <= THURSDAY) { - // A leap year starting on Wednesday or Thursday will always end with week 53. - testOp('$isoWeek', newYearsEve, 53); - testOp('$isoWeekYear', newYearsEve, year); - testOp('$isoWeek', endOfSecondToLastWeekInYear, 52); - testOp('$isoWeekYear', endOfSecondToLastWeekInYear, year); - testOp('$isoWeek', startOfLastWeekInYear, 53); - testOp('$isoWeekYear', startOfLastWeekInYear, year); - testOp('$dateToString', - {format: '%G-W%V-%u', date: newYearsEve}, - "" + (year) + "-W53-" + DateUtil.shiftWeekday(day, 1)); - } else if (day <= SATURDAY) { - // A leap year starting on Friday or Sarturday will always and with week 52 - testOp('$isoWeek', newYearsEve, 52); - testOp('$isoWeekYear', newYearsEve, year); - testOp('$isoWeek', endOfSecondToLastWeekInYear, 51); - testOp('$isoWeekYear', endOfSecondToLastWeekInYear, year); - testOp('$isoWeek', startOfLastWeekInYear, 52); - testOp('$isoWeekYear', startOfLastWeekInYear, year); - testOp('$dateToString', - {format: '%G-W%V-%u', date: newYearsEve}, - "" + (year) + "-W52-" + DateUtil.shiftWeekday(day, 1)); - } else { - // A leap year starting on Sunday will always end with week 1 - testOp('$isoWeek', newYearsEve, 1); - testOp('$isoWeekYear', newYearsEve, year + 1); - testOp('$isoWeek', endOfSecondToLastWeekInYear, 51); - testOp('$isoWeekYear', endOfSecondToLastWeekInYear, year); - testOp('$isoWeek', startOfLastWeekInYear, 52); - testOp('$isoWeekYear', startOfLastWeekInYear, year); - testOp('$dateToString', - {format: '%G-W%V-%u', date: newYearsEve}, - "" + (year + 1) + "-W01-" + DateUtil.shiftWeekday(day, 1)); - } + } else { + if (day <= WEDNESDAY) { + // A common year starting between Monday and Wednesday will always end in week 1 + // of the next year. + testOp('$isoWeek', newYearsEve, 1); + testOp('$isoWeekYear', newYearsEve, year + 1); + testOp('$isoWeek', endOfSecondToLastWeekInYear, 52); + testOp('$isoWeekYear', endOfSecondToLastWeekInYear, year); + testOp('$isoWeek', startOfLastWeekInYear, 1); + testOp('$isoWeekYear', startOfLastWeekInYear, year + 1); + testOp('$dateToString', + {format: '%G-W%V-%u', date: newYearsEve}, + "" + (year + 1) + "-W01-" + day); + } else if (day === THURSDAY) { + // A common year starting on Thursday will always end with week 53. + testOp('$isoWeek', newYearsEve, 53); + testOp('$isoWeekYear', newYearsEve, year); + testOp('$isoWeek', endOfSecondToLastWeekInYear, 52); + testOp('$isoWeekYear', endOfSecondToLastWeekInYear, year); + testOp('$isoWeek', startOfLastWeekInYear, 53); + testOp('$isoWeekYear', startOfLastWeekInYear, year); + testOp('$dateToString', + {format: '%G-W%V-%u', date: newYearsEve}, + "" + (year) + "-W53-" + day); } else { - if (day <= WEDNESDAY) { - // A common year starting between Monday and Wednesday will always end in week 1 - // of the next year. - testOp('$isoWeek', newYearsEve, 1); - testOp('$isoWeekYear', newYearsEve, year + 1); - testOp('$isoWeek', endOfSecondToLastWeekInYear, 52); - testOp('$isoWeekYear', endOfSecondToLastWeekInYear, year); - testOp('$isoWeek', startOfLastWeekInYear, 1); - testOp('$isoWeekYear', startOfLastWeekInYear, year + 1); - testOp('$dateToString', - {format: '%G-W%V-%u', date: newYearsEve}, - "" + (year + 1) + "-W01-" + day); - } else if (day === THURSDAY) { - // A common year starting on Thursday will always end with week 53. - testOp('$isoWeek', newYearsEve, 53); - testOp('$isoWeekYear', newYearsEve, year); - testOp('$isoWeek', endOfSecondToLastWeekInYear, 52); - testOp('$isoWeekYear', endOfSecondToLastWeekInYear, year); - testOp('$isoWeek', startOfLastWeekInYear, 53); - testOp('$isoWeekYear', startOfLastWeekInYear, year); - testOp('$dateToString', - {format: '%G-W%V-%u', date: newYearsEve}, - "" + (year) + "-W53-" + day); - } else { - // A common year starting on between Friday and Sunday will always end with week - // 52. - testOp('$isoWeek', newYearsEve, 52); - testOp('$isoWeekYear', newYearsEve, year); - testOp('$isoWeek', endOfSecondToLastWeekInYear, 51); - testOp('$isoWeekYear', endOfSecondToLastWeekInYear, year); - testOp('$isoWeek', startOfLastWeekInYear, 52); - testOp('$isoWeekYear', startOfLastWeekInYear, year); - testOp('$dateToString', - {format: '%G-W%V-%u', date: newYearsEve}, - "" + (year) + "-W52-" + day); - } + // A common year starting on between Friday and Sunday will always end with week + // 52. + testOp('$isoWeek', newYearsEve, 52); + testOp('$isoWeekYear', newYearsEve, year); + testOp('$isoWeek', endOfSecondToLastWeekInYear, 51); + testOp('$isoWeekYear', endOfSecondToLastWeekInYear, year); + testOp('$isoWeek', startOfLastWeekInYear, 52); + testOp('$isoWeekYear', startOfLastWeekInYear, year); + testOp('$dateToString', + {format: '%G-W%V-%u', date: newYearsEve}, + "" + (year) + "-W52-" + day); } - }); + } }); - assert.eq(testOpCount, 485, 'Expected 485 tests to run'); +}); +assert.eq(testOpCount, 485, 'Expected 485 tests to run'); })(); diff --git a/jstests/aggregation/bugs/server7781.js b/jstests/aggregation/bugs/server7781.js index f755a6af0ad..19700cc2202 100644 --- a/jstests/aggregation/bugs/server7781.js +++ b/jstests/aggregation/bugs/server7781.js @@ -4,128 +4,128 @@ // requires_spawning_own_processes, // ] (function() { - 'use strict'; - - load('jstests/libs/geo_near_random.js'); - load('jstests/aggregation/extras/utils.js'); - - var coll = 'server7781'; - +'use strict'; + +load('jstests/libs/geo_near_random.js'); +load('jstests/aggregation/extras/utils.js'); + +var coll = 'server7781'; + +db[coll].drop(); +db[coll].insert({loc: [0, 0]}); + +// $geoNear is only allowed as the first stage in a pipeline, nowhere else. +assert.throws( + () => db[coll].aggregate( + [{$match: {x: 1}}, {$geoNear: {near: [1, 1], spherical: true, distanceField: 'dis'}}])); + +const kDistanceField = "dis"; +const kIncludeLocsField = "loc"; + +/** + * Tests the output of the $geoNear command. This function expects a document with the following + * fields: + * - 'geoNearSpec' is the specification for a $geoNear aggregation stage. + * - 'limit' is an integer limiting the number of pipeline results. + * - 'batchSize', if specified, is the batchSize to use for the aggregation. + */ +function testGeoNearStageOutput({geoNearSpec, limit, batchSize}) { + const aggOptions = batchSize ? {batchSize: batchSize} : {}; + const result = + db[coll].aggregate([{$geoNear: geoNearSpec}, {$limit: limit}], aggOptions).toArray(); + const errmsg = () => tojson(result); + + // Verify that we got the expected number of results. + assert.eq(result.length, limit, errmsg); + + // Run though the array, checking for proper sort order and sane computed distances. + result.reduce((lastDist, curDoc) => { + const curDist = curDoc[kDistanceField]; + + // Verify that distances are in increasing order. + assert.lte(lastDist, curDist, errmsg); + + // Verify that the computed distance is correct. + const computed = Geo.sphereDistance(geoNearSpec["near"], curDoc[kIncludeLocsField]); + assert.close(computed, curDist, errmsg); + return curDist; + }, 0); +} + +// We use this to generate points. Using a single global to avoid reseting RNG in each pass. +var pointMaker = new GeoNearRandomTest(coll); + +function test(db, sharded, indexType) { db[coll].drop(); - db[coll].insert({loc: [0, 0]}); - - // $geoNear is only allowed as the first stage in a pipeline, nowhere else. - assert.throws( - () => db[coll].aggregate( - [{$match: {x: 1}}, {$geoNear: {near: [1, 1], spherical: true, distanceField: 'dis'}}])); - - const kDistanceField = "dis"; - const kIncludeLocsField = "loc"; - - /** - * Tests the output of the $geoNear command. This function expects a document with the following - * fields: - * - 'geoNearSpec' is the specification for a $geoNear aggregation stage. - * - 'limit' is an integer limiting the number of pipeline results. - * - 'batchSize', if specified, is the batchSize to use for the aggregation. - */ - function testGeoNearStageOutput({geoNearSpec, limit, batchSize}) { - const aggOptions = batchSize ? {batchSize: batchSize} : {}; - const result = - db[coll].aggregate([{$geoNear: geoNearSpec}, {$limit: limit}], aggOptions).toArray(); - const errmsg = () => tojson(result); - - // Verify that we got the expected number of results. - assert.eq(result.length, limit, errmsg); - - // Run though the array, checking for proper sort order and sane computed distances. - result.reduce((lastDist, curDoc) => { - const curDist = curDoc[kDistanceField]; - - // Verify that distances are in increasing order. - assert.lte(lastDist, curDist, errmsg); - - // Verify that the computed distance is correct. - const computed = Geo.sphereDistance(geoNearSpec["near"], curDoc[kIncludeLocsField]); - assert.close(computed, curDist, errmsg); - return curDist; - }, 0); - } - - // We use this to generate points. Using a single global to avoid reseting RNG in each pass. - var pointMaker = new GeoNearRandomTest(coll); - function test(db, sharded, indexType) { - db[coll].drop(); - - if (sharded) { // sharded setup - var shards = []; - var config = db.getSiblingDB("config"); - config.shards.find().forEach(function(shard) { - shards.push(shard._id); - }); + if (sharded) { // sharded setup + var shards = []; + var config = db.getSiblingDB("config"); + config.shards.find().forEach(function(shard) { + shards.push(shard._id); + }); + assert.commandWorked( + db.adminCommand({shardCollection: db[coll].getFullName(), key: {rand: 1}})); + for (var i = 1; i < 10; i++) { + // split at 0.1, 0.2, ... 0.9 assert.commandWorked( - db.adminCommand({shardCollection: db[coll].getFullName(), key: {rand: 1}})); - for (var i = 1; i < 10; i++) { - // split at 0.1, 0.2, ... 0.9 - assert.commandWorked( - db.adminCommand({split: db[coll].getFullName(), middle: {rand: i / 10}})); - db.adminCommand({ - moveChunk: db[coll].getFullName(), - find: {rand: i / 10}, - to: shards[i % shards.length] - }); - } - - assert.eq(config.chunks.count({'ns': db[coll].getFullName()}), 10); - } - - // insert points - var numPts = 10 * 1000; - var bulk = db[coll].initializeUnorderedBulkOp(); - for (var i = 0; i < numPts; i++) { - bulk.insert({rand: Math.random(), loc: pointMaker.mkPt()}); + db.adminCommand({split: db[coll].getFullName(), middle: {rand: i / 10}})); + db.adminCommand({ + moveChunk: db[coll].getFullName(), + find: {rand: i / 10}, + to: shards[i % shards.length] + }); } - assert.writeOK(bulk.execute()); - - assert.eq(db[coll].count(), numPts); - - db[coll].ensureIndex({loc: indexType}); - - // Test $geoNear with spherical coordinates. - testGeoNearStageOutput({ - geoNearSpec: { - near: pointMaker.mkPt(0.25), - distanceField: kDistanceField, - includeLocs: kIncludeLocsField, - spherical: true, - }, - limit: 100 - }); - // Test $geoNear with an initial batchSize of 1. - testGeoNearStageOutput({ - geoNearSpec: { - near: pointMaker.mkPt(0.25), - distanceField: kDistanceField, - includeLocs: kIncludeLocsField, - spherical: true, - }, - limit: 70, - batchSize: 1 - }); + assert.eq(config.chunks.count({'ns': db[coll].getFullName()}), 10); } - test(db, false, '2d'); - test(db, false, '2dsphere'); - - var sharded = new ShardingTest({shards: 3, mongos: 1}); - assert.commandWorked(sharded.s0.adminCommand({enablesharding: "test"})); - sharded.ensurePrimaryShard('test', sharded.shard1.shardName); - - test(sharded.getDB('test'), true, '2d'); - test(sharded.getDB('test'), true, '2dsphere'); - - sharded.stop(); + // insert points + var numPts = 10 * 1000; + var bulk = db[coll].initializeUnorderedBulkOp(); + for (var i = 0; i < numPts; i++) { + bulk.insert({rand: Math.random(), loc: pointMaker.mkPt()}); + } + assert.writeOK(bulk.execute()); + + assert.eq(db[coll].count(), numPts); + + db[coll].ensureIndex({loc: indexType}); + + // Test $geoNear with spherical coordinates. + testGeoNearStageOutput({ + geoNearSpec: { + near: pointMaker.mkPt(0.25), + distanceField: kDistanceField, + includeLocs: kIncludeLocsField, + spherical: true, + }, + limit: 100 + }); + + // Test $geoNear with an initial batchSize of 1. + testGeoNearStageOutput({ + geoNearSpec: { + near: pointMaker.mkPt(0.25), + distanceField: kDistanceField, + includeLocs: kIncludeLocsField, + spherical: true, + }, + limit: 70, + batchSize: 1 + }); +} + +test(db, false, '2d'); +test(db, false, '2dsphere'); + +var sharded = new ShardingTest({shards: 3, mongos: 1}); +assert.commandWorked(sharded.s0.adminCommand({enablesharding: "test"})); +sharded.ensurePrimaryShard('test', sharded.shard1.shardName); + +test(sharded.getDB('test'), true, '2d'); +test(sharded.getDB('test'), true, '2dsphere'); + +sharded.stop(); })(); diff --git a/jstests/aggregation/bugs/server8141.js b/jstests/aggregation/bugs/server8141.js index 9777737517b..908fd952059 100644 --- a/jstests/aggregation/bugs/server8141.js +++ b/jstests/aggregation/bugs/server8141.js @@ -1,52 +1,50 @@ // SERVER-8141 Avoid treating arrays as literals in aggregation pipeline. (function() { - 'use strict'; - var coll = db.exprs_in_arrays; - coll.drop(); +'use strict'; +var coll = db.exprs_in_arrays; +coll.drop(); - assert.writeOK(coll.insert({_id: 0, a: ['foo', 'bar', 'baz'], b: 'bar', c: 'Baz'})); +assert.writeOK(coll.insert({_id: 0, a: ['foo', 'bar', 'baz'], b: 'bar', c: 'Baz'})); - // An array of constants should still evaluate to an array of constants. - var pipeline = [{$project: {_id: 0, d: ['constant', 1]}}]; - assert.eq(coll.aggregate(pipeline).toArray(), [{d: ['constant', 1]}]); +// An array of constants should still evaluate to an array of constants. +var pipeline = [{$project: {_id: 0, d: ['constant', 1]}}]; +assert.eq(coll.aggregate(pipeline).toArray(), [{d: ['constant', 1]}]); - // A field name inside an array should take on the value of that field. - pipeline = [{$project: {_id: 0, d: ['$b']}}]; - assert.eq(coll.aggregate(pipeline).toArray(), [{d: ['bar']}]); +// A field name inside an array should take on the value of that field. +pipeline = [{$project: {_id: 0, d: ['$b']}}]; +assert.eq(coll.aggregate(pipeline).toArray(), [{d: ['bar']}]); - // An expression inside an array should be evaluated. - pipeline = [{$project: {_id: 0, d: [{$toLower: 'FoO'}]}}]; - assert.eq(coll.aggregate(pipeline).toArray(), [{d: ['foo']}]); +// An expression inside an array should be evaluated. +pipeline = [{$project: {_id: 0, d: [{$toLower: 'FoO'}]}}]; +assert.eq(coll.aggregate(pipeline).toArray(), [{d: ['foo']}]); - // Both an expression and a field name inside an array should be evaluated. - pipeline = [{$project: {_id: 0, d: ['$b', {$toLower: 'FoO'}]}}]; - assert.eq(coll.aggregate(pipeline).toArray(), [{d: ['bar', 'foo']}]); +// Both an expression and a field name inside an array should be evaluated. +pipeline = [{$project: {_id: 0, d: ['$b', {$toLower: 'FoO'}]}}]; +assert.eq(coll.aggregate(pipeline).toArray(), [{d: ['bar', 'foo']}]); - // A nested array should still be evaluated. - pipeline = [{$project: {_id: 0, d: ['$b', 'constant', [1, {$toLower: 'FoO'}]]}}]; - assert.eq(coll.aggregate(pipeline).toArray(), [{d: ['bar', 'constant', [1, 'foo']]}]); +// A nested array should still be evaluated. +pipeline = [{$project: {_id: 0, d: ['$b', 'constant', [1, {$toLower: 'FoO'}]]}}]; +assert.eq(coll.aggregate(pipeline).toArray(), [{d: ['bar', 'constant', [1, 'foo']]}]); - // Should still evaluate array elements inside arguments to an expression. - pipeline = [{$project: {_id: 0, d: {$setIntersection: ['$a', ['$b']]}}}]; - assert.eq(coll.aggregate(pipeline).toArray(), [{d: ['bar']}]); +// Should still evaluate array elements inside arguments to an expression. +pipeline = [{$project: {_id: 0, d: {$setIntersection: ['$a', ['$b']]}}}]; +assert.eq(coll.aggregate(pipeline).toArray(), [{d: ['bar']}]); - pipeline = [{$project: {_id: 0, d: {$setIntersection: ['$a', [{$toLower: 'FoO'}]]}}}]; - assert.eq(coll.aggregate(pipeline).toArray(), [{d: ['foo']}]); +pipeline = [{$project: {_id: 0, d: {$setIntersection: ['$a', [{$toLower: 'FoO'}]]}}}]; +assert.eq(coll.aggregate(pipeline).toArray(), [{d: ['foo']}]); - // Nested arrays. - pipeline = [{ - $project: { - _id: 0, - d: {$setIntersection: [[[1, 'foo', 'bar']], [[1, {$toLower: 'FoO'}, '$b']]]} - } - }]; - assert.eq(coll.aggregate(pipeline).toArray(), [{d: [[1, 'foo', 'bar']]}]); +// Nested arrays. +pipeline = [{ + $project: + {_id: 0, d: {$setIntersection: [[[1, 'foo', 'bar']], [[1, {$toLower: 'FoO'}, '$b']]]}} +}]; +assert.eq(coll.aggregate(pipeline).toArray(), [{d: [[1, 'foo', 'bar']]}]); - coll.drop(); +coll.drop(); - // Should replace missing values with NULL to preserve indices. - assert.writeOK(coll.insert({_id: 1, x: 1, z: 2})); +// Should replace missing values with NULL to preserve indices. +assert.writeOK(coll.insert({_id: 1, x: 1, z: 2})); - pipeline = [{$project: {_id: 0, coordinate: ['$x', '$y', '$z']}}]; - assert.eq(coll.aggregate(pipeline).toArray(), [{coordinate: [1, null, 2]}]); +pipeline = [{$project: {_id: 0, coordinate: ['$x', '$y', '$z']}}]; +assert.eq(coll.aggregate(pipeline).toArray(), [{coordinate: [1, null, 2]}]); }()); diff --git a/jstests/aggregation/bugs/server8164.js b/jstests/aggregation/bugs/server8164.js index 89cb360d91f..5b137b18d87 100644 --- a/jstests/aggregation/bugs/server8164.js +++ b/jstests/aggregation/bugs/server8164.js @@ -1,144 +1,144 @@ // SERVER-8164: ISODate doesn't handle years less than 100 properly. (function() { - assert.eq(tojson(ISODate("0000-01-01")), 'ISODate("0000-01-01T00:00:00Z")'); - assert.eq(tojson(ISODate("0000-01-01T00:00:00")), 'ISODate("0000-01-01T00:00:00Z")'); - assert.eq(tojson(ISODate("0000-01-01T00:00:00Z")), 'ISODate("0000-01-01T00:00:00Z")'); - assert.eq(tojson(ISODate("0000-01-01T00:00:00.123")), 'ISODate("0000-01-01T00:00:00.123Z")'); - assert.eq(tojson(ISODate("0000-01-01T00:00:00.123Z")), 'ISODate("0000-01-01T00:00:00.123Z")'); - - assert.eq(tojson(ISODate("0000-01-01T00:00:00.1Z")), 'ISODate("0000-01-01T00:00:00.100Z")'); - assert.eq(tojson(ISODate("0000-01-01T00:00:00.10Z")), 'ISODate("0000-01-01T00:00:00.100Z")'); - assert.eq(tojson(ISODate("0000-01-01T00:00:00.100Z")), 'ISODate("0000-01-01T00:00:00.100Z")'); - assert.eq(tojson(ISODate("0000-01-01T00:00:00.1000Z")), 'ISODate("0000-01-01T00:00:00.100Z")'); - - assert.eq(tojson(ISODate("0000-01-01T00:00:00.1234Z")), 'ISODate("0000-01-01T00:00:00.123Z")'); - assert.eq(tojson(ISODate("0000-01-01T00:00:00.1235Z")), 'ISODate("0000-01-01T00:00:00.124Z")'); - - /* Testing different years */ - assert.eq(tojson(ISODate("0000-01-01T00:00:00Z")), 'ISODate("0000-01-01T00:00:00Z")'); - assert.eq(tojson(ISODate("0001-01-01T00:00:00Z")), 'ISODate("0001-01-01T00:00:00Z")'); - assert.eq(tojson(ISODate("0069-01-01T00:00:00Z")), 'ISODate("0069-01-01T00:00:00Z")'); - assert.eq(tojson(ISODate("0070-01-01T00:00:00Z")), 'ISODate("0070-01-01T00:00:00Z")'); - assert.eq(tojson(ISODate("0099-01-01T00:00:00Z")), 'ISODate("0099-01-01T00:00:00Z")'); - assert.eq(tojson(ISODate("0100-01-01T00:00:00Z")), 'ISODate("0100-01-01T00:00:00Z")'); - assert.eq(tojson(ISODate("1800-01-01T00:00:00Z")), 'ISODate("1800-01-01T00:00:00Z")'); - assert.eq(tojson(ISODate("1801-01-01T00:00:00Z")), 'ISODate("1801-01-01T00:00:00Z")'); - assert.eq(tojson(ISODate("1869-01-01T00:00:00Z")), 'ISODate("1869-01-01T00:00:00Z")'); - assert.eq(tojson(ISODate("1870-01-01T00:00:00Z")), 'ISODate("1870-01-01T00:00:00Z")'); - assert.eq(tojson(ISODate("1899-01-01T00:00:00Z")), 'ISODate("1899-01-01T00:00:00Z")'); - assert.eq(tojson(ISODate("1900-01-01T00:00:00Z")), 'ISODate("1900-01-01T00:00:00Z")'); - assert.eq(tojson(ISODate("1901-01-01T00:00:00Z")), 'ISODate("1901-01-01T00:00:00Z")'); - assert.eq(tojson(ISODate("1969-01-01T00:00:00Z")), 'ISODate("1969-01-01T00:00:00Z")'); - assert.eq(tojson(ISODate("1970-01-01T00:00:00Z")), 'ISODate("1970-01-01T00:00:00Z")'); - assert.eq(tojson(ISODate("1999-01-01T00:00:00Z")), 'ISODate("1999-01-01T00:00:00Z")'); - assert.eq(tojson(ISODate("2000-01-01T00:00:00Z")), 'ISODate("2000-01-01T00:00:00Z")'); - assert.eq(tojson(ISODate("2001-01-01T00:00:00Z")), 'ISODate("2001-01-01T00:00:00Z")'); - assert.eq(tojson(ISODate("2069-01-01T00:00:00Z")), 'ISODate("2069-01-01T00:00:00Z")'); - assert.eq(tojson(ISODate("2070-01-01T00:00:00Z")), 'ISODate("2070-01-01T00:00:00Z")'); - assert.eq(tojson(ISODate("2099-01-01T00:00:00Z")), 'ISODate("2099-01-01T00:00:00Z")'); - assert.eq(tojson(ISODate("9999-01-01T00:00:00Z")), 'ISODate("9999-01-01T00:00:00Z")'); - - /* Testing without - in date and : in time */ - assert.eq(tojson(ISODate("19980101T00:00:00Z")), 'ISODate("1998-01-01T00:00:00Z")'); - assert.eq(tojson(ISODate("1999-0101T00:00:00Z")), 'ISODate("1999-01-01T00:00:00Z")'); - assert.eq(tojson(ISODate("200001-01T00:00:00Z")), 'ISODate("2000-01-01T00:00:00Z")'); - assert.eq(tojson(ISODate("1998-01-01T000000Z")), 'ISODate("1998-01-01T00:00:00Z")'); - assert.eq(tojson(ISODate("1999-01-01T00:0000Z")), 'ISODate("1999-01-01T00:00:00Z")'); - assert.eq(tojson(ISODate("2000-01-01T0000:00Z")), 'ISODate("2000-01-01T00:00:00Z")'); - - /* Testing field overflows */ - assert.eq(tojson(ISODate("0000-01-01T00:00:60Z")), 'ISODate("0000-01-01T00:01:00Z")'); - assert.eq(tojson(ISODate("0000-01-01T00:00:99Z")), 'ISODate("0000-01-01T00:01:39Z")'); - - assert.eq(tojson(ISODate("0000-01-01T00:60:00Z")), 'ISODate("0000-01-01T01:00:00Z")'); - assert.eq(tojson(ISODate("0000-01-01T00:99:00Z")), 'ISODate("0000-01-01T01:39:00Z")'); - - assert.eq(tojson(ISODate("0000-01-01T24:00:00Z")), 'ISODate("0000-01-02T00:00:00Z")'); - assert.eq(tojson(ISODate("0000-01-01T99:00:00Z")), 'ISODate("0000-01-05T03:00:00Z")'); - - assert.eq(tojson(ISODate("0000-01-32T00:00:00Z")), 'ISODate("0000-02-01T00:00:00Z")'); - assert.eq(tojson(ISODate("0000-01-99T00:00:00Z")), 'ISODate("0000-04-08T00:00:00Z")'); - assert.eq(tojson(ISODate("0000-02-29T00:00:00Z")), 'ISODate("0000-02-29T00:00:00Z")'); - assert.eq(tojson(ISODate("0000-02-30T00:00:00Z")), 'ISODate("0000-03-01T00:00:00Z")'); - assert.eq(tojson(ISODate("0000-02-31T00:00:00Z")), 'ISODate("0000-03-02T00:00:00Z")'); - assert.eq(tojson(ISODate("0000-02-99T00:00:00Z")), 'ISODate("0000-05-09T00:00:00Z")'); - - assert.eq(tojson(ISODate("0001-02-29T00:00:00Z")), 'ISODate("0001-03-01T00:00:00Z")'); - assert.eq(tojson(ISODate("0001-02-30T00:00:00Z")), 'ISODate("0001-03-02T00:00:00Z")'); - assert.eq(tojson(ISODate("0001-02-31T00:00:00Z")), 'ISODate("0001-03-03T00:00:00Z")'); - assert.eq(tojson(ISODate("0001-02-99T00:00:00Z")), 'ISODate("0001-05-10T00:00:00Z")'); - - assert.eq(tojson(ISODate("0000-13-01T00:00:00Z")), 'ISODate("0001-01-01T00:00:00Z")'); - assert.eq(tojson(ISODate("0000-99-01T00:00:00Z")), 'ISODate("0008-03-01T00:00:00Z")'); - - /* Testing GMT offset instead of Z */ - assert.eq(tojson(ISODate("0001-01-01T00:00:00+01")), 'ISODate("0000-12-31T23:00:00Z")'); - assert.eq(tojson(ISODate("0001-01-01T00:00:00+99")), 'ISODate("0000-12-27T21:00:00Z")'); - - assert.eq(tojson(ISODate("0001-01-01T00:00:00-01")), 'ISODate("0001-01-01T01:00:00Z")'); - assert.eq(tojson(ISODate("0001-01-01T00:00:00-99")), 'ISODate("0001-01-05T03:00:00Z")'); - - assert.eq(tojson(ISODate("0001-01-01T00:00:00+0100")), 'ISODate("0000-12-31T23:00:00Z")'); - assert.eq(tojson(ISODate("0001-01-01T00:00:00+0160")), 'ISODate("0000-12-31T22:00:00Z")'); - assert.eq(tojson(ISODate("0001-01-01T00:00:00+0199")), 'ISODate("0000-12-31T21:21:00Z")'); - assert.eq(tojson(ISODate("0001-01-01T00:00:00+9999")), 'ISODate("0000-12-27T19:21:00Z")'); - - assert.eq(tojson(ISODate("0001-01-01T00:00:00-0100")), 'ISODate("0001-01-01T01:00:00Z")'); - assert.eq(tojson(ISODate("0001-01-01T00:00:00-0160")), 'ISODate("0001-01-01T02:00:00Z")'); - assert.eq(tojson(ISODate("0001-01-01T00:00:00-0199")), 'ISODate("0001-01-01T02:39:00Z")'); - assert.eq(tojson(ISODate("0001-01-01T00:00:00-9999")), 'ISODate("0001-01-05T04:39:00Z")'); - - assert.eq(tojson(ISODate("0001-01-01T00:00:00+01:00")), 'ISODate("0000-12-31T23:00:00Z")'); - assert.eq(tojson(ISODate("0001-01-01T00:00:00+01:60")), 'ISODate("0000-12-31T22:00:00Z")'); - assert.eq(tojson(ISODate("0001-01-01T00:00:00+01:99")), 'ISODate("0000-12-31T21:21:00Z")'); - assert.eq(tojson(ISODate("0001-01-01T00:00:00+99:99")), 'ISODate("0000-12-27T19:21:00Z")'); - - assert.eq(tojson(ISODate("0001-01-01T00:00:00-01:00")), 'ISODate("0001-01-01T01:00:00Z")'); - assert.eq(tojson(ISODate("0001-01-01T00:00:00-01:60")), 'ISODate("0001-01-01T02:00:00Z")'); - assert.eq(tojson(ISODate("0001-01-01T00:00:00-01:99")), 'ISODate("0001-01-01T02:39:00Z")'); - assert.eq(tojson(ISODate("0001-01-01T00:00:00-99:99")), 'ISODate("0001-01-05T04:39:00Z")'); - - /* Testing field underflows */ - assert.eq(tojson(ISODate("0001-01-00T00:00:00Z")), 'ISODate("0000-12-31T00:00:00Z")'); - assert.eq(tojson(ISODate("0001-00-00T00:00:00Z")), 'ISODate("0000-11-30T00:00:00Z")'); - assert.eq(tojson(ISODate("0001-00-01T00:00:00Z")), 'ISODate("0000-12-01T00:00:00Z")'); - - /* Testing lowest and highest */ - assert.eq(tojson(ISODate("0000-01-01T00:00:00Z")), 'ISODate("0000-01-01T00:00:00Z")'); - assert.eq(tojson(ISODate("9999-12-31T23:59:59.999Z")), 'ISODate("9999-12-31T23:59:59.999Z")'); - - /* Testing out of range */ +assert.eq(tojson(ISODate("0000-01-01")), 'ISODate("0000-01-01T00:00:00Z")'); +assert.eq(tojson(ISODate("0000-01-01T00:00:00")), 'ISODate("0000-01-01T00:00:00Z")'); +assert.eq(tojson(ISODate("0000-01-01T00:00:00Z")), 'ISODate("0000-01-01T00:00:00Z")'); +assert.eq(tojson(ISODate("0000-01-01T00:00:00.123")), 'ISODate("0000-01-01T00:00:00.123Z")'); +assert.eq(tojson(ISODate("0000-01-01T00:00:00.123Z")), 'ISODate("0000-01-01T00:00:00.123Z")'); + +assert.eq(tojson(ISODate("0000-01-01T00:00:00.1Z")), 'ISODate("0000-01-01T00:00:00.100Z")'); +assert.eq(tojson(ISODate("0000-01-01T00:00:00.10Z")), 'ISODate("0000-01-01T00:00:00.100Z")'); +assert.eq(tojson(ISODate("0000-01-01T00:00:00.100Z")), 'ISODate("0000-01-01T00:00:00.100Z")'); +assert.eq(tojson(ISODate("0000-01-01T00:00:00.1000Z")), 'ISODate("0000-01-01T00:00:00.100Z")'); + +assert.eq(tojson(ISODate("0000-01-01T00:00:00.1234Z")), 'ISODate("0000-01-01T00:00:00.123Z")'); +assert.eq(tojson(ISODate("0000-01-01T00:00:00.1235Z")), 'ISODate("0000-01-01T00:00:00.124Z")'); + +/* Testing different years */ +assert.eq(tojson(ISODate("0000-01-01T00:00:00Z")), 'ISODate("0000-01-01T00:00:00Z")'); +assert.eq(tojson(ISODate("0001-01-01T00:00:00Z")), 'ISODate("0001-01-01T00:00:00Z")'); +assert.eq(tojson(ISODate("0069-01-01T00:00:00Z")), 'ISODate("0069-01-01T00:00:00Z")'); +assert.eq(tojson(ISODate("0070-01-01T00:00:00Z")), 'ISODate("0070-01-01T00:00:00Z")'); +assert.eq(tojson(ISODate("0099-01-01T00:00:00Z")), 'ISODate("0099-01-01T00:00:00Z")'); +assert.eq(tojson(ISODate("0100-01-01T00:00:00Z")), 'ISODate("0100-01-01T00:00:00Z")'); +assert.eq(tojson(ISODate("1800-01-01T00:00:00Z")), 'ISODate("1800-01-01T00:00:00Z")'); +assert.eq(tojson(ISODate("1801-01-01T00:00:00Z")), 'ISODate("1801-01-01T00:00:00Z")'); +assert.eq(tojson(ISODate("1869-01-01T00:00:00Z")), 'ISODate("1869-01-01T00:00:00Z")'); +assert.eq(tojson(ISODate("1870-01-01T00:00:00Z")), 'ISODate("1870-01-01T00:00:00Z")'); +assert.eq(tojson(ISODate("1899-01-01T00:00:00Z")), 'ISODate("1899-01-01T00:00:00Z")'); +assert.eq(tojson(ISODate("1900-01-01T00:00:00Z")), 'ISODate("1900-01-01T00:00:00Z")'); +assert.eq(tojson(ISODate("1901-01-01T00:00:00Z")), 'ISODate("1901-01-01T00:00:00Z")'); +assert.eq(tojson(ISODate("1969-01-01T00:00:00Z")), 'ISODate("1969-01-01T00:00:00Z")'); +assert.eq(tojson(ISODate("1970-01-01T00:00:00Z")), 'ISODate("1970-01-01T00:00:00Z")'); +assert.eq(tojson(ISODate("1999-01-01T00:00:00Z")), 'ISODate("1999-01-01T00:00:00Z")'); +assert.eq(tojson(ISODate("2000-01-01T00:00:00Z")), 'ISODate("2000-01-01T00:00:00Z")'); +assert.eq(tojson(ISODate("2001-01-01T00:00:00Z")), 'ISODate("2001-01-01T00:00:00Z")'); +assert.eq(tojson(ISODate("2069-01-01T00:00:00Z")), 'ISODate("2069-01-01T00:00:00Z")'); +assert.eq(tojson(ISODate("2070-01-01T00:00:00Z")), 'ISODate("2070-01-01T00:00:00Z")'); +assert.eq(tojson(ISODate("2099-01-01T00:00:00Z")), 'ISODate("2099-01-01T00:00:00Z")'); +assert.eq(tojson(ISODate("9999-01-01T00:00:00Z")), 'ISODate("9999-01-01T00:00:00Z")'); + +/* Testing without - in date and : in time */ +assert.eq(tojson(ISODate("19980101T00:00:00Z")), 'ISODate("1998-01-01T00:00:00Z")'); +assert.eq(tojson(ISODate("1999-0101T00:00:00Z")), 'ISODate("1999-01-01T00:00:00Z")'); +assert.eq(tojson(ISODate("200001-01T00:00:00Z")), 'ISODate("2000-01-01T00:00:00Z")'); +assert.eq(tojson(ISODate("1998-01-01T000000Z")), 'ISODate("1998-01-01T00:00:00Z")'); +assert.eq(tojson(ISODate("1999-01-01T00:0000Z")), 'ISODate("1999-01-01T00:00:00Z")'); +assert.eq(tojson(ISODate("2000-01-01T0000:00Z")), 'ISODate("2000-01-01T00:00:00Z")'); + +/* Testing field overflows */ +assert.eq(tojson(ISODate("0000-01-01T00:00:60Z")), 'ISODate("0000-01-01T00:01:00Z")'); +assert.eq(tojson(ISODate("0000-01-01T00:00:99Z")), 'ISODate("0000-01-01T00:01:39Z")'); + +assert.eq(tojson(ISODate("0000-01-01T00:60:00Z")), 'ISODate("0000-01-01T01:00:00Z")'); +assert.eq(tojson(ISODate("0000-01-01T00:99:00Z")), 'ISODate("0000-01-01T01:39:00Z")'); + +assert.eq(tojson(ISODate("0000-01-01T24:00:00Z")), 'ISODate("0000-01-02T00:00:00Z")'); +assert.eq(tojson(ISODate("0000-01-01T99:00:00Z")), 'ISODate("0000-01-05T03:00:00Z")'); + +assert.eq(tojson(ISODate("0000-01-32T00:00:00Z")), 'ISODate("0000-02-01T00:00:00Z")'); +assert.eq(tojson(ISODate("0000-01-99T00:00:00Z")), 'ISODate("0000-04-08T00:00:00Z")'); +assert.eq(tojson(ISODate("0000-02-29T00:00:00Z")), 'ISODate("0000-02-29T00:00:00Z")'); +assert.eq(tojson(ISODate("0000-02-30T00:00:00Z")), 'ISODate("0000-03-01T00:00:00Z")'); +assert.eq(tojson(ISODate("0000-02-31T00:00:00Z")), 'ISODate("0000-03-02T00:00:00Z")'); +assert.eq(tojson(ISODate("0000-02-99T00:00:00Z")), 'ISODate("0000-05-09T00:00:00Z")'); + +assert.eq(tojson(ISODate("0001-02-29T00:00:00Z")), 'ISODate("0001-03-01T00:00:00Z")'); +assert.eq(tojson(ISODate("0001-02-30T00:00:00Z")), 'ISODate("0001-03-02T00:00:00Z")'); +assert.eq(tojson(ISODate("0001-02-31T00:00:00Z")), 'ISODate("0001-03-03T00:00:00Z")'); +assert.eq(tojson(ISODate("0001-02-99T00:00:00Z")), 'ISODate("0001-05-10T00:00:00Z")'); + +assert.eq(tojson(ISODate("0000-13-01T00:00:00Z")), 'ISODate("0001-01-01T00:00:00Z")'); +assert.eq(tojson(ISODate("0000-99-01T00:00:00Z")), 'ISODate("0008-03-01T00:00:00Z")'); + +/* Testing GMT offset instead of Z */ +assert.eq(tojson(ISODate("0001-01-01T00:00:00+01")), 'ISODate("0000-12-31T23:00:00Z")'); +assert.eq(tojson(ISODate("0001-01-01T00:00:00+99")), 'ISODate("0000-12-27T21:00:00Z")'); + +assert.eq(tojson(ISODate("0001-01-01T00:00:00-01")), 'ISODate("0001-01-01T01:00:00Z")'); +assert.eq(tojson(ISODate("0001-01-01T00:00:00-99")), 'ISODate("0001-01-05T03:00:00Z")'); + +assert.eq(tojson(ISODate("0001-01-01T00:00:00+0100")), 'ISODate("0000-12-31T23:00:00Z")'); +assert.eq(tojson(ISODate("0001-01-01T00:00:00+0160")), 'ISODate("0000-12-31T22:00:00Z")'); +assert.eq(tojson(ISODate("0001-01-01T00:00:00+0199")), 'ISODate("0000-12-31T21:21:00Z")'); +assert.eq(tojson(ISODate("0001-01-01T00:00:00+9999")), 'ISODate("0000-12-27T19:21:00Z")'); + +assert.eq(tojson(ISODate("0001-01-01T00:00:00-0100")), 'ISODate("0001-01-01T01:00:00Z")'); +assert.eq(tojson(ISODate("0001-01-01T00:00:00-0160")), 'ISODate("0001-01-01T02:00:00Z")'); +assert.eq(tojson(ISODate("0001-01-01T00:00:00-0199")), 'ISODate("0001-01-01T02:39:00Z")'); +assert.eq(tojson(ISODate("0001-01-01T00:00:00-9999")), 'ISODate("0001-01-05T04:39:00Z")'); + +assert.eq(tojson(ISODate("0001-01-01T00:00:00+01:00")), 'ISODate("0000-12-31T23:00:00Z")'); +assert.eq(tojson(ISODate("0001-01-01T00:00:00+01:60")), 'ISODate("0000-12-31T22:00:00Z")'); +assert.eq(tojson(ISODate("0001-01-01T00:00:00+01:99")), 'ISODate("0000-12-31T21:21:00Z")'); +assert.eq(tojson(ISODate("0001-01-01T00:00:00+99:99")), 'ISODate("0000-12-27T19:21:00Z")'); + +assert.eq(tojson(ISODate("0001-01-01T00:00:00-01:00")), 'ISODate("0001-01-01T01:00:00Z")'); +assert.eq(tojson(ISODate("0001-01-01T00:00:00-01:60")), 'ISODate("0001-01-01T02:00:00Z")'); +assert.eq(tojson(ISODate("0001-01-01T00:00:00-01:99")), 'ISODate("0001-01-01T02:39:00Z")'); +assert.eq(tojson(ISODate("0001-01-01T00:00:00-99:99")), 'ISODate("0001-01-05T04:39:00Z")'); + +/* Testing field underflows */ +assert.eq(tojson(ISODate("0001-01-00T00:00:00Z")), 'ISODate("0000-12-31T00:00:00Z")'); +assert.eq(tojson(ISODate("0001-00-00T00:00:00Z")), 'ISODate("0000-11-30T00:00:00Z")'); +assert.eq(tojson(ISODate("0001-00-01T00:00:00Z")), 'ISODate("0000-12-01T00:00:00Z")'); + +/* Testing lowest and highest */ +assert.eq(tojson(ISODate("0000-01-01T00:00:00Z")), 'ISODate("0000-01-01T00:00:00Z")'); +assert.eq(tojson(ISODate("9999-12-31T23:59:59.999Z")), 'ISODate("9999-12-31T23:59:59.999Z")'); + +/* Testing out of range */ +assert.throws(function() { + tojson(ISODate("0000-01-00T23:59:59.999Z")); +}); +assert.throws(function() { + tojson(ISODate("9999-12-31T23:59:60Z")); +}); + +/* Testing broken format */ +var brokenFormatTests = [ + "2017", + "2017-09", + "2017-09-16T18:37 25Z", + "2017-09-16T18 37:25Z", + "2017-09-16X18:37:25Z", + "2017-09 16T18:37:25Z", + "2017 09-16T18:37:25Z", + "2017-09-16T18:37:25 123Z", + "2017-09-16T18:37:25 0600", +]; + +brokenFormatTests.forEach(function(test) { assert.throws(function() { - tojson(ISODate("0000-01-00T23:59:59.999Z")); - }); - assert.throws(function() { - tojson(ISODate("9999-12-31T23:59:60Z")); - }); - - /* Testing broken format */ - var brokenFormatTests = [ - "2017", - "2017-09", - "2017-09-16T18:37 25Z", - "2017-09-16T18 37:25Z", - "2017-09-16X18:37:25Z", - "2017-09 16T18:37:25Z", - "2017 09-16T18:37:25Z", - "2017-09-16T18:37:25 123Z", - "2017-09-16T18:37:25 0600", - ]; - - brokenFormatTests.forEach(function(test) { - assert.throws(function() { - print(tojson(ISODate(test))); - }, [tojson(test)]); - }); - - /* Testing conversion to milliseconds */ - assert.eq(ISODate("1969-12-31T23:59:59.999Z"), new Date(-1)); - assert.eq(ISODate("1969-12-31T23:59:59.000Z"), new Date(-1000)); - assert.eq(ISODate("1900-01-01T00:00:00.000Z"), new Date(-2208988800000)); - assert.eq(ISODate("1899-12-31T23:59:59.999Z"), new Date(-2208988800001)); - assert.eq(ISODate("0000-01-01T00:00:00.000Z"), new Date(-62167219200000)); - assert.eq(ISODate("9999-12-31T23:59:59.999Z"), new Date(253402300799999)); + print(tojson(ISODate(test))); + }, [tojson(test)]); +}); + +/* Testing conversion to milliseconds */ +assert.eq(ISODate("1969-12-31T23:59:59.999Z"), new Date(-1)); +assert.eq(ISODate("1969-12-31T23:59:59.000Z"), new Date(-1000)); +assert.eq(ISODate("1900-01-01T00:00:00.000Z"), new Date(-2208988800000)); +assert.eq(ISODate("1899-12-31T23:59:59.999Z"), new Date(-2208988800001)); +assert.eq(ISODate("0000-01-01T00:00:00.000Z"), new Date(-62167219200000)); +assert.eq(ISODate("9999-12-31T23:59:59.999Z"), new Date(253402300799999)); }()); diff --git a/jstests/aggregation/bugs/server8568.js b/jstests/aggregation/bugs/server8568.js index ae9a9ad8202..71793f5696b 100644 --- a/jstests/aggregation/bugs/server8568.js +++ b/jstests/aggregation/bugs/server8568.js @@ -4,40 +4,40 @@ load('jstests/aggregation/extras/utils.js'); (function() { - 'use strict'; - var coll = db.sqrt; - coll.drop(); - assert.writeOK(coll.insert({_id: 0})); - - // Helper for testing that op returns expResult. - function testOp(op, expResult) { - var pipeline = [{$project: {_id: 0, result: op}}]; - assert.eq(coll.aggregate(pipeline).toArray(), [{result: expResult}]); - } - - // Helper for testing that op results in error with code errorCode. - function testError(op, errorCode) { - var pipeline = [{$project: {_id: 0, result: op}}]; - assertErrorCode(coll, pipeline, errorCode); - } - - // Valid input: Numeric arg >= 0, null, or NaN. - - testOp({$sqrt: [100]}, 10); - testOp({$sqrt: [0]}, 0); - // All types converted to doubles. - testOp({$sqrt: [NumberLong("100")]}, 10); - // LLONG_MAX is converted to a double. - testOp({$sqrt: [NumberLong("9223372036854775807")]}, 3037000499.97605); - // Null inputs result in null. - testOp({$sqrt: [null]}, null); - // NaN inputs result in NaN. - testOp({$sqrt: [NaN]}, NaN); - - // Invalid input: non-numeric/non-null, arg is negative. - - // Arg must be numeric or null. - testError({$sqrt: ["string"]}, 28765); - // Args cannot be negative. - testError({$sqrt: [-1]}, 28714); +'use strict'; +var coll = db.sqrt; +coll.drop(); +assert.writeOK(coll.insert({_id: 0})); + +// Helper for testing that op returns expResult. +function testOp(op, expResult) { + var pipeline = [{$project: {_id: 0, result: op}}]; + assert.eq(coll.aggregate(pipeline).toArray(), [{result: expResult}]); +} + +// Helper for testing that op results in error with code errorCode. +function testError(op, errorCode) { + var pipeline = [{$project: {_id: 0, result: op}}]; + assertErrorCode(coll, pipeline, errorCode); +} + +// Valid input: Numeric arg >= 0, null, or NaN. + +testOp({$sqrt: [100]}, 10); +testOp({$sqrt: [0]}, 0); +// All types converted to doubles. +testOp({$sqrt: [NumberLong("100")]}, 10); +// LLONG_MAX is converted to a double. +testOp({$sqrt: [NumberLong("9223372036854775807")]}, 3037000499.97605); +// Null inputs result in null. +testOp({$sqrt: [null]}, null); +// NaN inputs result in NaN. +testOp({$sqrt: [NaN]}, NaN); + +// Invalid input: non-numeric/non-null, arg is negative. + +// Arg must be numeric or null. +testError({$sqrt: ["string"]}, 28765); +// Args cannot be negative. +testError({$sqrt: [-1]}, 28714); }()); diff --git a/jstests/aggregation/bugs/server8581.js b/jstests/aggregation/bugs/server8581.js index 54b97be3d08..fa81578ccbd 100644 --- a/jstests/aggregation/bugs/server8581.js +++ b/jstests/aggregation/bugs/server8581.js @@ -79,47 +79,47 @@ a3result = [{ a4result = [ { - _id: 1, - level: 1, - b: { - level: 3, - c: 5, - d: [{level: 1, e: 4}, {f: 6}, "NOT AN OBJECT!!11!", [2, 3, 4, {level: 1, r: 11}]] - }, - h: {level: 2, i: {level: 4, j: {level: 1, k: 8}}}, - l: {m: {level: 3, n: 12}}, - o: [], - q: 14 + _id: 1, + level: 1, + b: { + level: 3, + c: 5, + d: [{level: 1, e: 4}, {f: 6}, "NOT AN OBJECT!!11!", [2, 3, 4, {level: 1, r: 11}]] + }, + h: {level: 2, i: {level: 4, j: {level: 1, k: 8}}}, + l: {m: {level: 3, n: 12}}, + o: [], + q: 14 }, { - _id: 2, - level: 4, + _id: 2, + level: 4, } ]; a5result = [ { - _id: 1, - level: 1, - b: { - level: 3, - c: 5, - d: [ - {level: 1, e: 4}, - {f: 6}, - {level: 5, g: 9}, - "NOT AN OBJECT!!11!", - [2, 3, 4, {level: 1, r: 11}, {level: 5, s: 99}] - ] - }, - h: {level: 2, i: {level: 4, j: {level: 1, k: 8}}}, - l: {m: {level: 3, n: 12}}, - o: [{level: 5, p: 19}], - q: 14 + _id: 1, + level: 1, + b: { + level: 3, + c: 5, + d: [ + {level: 1, e: 4}, + {f: 6}, + {level: 5, g: 9}, + "NOT AN OBJECT!!11!", + [2, 3, 4, {level: 1, r: 11}, {level: 5, s: 99}] + ] + }, + h: {level: 2, i: {level: 4, j: {level: 1, k: 8}}}, + l: {m: {level: 3, n: 12}}, + o: [{level: 5, p: 19}], + q: 14 }, { - _id: 2, - level: 4, + _id: 2, + level: 4, } ]; diff --git a/jstests/aggregation/bugs/server9444.js b/jstests/aggregation/bugs/server9444.js index f3c6a449fad..6bb554c8e62 100644 --- a/jstests/aggregation/bugs/server9444.js +++ b/jstests/aggregation/bugs/server9444.js @@ -1,64 +1,64 @@ // server-9444 support disk storage of intermediate results in aggregation (function() { - 'use strict'; +'use strict'; - load('jstests/libs/fixture_helpers.js'); // For 'FixtureHelpers' +load('jstests/libs/fixture_helpers.js'); // For 'FixtureHelpers' - const t = db.server9444; - t.drop(); +const t = db.server9444; +t.drop(); - const sharded = FixtureHelpers.isSharded(t); +const sharded = FixtureHelpers.isSharded(t); - var memoryLimitMB = sharded ? 200 : 100; +var memoryLimitMB = sharded ? 200 : 100; - function loadData() { - var bigStr = Array(1024 * 1024 + 1).toString(); // 1MB of ',' - for (var i = 0; i < memoryLimitMB + 1; i++) - t.insert({_id: i, bigStr: i + bigStr, random: Math.random()}); +function loadData() { + var bigStr = Array(1024 * 1024 + 1).toString(); // 1MB of ',' + for (var i = 0; i < memoryLimitMB + 1; i++) + t.insert({_id: i, bigStr: i + bigStr, random: Math.random()}); - assert.gt(t.stats().size, memoryLimitMB * 1024 * 1024); - } - loadData(); + assert.gt(t.stats().size, memoryLimitMB * 1024 * 1024); +} +loadData(); - function test(pipeline, outOfMemoryCode) { - // ensure by default we error out if exceeding memory limit - var res = t.runCommand('aggregate', {pipeline: pipeline, cursor: {}}); - assert.commandFailed(res); - assert.eq(res.code, outOfMemoryCode); +function test(pipeline, outOfMemoryCode) { + // ensure by default we error out if exceeding memory limit + var res = t.runCommand('aggregate', {pipeline: pipeline, cursor: {}}); + assert.commandFailed(res); + assert.eq(res.code, outOfMemoryCode); - // ensure allowDiskUse: false does what it says - res = t.runCommand('aggregate', {pipeline: pipeline, cursor: {}, allowDiskUse: false}); - assert.commandFailed(res); - assert.eq(res.code, outOfMemoryCode); + // ensure allowDiskUse: false does what it says + res = t.runCommand('aggregate', {pipeline: pipeline, cursor: {}, allowDiskUse: false}); + assert.commandFailed(res); + assert.eq(res.code, outOfMemoryCode); - // allowDiskUse only supports bool. In particular, numbers aren't allowed. - res = t.runCommand('aggregate', {pipeline: pipeline, cursor: {}, allowDiskUse: 1}); - assert.commandFailed(res); + // allowDiskUse only supports bool. In particular, numbers aren't allowed. + res = t.runCommand('aggregate', {pipeline: pipeline, cursor: {}, allowDiskUse: 1}); + assert.commandFailed(res); - // ensure we work when allowDiskUse === true - res = t.aggregate(pipeline, {allowDiskUse: true}); - assert.eq(res.itcount(), t.count()); // all tests output one doc per input doc - } + // ensure we work when allowDiskUse === true + res = t.aggregate(pipeline, {allowDiskUse: true}); + assert.eq(res.itcount(), t.count()); // all tests output one doc per input doc +} - var groupCode = 16945; - var sortCode = 16819; - var sortLimitCode = 16820; +var groupCode = 16945; +var sortCode = 16819; +var sortLimitCode = 16820; - test([{$group: {_id: '$_id', bigStr: {$min: '$bigStr'}}}], groupCode); +test([{$group: {_id: '$_id', bigStr: {$min: '$bigStr'}}}], groupCode); - // sorting with _id would use index which doesn't require extsort - test([{$sort: {random: 1}}], sortCode); - test([{$sort: {bigStr: 1}}], sortCode); // big key and value +// sorting with _id would use index which doesn't require extsort +test([{$sort: {random: 1}}], sortCode); +test([{$sort: {bigStr: 1}}], sortCode); // big key and value - // make sure sort + large limit won't crash the server (SERVER-10136) - test([{$sort: {bigStr: 1}}, {$limit: 1000 * 1000 * 1000}], sortLimitCode); +// make sure sort + large limit won't crash the server (SERVER-10136) +test([{$sort: {bigStr: 1}}, {$limit: 1000 * 1000 * 1000}], sortLimitCode); - // test combining two extSorts in both same and different orders - test([{$group: {_id: '$_id', bigStr: {$min: '$bigStr'}}}, {$sort: {_id: 1}}], groupCode); - test([{$group: {_id: '$_id', bigStr: {$min: '$bigStr'}}}, {$sort: {_id: -1}}], groupCode); - test([{$group: {_id: '$_id', bigStr: {$min: '$bigStr'}}}, {$sort: {random: 1}}], groupCode); - test([{$sort: {random: 1}}, {$group: {_id: '$_id', bigStr: {$first: '$bigStr'}}}], sortCode); +// test combining two extSorts in both same and different orders +test([{$group: {_id: '$_id', bigStr: {$min: '$bigStr'}}}, {$sort: {_id: 1}}], groupCode); +test([{$group: {_id: '$_id', bigStr: {$min: '$bigStr'}}}, {$sort: {_id: -1}}], groupCode); +test([{$group: {_id: '$_id', bigStr: {$min: '$bigStr'}}}, {$sort: {random: 1}}], groupCode); +test([{$sort: {random: 1}}, {$group: {_id: '$_id', bigStr: {$first: '$bigStr'}}}], sortCode); - // don't leave large collection laying around - t.drop(); +// don't leave large collection laying around +t.drop(); })(); diff --git a/jstests/aggregation/bugs/server9625.js b/jstests/aggregation/bugs/server9625.js index 4a525aba518..4cbf487b5e0 100644 --- a/jstests/aggregation/bugs/server9625.js +++ b/jstests/aggregation/bugs/server9625.js @@ -5,70 +5,70 @@ load('jstests/aggregation/extras/utils.js'); (function() { - 'use strict'; - var coll = db.server9625; - coll.drop(); - assert.writeOK(coll.insert({})); +'use strict'; +var coll = db.server9625; +coll.drop(); +assert.writeOK(coll.insert({})); - // Helper for testing that op returns expResult. - function testOp(op, expResult) { - var pipeline = [{$project: {_id: 0, result: op}}]; - assert.eq(coll.aggregate(pipeline).toArray(), [{result: expResult}]); - } +// Helper for testing that op returns expResult. +function testOp(op, expResult) { + var pipeline = [{$project: {_id: 0, result: op}}]; + assert.eq(coll.aggregate(pipeline).toArray(), [{result: expResult}]); +} - // ExpressionFromAccumulators take either a list of arguments or a single array argument. - testOp({$avg: [1, 2, 3, 4, 5]}, 3); - testOp({$avg: [[1, 2, 3, 4, 5]]}, 3); - testOp({$min: [1, 2, 3, 4, 5]}, 1); - testOp({$min: [[1, 2, 3, 4, 5]]}, 1); - testOp({$max: [1, 2, 3, 4, 5]}, 5); - testOp({$max: [[1, 2, 3, 4, 5]]}, 5); - testOp({$sum: [1, 2, 3, 4, 5]}, 15); - testOp({$sum: [[1, 2, 3, 4, 5]]}, 15); - testOp({$stdDevPop: [1, 3]}, 1); - testOp({$stdDevPop: [[1, 3]]}, 1); - testOp({$stdDevSamp: [1, 2, 3]}, 1); - testOp({$stdDevSamp: [[1, 2, 3]]}, 1); +// ExpressionFromAccumulators take either a list of arguments or a single array argument. +testOp({$avg: [1, 2, 3, 4, 5]}, 3); +testOp({$avg: [[1, 2, 3, 4, 5]]}, 3); +testOp({$min: [1, 2, 3, 4, 5]}, 1); +testOp({$min: [[1, 2, 3, 4, 5]]}, 1); +testOp({$max: [1, 2, 3, 4, 5]}, 5); +testOp({$max: [[1, 2, 3, 4, 5]]}, 5); +testOp({$sum: [1, 2, 3, 4, 5]}, 15); +testOp({$sum: [[1, 2, 3, 4, 5]]}, 15); +testOp({$stdDevPop: [1, 3]}, 1); +testOp({$stdDevPop: [[1, 3]]}, 1); +testOp({$stdDevSamp: [1, 2, 3]}, 1); +testOp({$stdDevSamp: [[1, 2, 3]]}, 1); - // Null arguments are ignored. - testOp({$avg: [1, 2, 3, 4, 5, null]}, 3); - testOp({$min: [1, 2, 3, 4, 5, null]}, 1); - testOp({$max: [1, 2, 3, 4, 5, null]}, 5); - testOp({$sum: [1, 2, 3, 4, 5, null]}, 15); - testOp({$stdDevPop: [1, 3, null]}, 1); - testOp({$stdDevSamp: [1, 2, 3, null]}, 1); +// Null arguments are ignored. +testOp({$avg: [1, 2, 3, 4, 5, null]}, 3); +testOp({$min: [1, 2, 3, 4, 5, null]}, 1); +testOp({$max: [1, 2, 3, 4, 5, null]}, 5); +testOp({$sum: [1, 2, 3, 4, 5, null]}, 15); +testOp({$stdDevPop: [1, 3, null]}, 1); +testOp({$stdDevSamp: [1, 2, 3, null]}, 1); - // NaN arguments are processed by all expressions. - testOp({$avg: [1, 2, 3, 4, 5, NaN]}, NaN); - testOp({$min: [1, 2, 3, 4, 5, NaN]}, NaN); - testOp({$max: [1, 2, 3, 4, 5, NaN]}, 5); - testOp({$sum: [1, 2, 3, 4, 5, NaN]}, NaN); - testOp({$stdDevPop: [1, 3, NaN]}, NaN); - testOp({$stdDevSamp: [1, 2, 3, NaN]}, NaN); +// NaN arguments are processed by all expressions. +testOp({$avg: [1, 2, 3, 4, 5, NaN]}, NaN); +testOp({$min: [1, 2, 3, 4, 5, NaN]}, NaN); +testOp({$max: [1, 2, 3, 4, 5, NaN]}, 5); +testOp({$sum: [1, 2, 3, 4, 5, NaN]}, NaN); +testOp({$stdDevPop: [1, 3, NaN]}, NaN); +testOp({$stdDevSamp: [1, 2, 3, NaN]}, NaN); - // Use at least one non-constant value in the following tests, to ensure - // isAssociative() and isCommutative() are called. If all arguments are constant, the - // optimization will evaluate them all into one, without calling isAssociative() nor - // isCommutative(). - coll.drop(); - assert.writeOK(coll.insert({"a": 1, "b": 6})); +// Use at least one non-constant value in the following tests, to ensure +// isAssociative() and isCommutative() are called. If all arguments are constant, the +// optimization will evaluate them all into one, without calling isAssociative() nor +// isCommutative(). +coll.drop(); +assert.writeOK(coll.insert({"a": 1, "b": 6})); - // These expressions are associative and commutative so inner expression can be combined with - // outer. - testOp({$sum: ["$a", 2, 3, {$sum: [4, 5]}]}, 15); - testOp({$min: ["$a", 2, 3, {$min: [4, 5]}]}, 1); - testOp({$max: ["$a", 2, 3, {$max: [4, 5]}]}, 5); +// These expressions are associative and commutative so inner expression can be combined with +// outer. +testOp({$sum: ["$a", 2, 3, {$sum: [4, 5]}]}, 15); +testOp({$min: ["$a", 2, 3, {$min: [4, 5]}]}, 1); +testOp({$max: ["$a", 2, 3, {$max: [4, 5]}]}, 5); - // These expressions are not associative and commutative so inner expression cannot be combined - // with outer. - testOp({$avg: ["$a", 3, {$avg: [4, 6]}]}, 3); - testOp({$stdDevPop: ["$a", {$stdDevPop: [1, 3]}]}, 0); - testOp({$stdDevSamp: ["$a", {$stdDevSamp: [1, 2, 3]}]}, 0); +// These expressions are not associative and commutative so inner expression cannot be combined +// with outer. +testOp({$avg: ["$a", 3, {$avg: [4, 6]}]}, 3); +testOp({$stdDevPop: ["$a", {$stdDevPop: [1, 3]}]}, 0); +testOp({$stdDevSamp: ["$a", {$stdDevSamp: [1, 2, 3]}]}, 0); - // If isAssociative() and isCommutative() did not return false when provided a single argument, - // the single array argument provided to the inner expression would be ignored instead of - // treated as a list of arguments, and these tests would fail. - testOp({$sum: ["$a", 2, 3, {$sum: [["$a", 4, 5]]}]}, 16); - testOp({$min: ["$b", 2, 3, {$min: [["$a", 4, 5]]}]}, 1); - testOp({$max: ["$a", 2, 3, {$max: [["$b", 4, 5]]}]}, 6); +// If isAssociative() and isCommutative() did not return false when provided a single argument, +// the single array argument provided to the inner expression would be ignored instead of +// treated as a list of arguments, and these tests would fail. +testOp({$sum: ["$a", 2, 3, {$sum: [["$a", 4, 5]]}]}, 16); +testOp({$min: ["$b", 2, 3, {$min: [["$a", 4, 5]]}]}, 1); +testOp({$max: ["$a", 2, 3, {$max: [["$b", 4, 5]]}]}, 6); }()); diff --git a/jstests/aggregation/bugs/skip_limit_overflow.js b/jstests/aggregation/bugs/skip_limit_overflow.js index f0d7e0b27c7..50e665b178f 100644 --- a/jstests/aggregation/bugs/skip_limit_overflow.js +++ b/jstests/aggregation/bugs/skip_limit_overflow.js @@ -8,116 +8,115 @@ * @tags: [do_not_wrap_aggregations_in_facets, assumes_unsharded_collection] */ (function() { - "use strict"; +"use strict"; - load("jstests/libs/analyze_plan.js"); // For 'aggPlanHasStages' and other explain helpers. +load("jstests/libs/analyze_plan.js"); // For 'aggPlanHasStages' and other explain helpers. - const coll = db.server39788; - coll.drop(); +const coll = db.server39788; +coll.drop(); - function testPipeline(pipeline, expectedResult, optimizedAwayStages) { - const explainOutput = coll.explain().aggregate(pipeline); +function testPipeline(pipeline, expectedResult, optimizedAwayStages) { + const explainOutput = coll.explain().aggregate(pipeline); - assert(explainOutput.hasOwnProperty("stages"), - "Expected pipeline " + tojsononeline(pipeline) + - " to use an aggregation framework in the explain output: " + - tojson(explainOutput)); + assert(explainOutput.hasOwnProperty("stages"), + "Expected pipeline " + tojsononeline(pipeline) + + " to use an aggregation framework in the explain output: " + tojson(explainOutput)); - if (optimizedAwayStages) { - optimizedAwayStages.forEach( - (stage) => - assert(!aggPlanHasStage(explainOutput, stage), - "Expected pipeline " + tojsononeline(pipeline) + " to *not* include a " + - stage + " stage in the explain output: " + tojson(explainOutput))); - } - - for (let path in expectedResult) { - const subPaths = path.split("."); - const stageName = subPaths[0]; - const stages = getAggPlanStages(explainOutput, stageName); - assert(stages !== null, - "Expected pipeline " + tojsononeline(pipeline) + " to include a " + stageName + - " stage in the explain output: " + tojson(explainOutput)); - assert(stages.length == expectedResult[path].length, - "Expected pipeline " + tojsononeline(pipeline) + " to include " + - expectedResult[path].length + stageName + " stages in the explain output: " + - tojson(explainOutput)); - assert.eq( - stages.reduce( - (res, stage) => { - res.push(subPaths.reduce((res, cur) => res[cur], stage)); - return res; - }, - []), - expectedResult[path], - "Stage: " + stageName + ", path: " + path + ", explain: " + tojson(explainOutput)); - } + if (optimizedAwayStages) { + optimizedAwayStages.forEach( + (stage) => + assert(!aggPlanHasStage(explainOutput, stage), + "Expected pipeline " + tojsononeline(pipeline) + " to *not* include a " + + stage + " stage in the explain output: " + tojson(explainOutput))); + } - // Ensure the aggregate command doesn't fail. - assert.eq(coll.aggregate(pipeline).toArray(), []); + for (let path in expectedResult) { + const subPaths = path.split("."); + const stageName = subPaths[0]; + const stages = getAggPlanStages(explainOutput, stageName); + assert(stages !== null, + "Expected pipeline " + tojsononeline(pipeline) + " to include a " + stageName + + " stage in the explain output: " + tojson(explainOutput)); + assert(stages.length == expectedResult[path].length, + "Expected pipeline " + tojsononeline(pipeline) + " to include " + + expectedResult[path].length + stageName + + " stages in the explain output: " + tojson(explainOutput)); + assert.eq( + stages.reduce( + (res, stage) => { + res.push(subPaths.reduce((res, cur) => res[cur], stage)); + return res; + }, + []), + expectedResult[path], + "Stage: " + stageName + ", path: " + path + ", explain: " + tojson(explainOutput)); } - // Case where overflow of limit + skip prevents limit stage from being absorbed. Values - // are specified as integrals > MAX_LONG. Note that we cannot specify this huge value as - // a NumberLong, as we get a number conversion error (even if it's passed as a string). - testPipeline([{$sort: {x: -1}}, {$skip: 18446744073709552000}, {$limit: 6}], - {"$limit": [NumberLong(6)], "$skip": [NumberLong("9223372036854775807")]}); - testPipeline([{$sort: {x: -1}}, {$skip: 6}, {$limit: 18446744073709552000}], - {"$limit": [NumberLong("9223372036854775807")], "$skip": [NumberLong(6)]}); + // Ensure the aggregate command doesn't fail. + assert.eq(coll.aggregate(pipeline).toArray(), []); +} + +// Case where overflow of limit + skip prevents limit stage from being absorbed. Values +// are specified as integrals > MAX_LONG. Note that we cannot specify this huge value as +// a NumberLong, as we get a number conversion error (even if it's passed as a string). +testPipeline([{$sort: {x: -1}}, {$skip: 18446744073709552000}, {$limit: 6}], + {"$limit": [NumberLong(6)], "$skip": [NumberLong("9223372036854775807")]}); +testPipeline([{$sort: {x: -1}}, {$skip: 6}, {$limit: 18446744073709552000}], + {"$limit": [NumberLong("9223372036854775807")], "$skip": [NumberLong(6)]}); - // Case where overflow of limit + skip prevents limit stage from being absorbed. One of the - // values == MAX_LONG, another one is 1. - testPipeline([{$sort: {x: -1}}, {$skip: NumberLong("9223372036854775807")}, {$limit: 1}], - {"$limit": [NumberLong(1)], "$skip": [NumberLong("9223372036854775807")]}); - testPipeline([{$sort: {x: -1}}, {$skip: 1}, {$limit: NumberLong("9223372036854775807")}], - {"$limit": [NumberLong("9223372036854775807")], "$skip": [NumberLong(1)]}); +// Case where overflow of limit + skip prevents limit stage from being absorbed. One of the +// values == MAX_LONG, another one is 1. +testPipeline([{$sort: {x: -1}}, {$skip: NumberLong("9223372036854775807")}, {$limit: 1}], + {"$limit": [NumberLong(1)], "$skip": [NumberLong("9223372036854775807")]}); +testPipeline([{$sort: {x: -1}}, {$skip: 1}, {$limit: NumberLong("9223372036854775807")}], + {"$limit": [NumberLong("9223372036854775807")], "$skip": [NumberLong(1)]}); - // Case where limit + skip do not overflow. Limit == MAX_LONG and skip is 0. Should be able to - // absorb the limit and skip stages. - // Note that we cannot specify limit == 0, so we expect an error in this case. - testPipeline([{$sort: {x: -1}}, {$skip: 0}, {$limit: NumberLong("9223372036854775807")}], - {"$cursor.limit": [NumberLong("9223372036854775807")]}, - ["$skip", "$limit"]); +// Case where limit + skip do not overflow. Limit == MAX_LONG and skip is 0. Should be able to +// absorb the limit and skip stages. +// Note that we cannot specify limit == 0, so we expect an error in this case. +testPipeline([{$sort: {x: -1}}, {$skip: 0}, {$limit: NumberLong("9223372036854775807")}], + {"$cursor.limit": [NumberLong("9223372036854775807")]}, + ["$skip", "$limit"]); - // Case where limit + skip do not overflow. One value is MAX_LONG - 1 and another one is 1. - // Should be able to absorb the limit stage. - testPipeline([{$sort: {x: -1}}, {$skip: NumberLong("9223372036854775806")}, {$limit: 1}], - { - "$cursor.limit": [NumberLong("9223372036854775807")], - "$skip": [NumberLong("9223372036854775806")] - }, - ["$limit"]); - testPipeline([{$sort: {x: -1}}, {$skip: 1}, {$limit: NumberLong("9223372036854775806")}], - {"$cursor.limit": [NumberLong("9223372036854775807")], "$skip": [NumberLong(1)]}, - ["$limit"]); +// Case where limit + skip do not overflow. One value is MAX_LONG - 1 and another one is 1. +// Should be able to absorb the limit stage. +testPipeline([{$sort: {x: -1}}, {$skip: NumberLong("9223372036854775806")}, {$limit: 1}], + { + "$cursor.limit": [NumberLong("9223372036854775807")], + "$skip": [NumberLong("9223372036854775806")] + }, + ["$limit"]); +testPipeline([{$sort: {x: -1}}, {$skip: 1}, {$limit: NumberLong("9223372036854775806")}], + {"$cursor.limit": [NumberLong("9223372036854775807")], "$skip": [NumberLong(1)]}, + ["$limit"]); - // Case where limit + skip do not overflow. Both values are < MAX_LONG. - testPipeline([{$sort: {x: -1}}, {$skip: 674761616283}, {$limit: 35361718}], - {"$cursor.limit": [NumberLong(674796978001)], "$skip": [NumberLong(674761616283)]}, - ["$limit"]); - testPipeline([{$sort: {x: -1}}, {$skip: 35361718}, {$limit: 674761616283}], - {"$cursor.limit": [NumberLong(674796978001)], "$skip": [NumberLong(35361718)]}, - ["$limit"]); +// Case where limit + skip do not overflow. Both values are < MAX_LONG. +testPipeline([{$sort: {x: -1}}, {$skip: 674761616283}, {$limit: 35361718}], + {"$cursor.limit": [NumberLong(674796978001)], "$skip": [NumberLong(674761616283)]}, + ["$limit"]); +testPipeline([{$sort: {x: -1}}, {$skip: 35361718}, {$limit: 674761616283}], + {"$cursor.limit": [NumberLong(674796978001)], "$skip": [NumberLong(35361718)]}, + ["$limit"]); - // Case where where overflow of limit + skip + skip prevents limit stage from being absorbed. - // One skip == MAX_LONG - 1, another one is 1. Should merge two skip stages into one. - testPipeline( - [{$sort: {x: -1}}, {$skip: 1}, {$skip: NumberLong("9223372036854775806")}, {$limit: 1}], - {"$limit": [NumberLong(1)], "$skip": [NumberLong("9223372036854775807")]}); +// Case where where overflow of limit + skip + skip prevents limit stage from being absorbed. +// One skip == MAX_LONG - 1, another one is 1. Should merge two skip stages into one. +testPipeline( + [{$sort: {x: -1}}, {$skip: 1}, {$skip: NumberLong("9223372036854775806")}, {$limit: 1}], + {"$limit": [NumberLong(1)], "$skip": [NumberLong("9223372036854775807")]}); - // Case where where overflow of limit + skip + skip prevents limit stage from being absorbed. - // One skip == MAX_LONG, another one is 1. Should not absorb or merge any stages. - testPipeline( - [{$sort: {x: -1}}, {$skip: 1}, {$skip: NumberLong("9223372036854775807")}, {$limit: 1}], - {"$limit": [NumberLong(1)], "$skip": [NumberLong(1), NumberLong("9223372036854775807")]}); +// Case where where overflow of limit + skip + skip prevents limit stage from being absorbed. +// One skip == MAX_LONG, another one is 1. Should not absorb or merge any stages. +testPipeline( + [{$sort: {x: -1}}, {$skip: 1}, {$skip: NumberLong("9223372036854775807")}, {$limit: 1}], + {"$limit": [NumberLong(1)], "$skip": [NumberLong(1), NumberLong("9223372036854775807")]}); - // Case where sample size is > MAX_LONG. - testPipeline([{$sample: {size: 18446744073709552000}}], - {"$sample.size": [NumberLong("9223372036854775807")]}); - // Case where sample size is == MAX_LONG. - testPipeline([{$sample: {size: NumberLong("9223372036854775807")}}], - {"$sample.size": [NumberLong("9223372036854775807")]}); - // Case where sample size is == MAX_LONG - 1. - testPipeline([{$sample: {size: NumberLong("9223372036854775806")}}], - {"$sample.size": [NumberLong("9223372036854775806")]}); +// Case where sample size is > MAX_LONG. +testPipeline([{$sample: {size: 18446744073709552000}}], + {"$sample.size": [NumberLong("9223372036854775807")]}); +// Case where sample size is == MAX_LONG. +testPipeline([{$sample: {size: NumberLong("9223372036854775807")}}], + {"$sample.size": [NumberLong("9223372036854775807")]}); +// Case where sample size is == MAX_LONG - 1. +testPipeline([{$sample: {size: NumberLong("9223372036854775806")}}], + {"$sample.size": [NumberLong("9223372036854775806")]}); })(); diff --git a/jstests/aggregation/bugs/sort_arrays.js b/jstests/aggregation/bugs/sort_arrays.js index 9fbb707decb..e83b4466cc6 100644 --- a/jstests/aggregation/bugs/sort_arrays.js +++ b/jstests/aggregation/bugs/sort_arrays.js @@ -1,17 +1,17 @@ // Tests that sorting by a field that contains an array will sort by the minimum element in that // array. (function() { - "use strict"; +"use strict"; - const coll = db.foo; - coll.drop(); - assert.writeOK(coll.insert([{_id: 2, a: [2, 3]}, {_id: 3, a: [2, 4]}, {_id: 4, a: [2, 1]}])); - const expectedOrder = [{_id: 4, a: [2, 1]}, {_id: 2, a: [2, 3]}, {_id: 3, a: [2, 4]}]; +const coll = db.foo; +coll.drop(); +assert.writeOK(coll.insert([{_id: 2, a: [2, 3]}, {_id: 3, a: [2, 4]}, {_id: 4, a: [2, 1]}])); +const expectedOrder = [{_id: 4, a: [2, 1]}, {_id: 2, a: [2, 3]}, {_id: 3, a: [2, 4]}]; - assert.eq(coll.aggregate([{$sort: {a: 1, _id: 1}}]).toArray(), expectedOrder); - assert.eq(coll.find().sort({a: 1, _id: 1}).toArray(), expectedOrder); +assert.eq(coll.aggregate([{$sort: {a: 1, _id: 1}}]).toArray(), expectedOrder); +assert.eq(coll.find().sort({a: 1, _id: 1}).toArray(), expectedOrder); - assert.commandWorked(coll.ensureIndex({a: 1})); - assert.eq(coll.aggregate([{$sort: {a: 1, _id: 1}}]).toArray(), expectedOrder); - assert.eq(coll.find().sort({a: 1, _id: 1}).toArray(), expectedOrder); +assert.commandWorked(coll.ensureIndex({a: 1})); +assert.eq(coll.aggregate([{$sort: {a: 1, _id: 1}}]).toArray(), expectedOrder); +assert.eq(coll.find().sort({a: 1, _id: 1}).toArray(), expectedOrder); }()); diff --git a/jstests/aggregation/bugs/substr.js b/jstests/aggregation/bugs/substr.js index 1090b09dffb..c4eaff7e137 100644 --- a/jstests/aggregation/bugs/substr.js +++ b/jstests/aggregation/bugs/substr.js @@ -122,8 +122,8 @@ assert.eq( a: { $substrBytes: [ { - $substrBytes: - [{$substrBytes: [{$substrBytes: ['abcdefghij', 1, 6]}, 2, 5]}, 0, 3] + $substrBytes: + [{$substrBytes: [{$substrBytes: ['abcdefghij', 1, 6]}, 2, 5]}, 0, 3] }, 1, 1 |