diff options
-rw-r--r-- | buildscripts/resmokeconfig/suites/causally_consistent_jscore_passthrough.yml | 21 | ||||
-rw-r--r-- | buildscripts/resmokeconfig/suites/causally_consistent_jscore_passthrough_auth.yml | 21 | ||||
-rw-r--r-- | jstests/core/distinct1.js | 101 | ||||
-rw-r--r-- | jstests/core/elemMatchProjection.js | 488 | ||||
-rw-r--r-- | jstests/core/find4.js | 66 | ||||
-rw-r--r-- | jstests/core/find5.js | 77 | ||||
-rw-r--r-- | jstests/core/find_dedup.js | 69 | ||||
-rw-r--r-- | jstests/core/fts1.js | 31 | ||||
-rw-r--r-- | jstests/core/geo_distinct.js | 211 | ||||
-rw-r--r-- | jstests/core/geo_s2ordering.js | 98 | ||||
-rw-r--r-- | jstests/core/group1.js | 289 | ||||
-rw-r--r-- | jstests/core/group2.js | 82 | ||||
-rw-r--r-- | jstests/core/maxscan.js | 29 | ||||
-rw-r--r-- | jstests/core/nan.js | 122 | ||||
-rw-r--r-- | jstests/core/not2.js | 144 | ||||
-rw-r--r-- | jstests/core/sort3.js | 24 | ||||
-rw-r--r-- | jstests/core/sort4.js | 85 |
17 files changed, 959 insertions, 999 deletions
diff --git a/buildscripts/resmokeconfig/suites/causally_consistent_jscore_passthrough.yml b/buildscripts/resmokeconfig/suites/causally_consistent_jscore_passthrough.yml index 21648575d6f..67faf503c33 100644 --- a/buildscripts/resmokeconfig/suites/causally_consistent_jscore_passthrough.yml +++ b/buildscripts/resmokeconfig/suites/causally_consistent_jscore_passthrough.yml @@ -111,32 +111,19 @@ selector: - jstests/core/or4.js - jstests/core/recursion.js - jstests/core/temp_cleanup.js + # Tests that use $snapshot and expect documents to arrive in some implicit sort order are not + # causally consistent. + - jstests/core/snapshot_queries.js + - jstests/core/find1.js # Tests that fail for Causal Consistency with default injected readPreference 'secondary' # "TODO SERVER-30384: These tests assume that documents are returned in the same order they are # written when no sort is specified; however, the order of documents within a collection can # be different across a primary and secondary." - jstests/core/coveredIndex1.js - - jstests/core/distinct1.js - - jstests/core/elemMatchProjection.js - - jstests/core/find1.js - - jstests/core/find4.js - - jstests/core/find5.js - - jstests/core/fts1.js - - jstests/core/find_dedup.js - jstests/core/fts_spanish.js - - jstests/core/geo_distinct.js - - jstests/core/geo_s2ordering.js - - jstests/core/group1.js - - jstests/core/group2.js - - jstests/core/maxscan.js - - jstests/core/nan.js - jstests/core/null2.js - - jstests/core/not2.js - - jstests/core/snapshot_queries.js - jstests/core/sorta.js - jstests/core/sortc.js - - jstests/core/sort3.js - - jstests/core/sort4.js - jstests/core/ord.js # plan_cache tests does not work because they check the plan_cache on the primary while the # cache is created on the secondary. diff --git a/buildscripts/resmokeconfig/suites/causally_consistent_jscore_passthrough_auth.yml b/buildscripts/resmokeconfig/suites/causally_consistent_jscore_passthrough_auth.yml index a3eaa46d3fd..9af91f857a9 100644 --- a/buildscripts/resmokeconfig/suites/causally_consistent_jscore_passthrough_auth.yml +++ b/buildscripts/resmokeconfig/suites/causally_consistent_jscore_passthrough_auth.yml @@ -126,32 +126,19 @@ selector: - jstests/core/or4.js - jstests/core/recursion.js - jstests/core/temp_cleanup.js + # Tests that use $snapshot and expect documents to arrive in some implicit sort order are not + # causally consistent. + - jstests/core/snapshot_queries.js + - jstests/core/find1.js # Tests that fail for Causal Consistency with default injected readPreference 'secondary' # "TODO SERVER-30384: These tests assume that documents are returned in the same order they are # written when no sort is specified; however, the order of documents within a collection can # be different across a primary and secondary." - jstests/core/coveredIndex1.js - - jstests/core/distinct1.js - - jstests/core/elemMatchProjection.js - - jstests/core/find1.js - - jstests/core/find4.js - - jstests/core/find5.js - - jstests/core/fts1.js - - jstests/core/find_dedup.js - jstests/core/fts_spanish.js - - jstests/core/geo_distinct.js - - jstests/core/geo_s2ordering.js - - jstests/core/group1.js - - jstests/core/group2.js - - jstests/core/maxscan.js - - jstests/core/nan.js - jstests/core/null2.js - - jstests/core/not2.js - - jstests/core/snapshot_queries.js - jstests/core/sorta.js - jstests/core/sortc.js - - jstests/core/sort3.js - - jstests/core/sort4.js - jstests/core/ord.js # plan_cache tests does not work because they check the plan_cache on the primary while the # cache is created on the secondary. diff --git a/jstests/core/distinct1.js b/jstests/core/distinct1.js index a70d027e51b..aee7b604926 100644 --- a/jstests/core/distinct1.js +++ b/jstests/core/distinct1.js @@ -1,62 +1,69 @@ +(function() { + "use strict"; + const collName = "distinct1"; + const coll = db.getCollection(collName); + coll.drop(); -t = db.distinct1; -t.drop(); + assert.eq(0, coll.distinct("a").length, "test empty"); -assert.eq(0, t.distinct("a").length, "test empty"); + assert.writeOK(coll.insert({a: 1})); + assert.writeOK(coll.insert({a: 2})); + assert.writeOK(coll.insert({a: 2})); + assert.writeOK(coll.insert({a: 2})); + assert.writeOK(coll.insert({a: 3})); -t.save({a: 1}); -t.save({a: 2}); -t.save({a: 2}); -t.save({a: 2}); -t.save({a: 3}); + // Test that distinct returns all the distinct values. + assert.eq([1, 2, 3], coll.distinct("a").sort(), "distinct returned unexpected results"); -res = t.distinct("a"); -assert.eq("1,2,3", res.toString(), "A1"); + // Test that distinct respects the query condition. + assert.eq([1, 2], + coll.distinct("a", {a: {$lt: 3}}).sort(), + "distinct with query returned unexpected results"); -assert.eq("1,2", t.distinct("a", {a: {$lt: 3}}), "A2"); + assert(coll.drop()); -t.drop(); + assert.writeOK(coll.insert({a: {b: "a"}, c: 12})); + assert.writeOK(coll.insert({a: {b: "b"}, c: 12})); + assert.writeOK(coll.insert({a: {b: "c"}, c: 12})); + assert.writeOK(coll.insert({a: {b: "c"}, c: 12})); -t.save({a: {b: "a"}, c: 12}); -t.save({a: {b: "b"}, c: 12}); -t.save({a: {b: "c"}, c: 12}); -t.save({a: {b: "c"}, c: 12}); + // Test that distinct works on fields in embedded documents. + assert.eq(["a", "b", "c"], + coll.distinct("a.b").sort(), + "distinct on dotted field returned unexpected results"); -res = t.distinct("a.b"); -assert.eq("a,b,c", res.toString(), "B1"); + assert(coll.drop()); -t.drop(); + assert.writeOK(coll.insert({_id: 1, a: 1})); + assert.writeOK(coll.insert({_id: 2, a: 2})); -t.save({_id: 1, a: 1}); -t.save({_id: 2, a: 2}); + // Test that distinct works on the _id field. + assert.eq([1, 2], coll.distinct("_id").sort(), "distinct on _id returned unexpected results"); -// Test distinct with _id. -res = t.distinct("_id"); -assert.eq("1,2", res.toString(), "C1"); -res = t.distinct("a", {_id: 1}); -assert.eq("1", res.toString(), "C2"); + // Test that distinct works with a query on the _id field. + assert.eq([1], + coll.distinct("a", {_id: 1}), + "distinct with query on _id returned unexpected results"); -// Test distinct with db.runCommand -t.drop(); + assert(coll.drop()); -t.save({a: 1, b: 2}); -t.save({a: 2, b: 2}); -t.save({a: 2, b: 1}); -t.save({a: 2, b: 2}); -t.save({a: 3, b: 2}); -t.save({a: 4, b: 1}); -t.save({a: 4, b: 1}); + assert.writeOK(coll.insert({a: 1, b: 2})); + assert.writeOK(coll.insert({a: 2, b: 2})); + assert.writeOK(coll.insert({a: 2, b: 1})); + assert.writeOK(coll.insert({a: 2, b: 2})); + assert.writeOK(coll.insert({a: 3, b: 2})); + assert.writeOK(coll.insert({a: 4, b: 1})); + assert.writeOK(coll.insert({a: 4, b: 1})); -res = db.runCommand({distinct: "distinct1", key: "a"}); -assert.commandWorked(res); -assert.eq([1, 2, 3, 4], res["values"], "D1"); -res = db.runCommand({distinct: "distinct1", key: "a", query: null}); -assert.commandWorked(res); -assert.eq([1, 2, 3, 4], res["values"], "D2"); -res = db.runCommand({distinct: "distinct1", key: "a", query: {b: 2}}); -assert.commandWorked(res); -assert.eq([1, 2, 3], res["values"], "D3"); -res = db.runCommand({distinct: "distinct1", key: "a", query: 1}); -assert.commandFailed(res); + // Test running the distinct command directly, rather than via shell helper. + let res = assert.commandWorked(db.runCommand({distinct: collName, key: "a"})); + assert.eq([1, 2, 3, 4], res.values.sort()); -t.drop(); + res = assert.commandWorked(db.runCommand({distinct: collName, key: "a", query: null})); + assert.eq([1, 2, 3, 4], res.values.sort()); + + res = assert.commandWorked(db.runCommand({distinct: collName, key: "a", query: {b: 2}})); + assert.eq([1, 2, 3], res.values.sort()); + + assert.commandFailed(db.runCommand({distinct: collName, key: "a", query: 1})); +}()); diff --git a/jstests/core/elemMatchProjection.js b/jstests/core/elemMatchProjection.js index ce4fd0639a7..7114959b6ca 100644 --- a/jstests/core/elemMatchProjection.js +++ b/jstests/core/elemMatchProjection.js @@ -1,273 +1,221 @@ // Tests for $elemMatch projections and $ positional operator projection. -t = db.SERVER828Test; -t.drop(); - -date1 = new Date(); - -// Insert various styles of arrays -for (i = 0; i < 100; i++) { - t.insert({group: 1, x: [1, 2, 3, 4, 5]}); - t.insert({group: 2, x: [{a: 1, b: 2}, {a: 2, c: 3}, {a: 1, d: 5}]}); - t.insert({ - group: 3, - x: [{a: 1, b: 2}, {a: 2, c: 3}, {a: 1, d: 5}], - y: [{aa: 1, bb: 2}, {aa: 2, cc: 3}, {aa: 1, dd: 5}] - }); - t.insert({group: 3, x: [{a: 1, b: 3}, {a: -6, c: 3}]}); - t.insert({group: 4, x: [{a: 1, b: 4}, {a: -6, c: 3}]}); - t.insert({group: 5, x: [new Date(), 5, 10, 'string', new ObjectId(), 123.456]}); - t.insert({ - group: 6, - x: [{a: 'string', b: date1}, {a: new ObjectId(), b: 1.2345}, {a: 'string2', b: date1}] - }); - t.insert({group: 7, x: [{y: [1, 2, 3, 4]}]}); - t.insert({group: 8, x: [{y: [{a: 1, b: 2}, {a: 3, b: 4}]}]}); - t.insert({group: 9, x: [{y: [{a: 1, b: 2}, {a: 3, b: 4}]}, {z: [{a: 1, b: 2}, {a: 3, b: 4}]}]}); - t.insert({group: 10, x: [{a: 1, b: 2}, {a: 3, b: 4}], y: [{c: 1, d: 2}, {c: 3, d: 4}]}); - t.insert({group: 10, x: [{a: 1, b: 2}, {a: 3, b: 4}], y: [{c: 1, d: 2}, {c: 3, d: 4}]}); - t.insert({ - group: 11, - x: [{a: 1, b: 2}, {a: 2, c: 3}, {a: 1, d: 5}], - covered: [{aa: 1, bb: 2}, {aa: 2, cc: 3}, {aa: 1, dd: 5}] - }); - t.insert({group: 12, x: {y: [{a: 1, b: 1}, {a: 1, b: 2}]}}); - t.insert({group: 13, x: [{a: 1, b: 1}, {a: 1, b: 2}]}); - t.insert({group: 13, x: [{a: 1, b: 2}, {a: 1, b: 1}]}); -} -t.ensureIndex({ - group: 1, - 'y.d': 1 -}); // for regular index test (not sure if this is really adding anything useful) -t.ensureIndex({group: 1, covered: 1}); // for covered index test - -// -// SERVER-828: Positional operator ($) projection tests -// -assert.eq(1, - t.find({group: 3, 'x.a': 2}, {'x.$': 1}).toArray()[0].x.length, - "single object match (array length match)"); - -assert.eq( - 2, t.find({group: 3, 'x.a': 1}, {'x.$': 1}).toArray()[0].x[0].b, "single object match first"); - -assert.eq(undefined, - t.find({group: 3, 'x.a': 2}, {_id: 0, 'x.$': 1}).toArray()[0]._id, - "single object match with filtered _id"); - -assert.eq(1, - t.find({group: 3, 'x.a': 2}, {'x.$': 1}).sort({_id: 1}).toArray()[0].x.length, - "sorted single object match with filtered _id (array length match)"); - -assert.eq( - 1, - t.find({'group': 2, 'x': {'$elemMatch': {'a': 1, 'b': 2}}}, {'x.$': 1}).toArray()[0].x.length, - "single object match with elemMatch"); - -assert.eq(1, - t.find({'group': 2, 'x': {'$elemMatch': {'a': 1, 'b': 2}}}, {'x.$': {'$slice': 1}}) - .toArray()[0] - .x.length, - "single object match with elemMatch and positive slice"); - -assert.eq(1, - t.find({'group': 2, 'x': {'$elemMatch': {'a': 1, 'b': 2}}}, {'x.$': {'$slice': -1}}) - .toArray()[0] - .x.length, - "single object match with elemMatch and negative slice"); - -assert.eq(1, - t.find({'group': 12, 'x.y.a': 1}, {'x.y.$': 1}).toArray()[0].x.y.length, - "single object match with two level dot notation"); - -assert.eq(1, - t.find({group: 3, 'x.a': 2}, {'x.$': 1}).sort({x: 1}).toArray()[0].x.length, - "sorted object match (array length match)"); - -assert.eq({aa: 1, dd: 5}, - t.find({group: 3, 'y.dd': 5}, {'y.$': 1}).toArray()[0].y[0], - "single object match (value match)"); - -assert.throws(function() { - t.find({group: 3, 'x.a': 2}, {'y.$': 1}).toArray(); -}, [], "throw on invalid projection (field mismatch)"); - -assert.throws(function() { - t.find({group: 3, 'x.a': 2}, {'y.$': 1}).sort({x: 1}).toArray(); -}, [], "throw on invalid sorted projection (field mismatch)"); - -assert.throws(function() { - t.find({group: 3, 'x.a': 2}, {'x.$': 1, group: 0}).sort({x: 1}).toArray(); -}, [], "throw on invalid projection combination (include and exclude)"); - -assert.throws(function() { - t.find({group: 3, 'x.a': 1, 'y.aa': 1}, {'x.$': 1, 'y.$': 1}).toArray(); -}, [], "throw on multiple projections"); - -assert.throws(function() { - t.find({group: 3}, {'g.$': 1}).toArray(); -}, [], "throw on invalid projection (non-array field)"); - -assert.eq({aa: 1, dd: 5}, - t.find({group: 11, 'covered.dd': 5}, {'covered.$': 1}).toArray()[0].covered[0], - "single object match (covered index)"); - -assert.eq({aa: 1, dd: 5}, - t.find({group: 11, 'covered.dd': 5}, {'covered.$': 1}) - .sort({covered: 1}) - .toArray()[0] - .covered[0], - "single object match (sorted covered index)"); - -assert.eq(1, - t.find({group: 10, 'y.d': 4}, {'y.$': 1}).toArray()[0].y.length, - "single object match (regular index"); - -if (false) { - assert.eq(2, // SERVER-1013: allow multiple positional operators - t.find({group: 3, 'y.bb': 2, 'x.d': 5}, {'y.$': 1, 'x.$': 1}).toArray()[0].y[0].bb, - "multi match, multi proj 1"); - - assert.eq(5, // SSERVER-1013: allow multiple positional operators - t.find({group: 3, 'y.bb': 2, 'x.d': 5}, {'y.$': 1, 'x.$': 1}).toArray()[0].x[0].d, - "multi match, multi proj 2"); - - assert.eq(2, // SERVER-1243: allow multiple results from same matcher - t.find({group: 2, x: {$elemMatchAll: {a: 1}}}, {'x.$': 1}).toArray()[0].x.length, - "multi element match, single proj"); - - assert.eq(2, // SERVER-1013: multiple array matches with one prositional operator - t.find({group: 3, 'y.bb': 2, 'x.d': 5}, {'y.$': 1}).toArray()[0].y[0].bb, - "multi match, single proj 1"); - - assert.eq(2, // SERVER-1013: multiple array matches with one positional operator - t.find({group: 3, 'y.cc': 3, 'x.b': 2}, {'x.$': 1}).toArray()[0].x[0].b, - "multi match, single proj 2"); -} - -// -// SERVER-2238: $elemMatch projections -// -assert.eq( - -6, t.find({group: 4}, {x: {$elemMatch: {a: -6}}}).toArray()[0].x[0].a, "single object match"); - -assert.eq(1, - t.find({group: 4}, {x: {$elemMatch: {a: -6}}}).toArray()[0].x.length, - "filters non-matching array elements"); - -assert.eq(1, - t.find({group: 4}, {x: {$elemMatch: {a: -6, c: 3}}}).toArray()[0].x.length, - "filters non-matching array elements with multiple elemMatch criteria"); - -assert.eq( - 1, - t.find({group: 13}, {'x': {'$elemMatch': {a: {$gt: 0, $lt: 2}}}}).toArray()[0].x.length, - "filters non-matching array elements with multiple criteria for a single element in the array"); - -assert.eq(3, - t.find({group: 4}, {x: {$elemMatch: {a: {$lt: 1}}}}).toArray()[0].x[0].c, - "object operator match"); - -assert.eq([4], - t.find({group: 1}, {x: {$elemMatch: {$in: [100, 4, -123]}}}).toArray()[0].x, - "$in number match"); - -assert.eq([{a: 1, b: 2}], - t.find({group: 2}, {x: {$elemMatch: {a: {$in: [1]}}}}).toArray()[0].x, - "$in number match"); - -assert.eq([1], - t.find({group: 1}, {x: {$elemMatch: {$nin: [4, 5, 6]}}}).toArray()[0].x, - "$nin number match"); - -// but this may become a user assertion, since a single element of an array can't match more than -// one value -assert.eq( - [1], t.find({group: 1}, {x: {$elemMatch: {$all: [1]}}}).toArray()[0].x, "$in number match"); - -assert.eq([{a: 'string', b: date1}], - t.find({group: 6}, {x: {$elemMatch: {a: 'string'}}}).toArray()[0].x, - "mixed object match on string eq"); - -assert.eq([{a: 'string2', b: date1}], - t.find({group: 6}, {x: {$elemMatch: {a: /ring2/}}}).toArray()[0].x, - "mixed object match on regexp"); - -assert.eq([{a: 'string', b: date1}], - t.find({group: 6}, {x: {$elemMatch: {a: {$type: 2}}}}).toArray()[0].x, - "mixed object match on type"); - -assert.eq([{a: 2, c: 3}], - t.find({group: 2}, {x: {$elemMatch: {a: {$ne: 1}}}}).toArray()[0].x, - "mixed object match on ne"); - -assert.eq([{a: 1, d: 5}], - t.find({group: 3}, {x: {$elemMatch: {d: {$exists: true}}}}).toArray()[0].x, - "mixed object match on exists"); - -assert.eq([{a: 2, c: 3}], - t.find({group: 3}, {x: {$elemMatch: {a: {$mod: [2, 0]}}}}).toArray()[0].x, - "mixed object match on mod"); - -assert.eq( - {"x": [{"a": 1, "b": 2}], "y": [{"c": 3, "d": 4}]}, - t.find({group: 10}, {_id: 0, x: {$elemMatch: {a: 1}}, y: {$elemMatch: {c: 3}}}).toArray()[0], - "multiple $elemMatch on unique fields 1"); - -if (false) { - assert.eq(2, // SERVER-1243: handle multiple $elemMatch results - t.find({group: 4}, {x: {$elemMatchAll: {a: {$lte: 2}}}}).toArray()[0].x.length, - "multi object match"); - - assert.eq(3, // SERVER-1243: handle multiple $elemMatch results - t.find({group: 1}, {x: {$elemMatchAll: {$in: [1, 2, 3]}}}).toArray()[0].x.length, +(function() { + "use strict"; + + const coll = db.SERVER828Test; + coll.drop(); + + const date1 = new Date(); + + // Insert various styles of arrays. + const bulk = coll.initializeUnorderedBulkOp(); + for (let i = 0; i < 100; i++) { + bulk.insert({group: 1, x: [1, 2, 3, 4, 5]}); + bulk.insert({group: 2, x: [{a: 1, b: 2}, {a: 2, c: 3}, {a: 1, d: 5}]}); + bulk.insert({ + group: 3, + x: [{a: 1, b: 2}, {a: 2, c: 3}, {a: 1, d: 5}], + y: [{aa: 1, bb: 2}, {aa: 2, cc: 3}, {aa: 1, dd: 5}] + }); + bulk.insert({group: 3, x: [{a: 1, b: 3}, {a: -6, c: 3}]}); + bulk.insert({group: 4, x: [{a: 1, b: 4}, {a: -6, c: 3}]}); + bulk.insert({group: 5, x: [new Date(), 5, 10, 'string', new ObjectId(), 123.456]}); + bulk.insert({ + group: 6, + x: [ + {a: 'string', b: date1}, + {a: new ObjectId(), b: 1.2345}, + {a: 'string2', b: date1} + ] + }); + bulk.insert({group: 7, x: [{y: [1, 2, 3, 4]}]}); + bulk.insert({group: 8, x: [{y: [{a: 1, b: 2}, {a: 3, b: 4}]}]}); + bulk.insert( + {group: 9, x: [{y: [{a: 1, b: 2}, {a: 3, b: 4}]}, {z: [{a: 1, b: 2}, {a: 3, b: 4}]}]}); + bulk.insert({group: 10, x: [{a: 1, b: 2}, {a: 3, b: 4}], y: [{c: 1, d: 2}, {c: 3, d: 4}]}); + bulk.insert({group: 10, x: [{a: 1, b: 2}, {a: 3, b: 4}], y: [{c: 1, d: 2}, {c: 3, d: 4}]}); + bulk.insert({ + group: 11, + x: [{a: 1, b: 2}, {a: 2, c: 3}, {a: 1, d: 5}], + covered: [{aa: 1, bb: 2}, {aa: 2, cc: 3}, {aa: 1, dd: 5}] + }); + bulk.insert({group: 12, x: {y: [{a: 1, b: 1}, {a: 1, b: 2}]}}); + bulk.insert({group: 13, x: [{a: 1, b: 1}, {a: 1, b: 2}]}); + bulk.insert({group: 13, x: [{a: 1, b: 2}, {a: 1, b: 1}]}); + } + assert.writeOK(bulk.execute()); + + assert.writeOK(coll.createIndex({group: 1, 'y.d': 1})); + assert.writeOK(coll.createIndex({group: 1, covered: 1})); // for covered index test + + // Tests for the $-positional operator. + assert.eq(1, + coll.find({group: 3, 'x.a': 2}, {'x.$': 1}).sort({_id: 1}).toArray()[0].x.length, + "single object match (array length match)"); + + assert.eq(2, + coll.find({group: 3, 'x.a': 1}, {'x.$': 1}).sort({_id: 1}).toArray()[0].x[0].b, + "single object match first"); + + assert.eq(undefined, + coll.find({group: 3, 'x.a': 2}, {_id: 0, 'x.$': 1}).sort({_id: 1}).toArray()[0]._id, + "single object match with filtered _id"); + + assert.eq(1, + coll.find({group: 3, 'x.a': 2}, {'x.$': 1}).sort({_id: 1}).toArray()[0].x.length, + "sorted single object match with filtered _id (array length match)"); + + assert.eq(1, + coll.find({'group': 2, 'x': {'$elemMatch': {'a': 1, 'b': 2}}}, {'x.$': 1}) + .toArray()[0] + .x.length, + "single object match with elemMatch"); + + assert.eq(1, + coll.find({'group': 2, 'x': {'$elemMatch': {'a': 1, 'b': 2}}}, {'x.$': {'$slice': 1}}) + .toArray()[0] + .x.length, + "single object match with elemMatch and positive slice"); + + assert.eq( + 1, + coll.find({'group': 2, 'x': {'$elemMatch': {'a': 1, 'b': 2}}}, {'x.$': {'$slice': -1}}) + .toArray()[0] + .x.length, + "single object match with elemMatch and negative slice"); + + assert.eq(1, + coll.find({'group': 12, 'x.y.a': 1}, {'x.y.$': 1}).toArray()[0].x.y.length, + "single object match with two level dot notation"); + + assert.eq(1, + coll.find({group: 3, 'x.a': 2}, {'x.$': 1}).sort({x: 1}).toArray()[0].x.length, + "sorted object match (array length match)"); + + assert.eq({aa: 1, dd: 5}, + coll.find({group: 3, 'y.dd': 5}, {'y.$': 1}).sort({_id: 1}).toArray()[0].y[0], + "single object match (value match)"); + + assert.throws(function() { + coll.find({group: 3, 'x.a': 2}, {'y.$': 1}).toArray(); + }, [], "throw on invalid projection (field mismatch)"); + + assert.throws(function() { + coll.find({group: 3, 'x.a': 2}, {'y.$': 1}).sort({x: 1}).toArray(); + }, [], "throw on invalid sorted projection (field mismatch)"); + + assert.throws(function() { + coll.find({group: 3, 'x.a': 2}, {'x.$': 1, group: 0}).sort({x: 1}).toArray(); + }, [], "throw on invalid projection combination (include and exclude)"); + + assert.throws(function() { + coll.find({group: 3, 'x.a': 1, 'y.aa': 1}, {'x.$': 1, 'y.$': 1}).toArray(); + }, [], "throw on multiple projections"); + + assert.throws(function() { + coll.find({group: 3}, {'g.$': 1}).toArray(); + }, [], "throw on invalid projection (non-array field)"); + + assert.eq({aa: 1, dd: 5}, + coll.find({group: 11, 'covered.dd': 5}, {'covered.$': 1}).toArray()[0].covered[0], + "single object match (covered index)"); + + assert.eq({aa: 1, dd: 5}, + coll.find({group: 11, 'covered.dd': 5}, {'covered.$': 1}) + .sort({covered: 1}) + .toArray()[0] + .covered[0], + "single object match (sorted covered index)"); + + assert.eq(1, + coll.find({group: 10, 'y.d': 4}, {'y.$': 1}).sort({_id: 1}).toArray()[0].y.length, + "single object match (regular index"); + + // Tests for $elemMatch projection. + assert.eq(-6, + coll.find({group: 4}, {x: {$elemMatch: {a: -6}}}).toArray()[0].x[0].a, + "single object match"); + + assert.eq(1, + coll.find({group: 4}, {x: {$elemMatch: {a: -6}}}).toArray()[0].x.length, + "filters non-matching array elements"); + + assert.eq(1, + coll.find({group: 4}, {x: {$elemMatch: {a: -6, c: 3}}}).toArray()[0].x.length, + "filters non-matching array elements with multiple elemMatch criteria"); + + assert.eq( + 1, + coll.find({group: 13}, {'x': {'$elemMatch': {a: {$gt: 0, $lt: 2}}}}) + .sort({_id: 1}) + .toArray()[0] + .x.length, + "filters non-matching array elements with multiple criteria for a single element in the array"); + + assert.eq( + 3, + coll.find({group: 4}, {x: {$elemMatch: {a: {$lt: 1}}}}).sort({_id: 1}).toArray()[0].x[0].c, + "object operator match"); + + assert.eq([4], + coll.find({group: 1}, {x: {$elemMatch: {$in: [100, 4, -123]}}}).toArray()[0].x, "$in number match"); - assert.eq(1, // SERVER-1243: handle multiple $elemMatch results - t.find({group: 5}, {x: {$elemMatchAll: {$ne: 5}}}).toArray()[0].x.length, - "single mixed type match 1"); - - assert.eq(1, // SERVER-831: handle nested arrays - t.find({group: 9}, {'x.y': {$elemMatch: {a: 1}}}).toArray()[0].x.length, - "single dotted match"); -} - -// -// Batch/getMore tests -// -// test positional operator across multiple batches -a = t.find({group: 3, 'x.b': 2}, {'x.$': 1}).batchSize(1); -while (a.hasNext()) { - assert.eq(2, a.next().x[0].b, "positional getMore test"); -} - -// test $elemMatch operator across multiple batches -a = t.find({group: 3}, {x: {$elemMatch: {a: 1}}}).batchSize(1); -while (a.hasNext()) { - assert.eq(1, a.next().x[0].a, "positional getMore test"); -} - -// verify the positional update operator matches the same element as the the positional find. this -// is to ensure consistent behavior with updates until SERVER-1013 is resolved, at which point the -// following tests should be updated. - -t.update({group: 10, 'x.a': 3, 'y.c': 1}, {$set: {'x.$': 100}}, false, true); -// updated the wrong element, so the following assertions should be true -assert.eq(100, - t.find({group: 10, 'y.c': 1, x: 100}, {'x.$': 1}).toArray()[0].x[0], - "wrong single element match after update"); - -assert.eq(100, - t.find({group: 10, x: 100, 'y.c': 1}, {'x.$': 1}).toArray()[0].x[0], - "wrong single element match after update"); - -t.remove({group: 10}); -t.insert({group: 10, x: [{a: 1, b: 2}, {a: 3, b: 4}], y: [{c: 1, d: 2}, {c: 3, d: 4}]}); - -t.update({group: 10, 'y.c': 1, 'x.a': 3}, {$set: {'x.$': 100}}, false, true); -// updated the correct element -assert.eq(100, - t.find({group: 10, 'y.c': 1, x: 100}, {'x.$': 1}).toArray()[0].x[0], - "right single element match after update"); -assert.eq(100, - t.find({group: 10, x: 100, 'y.c': 1}, {'x.$': 1}).toArray()[0].x[0], - "right single element match after update"); + assert.eq([{a: 1, b: 2}], + coll.find({group: 2}, {x: {$elemMatch: {a: {$in: [1]}}}}).toArray()[0].x, + "$in number match"); + + assert.eq([1], + coll.find({group: 1}, {x: {$elemMatch: {$nin: [4, 5, 6]}}}).toArray()[0].x, + "$nin number match"); + + assert.eq([1], + coll.find({group: 1}, {x: {$elemMatch: {$all: [1]}}}).toArray()[0].x, + "$in number match"); + + assert.eq([{a: 'string', b: date1}], + coll.find({group: 6}, {x: {$elemMatch: {a: 'string'}}}).toArray()[0].x, + "mixed object match on string eq"); + + assert.eq([{a: 'string2', b: date1}], + coll.find({group: 6}, {x: {$elemMatch: {a: /ring2/}}}).toArray()[0].x, + "mixed object match on regexp"); + + assert.eq([{a: 'string', b: date1}], + coll.find({group: 6}, {x: {$elemMatch: {a: {$type: 2}}}}).toArray()[0].x, + "mixed object match on type"); + + assert.eq([{a: 2, c: 3}], + coll.find({group: 2}, {x: {$elemMatch: {a: {$ne: 1}}}}).toArray()[0].x, + "mixed object match on ne"); + + assert.eq([{a: 1, d: 5}], + coll.find({group: 3}, {x: {$elemMatch: {d: {$exists: true}}}}) + .sort({_id: 1}) + .toArray()[0] + .x, + "mixed object match on exists"); + + assert.eq( + [{a: 2, c: 3}], + coll.find({group: 3}, {x: {$elemMatch: {a: {$mod: [2, 0]}}}}).sort({_id: 1}).toArray()[0].x, + "mixed object match on mod"); + + assert.eq({"x": [{"a": 1, "b": 2}], "y": [{"c": 3, "d": 4}]}, + coll.find({group: 10}, {_id: 0, x: {$elemMatch: {a: 1}}, y: {$elemMatch: {c: 3}}}) + .sort({_id: 1}) + .toArray()[0], + "multiple $elemMatch on unique fields 1"); + + // Tests involving getMore. Test the $-positional operator across multiple batches. + let a = coll.find({group: 3, 'x.b': 2}, {'x.$': 1}).sort({_id: 1}).batchSize(1); + while (a.hasNext()) { + assert.eq(2, a.next().x[0].b, "positional getMore test"); + } + + // Test the $elemMatch operator across multiple batches. + a = coll.find({group: 3}, {x: {$elemMatch: {a: 1}}}).sort({_id: 1}).batchSize(1); + while (a.hasNext()) { + assert.eq(1, a.next().x[0].a, "positional getMore test"); + } +}()); diff --git a/jstests/core/find4.js b/jstests/core/find4.js index 204e7c511e2..ad482916a19 100644 --- a/jstests/core/find4.js +++ b/jstests/core/find4.js @@ -1,36 +1,42 @@ +(function() { + "use strict"; -t = db.find4; -t.drop(); + const coll = db.find4; + coll.drop(); -t.save({a: 1123, b: 54332}); + assert.writeOK(coll.insert({a: 1123, b: 54332})); -o = t.find({}, {})[0]; -assert.eq(1123, o.a, "A"); -assert.eq(54332, o.b, "B"); -assert(o._id.str, "C"); + let o = coll.findOne(); + assert.eq(1123, o.a, "A"); + assert.eq(54332, o.b, "B"); + assert(o._id.str, "C"); -o = t.find({}, {a: 1})[0]; -assert.eq(1123, o.a, "D"); -assert(o._id.str, "E"); -assert(!o.b, "F"); + o = coll.findOne({}, {a: 1}); + assert.eq(1123, o.a, "D"); + assert(o._id.str, "E"); + assert(!o.b, "F"); -o = t.find({}, {b: 1})[0]; -assert.eq(54332, o.b, "G"); -assert(o._id.str, "H"); -assert(!o.a, "I"); + o = coll.findOne({}, {b: 1}); + assert.eq(54332, o.b, "G"); + assert(o._id.str, "H"); + assert(!o.a, "I"); -t.drop(); -t.save({a: 1, b: 1}); -t.save({a: 2, b: 2}); -assert.eq("1-1,2-2", - t.find() - .map(function(z) { - return z.a + "-" + z.b; - }) - .toString()); -assert.eq("1-undefined,2-undefined", - t.find({}, {a: 1}) - .map(function(z) { - return z.a + "-" + z.b; - }) - .toString()); + assert(coll.drop()); + + assert.writeOK(coll.insert({a: 1, b: 1})); + assert.writeOK(coll.insert({a: 2, b: 2})); + assert.eq("1-1,2-2", + coll.find() + .sort({a: 1}) + .map(function(z) { + return z.a + "-" + z.b; + }) + .toString()); + assert.eq("1-undefined,2-undefined", + coll.find({}, {a: 1}) + .sort({a: 1}) + .map(function(z) { + return z.a + "-" + z.b; + }) + .toString()); +}()); diff --git a/jstests/core/find5.js b/jstests/core/find5.js index 33ba96ea103..633b723fe5a 100644 --- a/jstests/core/find5.js +++ b/jstests/core/find5.js @@ -1,51 +1,54 @@ +(function() { + "use strict"; -t = db.find5; -t.drop(); + const coll = db.find5; + coll.drop(); -t.save({a: 1}); -t.save({b: 5}); + assert.writeOK(coll.insert({a: 1})); + assert.writeOK(coll.insert({b: 5})); -assert.eq(2, t.find({}, {b: 1}).count(), "A"); + assert.eq(2, coll.find({}, {b: 1}).count(), "A"); -function getIds(f) { - return t.find({}, f).map(function(z) { - return z._id; - }); -} + function getIds(projection) { + return coll.find({}, projection).map(doc => doc._id); + } -assert.eq(Array.tojson(getIds(null)), Array.tojson(getIds({})), "B1 "); -assert.eq(Array.tojson(getIds(null)), Array.tojson(getIds({a: 1})), "B2 "); -assert.eq(Array.tojson(getIds(null)), Array.tojson(getIds({b: 1})), "B3 "); -assert.eq(Array.tojson(getIds(null)), Array.tojson(getIds({c: 1})), "B4 "); + assert.eq(Array.tojson(getIds(null)), Array.tojson(getIds({})), "B1 "); + assert.eq(Array.tojson(getIds(null)), Array.tojson(getIds({a: 1})), "B2 "); + assert.eq(Array.tojson(getIds(null)), Array.tojson(getIds({b: 1})), "B3 "); + assert.eq(Array.tojson(getIds(null)), Array.tojson(getIds({c: 1})), "B4 "); -x = t.find({}, {a: 1})[0]; -assert.eq(1, x.a, "C1"); -assert.isnull(x.b, "C2"); + let results = coll.find({}, {a: 1}).sort({a: -1}); + let first = results[0]; + assert.eq(1, first.a, "C1"); + assert.isnull(first.b, "C2"); -x = t.find({}, {a: 1})[1]; -assert.isnull(x.a, "C3"); -assert.isnull(x.b, "C4"); + let second = results[1]; + assert.isnull(second.a, "C3"); + assert.isnull(second.b, "C4"); -x = t.find({}, {b: 1})[0]; -assert.isnull(x.a, "C5"); -assert.isnull(x.b, "C6"); + results = coll.find({}, {b: 1}).sort({a: -1}); + first = results[0]; + assert.isnull(first.a, "C5"); + assert.isnull(first.b, "C6"); -x = t.find({}, {b: 1})[1]; -assert.isnull(x.a, "C7"); -assert.eq(5, x.b, "C8"); + second = results[1]; + assert.isnull(second.a, "C7"); + assert.eq(5, second.b, "C8"); -t.drop(); + assert(coll.drop()); -t.save({a: 1, b: {c: 2, d: 3, e: 4}}); -assert.eq(2, t.find({}, {"b.c": 1}).toArray()[0].b.c, "D"); + assert.writeOK(coll.insert({a: 1, b: {c: 2, d: 3, e: 4}})); + assert.eq(2, coll.findOne({}, {"b.c": 1}).b.c, "D"); -o = t.find({}, {"b.c": 1, "b.d": 1}).toArray()[0]; -assert(o.b.c, "E 1"); -assert(o.b.d, "E 2"); -assert(!o.b.e, "E 3"); + const o = coll.findOne({}, {"b.c": 1, "b.d": 1}); + assert(o.b.c, "E 1"); + assert(o.b.d, "E 2"); + assert(!o.b.e, "E 3"); -assert(!t.find({}, {"b.c": 1}).toArray()[0].b.d, "F"); + assert(!coll.findOne({}, {"b.c": 1}).b.d, "F"); -t.drop(); -t.save({a: {b: {c: 1}}}); -assert.eq(1, t.find({}, {"a.b.c": 1})[0].a.b.c, "G"); + assert(coll.drop()); + assert.writeOK(coll.insert({a: {b: {c: 1}}})); + assert.eq(1, coll.findOne({}, {"a.b.c": 1}).a.b.c, "G"); +}()); diff --git a/jstests/core/find_dedup.js b/jstests/core/find_dedup.js index 7a489bd185c..7e9bef61160 100644 --- a/jstests/core/find_dedup.js +++ b/jstests/core/find_dedup.js @@ -1,39 +1,44 @@ // Test that duplicate query results are not returned. +(function() { + "use strict"; -var t = db.jstests_find_dedup; + const coll = db.jstests_find_dedup; -function checkDedup(query, idArray) { - resultsArr = t.find(query).toArray(); - assert.eq(resultsArr.length, idArray.length, "same number of results"); + function checkDedup(query, idArray) { + const resultsArr = coll.find(query).sort({_id: 1}).toArray(); + assert.eq(resultsArr.length, idArray.length, "same number of results"); - for (var i = 0; i < idArray.length; i++) { - assert(("_id" in resultsArr[i]), "result doc missing _id"); - assert.eq(idArray[i], resultsArr[i]._id, "_id mismatch for doc " + i); + for (let i = 0; i < idArray.length; i++) { + assert(("_id" in resultsArr[i]), "result doc missing _id"); + assert.eq(idArray[i], resultsArr[i]._id, "_id mismatch for doc " + i); + } } -} -// Deduping $or -t.drop(); -t.ensureIndex({a: 1, b: 1}); -t.save({_id: 1, a: 1, b: 1}); -t.save({_id: 2, a: 1, b: 1}); -t.save({_id: 3, a: 2, b: 2}); -t.save({_id: 4, a: 3, b: 3}); -t.save({_id: 5, a: 3, b: 3}); -checkDedup({ - $or: [ - {a: {$gte: 0, $lte: 2}, b: {$gte: 0, $lte: 2}}, - {a: {$gte: 1, $lte: 3}, b: {$gte: 1, $lte: 3}}, - {a: {$gte: 1, $lte: 4}, b: {$gte: 1, $lte: 4}} - ] -}, - [1, 2, 3, 4, 5]); + // Deduping $or + coll.drop(); + coll.ensureIndex({a: 1, b: 1}); + assert.writeOK(coll.insert({_id: 1, a: 1, b: 1})); + assert.writeOK(coll.insert({_id: 2, a: 1, b: 1})); + assert.writeOK(coll.insert({_id: 3, a: 2, b: 2})); + assert.writeOK(coll.insert({_id: 4, a: 3, b: 3})); + assert.writeOK(coll.insert({_id: 5, a: 3, b: 3})); + checkDedup({ + $or: [ + {a: {$gte: 0, $lte: 2}, b: {$gte: 0, $lte: 2}}, + {a: {$gte: 1, $lte: 3}, b: {$gte: 1, $lte: 3}}, + {a: {$gte: 1, $lte: 4}, b: {$gte: 1, $lte: 4}} + ] + }, + [1, 2, 3, 4, 5]); -// Deduping multikey -t.drop(); -t.save({_id: 1, a: [1, 2, 3], b: [4, 5, 6]}); -t.save({_id: 2, a: [1, 2, 3], b: [4, 5, 6]}); -assert.eq(2, t.count()); -checkDedup({$or: [{a: {$in: [1, 2]}}, {b: {$in: [4, 5]}}]}, [1, 2]); -t.ensureIndex({a: 1}); -checkDedup({$or: [{a: {$in: [1, 2]}}, {b: {$in: [4, 5]}}]}, [1, 2]); + // Deduping multikey + assert(coll.drop()); + assert.writeOK(coll.insert({_id: 1, a: [1, 2, 3], b: [4, 5, 6]})); + assert.writeOK(coll.insert({_id: 2, a: [1, 2, 3], b: [4, 5, 6]})); + assert.eq(2, coll.count()); + + checkDedup({$or: [{a: {$in: [1, 2]}}, {b: {$in: [4, 5]}}]}, [1, 2]); + + assert.commandWorked(coll.createIndex({a: 1})); + checkDedup({$or: [{a: {$in: [1, 2]}}, {b: {$in: [4, 5]}}]}, [1, 2]); +}()); diff --git a/jstests/core/fts1.js b/jstests/core/fts1.js index 5f507733b05..9b95fa8dc14 100644 --- a/jstests/core/fts1.js +++ b/jstests/core/fts1.js @@ -1,24 +1,27 @@ // Cannot implicitly shard accessed collections because of extra shard key index in sharded // collection. // @tags: [assumes_no_implicit_index_creation] +(function() { + "use strict"; -load("jstests/libs/fts.js"); + load("jstests/libs/fts.js"); -t = db.text1; -t.drop(); + const coll = db.text1; + coll.drop(); -t.ensureIndex({x: "text"}); + assert.commandWorked(coll.createIndex({x: "text"}, {name: "x_text"})); -assert.eq([], queryIDS(t, "az"), "A0"); + assert.eq([], queryIDS(coll, "az"), "A0"); -t.save({_id: 1, x: "az b c"}); -t.save({_id: 2, x: "az b"}); -t.save({_id: 3, x: "b c"}); -t.save({_id: 4, x: "b c d"}); + assert.writeOK(coll.insert({_id: 1, x: "az b c"})); + assert.writeOK(coll.insert({_id: 2, x: "az b"})); + assert.writeOK(coll.insert({_id: 3, x: "b c"})); + assert.writeOK(coll.insert({_id: 4, x: "b c d"})); -assert.eq([1, 2, 3, 4], queryIDS(t, "c az"), "A1"); -assert.eq([4], queryIDS(t, "d"), "A2"); + assert.eq([1, 2, 3, 4], queryIDS(coll, "c az").sort(), "A1"); + assert.eq([4], queryIDS(coll, "d"), "A2"); -idx = t.getIndexes()[1]; -assert(idx.v >= 1, tojson(idx)); -assert(idx.textIndexVersion >= 1, tojson(idx)); + const index = coll.getIndexes().find(index => index.name === "x_text"); + assert.neq(index, undefined); + assert.gte(index.textIndexVersion, 1, tojson(index)); +}()); diff --git a/jstests/core/geo_distinct.js b/jstests/core/geo_distinct.js index 4dcc7a7f35f..ad7053f2013 100644 --- a/jstests/core/geo_distinct.js +++ b/jstests/core/geo_distinct.js @@ -5,106 +5,111 @@ // Tests distinct with geospatial field values. // 1. Test distinct with geo values for 'key' (SERVER-2135) // 2. Test distinct with geo predicates for 'query' (SERVER-13769) - -var coll = db.geo_distinct; -var res; - -// -// 1. Test distinct with geo values for 'key'. -// - -coll.drop(); -coll.insert({loc: {type: 'Point', coordinates: [10, 20]}}); -coll.insert({loc: {type: 'Point', coordinates: [10, 20]}}); -coll.insert({loc: {type: 'Point', coordinates: [20, 30]}}); -coll.insert({loc: {type: 'Point', coordinates: [20, 30]}}); -assert.eq(4, coll.count()); - -// Test distinct on GeoJSON points with/without a 2dsphere index. - -res = coll.runCommand('distinct', {key: 'loc'}); -assert.commandWorked(res); -assert.eq(res.values.sort(), - [{type: 'Point', coordinates: [10, 20]}, {type: 'Point', coordinates: [20, 30]}]); - -assert.commandWorked(coll.ensureIndex({loc: '2dsphere'})); - -res = coll.runCommand('distinct', {key: 'loc'}); -assert.commandWorked(res); -assert.eq(res.values.sort(), - [{type: 'Point', coordinates: [10, 20]}, {type: 'Point', coordinates: [20, 30]}]); - -// Test distinct on legacy points with/without a 2d index. - -// (Note that distinct on a 2d-indexed field doesn't produce a list of coordinate pairs, since -// distinct logically operates on unique values in an array. Hence, the results are unintuitive and -// not semantically meaningful.) - -coll.dropIndexes(); - -res = coll.runCommand('distinct', {key: 'loc.coordinates'}); -assert.commandWorked(res); -assert.eq(res.values.sort(), [10, 20, 30]); - -assert.commandWorked(coll.ensureIndex({'loc.coordinates': '2d'})); - -res = coll.runCommand('distinct', {key: 'loc.coordinates'}); -assert.commandWorked(res); -assert.eq(res.values.sort(), [10, 20, 30]); - -// -// 2. Test distinct with geo predicates for 'query'. -// - -coll.drop(); -for (var i = 0; i < 50; ++i) { - coll.insert({zone: 1, loc: {type: 'Point', coordinates: [-20, -20]}}); - coll.insert({zone: 2, loc: {type: 'Point', coordinates: [-10, -10]}}); - coll.insert({zone: 3, loc: {type: 'Point', coordinates: [0, 0]}}); - coll.insert({zone: 4, loc: {type: 'Point', coordinates: [10, 10]}}); - coll.insert({zone: 5, loc: {type: 'Point', coordinates: [20, 20]}}); -} -var originGeoJSON = {type: 'Point', coordinates: [0, 0]}; - -// Test distinct with $nearSphere query predicate. - -// A. Unindexed key, no geo index on query predicate. -res = coll.runCommand( - 'distinct', - {key: 'zone', query: {loc: {$nearSphere: {$geometry: originGeoJSON, $maxDistance: 1}}}}); -assert.commandFailed(res); -// B. Unindexed key, with 2dsphere index on query predicate. -assert.commandWorked(coll.ensureIndex({loc: '2dsphere'})); -res = coll.runCommand( - 'distinct', - {key: 'zone', query: {loc: {$nearSphere: {$geometry: originGeoJSON, $maxDistance: 1}}}}); -assert.commandWorked(res); -assert.eq(res.values.sort(), [3]); -// C. Indexed key, with 2dsphere index on query predicate. -assert.commandWorked(coll.ensureIndex({zone: 1})); -res = coll.runCommand( - 'distinct', - {key: 'zone', query: {loc: {$nearSphere: {$geometry: originGeoJSON, $maxDistance: 1}}}}); -assert.commandWorked(res); -assert.eq(res.values.sort(), [3]); - -// Test distinct with $near query predicate. - -coll.dropIndexes(); - -// A. Unindexed key, no geo index on query predicate. -res = coll.runCommand('distinct', - {key: 'zone', query: {'loc.coordinates': {$near: [0, 0], $maxDistance: 1}}}); -assert.commandFailed(res); -// B. Unindexed key, with 2d index on query predicate. -assert.commandWorked(coll.ensureIndex({'loc.coordinates': '2d'})); -res = coll.runCommand('distinct', - {key: 'zone', query: {'loc.coordinates': {$near: [0, 0], $maxDistance: 1}}}); -assert.commandWorked(res); -assert.eq(res.values.sort(), [3]); -// C. Indexed key, with 2d index on query predicate. -assert.commandWorked(coll.ensureIndex({zone: 1})); -res = coll.runCommand('distinct', - {key: 'zone', query: {'loc.coordinates': {$near: [0, 0], $maxDistance: 1}}}); -assert.commandWorked(res); -assert.eq(res.values.sort(), [3]); +(function() { + "use strict"; + const coll = db.geo_distinct; + let res; + + // + // 1. Test distinct with geo values for 'key'. + // + + coll.drop(); + assert.writeOK(coll.insert({loc: {type: 'Point', coordinates: [10, 20]}})); + assert.writeOK(coll.insert({loc: {type: 'Point', coordinates: [10, 20]}})); + assert.writeOK(coll.insert({loc: {type: 'Point', coordinates: [20, 30]}})); + assert.writeOK(coll.insert({loc: {type: 'Point', coordinates: [20, 30]}})); + assert.eq(4, coll.count()); + + // Test distinct on GeoJSON points with/without a 2dsphere index. + + res = coll.runCommand('distinct', {key: 'loc'}); + assert.commandWorked(res); + assert.eq(res.values.sort(), + [{type: 'Point', coordinates: [10, 20]}, {type: 'Point', coordinates: [20, 30]}]); + + assert.commandWorked(coll.createIndex({loc: '2dsphere'})); + + res = coll.runCommand('distinct', {key: 'loc'}); + assert.commandWorked(res); + assert.eq(res.values.sort(), + [{type: 'Point', coordinates: [10, 20]}, {type: 'Point', coordinates: [20, 30]}]); + + // Test distinct on legacy points with/without a 2d index. + + // (Note that distinct on a 2d-indexed field doesn't produce a list of coordinate pairs, since + // distinct logically operates on unique values in an array. Hence, the results are unintuitive + // and not semantically meaningful.) + + assert.commandWorked(coll.dropIndexes()); + + res = coll.runCommand('distinct', {key: 'loc.coordinates'}); + assert.commandWorked(res); + assert.eq(res.values.sort(), [10, 20, 30]); + + assert.commandWorked(coll.createIndex({'loc.coordinates': '2d'})); + + res = coll.runCommand('distinct', {key: 'loc.coordinates'}); + assert.commandWorked(res); + assert.eq(res.values.sort(), [10, 20, 30]); + + // + // 2. Test distinct with geo predicates for 'query'. + // + + assert(coll.drop()); + const bulk = coll.initializeUnorderedBulkOp(); + for (let i = 0; i < 50; ++i) { + bulk.insert({zone: 1, loc: {type: 'Point', coordinates: [-20, -20]}}); + bulk.insert({zone: 2, loc: {type: 'Point', coordinates: [-10, -10]}}); + bulk.insert({zone: 3, loc: {type: 'Point', coordinates: [0, 0]}}); + bulk.insert({zone: 4, loc: {type: 'Point', coordinates: [10, 10]}}); + bulk.insert({zone: 5, loc: {type: 'Point', coordinates: [20, 20]}}); + } + assert.writeOK(bulk.execute()); + + const originGeoJSON = {type: 'Point', coordinates: [0, 0]}; + + // Test distinct with $nearSphere query predicate. + + // A. Unindexed key, no geo index on query predicate. + res = coll.runCommand( + 'distinct', + {key: 'zone', query: {loc: {$nearSphere: {$geometry: originGeoJSON, $maxDistance: 1}}}}); + assert.commandFailed(res); + // B. Unindexed key, with 2dsphere index on query predicate. + assert.commandWorked(coll.createIndex({loc: '2dsphere'})); + res = coll.runCommand( + 'distinct', + {key: 'zone', query: {loc: {$nearSphere: {$geometry: originGeoJSON, $maxDistance: 1}}}}); + assert.commandWorked(res); + assert.eq(res.values.sort(), [3]); + // C. Indexed key, with 2dsphere index on query predicate. + assert.commandWorked(coll.createIndex({zone: 1})); + res = coll.runCommand( + 'distinct', + {key: 'zone', query: {loc: {$nearSphere: {$geometry: originGeoJSON, $maxDistance: 1}}}}); + assert.commandWorked(res); + assert.eq(res.values.sort(), [3]); + + // Test distinct with $near query predicate. + + assert.commandWorked(coll.dropIndexes()); + + // A. Unindexed key, no geo index on query predicate. + res = coll.runCommand( + 'distinct', {key: 'zone', query: {'loc.coordinates': {$near: [0, 0], $maxDistance: 1}}}); + assert.commandFailed(res); + // B. Unindexed key, with 2d index on query predicate. + assert.commandWorked(coll.createIndex({'loc.coordinates': '2d'})); + res = coll.runCommand( + 'distinct', {key: 'zone', query: {'loc.coordinates': {$near: [0, 0], $maxDistance: 1}}}); + assert.commandWorked(res); + assert.eq(res.values.sort(), [3]); + // C. Indexed key, with 2d index on query predicate. + assert.commandWorked(coll.createIndex({zone: 1})); + res = coll.runCommand( + 'distinct', {key: 'zone', query: {'loc.coordinates': {$near: [0, 0], $maxDistance: 1}}}); + assert.commandWorked(res); + assert.eq(res.values.sort(), [3]); +}()); diff --git a/jstests/core/geo_s2ordering.js b/jstests/core/geo_s2ordering.js index 026fdda62c6..165ccd07d96 100644 --- a/jstests/core/geo_s2ordering.js +++ b/jstests/core/geo_s2ordering.js @@ -2,51 +2,57 @@ // actually matters for lookup speed. That is, if we're looking for a non-geo key of which // there are not many, the index order (nongeo, geo) should be faster than (geo, nongeo) // for 2dsphere. -t = db.geo_s2ordering; -t.drop(); - -needle = "hari"; - -// We insert lots of points in a region and look for a non-geo key which is rare. -function makepoints(needle) { - lat = 0; - lng = 0; - points = 50.0; - var bulk = t.initializeUnorderedBulkOp(); - for (var x = -points; x < points; x += 1) { - for (var y = -points; y < points; y += 1) { - tag = x.toString() + "," + y.toString(); - bulk.insert({ - nongeo: tag, - geo: {type: "Point", coordinates: [lng + x / points, lat + y / points]} - }); +(function() { + "use strict"; + + const coll = db.geo_s2ordering; + coll.drop(); + + const needle = "hari"; + + // We insert lots of points in a region and look for a non-geo key which is rare. + function makepoints(needle) { + const lat = 0; + const lng = 0; + const points = 50.0; + + const bulk = coll.initializeUnorderedBulkOp(); + for (let x = -points; x < points; x += 1) { + for (let y = -points; y < points; y += 1) { + bulk.insert({ + nongeo: x.toString() + "," + y.toString(), + geo: {type: "Point", coordinates: [lng + x / points, lat + y / points]} + }); + } } + bulk.insert({nongeo: needle, geo: {type: "Point", coordinates: [0, 0]}}); + assert.writeOK(bulk.execute()); + } + + function runTest(index) { + assert.commandWorked(coll.ensureIndex(index)); + const cursor = + coll.find({nongeo: needle, geo: {$within: {$centerSphere: [[0, 0], Math.PI / 180.0]}}}); + const stats = cursor.explain("executionStats").executionStats; + assert.commandWorked(coll.dropIndex(index)); + return stats; } - bulk.insert({nongeo: needle, geo: {type: "Point", coordinates: [0, 0]}}); - assert.writeOK(bulk.execute()); -} - -function runTest(index) { - t.ensureIndex(index); - var resultcount = 0; - var cursor = - t.find({nongeo: needle, geo: {$within: {$centerSphere: [[0, 0], Math.PI / 180.0]}}}); - - var stats = cursor.explain("executionStats").executionStats; - t.dropIndex(index); - return stats; -} - -makepoints(needle); -// Indexing non-geo first should be quicker. -fast = runTest({nongeo: 1, geo: "2dsphere"}); -slow = runTest({geo: "2dsphere", nongeo: 1}); -// The nReturned should be the same -assert.eq(fast.nReturned, 1); -assert.eq(slow.nReturned, 1); -// Only one document is examined, since we use index. -assert.eq(fast.totalDocsExamined, 1); -assert.eq(slow.totalDocsExamined, 1); -// The ordering actually matters for lookup speed. -// totalKeysExamined is a direct measure of its speed. -assert.lt(fast.totalKeysExamined, slow.totalKeysExamined); + + makepoints(needle); + + // Indexing non-geo first should be quicker. + const fast = runTest({nongeo: 1, geo: "2dsphere"}); + const slow = runTest({geo: "2dsphere", nongeo: 1}); + + // The nReturned should be the same. + assert.eq(fast.nReturned, 1); + assert.eq(slow.nReturned, 1); + + // Only one document is examined, since we use the index. + assert.eq(fast.totalDocsExamined, 1); + assert.eq(slow.totalDocsExamined, 1); + + // The ordering actually matters for lookup speed. + // totalKeysExamined is a direct measure of its speed. + assert.lt(fast.totalKeysExamined, slow.totalKeysExamined); +}()); diff --git a/jstests/core/group1.js b/jstests/core/group1.js index 70a266e2a55..c6fd0fdf16d 100644 --- a/jstests/core/group1.js +++ b/jstests/core/group1.js @@ -1,146 +1,151 @@ // Cannot implicitly shard accessed collections because of unsupported group operator on sharded // collection. // @tags: [assumes_unsharded_collection] - -t = db.group1; -t.drop(); - -t.save({n: 1, a: 1}); -t.save({n: 2, a: 1}); -t.save({n: 3, a: 2}); -t.save({n: 4, a: 2}); -t.save({n: 5, a: 2}); - -var p = { - key: {a: true}, - reduce: function(obj, prev) { - prev.count++; - }, - initial: {count: 0} -}; - -res = t.group(p); - -assert(res.length == 2, "A"); -assert(res[0].a == 1, "B"); -assert(res[0].count == 2, "C"); -assert(res[1].a == 2, "D"); -assert(res[1].count == 3, "E"); - -assert.eq(res, t.groupcmd(p), "ZZ"); - -ret = t.groupcmd({key: {}, reduce: p.reduce, initial: p.initial}); -assert.eq(1, ret.length, "ZZ 2"); -assert.eq(5, ret[0].count, "ZZ 3"); - -ret = t.groupcmd({ - key: {}, - reduce: function(obj, prev) { - prev.sum += obj.n; - }, - initial: {sum: 0} -}); -assert.eq(1, ret.length, "ZZ 4"); -assert.eq(15, ret[0].sum, "ZZ 5"); - -t.drop(); - -t.save({"a": 2}); -t.save({"b": 5}); -t.save({"a": 1}); -t.save({"a": 2}); - -c = { - key: {a: 1}, - cond: {}, - initial: {"count": 0}, - reduce: function(obj, prev) { - prev.count++; - } -}; - -assert.eq(t.group(c), t.groupcmd(c), "ZZZZ"); - -t.drop(); - -t.save({name: {first: "a", last: "A"}}); -t.save({name: {first: "b", last: "B"}}); -t.save({name: {first: "a", last: "A"}}); - -p = { - key: {'name.first': true}, - reduce: function(obj, prev) { - prev.count++; - }, - initial: {count: 0} -}; - -res = t.group(p); -assert.eq(2, res.length, "Z1"); -assert.eq("a", res[0]['name.first'], "Z2"); -assert.eq("b", res[1]['name.first'], "Z3"); -assert.eq(2, res[0].count, "Z4"); -assert.eq(1, res[1].count, "Z5"); - -// SERVER-15851 Test invalid user input. -p = { - ns: "group1", - key: {"name.first": true}, - $reduce: function(obj, prev) { - prev.count++; - }, - initial: {count: 0}, - finalize: "abc" -}; -assert.commandFailedWithCode( - db.runCommand({group: p}), ErrorCodes.JSInterpreterFailure, "Illegal finalize function"); - -p = { - ns: "group1", - key: {"name.first": true}, - $reduce: function(obj, prev) { - prev.count++; - }, - initial: {count: 0}, - finalize: function(obj) { - throw new Error("Intentionally throwing exception in finalize function"); +(function() { + "use strict"; + + const coll = db.group1; + coll.drop(); + + assert.writeOK(coll.insert({n: 1, a: 1})); + assert.writeOK(coll.insert({n: 2, a: 1})); + assert.writeOK(coll.insert({n: 3, a: 2})); + assert.writeOK(coll.insert({n: 4, a: 2})); + assert.writeOK(coll.insert({n: 5, a: 2})); + + let p = { + key: {a: true}, + reduce: function(obj, prev) { + prev.count++; + }, + initial: {count: 0} + }; + + function sortFuncGenerator(key) { + return (doc1, doc2) => { + if (doc1[key] < doc2[key]) { + return -1; + } else if (doc1[key] > doc2[key]) { + return 1; + } else { + return 0; + } + }; } -}; -assert.commandFailedWithCode( - db.runCommand({group: p}), ErrorCodes.JSInterpreterFailure, "Illegal finalize function 2"); - -p = { - ns: "group1", - $keyf: "a", - $reduce: function(obj, prev) { - prev.count++; - }, - initial: {count: 0}, - finalize: function(obj) { - throw new Error("Intentionally throwing exception in finalize function"); - } -}; -assert.commandFailedWithCode( - db.runCommand({group: p}), ErrorCodes.JSInterpreterFailure, "Illegal keyf function"); - -p = { - ns: "group1", - key: {"name.first": true}, - $reduce: "abc", - initial: {count: 0} -}; -assert.commandFailedWithCode( - db.runCommand({group: p}), ErrorCodes.JSInterpreterFailure, "Illegal reduce function"); - -p = { - ns: "group1", - key: {"name.first": true}, - $reduce: function(obj, pre) { - prev.count++; - }, - initial: {count: 0} -}; -assert.commandFailedWithCode( - db.runCommand({group: p}), ErrorCodes.JSInterpreterFailure, "Illegal reduce function 2"); - -t.drop(); + + const sortOnA = sortFuncGenerator("a"); + let expected = [{a: 1, count: 2}, {a: 2, count: 3}]; + let result = coll.group(p).sort(sortOnA); + assert.eq(result, expected); + + result = coll.groupcmd(p).sort(sortOnA); + assert.eq(result, expected); + + expected = [{count: 5}]; + result = coll.groupcmd({key: {}, reduce: p.reduce, initial: p.initial}); + assert.eq(result, expected); + + expected = [{sum: 15}]; + result = coll.groupcmd({ + key: {}, + reduce: function(obj, prev) { + prev.sum += obj.n; + }, + initial: {sum: 0} + }); + assert.eq(result, expected); + + assert(coll.drop()); + + assert.writeOK(coll.insert({"a": 2})); + assert.writeOK(coll.insert({"b": 5})); + assert.writeOK(coll.insert({"a": 1})); + assert.writeOK(coll.insert({"a": 2})); + + const c = { + key: {a: 1}, + cond: {}, + initial: {"count": 0}, + reduce: function(obj, prev) { + prev.count++; + } + }; + + expected = [{a: null, count: 1}, {a: 1, count: 1}, {a: 2, count: 2}]; + assert.eq(coll.group(c).sort(sortOnA), expected); + assert.eq(coll.groupcmd(c).sort(sortOnA), expected); + + assert(coll.drop()); + + assert.writeOK(coll.insert({name: {first: "a", last: "A"}})); + assert.writeOK(coll.insert({name: {first: "b", last: "B"}})); + assert.writeOK(coll.insert({name: {first: "a", last: "A"}})); + + p = { + key: {'name.first': true}, + reduce: function(obj, prev) { + prev.count++; + }, + initial: {count: 0} + }; + const sortOnNameDotFirst = sortFuncGenerator("name.first"); + + expected = [{"name.first": "a", count: 2}, {"name.first": "b", count: 1}]; + assert.eq(coll.group(p).sort(sortOnNameDotFirst), expected); + + // SERVER-15851 Test invalid user input. + p = { + ns: "group1", + key: {"name.first": true}, + $reduce: function(obj, prev) { + prev.count++; + }, + initial: {count: 0}, + finalize: "abc" + }; + assert.commandFailedWithCode( + db.runCommand({group: p}), ErrorCodes.JSInterpreterFailure, "Illegal finalize function"); + + p = { + ns: "group1", + key: {"name.first": true}, + $reduce: function(obj, prev) { + prev.count++; + }, + initial: {count: 0}, + finalize: function(obj) { + throw new Error("Intentionally throwing exception in finalize function"); + } + }; + assert.commandFailedWithCode( + db.runCommand({group: p}), ErrorCodes.JSInterpreterFailure, "Illegal finalize function 2"); + + p = { + ns: "group1", + $keyf: "a", + $reduce: function(obj, prev) { + prev.count++; + }, + initial: {count: 0}, + finalize: function(obj) { + throw new Error("Intentionally throwing exception in finalize function"); + } + }; + assert.commandFailedWithCode( + db.runCommand({group: p}), ErrorCodes.JSInterpreterFailure, "Illegal keyf function"); + + p = {ns: "group1", key: {"name.first": true}, $reduce: "abc", initial: {count: 0}}; + assert.commandFailedWithCode( + db.runCommand({group: p}), ErrorCodes.JSInterpreterFailure, "Illegal reduce function"); + + p = { + ns: "group1", + key: {"name.first": true}, + $reduce: function(obj, pre) { + prev.count++; + }, + initial: {count: 0} + }; + assert.commandFailedWithCode( + db.runCommand({group: p}), ErrorCodes.JSInterpreterFailure, "Illegal reduce function 2"); +}()); diff --git a/jstests/core/group2.js b/jstests/core/group2.js index 5dc0ddce93d..26f65c3d8f8 100644 --- a/jstests/core/group2.js +++ b/jstests/core/group2.js @@ -1,45 +1,43 @@ // Cannot implicitly shard accessed collections because of unsupported group operator on sharded // collection. // @tags: [assumes_unsharded_collection] - -t = db.group2; -t.drop(); - -t.save({a: 2}); -t.save({b: 5}); -t.save({a: 1}); - -cmd = { - key: {a: 1}, - initial: {count: 0}, - reduce: function(obj, prev) { - prev.count++; - } -}; - -result = t.group(cmd); - -assert.eq(3, result.length, "A"); -assert.eq(null, result[1].a, "C"); -assert("a" in result[1], "D"); -assert.eq(1, result[2].a, "E"); - -assert.eq(1, result[0].count, "F"); -assert.eq(1, result[1].count, "G"); -assert.eq(1, result[2].count, "H"); - -var keyFn = function(x) { - return {a: 'a' in x ? x.a : null}; -}; - -delete cmd.key; -cmd["$keyf"] = keyFn; -result2 = t.group(cmd); - -assert.eq(result, result2, "check result2"); - -delete cmd.$keyf; -cmd["keyf"] = keyFn; -result3 = t.group(cmd); - -assert.eq(result, result3, "check result3"); +(function() { + "use strict"; + const coll = db.group2; + coll.drop(); + + assert.writeOK(coll.insert({a: 2})); + assert.writeOK(coll.insert({b: 5})); + assert.writeOK(coll.insert({a: 1})); + + const cmd = { + key: {a: 1}, + initial: {count: 0}, + reduce: function(obj, prev) { + prev.count++; + } + }; + const sortFunc = function(doc1, doc2) { + if (doc1.a < doc2.a) { + return -1; + } else if (doc1.a > doc2.a) { + return 1; + } else { + return 0; + } + }; + const expected = [{a: null, count: 1}, {a: 1, count: 1}, {a: 2, count: 1}]; + assert.eq(coll.group(cmd).sort(sortFunc), expected); + + const keyFn = function(x) { + return {a: 'a' in x ? x.a : null}; + }; + + delete cmd.key; + cmd["$keyf"] = keyFn; + assert.eq(coll.group(cmd).sort(sortFunc), expected); + + delete cmd.$keyf; + cmd["keyf"] = keyFn; + assert.eq(coll.group(cmd).sort(sortFunc), expected); +}()); diff --git a/jstests/core/maxscan.js b/jstests/core/maxscan.js index a862ed426a0..37cbf4b5b2f 100644 --- a/jstests/core/maxscan.js +++ b/jstests/core/maxscan.js @@ -1,18 +1,21 @@ +(function() { + "use strict"; -t = db.maxscan; -t.drop(); + const coll = db.maxscan; + coll.drop(); -N = 100; -for (i = 0; i < N; i++) { - t.insert({_id: i, x: i % 10}); -} + const N = 100; + for (let i = 0; i < N; i++) { + assert.writeOK(coll.insert({_id: i, x: i % 10})); + } -assert.eq(N, t.find().itcount(), "A"); -assert.eq(50, t.find().maxScan(50).itcount(), "B"); + assert.eq(N, coll.find().itcount(), "A"); + assert.eq(50, coll.find().maxScan(50).itcount(), "B"); -assert.eq(10, t.find({x: 2}).itcount(), "C"); -assert.eq(5, t.find({x: 2}).maxScan(50).itcount(), "D"); + assert.eq(10, coll.find({x: 2}).itcount(), "C"); + assert.eq(5, coll.find({x: 2}).sort({_id: 1}).maxScan(50).itcount(), "D"); -t.ensureIndex({x: 1}); -assert.eq(10, t.find({x: 2}).hint({x: 1}).maxScan(N).itcount(), "E"); -assert.eq(0, t.find({x: 2}).hint({x: 1}).maxScan(1).itcount(), "E"); + assert.commandWorked(coll.ensureIndex({x: 1})); + assert.eq(10, coll.find({x: 2}).sort({_id: 1}).hint({x: 1}).maxScan(N).itcount(), "E"); + assert.eq(0, coll.find({x: 2}).sort({_id: 1}).hint({x: 1}).maxScan(1).itcount(), "E"); +}()); diff --git a/jstests/core/nan.js b/jstests/core/nan.js index 611f3d14a6a..1b34a53e64d 100644 --- a/jstests/core/nan.js +++ b/jstests/core/nan.js @@ -1,66 +1,60 @@ -// Test basic NaN handling. - -var t = db.jstests_nan; -t.drop(); - -var cursor; - -t.insert({_id: 0, a: -Infinity}); -t.insert({_id: 1, a: -3}); -t.insert({_id: 2, a: 0}); -t.insert({_id: 3, a: 3}); -t.insert({_id: 4, a: Infinity}); -// WiredTiger indexes handle -NaN and NaN differently. -t.insert({_id: 5, a: NaN}); -t.insert({_id: 6, a: -NaN}); -t.insert({_id: 7, a: undefined}); -t.insert({_id: 8, a: null}); -t.insert({_id: 9, a: []}); -t.insert({_id: 10, a: {b: 1}}); -t.insert({_id: 11, a: {b: 1}}); - /** - * Ensures correct results for EQ, LT, LTE, GT, and GTE cases. + * Tests basic NaN handling. Note that WiredTiger indexes handle -NaN and NaN differently. */ -var testNaNComparisons = function() { - // EQ - cursor = t.find({a: NaN}); - assert.eq(5, cursor.next()["_id"]); - assert.eq(6, cursor.next()["_id"]); - assert(!cursor.hasNext()); - - // LT - cursor = t.find({a: {$lt: NaN}}); - assert(!cursor.hasNext()); - - // LTE - cursor = t.find({a: {$lte: NaN}}); - var id1 = cursor.next()["_id"]; - var id2 = cursor.next()["_id"]; - // Exactly one of the two conditions must be true. - var cond1 = id1 === 5 && id2 === 6; - var cond2 = id1 === 6 && id2 === 5; - assert(cond1 ^ cond2); - assert(!cursor.hasNext()); - - // GT - cursor = t.find({a: {$gt: NaN}}); - assert(!cursor.hasNext()); - - // GTE - cursor = t.find({a: {$gte: NaN}}); - var id1 = cursor.next()["_id"]; - var id2 = cursor.next()["_id"]; - // Exactly one of the two conditions must be true. - var cond1 = id1 === 5 && id2 === 6; - var cond2 = id1 === 6 && id2 === 5; - assert(cond1 ^ cond2); - assert(!cursor.hasNext()); -}; - -// Unindexed -testNaNComparisons(); - -// Indexed -t.ensureIndex({a: 1}); -testNaNComparisons(); +(function() { + "use strict"; + + const coll = db.jstests_nan; + coll.drop(); + + assert.writeOK(coll.insert({_id: 0, a: -Infinity})); + assert.writeOK(coll.insert({_id: 1, a: -3})); + assert.writeOK(coll.insert({_id: 2, a: 0})); + assert.writeOK(coll.insert({_id: 3, a: 3})); + assert.writeOK(coll.insert({_id: 4, a: Infinity})); + assert.writeOK(coll.insert({_id: 5, a: NaN})); + assert.writeOK(coll.insert({_id: 6, a: -NaN})); + assert.writeOK(coll.insert({_id: 7, a: undefined})); + assert.writeOK(coll.insert({_id: 8, a: null})); + assert.writeOK(coll.insert({_id: 9, a: []})); + assert.writeOK(coll.insert({_id: 10, a: {b: 1}})); + assert.writeOK(coll.insert({_id: 11, a: {b: 1}})); + + /** + * Ensures correct results for EQ, LT, LTE, GT, and GTE cases. + */ + var testNaNComparisons = function() { + // EQ + let cursor = coll.find({a: NaN}).sort({_id: 1}); + assert.eq(5, cursor.next()["_id"]); + assert.eq(6, cursor.next()["_id"]); + assert(!cursor.hasNext()); + + // LT + cursor = coll.find({a: {$lt: NaN}}); + assert(!cursor.hasNext()); + + // LTE + cursor = coll.find({a: {$lte: NaN}}).sort({_id: 1}); + assert.eq(5, cursor.next()["_id"]); + assert.eq(6, cursor.next()["_id"]); + assert(!cursor.hasNext()); + + // GT + cursor = coll.find({a: {$gt: NaN}}); + assert(!cursor.hasNext()); + + // GTE + cursor = coll.find({a: {$gte: NaN}}).sort({_id: 1}); + assert.eq(5, cursor.next()["_id"]); + assert.eq(6, cursor.next()["_id"]); + assert(!cursor.hasNext()); + }; + + // Unindexed. + testNaNComparisons(); + + // Indexed. + assert.commandWorked(coll.createIndex({a: 1})); + testNaNComparisons(); +}()); diff --git a/jstests/core/not2.js b/jstests/core/not2.js index 98eb19cee6f..a02399139df 100644 --- a/jstests/core/not2.js +++ b/jstests/core/not2.js @@ -1,84 +1,86 @@ -t = db.jstests_not2; -t.drop(); +(function() { + "use strict"; -check = function(query, expected, size) { - if (size == null) { - size = 1; - } - assert.eq(size, t.find(query).itcount(), tojson(query)); - if (size > 0) { - assert.eq(expected, t.findOne(query).i, tojson(query)); + const coll = db.jstests_not2; + coll.drop(); + + function check(query, expected, size) { + if (size === undefined) { + size = 1; + } + assert.eq(size, coll.find(query).itcount(), tojson(query)); + if (size > 0) { + const cursor = coll.find(query).sort({i: 1}); + assert.eq(expected, cursor.toArray()[0].i, tojson(query)); + } } -}; -fail = function(query) { - try { - t.find(query).itcount(); - assert(false, tojson(query)); - } catch (e) { - // expected + function fail(query) { + assert.throws(() => coll.find(query).itcount()); } -}; -doTest = function() { + function doTest() { + assert.writeOK(coll.remove({})); - t.remove({}); + assert.writeOK(coll.insert({i: "a"})); + assert.writeOK(coll.insert({i: "b"})); - t.save({i: "a"}); - t.save({i: "b"}); + // TODO SERVER-12735: We currently do not handle double negatives during query + // canonicalization. + fail({i: {$not: {$not: "a"}}}); + check({i: {$not: {$not: {$gt: "a"}}}}, "b"); - fail({i: {$not: "a"}}); - // SERVER-12735: We currently do not handle double negatives - // during query canonicalization. - // fail( {i:{$not:{$not:"a"}}} ); - // fail( {i:{$not:{$not:{$gt:"a"}}}} ); - fail({i: {$not: {$ref: "foo"}}}); - fail({i: {$not: {}}}); - check({i: {$gt: "a"}}, "b"); - check({i: {$not: {$gt: "a"}}}, "a"); - check({i: {$not: {$ne: "a"}}}, "a"); - check({i: {$not: {$gte: "b"}}}, "a"); - check({i: {$exists: true}}, "a", 2); - check({i: {$not: {$exists: true}}}, "", 0); - check({j: {$not: {$exists: false}}}, "", 0); - check({j: {$not: {$exists: true}}}, "a", 2); - check({i: {$not: {$in: ["a"]}}}, "b"); - check({i: {$not: {$in: ["a", "b"]}}}, "", 0); - check({i: {$not: {$in: ["g"]}}}, "a", 2); - check({i: {$not: {$nin: ["a"]}}}, "a"); - check({i: {$not: /a/}}, "b"); - check({i: {$not: /(a|b)/}}, "", 0); - check({i: {$not: /a/, $regex: "a"}}, "", 0); - check({i: {$not: /aa/}}, "a", 2); - fail({i: {$not: {$regex: "a"}}}); - fail({i: {$not: {$options: "a"}}}); - check({i: {$type: 2}}, "a", 2); - check({i: {$not: {$type: 1}}}, "a", 2); - check({i: {$not: {$type: 2}}}, "", 0); + fail({i: {$not: "a"}}); + fail({i: {$not: {$ref: "foo"}}}); + fail({i: {$not: {}}}); + check({i: {$gt: "a"}}, "b"); + check({i: {$not: {$gt: "a"}}}, "a"); + check({i: {$not: {$ne: "a"}}}, "a"); + check({i: {$not: {$gte: "b"}}}, "a"); + check({i: {$exists: true}}, "a", 2); + check({i: {$not: {$exists: true}}}, "", 0); + check({j: {$not: {$exists: false}}}, "", 0); + check({j: {$not: {$exists: true}}}, "a", 2); + check({i: {$not: {$in: ["a"]}}}, "b"); + check({i: {$not: {$in: ["a", "b"]}}}, "", 0); + check({i: {$not: {$in: ["g"]}}}, "a", 2); + check({i: {$not: {$nin: ["a"]}}}, "a"); + check({i: {$not: /a/}}, "b"); + check({i: {$not: /(a|b)/}}, "", 0); + check({i: {$not: /a/, $regex: "a"}}, "", 0); + check({i: {$not: /aa/}}, "a", 2); + fail({i: {$not: {$regex: "a"}}}); + fail({i: {$not: {$options: "a"}}}); + check({i: {$type: 2}}, "a", 2); + check({i: {$not: {$type: 1}}}, "a", 2); + check({i: {$not: {$type: 2}}}, "", 0); - t.remove({}); - t.save({i: 1}); - check({i: {$not: {$mod: [5, 1]}}}, null, 0); - check({i: {$mod: [5, 2]}}, null, 0); - check({i: {$not: {$mod: [5, 2]}}}, 1, 1); + assert.writeOK(coll.remove({})); + assert.writeOK(coll.insert({i: 1})); + check({i: {$not: {$mod: [5, 1]}}}, null, 0); + check({i: {$mod: [5, 2]}}, null, 0); + check({i: {$not: {$mod: [5, 2]}}}, 1, 1); - t.remove({}); - t.save({i: ["a", "b"]}); - check({i: {$not: {$size: 2}}}, null, 0); - check({i: {$not: {$size: 3}}}, ["a", "b"]); - check({i: {$not: {$gt: "a"}}}, null, 0); - check({i: {$not: {$gt: "c"}}}, ["a", "b"]); - check({i: {$not: {$all: ["a", "b"]}}}, null, 0); - check({i: {$not: {$all: ["c"]}}}, ["a", "b"]); + assert.writeOK(coll.remove({})); + assert.writeOK(coll.insert({i: ["a", "b"]})); + check({i: {$not: {$size: 2}}}, null, 0); + check({i: {$not: {$size: 3}}}, ["a", "b"]); + check({i: {$not: {$gt: "a"}}}, null, 0); + check({i: {$not: {$gt: "c"}}}, ["a", "b"]); + check({i: {$not: {$all: ["a", "b"]}}}, null, 0); + check({i: {$not: {$all: ["c"]}}}, ["a", "b"]); - t.remove({}); - t.save({i: [{j: "a"}]}); - t.save({i: [{j: "b"}]}); - check({i: {$not: {$elemMatch: {j: "a"}}}}, [{j: "b"}]); - check({i: {$not: {$elemMatch: {j: "f"}}}}, [{j: "a"}], 2); + assert.writeOK(coll.remove({})); + assert.writeOK(coll.insert({i: [{j: "a"}]})); + assert.writeOK(coll.insert({i: [{j: "b"}]})); + check({i: {$not: {$elemMatch: {j: "a"}}}}, [{j: "b"}]); + check({i: {$not: {$elemMatch: {j: "f"}}}}, [{j: "a"}], 2); + } -}; + // Run the test without any index. + doTest(); -doTest(); -t.ensureIndex({i: 1}); -doTest(); + // Run the test with an index present. + assert.commandWorked(coll.ensureIndex({i: 1})); + doTest(); +}()); diff --git a/jstests/core/sort3.js b/jstests/core/sort3.js index 933f16da6cb..1a1df005fb3 100644 --- a/jstests/core/sort3.js +++ b/jstests/core/sort3.js @@ -1,17 +1,13 @@ -t = db.sort3; -t.drop(); +(function() { + "use strict"; -t.save({a: 1}); -t.save({a: 5}); -t.save({a: 3}); + const coll = db.sort3; + coll.drop(); -assert.eq("1,5,3", t.find().toArray().map(function(z) { - return z.a; -})); + assert.writeOK(coll.insert({a: 1})); + assert.writeOK(coll.insert({a: 5})); + assert.writeOK(coll.insert({a: 3})); -assert.eq("1,3,5", t.find().sort({a: 1}).toArray().map(function(z) { - return z.a; -})); -assert.eq("5,3,1", t.find().sort({a: -1}).toArray().map(function(z) { - return z.a; -})); + assert.eq([1, 3, 5], coll.find().sort({a: 1}).toArray().map(doc => doc.a)); + assert.eq([5, 3, 1], coll.find().sort({a: -1}).toArray().map(doc => doc.a)); +}()); diff --git a/jstests/core/sort4.js b/jstests/core/sort4.js index 41b4e25fe11..ef33e779d8e 100644 --- a/jstests/core/sort4.js +++ b/jstests/core/sort4.js @@ -1,40 +1,45 @@ -t = db.sort4; -t.drop(); - -function nice(sort, correct, extra) { - var c = t.find().sort(sort); - var s = ""; - c.forEach(function(z) { - if (s.length) - s += ","; - s += z.name; - if (z.prename) - s += z.prename; - }); - print(tojson(sort) + "\t" + s); - if (correct) - assert.eq(correct, s, tojson(sort) + "(" + extra + ")"); - return s; -} - -t.save({name: 'A', prename: 'B'}); -t.save({name: 'A', prename: 'C'}); -t.save({name: 'B', prename: 'B'}); -t.save({name: 'B', prename: 'D'}); - -nice({name: 1}, "AB,AC,BB,BD", "s1"); -nice({prename: 1}, "AB,BB,AC,BD", "s2"); -nice({name: 1, prename: 1}, "AB,AC,BB,BD", "s3"); - -t.save({name: 'A'}); -nice({name: 1, prename: 1}, "A,AB,AC,BB,BD", "e1"); - -t.save({name: 'C'}); -nice({name: 1, prename: 1}, "A,AB,AC,BB,BD,C", "e2"); // SERVER-282 - -t.ensureIndex({name: 1, prename: 1}); -nice({name: 1, prename: 1}, "A,AB,AC,BB,BD,C", "e2ia"); // SERVER-282 - -t.dropIndexes(); -t.ensureIndex({name: 1}); -nice({name: 1, prename: 1}, "A,AB,AC,BB,BD,C", "e2ib"); // SERVER-282 +(function() { + "use strict"; + + const coll = db.sort4; + coll.drop(); + + function nice(sort, correct, extra) { + const c = coll.find().sort(sort); + let s = ""; + c.forEach(function(z) { + if (s.length) { + s += ","; + } + s += z.name; + if (z.prename) { + s += z.prename; + } + }); + if (correct) { + assert.eq(correct, s, tojson(sort) + "(" + extra + ")"); + } + return s; + } + + assert.writeOK(coll.insert({name: 'A', prename: 'B'})); + assert.writeOK(coll.insert({name: 'A', prename: 'C'})); + assert.writeOK(coll.insert({name: 'B', prename: 'B'})); + assert.writeOK(coll.insert({name: 'B', prename: 'D'})); + + nice({name: 1, prename: 1}, "AB,AC,BB,BD", "s3"); + nice({prename: 1, name: 1}, "AB,BB,AC,BD", "s3"); + + assert.writeOK(coll.insert({name: 'A'})); + nice({name: 1, prename: 1}, "A,AB,AC,BB,BD", "e1"); + + assert.writeOK(coll.insert({name: 'C'})); + nice({name: 1, prename: 1}, "A,AB,AC,BB,BD,C", "e2"); // SERVER-282 + + assert.commandWorked(coll.ensureIndex({name: 1, prename: 1})); + nice({name: 1, prename: 1}, "A,AB,AC,BB,BD,C", "e2ia"); // SERVER-282 + + assert.commandWorked(coll.dropIndexes()); + assert.commandWorked(coll.ensureIndex({name: 1})); + nice({name: 1, prename: 1}, "A,AB,AC,BB,BD,C", "e2ib"); // SERVER-282 +}()); |