diff options
author | Charlie Swanson <charlie.swanson@mongodb.com> | 2018-01-11 14:00:27 -0500 |
---|---|---|
committer | Charlie Swanson <charlie.swanson@mongodb.com> | 2018-01-19 09:55:59 -0500 |
commit | 87c9442cc30d4101693bb8ccb6fd4509aa048558 (patch) | |
tree | 99ab7b36d89776693c09f271e4de4fd4f64846e3 /jstests/core | |
parent | 71ae3ed5b7e99ddb629ec64b85f4bd75b73aff17 (diff) | |
download | mongo-87c9442cc30d4101693bb8ccb6fd4509aa048558.tar.gz |
SERVER-31785 Use 2 shards in sharded jscore passthrough.
Diffstat (limited to 'jstests/core')
66 files changed, 748 insertions, 580 deletions
diff --git a/jstests/core/batch_size.js b/jstests/core/batch_size.js index b715f936052..cdd60e77cd2 100644 --- a/jstests/core/batch_size.js +++ b/jstests/core/batch_size.js @@ -2,113 +2,128 @@ // Test subtleties of batchSize and limit. -var t = db.jstests_batch_size; -t.drop(); - -for (var i = 0; i < 4; i++) { - t.save({_id: i, a: i}); -} - -function runIndexedTests() { - // With limit, indexed. - assert.eq(2, t.find().limit(2).itcount(), 'G'); - assert.eq(2, t.find().sort({a: 1}).limit(2).itcount(), 'H'); - - // With batchSize, indexed. - // SERVER-12438: If there is an index that provides the sort, - // then a plan with an unindexed sort should never be used. - // Consequently, batchSize will NOT be a hard limit in this case. - // WARNING: the behavior described above may change in the future. - assert.eq(4, t.find().batchSize(2).itcount(), 'I'); - assert.eq(4, t.find().sort({a: 1}).batchSize(2).itcount(), 'J'); -} - -// Without batch size or limit, unindexed. -assert.eq(4, t.find().itcount(), 'A'); -assert.eq(4, t.find().sort({a: 1}).itcount(), 'B'); - -// With limit, unindexed. -assert.eq(2, t.find().limit(2).itcount(), 'C'); -assert.eq(2, t.find().sort({a: 1}).limit(2).itcount(), 'D'); - -assert.eq(4, t.find().batchSize(2).itcount(), 'E'); -assert.eq(4, t.find().sort({a: 1}).batchSize(2).itcount(), 'F'); - -// With negative batchSize. A negative batchSize value instructs the server -// to return just a single batch of results. -assert.eq(1, t.find().batchSize(-1).itcount(), 'G'); -assert.eq(2, t.find().batchSize(-2).itcount(), 'H'); - -// Run the tests with the index twice in order to double check plan caching. -t.ensureIndex({a: 1}); -for (var i = 0; i < 2; i++) { - runIndexedTests(); -} - -// The next tests make sure that we obey limit and batchSize properly when -// the sort could be either indexed or unindexed. -t.drop(); -t.ensureIndex({a: 1}); -t.ensureIndex({b: 1}); - -for (var i = 0; i < 100; i++) { - t.save({_id: i, a: i, b: 1}); -} - -// Without a hint. Do it twice to make sure caching is ok. -for (var i = 0; i < 2; i++) { - assert.eq(15, t.find({a: {$gte: 85}}).sort({b: 1}).batchSize(2).itcount(), 'K'); - assert.eq(6, t.find({a: {$gte: 85}}).sort({b: 1}).limit(6).itcount(), 'L'); -} - -// Hinting 'a'. -assert.eq(15, t.find({a: {$gte: 85}}).sort({b: 1}).hint({a: 1}).batchSize(2).itcount(), 'M'); -assert.eq(6, t.find({a: {$gte: 85}}).sort({b: 1}).hint({a: 1}).limit(6).itcount(), 'N'); - -// Hinting 'b'. -assert.eq(15, t.find({a: {$gte: 85}}).sort({b: 1}).hint({b: 1}).batchSize(2).itcount(), 'O'); -assert.eq(6, t.find({a: {$gte: 85}}).sort({b: 1}).hint({b: 1}).limit(6).itcount(), 'P'); - -// With explain. -var explain = t.find({a: {$gte: 85}}).sort({b: 1}).batchSize(2).explain("executionStats"); -assert.eq(15, explain.executionStats.nReturned, 'Q'); -explain = t.find({a: {$gte: 85}}).sort({b: 1}).limit(6).explain("executionStats"); -assert.eq(6, explain.executionStats.nReturned, 'R'); - -// Double check that we're not scanning more stuff than we have to. -// In order to get the sort using index 'a', we should need to scan -// about 50 keys and 50 documents. -var explain = t.find({a: {$gte: 50}}).sort({b: 1}).hint({a: 1}).limit(6).explain("executionStats"); -assert.lte(explain.executionStats.totalKeysExamined, 60, 'S'); -assert.lte(explain.executionStats.totalDocsExamined, 60, 'T'); -assert.eq(explain.executionStats.nReturned, 6, 'U'); - -// ------- - -// During plan ranking, we treat ntoreturn as a limit. This prevents us from buffering -// too much data in a blocking sort stage during plan ranking. -t.drop(); - -// Generate big string to use in the object - 1MB+ String -var bigStr = "ABCDEFGHIJKLMNBOPQRSTUVWXYZ012345687890"; -while (bigStr.length < 1000000) { - bigStr = bigStr + "::" + bigStr; -} - -// Insert enough documents to exceed the 32 MB in-memory sort limit. -for (var i = 0; i < 40; i++) { - var doc = {x: 1, y: 1, z: i, big: bigStr}; - t.insert(doc); -} - -// Two indices needed in order to trigger plan ranking. Neither index provides -// the sort order. -t.ensureIndex({x: 1}); -t.ensureIndex({y: 1}); - -// We should only buffer 3 docs in memory. -var cursor = t.find({x: 1, y: 1}).sort({z: -1}).limit(3); -assert.eq(39, cursor.next()["z"]); -assert.eq(38, cursor.next()["z"]); -assert.eq(37, cursor.next()["z"]); -assert(!cursor.hasNext()); +(function() { + "use strict"; + load("jstests/libs/fixture_helpers.js"); // For FixtureHelpers. + var t = db.jstests_batch_size; + t.drop(); + + for (var i = 0; i < 4; i++) { + t.save({_id: i, a: i}); + } + + function runIndexedTests() { + // With limit, indexed. + assert.eq(2, t.find().limit(2).itcount()); + assert.eq(2, t.find().sort({a: 1}).limit(2).itcount()); + + // With batchSize, indexed. + // SERVER-12438: If there is an index that provides the sort, then a plan with an unindexed + // sort should never be used. Consequently, batchSize will NOT be a hard limit in this + // case. WARNING: the behavior described above may change in the future. + assert.eq(4, t.find().batchSize(2).itcount()); + assert.eq(4, t.find().sort({a: 1}).batchSize(2).itcount()); + } + + // Without batch size or limit, unindexed. + assert.eq(4, t.find().itcount()); + assert.eq(4, t.find().sort({a: 1}).itcount()); + + // With limit, unindexed. + assert.eq(2, t.find().limit(2).itcount()); + assert.eq(2, t.find().sort({a: 1}).limit(2).itcount()); + + assert.eq(4, t.find().batchSize(2).itcount()); + assert.eq(4, t.find().sort({a: 1}).batchSize(2).itcount()); + + // With negative batchSize. A negative batchSize value instructs the server + // to return just a single batch of results. + assert.eq(1, t.find().batchSize(-1).itcount()); + assert.eq(2, t.find().batchSize(-2).itcount()); + + // Run the tests with the index twice in order to double check plan caching. + t.ensureIndex({a: 1}); + for (var i = 0; i < 2; i++) { + runIndexedTests(); + } + + // The next tests make sure that we obey limit and batchSize properly when the sort could be + // either indexed or unindexed. + t.drop(); + t.ensureIndex({a: 1}); + t.ensureIndex({b: 1}); + + for (var i = 0; i < 100; i++) { + t.save({_id: i, a: i, b: 1}); + } + + // Without a hint. Do it twice to make sure caching is ok. + for (var i = 0; i < 2; i++) { + assert.eq(15, t.find({a: {$gte: 85}}).sort({b: 1}).batchSize(2).itcount()); + assert.eq(6, t.find({a: {$gte: 85}}).sort({b: 1}).limit(6).itcount()); + } + + // Hinting 'a'. + assert.eq(15, t.find({a: {$gte: 85}}).sort({b: 1}).hint({a: 1}).batchSize(2).itcount()); + assert.eq(6, t.find({a: {$gte: 85}}).sort({b: 1}).hint({a: 1}).limit(6).itcount()); + + // Hinting 'b'. + assert.eq(15, t.find({a: {$gte: 85}}).sort({b: 1}).hint({b: 1}).batchSize(2).itcount()); + assert.eq(6, t.find({a: {$gte: 85}}).sort({b: 1}).hint({b: 1}).limit(6).itcount()); + + // With explain. + var explain = t.find({a: {$gte: 85}}).sort({b: 1}).batchSize(2).explain("executionStats"); + assert.eq(15, explain.executionStats.nReturned); + explain = t.find({a: {$gte: 85}}).sort({b: 1}).limit(6).explain("executionStats"); + if (FixtureHelpers.isMongos(db)) { + // If we're talking to a mongos, we expect at most one batch from each shard. + assert.eq(FixtureHelpers.numberOfShardsForCollection(t) * 6, + explain.executionStats.nReturned); + } else { + assert.eq(6, explain.executionStats.nReturned); + } + + // Double check that we're not scanning more stuff than we have to. In order to get the sort + // using index 'a', we should need to scan about 50 keys and 50 documents. + var explain = + t.find({a: {$gte: 50}}).sort({b: 1}).hint({a: 1}).limit(6).explain("executionStats"); + assert.lte(explain.executionStats.totalKeysExamined, 60); + assert.lte(explain.executionStats.totalDocsExamined, 60); + if (FixtureHelpers.isMongos(db)) { + // If we're talking to a mongos, we expect at most one batch from each shard. + assert.eq(FixtureHelpers.numberOfShardsForCollection(t) * 6, + explain.executionStats.nReturned); + } else { + assert.eq(6, explain.executionStats.nReturned); + } + + // ------- + + // During plan ranking, we treat ntoreturn as a limit. This prevents us from buffering too much + // data in a blocking sort stage during plan ranking. + t.drop(); + + // Generate big string to use in the object - 1MB+ String + var bigStr = "ABCDEFGHIJKLMNBOPQRSTUVWXYZ012345687890"; + while (bigStr.length < 1000000) { + bigStr = bigStr + "::" + bigStr; + } + + // Insert enough documents to exceed the 32 MB in-memory sort limit. + const nDocs = 40 * FixtureHelpers.numberOfShardsForCollection(t); + for (var i = 0; i < nDocs; i++) { + var doc = {x: 1, y: 1, z: i, big: bigStr}; + t.insert(doc); + } + + // Two indices needed in order to trigger plan ranking. Neither index provides the sort order. + t.ensureIndex({x: 1}); + t.ensureIndex({y: 1}); + + // We should only buffer 3 docs in memory. + var cursor = t.find({x: 1, y: 1}).sort({z: -1}).limit(3); + assert.eq(nDocs - 1, cursor.next().z); + assert.eq(nDocs - 2, cursor.next().z); + assert.eq(nDocs - 3, cursor.next().z); + assert(!cursor.hasNext()); +}()); diff --git a/jstests/core/bindata_indexonly.js b/jstests/core/bindata_indexonly.js index d8f8bf931b9..4eb24476010 100644 --- a/jstests/core/bindata_indexonly.js +++ b/jstests/core/bindata_indexonly.js @@ -28,7 +28,7 @@ .hint({_id: 1, a: 1}) .explain("executionStats"); - assert(isIndexOnly(explain.queryPlanner.winningPlan), + assert(isIndexOnly(db, explain.queryPlanner.winningPlan), "indexonly.BinData(0, " + blob + ") - must be index-only"); assert.eq(1, explain.executionStats.nReturned, @@ -45,7 +45,7 @@ explain = coll.find({_id: {$lt: BinData(0, "AAAAAAAAAAAAAAAAAAAAAAAAAAAA")}}, {_id: 1, a: 1}) .hint({_id: 1, a: 1}) .explain("executionStats"); - assert(isIndexOnly(explain), "indexonly.$lt.1 - must be index-only"); + assert(isIndexOnly(db, explain), "indexonly.$lt.1 - must be index-only"); assert.eq(0, explain.executionStats.nReturned, "correctcount.$lt.1 - not returning correct documents"); @@ -53,7 +53,7 @@ explain = coll.find({_id: {$gt: BinData(0, "////////////////////////////")}}, {_id: 1, a: 1}) .hint({_id: 1, a: 1}) .explain("executionStats"); - assert(isIndexOnly(explain), "indexonly.$gt.2 - must be index-only"); + assert(isIndexOnly(db, explain), "indexonly.$gt.2 - must be index-only"); assert.eq(0, explain.executionStats.nReturned, "correctcount.$gt.2 - not returning correct documents"); @@ -61,7 +61,7 @@ explain = coll.find({_id: {$lte: BinData(0, "AQAAAAEBAAVlbl9VSwAAAAAAAAhv")}}, {_id: 1, a: 1}) .hint({_id: 1, a: 1}) .explain("executionStats"); - assert(isIndexOnly(explain), "indexonly.$lte.3 - must be index-only"); + assert(isIndexOnly(db, explain), "indexonly.$lte.3 - must be index-only"); assert.eq(2, explain.executionStats.nReturned, "correctcount.$lte.3 - not returning correct documents"); @@ -69,7 +69,7 @@ explain = coll.find({_id: {$gte: BinData(0, "AQAAAAEBAAVlbl9VSwAAAAAAAAhz")}}, {_id: 1, a: 1}) .hint({_id: 1, a: 1}) .explain("executionStats"); - assert(isIndexOnly(explain), "indexonly.$gte.3 - must be index-only"); + assert(isIndexOnly(db, explain), "indexonly.$gte.3 - must be index-only"); assert.eq(2, explain.executionStats.nReturned, "correctcount.$gte.3 - not returning correct documents"); diff --git a/jstests/core/bittest.js b/jstests/core/bittest.js index 0a54215ee60..194bf3df048 100644 --- a/jstests/core/bittest.js +++ b/jstests/core/bittest.js @@ -10,7 +10,7 @@ function assertQueryCorrect(query, count) { var explain = coll.find(query).explain("executionStats"); - assert(isCollscan(explain.queryPlanner.winningPlan), + assert(isCollscan(db, explain.queryPlanner.winningPlan), "expected bit test query plan to be COLLSCAN"); assert.eq(count, explain.executionStats.nReturned, @@ -153,4 +153,4 @@ 1); coll.drop(); -})();
\ No newline at end of file +})(); diff --git a/jstests/core/collation.js b/jstests/core/collation.js index 0e6b4974af4..dbc0bd3307a 100644 --- a/jstests/core/collation.js +++ b/jstests/core/collation.js @@ -258,17 +258,17 @@ // Query has simple collation, but index has fr_CA collation. explainRes = coll.find({a: "foo"}).explain(); assert.commandWorked(explainRes); - assert(planHasStage(explainRes.queryPlanner.winningPlan, "COLLSCAN")); + assert(planHasStage(db, explainRes.queryPlanner.winningPlan, "COLLSCAN")); // Query has en_US collation, but index has fr_CA collation. explainRes = coll.find({a: "foo"}).collation({locale: "en_US"}).explain(); assert.commandWorked(explainRes); - assert(planHasStage(explainRes.queryPlanner.winningPlan, "COLLSCAN")); + assert(planHasStage(db, explainRes.queryPlanner.winningPlan, "COLLSCAN")); // Matching collations. explainRes = coll.find({a: "foo"}).collation({locale: "fr_CA"}).explain(); assert.commandWorked(explainRes); - assert(planHasStage(explainRes.queryPlanner.winningPlan, "IXSCAN")); + assert(planHasStage(db, explainRes.queryPlanner.winningPlan, "IXSCAN")); } // Should not be possible to create a text index with an explicit non-simple collation. @@ -327,7 +327,7 @@ assert.commandWorked(db.createCollection(coll.getName(), {collation: {locale: "en_US"}})); assert.commandWorked(coll.ensureIndex({a: 1}, {collation: {locale: "en_US"}})); var explain = coll.explain("queryPlanner").aggregate([{$match: {a: "foo"}}]).stages[0].$cursor; - assert(isIxscan(explain.queryPlanner.winningPlan)); + assert(isIxscan(db, explain.queryPlanner.winningPlan)); // Aggregation should not use index when no collation specified and collection default // collation is incompatible with index collation. @@ -335,7 +335,7 @@ assert.commandWorked(db.createCollection(coll.getName(), {collation: {locale: "en_US"}})); assert.commandWorked(coll.ensureIndex({a: 1}, {collation: {locale: "simple"}})); var explain = coll.explain("queryPlanner").aggregate([{$match: {a: "foo"}}]).stages[0].$cursor; - assert(isCollscan(explain.queryPlanner.winningPlan)); + assert(isCollscan(db, explain.queryPlanner.winningPlan)); // Explain of aggregation with collation should succeed. assert.commandWorked(coll.explain().aggregate([], {collation: {locale: "fr"}})); @@ -442,8 +442,8 @@ assert.commandWorked(coll.createIndex({a: 1})); explainRes = coll.explain("executionStats").find({a: "foo"}).count(); assert.commandWorked(explainRes); - assert(planHasStage(explainRes.executionStats.executionStages, "COUNT_SCAN")); - assert(!planHasStage(explainRes.executionStats.executionStages, "FETCH")); + assert(planHasStage(db, explainRes.executionStats.executionStages, "COUNT_SCAN")); + assert(!planHasStage(db, explainRes.executionStats.executionStages, "FETCH")); // // Collation tests for distinct. @@ -498,28 +498,28 @@ assert.commandWorked(db.createCollection(coll.getName(), {collation: {locale: "en_US"}})); assert.commandWorked(coll.ensureIndex({a: 1}, {collation: {locale: "en_US"}})); var explain = coll.explain("queryPlanner").distinct("a"); - assert(planHasStage(explain.queryPlanner.winningPlan, "DISTINCT_SCAN")); - assert(planHasStage(explain.queryPlanner.winningPlan, "FETCH")); + assert(planHasStage(db, explain.queryPlanner.winningPlan, "DISTINCT_SCAN")); + assert(planHasStage(db, explain.queryPlanner.winningPlan, "FETCH")); // Distinct scan on strings can be used over an index with a collation when the predicate has // exact bounds. explain = coll.explain("queryPlanner").distinct("a", {a: {$gt: "foo"}}); - assert(planHasStage(explain.queryPlanner.winningPlan, "DISTINCT_SCAN")); - assert(planHasStage(explain.queryPlanner.winningPlan, "FETCH")); - assert(!planHasStage(explain.queryPlanner.winningPlan, "PROJECTION")); + assert(planHasStage(db, explain.queryPlanner.winningPlan, "DISTINCT_SCAN")); + assert(planHasStage(db, explain.queryPlanner.winningPlan, "FETCH")); + assert(!planHasStage(db, explain.queryPlanner.winningPlan, "PROJECTION")); // Distinct scan cannot be used over an index with a collation when the predicate has inexact // bounds. explain = coll.explain("queryPlanner").distinct("a", {a: {$exists: true}}); - assert(planHasStage(explain.queryPlanner.winningPlan, "IXSCAN")); - assert(planHasStage(explain.queryPlanner.winningPlan, "FETCH")); - assert(!planHasStage(explain.queryPlanner.winningPlan, "DISTINCT_SCAN")); + assert(planHasStage(db, explain.queryPlanner.winningPlan, "IXSCAN")); + assert(planHasStage(db, explain.queryPlanner.winningPlan, "FETCH")); + assert(!planHasStage(db, explain.queryPlanner.winningPlan, "DISTINCT_SCAN")); // Distinct scan can be used without a fetch when predicate has exact non-string bounds. explain = coll.explain("queryPlanner").distinct("a", {a: {$gt: 3}}); - assert(planHasStage(explain.queryPlanner.winningPlan, "DISTINCT_SCAN")); - assert(planHasStage(explain.queryPlanner.winningPlan, "PROJECTION")); - assert(!planHasStage(explain.queryPlanner.winningPlan, "FETCH")); + assert(planHasStage(db, explain.queryPlanner.winningPlan, "DISTINCT_SCAN")); + assert(planHasStage(db, explain.queryPlanner.winningPlan, "PROJECTION")); + assert(!planHasStage(db, explain.queryPlanner.winningPlan, "FETCH")); // Distinct should not use index when no collation specified and collection default collation is // incompatible with index collation. @@ -527,7 +527,7 @@ assert.commandWorked(db.createCollection(coll.getName(), {collation: {locale: "en_US"}})); assert.commandWorked(coll.ensureIndex({a: 1}, {collation: {locale: "simple"}})); var explain = coll.explain("queryPlanner").distinct("a"); - assert(isCollscan(explain.queryPlanner.winningPlan)); + assert(isCollscan(db, explain.queryPlanner.winningPlan)); // Explain of DISTINCT_SCAN stage should include index collation. coll.drop(); @@ -779,7 +779,7 @@ assert.commandWorked(db.createCollection(coll.getName(), {collation: {locale: "en_US"}})); assert.commandWorked(coll.ensureIndex({a: 1}, {collation: {locale: "en_US"}})); var explain = coll.find({a: "foo"}).explain("queryPlanner"); - assert(isIxscan(explain.queryPlanner.winningPlan)); + assert(isIxscan(db, explain.queryPlanner.winningPlan)); // Find should select compatible index when no collation specified and collection default // collation is "simple". @@ -787,7 +787,7 @@ assert.commandWorked(db.createCollection(coll.getName(), {collation: {locale: "simple"}})); assert.commandWorked(coll.ensureIndex({a: 1}, {collation: {locale: "simple"}})); var explain = coll.find({a: "foo"}).explain("queryPlanner"); - assert(isIxscan(explain.queryPlanner.winningPlan)); + assert(isIxscan(db, explain.queryPlanner.winningPlan)); // Find should not use index when no collation specified, index collation is "simple", and // collection has a non-"simple" default collation. @@ -795,7 +795,7 @@ assert.commandWorked(db.createCollection(coll.getName(), {collation: {locale: "en_US"}})); assert.commandWorked(coll.ensureIndex({a: 1}, {collation: {locale: "simple"}})); var explain = coll.find({a: "foo"}).explain("queryPlanner"); - assert(isCollscan(explain.queryPlanner.winningPlan)); + assert(isCollscan(db, explain.queryPlanner.winningPlan)); // Find should select compatible index when "simple" collation specified and collection has a // non-"simple" default collation. @@ -803,7 +803,7 @@ assert.commandWorked(db.createCollection(coll.getName(), {collation: {locale: "en_US"}})); assert.commandWorked(coll.ensureIndex({a: 1}, {collation: {locale: "simple"}})); var explain = coll.find({a: "foo"}).collation({locale: "simple"}).explain("queryPlanner"); - assert(isIxscan(explain.queryPlanner.winningPlan)); + assert(isIxscan(db, explain.queryPlanner.winningPlan)); // Find should return correct results when collation specified and run with explain. coll.drop(); @@ -2150,7 +2150,7 @@ .sort({a: 1, b: 1}) .explain(); assert.commandWorked(explainRes); - assert(planHasStage(explainRes.queryPlanner.winningPlan, "SORT")); + assert(planHasStage(db, explainRes.queryPlanner.winningPlan, "SORT")); // This query should fail since min has a string as one of it's boundaries, and the // collation doesn't match that of the index. diff --git a/jstests/core/coveredIndex1.js b/jstests/core/coveredIndex1.js index 9e6c892567e..7776a48c014 100644 --- a/jstests/core/coveredIndex1.js +++ b/jstests/core/coveredIndex1.js @@ -40,11 +40,11 @@ assert(explain.queryPlanner.hasOwnProperty("winningPlan"), tojson(explain)); const winningPlan = explain.queryPlanner.winningPlan; if (isCovered) { - assert(isIndexOnly(winningPlan), + assert(isIndexOnly(db, winningPlan), "Query " + tojson(query) + " with projection " + tojson(projection) + " should have been covered, but got this plan: " + tojson(winningPlan)); } else { - assert(!isIndexOnly(winningPlan), + assert(!isIndexOnly(db, winningPlan), "Query " + tojson(query) + " with projection " + tojson(projection) + " should not have been covered, but got this plan: " + tojson(winningPlan)); } diff --git a/jstests/core/coveredIndex2.js b/jstests/core/coveredIndex2.js index 058b739fba2..7f9983618fc 100644 --- a/jstests/core/coveredIndex2.js +++ b/jstests/core/coveredIndex2.js @@ -17,15 +17,16 @@ assert.eq(t.count(), 2, "Not right length"); // use simple index t.ensureIndex({a: 1}); var plan = t.find({a: 1}).explain(); -assert(!isIndexOnly(plan.queryPlanner.winningPlan), +assert(!isIndexOnly(db, plan.queryPlanner.winningPlan), "Find using covered index but all fields are returned"); var plan = t.find({a: 1}, {a: 1}).explain(); -assert(!isIndexOnly(plan.queryPlanner.winningPlan), "Find using covered index but _id is returned"); +assert(!isIndexOnly(db, plan.queryPlanner.winningPlan), + "Find using covered index but _id is returned"); var plan = t.find({a: 1}, {a: 1, _id: 0}).explain(); -assert(isIndexOnly(plan.queryPlanner.winningPlan), "Find is not using covered index"); +assert(isIndexOnly(db, plan.queryPlanner.winningPlan), "Find is not using covered index"); // add multikey t.save({a: [3, 4]}); var plan = t.find({a: 1}, {a: 1, _id: 0}).explain(); -assert(!isIndexOnly(plan.queryPlanner.winningPlan), +assert(!isIndexOnly(db, plan.queryPlanner.winningPlan), "Find is using covered index even after multikey insert"); diff --git a/jstests/core/covered_index_compound_1.js b/jstests/core/covered_index_compound_1.js index ba2c9b6ecc5..79b015220b5 100644 --- a/jstests/core/covered_index_compound_1.js +++ b/jstests/core/covered_index_compound_1.js @@ -19,7 +19,7 @@ coll.ensureIndex({a: 1, b: -1, c: 1}); var plan = coll.find({a: 10, b: "strvar_10", c: 0}, {a: 1, b: 1, c: 1, _id: 0}) .hint({a: 1, b: -1, c: 1}) .explain("executionStats"); -assert(isIndexOnly(plan.queryPlanner.winningPlan), +assert(isIndexOnly(db, plan.queryPlanner.winningPlan), "compound.1.1 - indexOnly should be true on covered query"); assert.eq(0, plan.executionStats.totalDocsExamined, @@ -29,7 +29,7 @@ assert.eq(0, var plan = coll.find({a: 26, b: "strvar_0"}, {a: 1, b: 1, c: 1, _id: 0}) .hint({a: 1, b: -1, c: 1}) .explain("executionStats"); -assert(isIndexOnly(plan.queryPlanner.winningPlan), +assert(isIndexOnly(db, plan.queryPlanner.winningPlan), "compound.1.2 - indexOnly should be true on covered query"); assert.eq(0, plan.executionStats.totalDocsExamined, @@ -39,7 +39,7 @@ assert.eq(0, var plan = coll.find({a: 38, b: "strvar_12", c: 8}, {b: 1, c: 1, _id: 0}) .hint({a: 1, b: -1, c: 1}) .explain("executionStats"); -assert(isIndexOnly(plan.queryPlanner.winningPlan), +assert(isIndexOnly(db, plan.queryPlanner.winningPlan), "compound.1.3 - indexOnly should be true on covered query"); assert.eq(0, plan.executionStats.totalDocsExamined, @@ -47,7 +47,7 @@ assert.eq(0, // Test no query var plan = coll.find({}, {b: 1, c: 1, _id: 0}).hint({a: 1, b: -1, c: 1}).explain("executionStats"); -assert(isIndexOnly(plan.queryPlanner.winningPlan), +assert(isIndexOnly(db, plan.queryPlanner.winningPlan), "compound.1.4 - indexOnly should be true on covered query"); assert.eq(0, plan.executionStats.totalDocsExamined, @@ -57,7 +57,7 @@ assert.eq(0, var plan = coll.find({a: {$gt: 25, $lt: 43}}, {b: 1, c: 1, _id: 0}) .hint({a: 1, b: -1, c: 1}) .explain("executionStats"); -assert(isIndexOnly(plan.queryPlanner.winningPlan), +assert(isIndexOnly(db, plan.queryPlanner.winningPlan), "compound.1.5 - indexOnly should be true on covered query"); assert.eq(0, plan.executionStats.totalDocsExamined, @@ -67,7 +67,7 @@ assert.eq(0, var plan = coll.find({a: 38, b: "strvar_12", c: {$in: [5, 8]}}, {b: 1, c: 1, _id: 0}) .hint({a: 1, b: -1, c: 1}) .explain("executionStats"); -assert(isIndexOnly(plan.queryPlanner.winningPlan), +assert(isIndexOnly(db, plan.queryPlanner.winningPlan), "compound.1.6 - indexOnly should be true on covered query"); assert.eq(0, plan.executionStats.totalDocsExamined, @@ -77,7 +77,7 @@ assert.eq(0, var plan = coll.find({a: 38, b: "strvar_12", c: 55}, {a: 1, b: 1, c: 1, _id: 0}) .hint({a: 1, b: -1, c: 1}) .explain("executionStats"); -assert(isIndexOnly(plan.queryPlanner.winningPlan), +assert(isIndexOnly(db, plan.queryPlanner.winningPlan), "compound.1.7 - indexOnly should be true on covered query"); assert.eq(0, plan.executionStats.totalDocsExamined, diff --git a/jstests/core/covered_index_negative_1.js b/jstests/core/covered_index_negative_1.js index 37a9b4dc3bb..2c684e0e7bb 100644 --- a/jstests/core/covered_index_negative_1.js +++ b/jstests/core/covered_index_negative_1.js @@ -20,7 +20,7 @@ coll.ensureIndex({f: "hashed"}); // Test no projection var plan = coll.find({a: 10, b: "strvar_10", c: 0}).hint({a: 1, b: -1, c: 1}).explain("executionStats"); -assert(!isIndexOnly(plan.queryPlanner.winningPlan), +assert(!isIndexOnly(db, plan.queryPlanner.winningPlan), "negative.1.1 - indexOnly should be false on a non covered query"); assert.neq(0, plan.executionStats.totalDocsExamined, @@ -30,7 +30,7 @@ assert.neq(0, var plan = coll.find({a: 10, b: "strvar_10", c: 0}, {a: 1, b: 1, c: 1}) .hint({a: 1, b: -1, c: 1}) .explain("executionStats"); -assert(!isIndexOnly(plan.queryPlanner.winningPlan), +assert(!isIndexOnly(db, plan.queryPlanner.winningPlan), "negative.1.2 - indexOnly should be false on a non covered query"); assert.neq(0, plan.executionStats.totalDocsExamined, @@ -38,7 +38,7 @@ assert.neq(0, // Test projection of non-indexed field var plan = coll.find({d: 100}, {d: 1, c: 1, _id: 0}).hint({d: 1}).explain("executionStats"); -assert(!isIndexOnly(plan.queryPlanner.winningPlan), +assert(!isIndexOnly(db, plan.queryPlanner.winningPlan), "negative.1.3 - indexOnly should be false on a non covered query"); assert.neq(0, plan.executionStats.totalDocsExamined, @@ -46,7 +46,7 @@ assert.neq(0, // Test query and projection on a multi-key index var plan = coll.find({e: 99}, {e: 1, _id: 0}).hint({e: 1}).explain("executionStats"); -assert(!isIndexOnly(plan.queryPlanner.winningPlan), +assert(!isIndexOnly(db, plan.queryPlanner.winningPlan), "negative.1.4 - indexOnly should be false on a non covered query"); assert.neq(0, plan.executionStats.totalDocsExamined, @@ -74,7 +74,7 @@ assert.neq(0, var plan = coll.find({d: {$lt: 1000}}, {a: 1, b: 1, c: 1, _id: 0}) .hint({a: 1, b: -1, c: 1}) .explain("executionStats"); -assert(!isIndexOnly(plan.queryPlanner.winningPlan), +assert(!isIndexOnly(db, plan.queryPlanner.winningPlan), "negative.1.7 - indexOnly should be false on a non covered query"); assert.neq(0, plan.executionStats.totalDocsExamined, @@ -82,7 +82,7 @@ assert.neq(0, // Test query on hashed indexed field var plan = coll.find({f: 10}, {f: 1, _id: 0}).hint({f: "hashed"}).explain("executionStats"); -assert(!isIndexOnly(plan.queryPlanner.winningPlan), +assert(!isIndexOnly(db, plan.queryPlanner.winningPlan), "negative.1.8 - indexOnly should be false on a non covered query"); assert.neq(0, plan.executionStats.totalDocsExamined, diff --git a/jstests/core/covered_index_simple_1.js b/jstests/core/covered_index_simple_1.js index b9e33f650dd..8e9bc782e1e 100644 --- a/jstests/core/covered_index_simple_1.js +++ b/jstests/core/covered_index_simple_1.js @@ -26,7 +26,7 @@ coll.ensureIndex({foo: 1}); // Test equality with int value var plan = coll.find({foo: 1}, {foo: 1, _id: 0}).hint({foo: 1}).explain("executionStats"); -assert(isIndexOnly(plan.queryPlanner.winningPlan), +assert(isIndexOnly(db, plan.queryPlanner.winningPlan), "simple.1.1 - indexOnly should be true on covered query"); assert.eq(0, plan.executionStats.totalDocsExamined, @@ -34,7 +34,7 @@ assert.eq(0, // Test equality with string value var plan = coll.find({foo: "string"}, {foo: 1, _id: 0}).hint({foo: 1}).explain("executionStats"); -assert(isIndexOnly(plan.queryPlanner.winningPlan), +assert(isIndexOnly(db, plan.queryPlanner.winningPlan), "simple.1.2 - indexOnly should be true on covered query"); assert.eq(0, plan.executionStats.totalDocsExamined, @@ -42,7 +42,7 @@ assert.eq(0, // Test equality with doc value var plan = coll.find({foo: {bar: 1}}, {foo: 1, _id: 0}).hint({foo: 1}).explain("executionStats"); -assert(isIndexOnly(plan.queryPlanner.winningPlan), +assert(isIndexOnly(db, plan.queryPlanner.winningPlan), "simple.1.3 - indexOnly should be true on covered query"); assert.eq(0, plan.executionStats.totalDocsExamined, @@ -50,7 +50,7 @@ assert.eq(0, // Test no query var plan = coll.find({}, {foo: 1, _id: 0}).hint({foo: 1}).explain("executionStats"); -assert(isIndexOnly(plan.queryPlanner.winningPlan), +assert(isIndexOnly(db, plan.queryPlanner.winningPlan), "simple.1.4 - indexOnly should be true on covered query"); assert.eq(0, plan.executionStats.totalDocsExamined, @@ -59,7 +59,7 @@ assert.eq(0, // Test range query var plan = coll.find({foo: {$gt: 2, $lt: 6}}, {foo: 1, _id: 0}).hint({foo: 1}).explain("executionStats"); -assert(isIndexOnly(plan.queryPlanner.winningPlan), +assert(isIndexOnly(db, plan.queryPlanner.winningPlan), "simple.1.5 - indexOnly should be true on covered query"); assert.eq(0, plan.executionStats.totalDocsExamined, @@ -68,7 +68,7 @@ assert.eq(0, // Test in query var plan = coll.find({foo: {$in: [5, 8]}}, {foo: 1, _id: 0}).hint({foo: 1}).explain("executionStats"); -assert(isIndexOnly(plan.queryPlanner.winningPlan), +assert(isIndexOnly(db, plan.queryPlanner.winningPlan), "simple.1.6 - indexOnly should be true on covered query"); assert.eq(0, plan.executionStats.totalDocsExamined, @@ -76,7 +76,7 @@ assert.eq(0, // Test no return var plan = coll.find({foo: "2"}, {foo: 1, _id: 0}).hint({foo: 1}).explain("executionStats"); -assert(isIndexOnly(plan.queryPlanner.winningPlan), +assert(isIndexOnly(db, plan.queryPlanner.winningPlan), "simple.1.7 - indexOnly should be true on covered query"); assert.eq(0, plan.executionStats.totalDocsExamined, diff --git a/jstests/core/covered_index_simple_2.js b/jstests/core/covered_index_simple_2.js index 0974ee0eb50..71da45922db 100644 --- a/jstests/core/covered_index_simple_2.js +++ b/jstests/core/covered_index_simple_2.js @@ -20,7 +20,7 @@ coll.ensureIndex({foo: 1}, {unique: true}); // Test equality with int value var plan = coll.find({foo: 1}, {foo: 1, _id: 0}).hint({foo: 1}).explain("executionStats"); -assert(isIndexOnly(plan.queryPlanner.winningPlan), +assert(isIndexOnly(db, plan.queryPlanner.winningPlan), "simple.2.1 - indexOnly should be true on covered query"); assert.eq(0, plan.executionStats.totalDocsExamined, @@ -28,7 +28,7 @@ assert.eq(0, // Test equality with string value var plan = coll.find({foo: "string"}, {foo: 1, _id: 0}).hint({foo: 1}).explain("executionStats"); -assert(isIndexOnly(plan.queryPlanner.winningPlan), +assert(isIndexOnly(db, plan.queryPlanner.winningPlan), "simple.2.2 - indexOnly should be true on covered query"); assert.eq(0, plan.executionStats.totalDocsExamined, @@ -36,7 +36,7 @@ assert.eq(0, // Test equality with int value on a dotted field var plan = coll.find({foo: {bar: 1}}, {foo: 1, _id: 0}).hint({foo: 1}).explain("executionStats"); -assert(isIndexOnly(plan.queryPlanner.winningPlan), +assert(isIndexOnly(db, plan.queryPlanner.winningPlan), "simple.2.3 - indexOnly should be true on covered query"); assert.eq(0, plan.executionStats.totalDocsExamined, @@ -44,7 +44,7 @@ assert.eq(0, // Test no query var plan = coll.find({}, {foo: 1, _id: 0}).hint({foo: 1}).explain("executionStats"); -assert(isIndexOnly(plan.queryPlanner.winningPlan), +assert(isIndexOnly(db, plan.queryPlanner.winningPlan), "simple.2.4 - indexOnly should be true on covered query"); assert.eq(0, plan.executionStats.totalDocsExamined, @@ -53,7 +53,7 @@ assert.eq(0, // Test range query var plan = coll.find({foo: {$gt: 2, $lt: 6}}, {foo: 1, _id: 0}).hint({foo: 1}).explain("executionStats"); -assert(isIndexOnly(plan.queryPlanner.winningPlan), +assert(isIndexOnly(db, plan.queryPlanner.winningPlan), "simple.2.5 - indexOnly should be true on covered query"); assert.eq(0, plan.executionStats.totalDocsExamined, @@ -62,7 +62,7 @@ assert.eq(0, // Test in query var plan = coll.find({foo: {$in: [5, 8]}}, {foo: 1, _id: 0}).hint({foo: 1}).explain("executionStats"); -assert(isIndexOnly(plan.queryPlanner.winningPlan), +assert(isIndexOnly(db, plan.queryPlanner.winningPlan), "simple.2.6 - indexOnly should be true on covered query"); assert.eq(0, plan.executionStats.totalDocsExamined, diff --git a/jstests/core/covered_index_simple_3.js b/jstests/core/covered_index_simple_3.js index 7d021a17ca8..b53ce20d85f 100644 --- a/jstests/core/covered_index_simple_3.js +++ b/jstests/core/covered_index_simple_3.js @@ -23,7 +23,7 @@ coll.ensureIndex({foo: 1}, {sparse: true, unique: true}); // Test equality with int value var plan = coll.find({foo: 1}, {foo: 1, _id: 0}).hint({foo: 1}).explain("executionStats"); -assert(isIndexOnly(plan.queryPlanner.winningPlan), +assert(isIndexOnly(db, plan.queryPlanner.winningPlan), "simple.3.1 - indexOnly should be true on covered query"); assert.eq(0, plan.executionStats.totalDocsExamined, @@ -31,7 +31,7 @@ assert.eq(0, // Test equality with string value var plan = coll.find({foo: "string"}, {foo: 1, _id: 0}).hint({foo: 1}).explain("executionStats"); -assert(isIndexOnly(plan.queryPlanner.winningPlan), +assert(isIndexOnly(db, plan.queryPlanner.winningPlan), "simple.3.2 - indexOnly should be true on covered query"); assert.eq(0, plan.executionStats.totalDocsExamined, @@ -39,7 +39,7 @@ assert.eq(0, // Test equality with int value on a dotted field var plan = coll.find({foo: {bar: 1}}, {foo: 1, _id: 0}).hint({foo: 1}).explain("executionStats"); -assert(isIndexOnly(plan.queryPlanner.winningPlan), +assert(isIndexOnly(db, plan.queryPlanner.winningPlan), "simple.3.3 - indexOnly should be true on covered query"); assert.eq(0, plan.executionStats.totalDocsExamined, @@ -47,7 +47,7 @@ assert.eq(0, // Test no query var plan = coll.find({}, {foo: 1, _id: 0}).hint({foo: 1}).explain("executionStats"); -assert(isIndexOnly(plan.queryPlanner.winningPlan), +assert(isIndexOnly(db, plan.queryPlanner.winningPlan), "simple.3.4 - indexOnly should be true on covered query"); assert.eq(0, plan.executionStats.totalDocsExamined, @@ -56,7 +56,7 @@ assert.eq(0, // Test range query var plan = coll.find({foo: {$gt: 2, $lt: 6}}, {foo: 1, _id: 0}).hint({foo: 1}).explain("executionStats"); -assert(isIndexOnly(plan.queryPlanner.winningPlan), +assert(isIndexOnly(db, plan.queryPlanner.winningPlan), "simple.3.5 - indexOnly should be true on covered query"); assert.eq(0, plan.executionStats.totalDocsExamined, @@ -65,7 +65,7 @@ assert.eq(0, // Test in query var plan = coll.find({foo: {$in: [5, 8]}}, {foo: 1, _id: 0}).hint({foo: 1}).explain("executionStats"); -assert(isIndexOnly(plan.queryPlanner.winningPlan), +assert(isIndexOnly(db, plan.queryPlanner.winningPlan), "simple.3.6 - indexOnly should be true on covered query"); assert.eq(0, plan.executionStats.totalDocsExamined, @@ -74,7 +74,7 @@ assert.eq(0, // Test $exists true var plan = coll.find({foo: {$exists: true}}, {foo: 1, _id: 0}).hint({foo: 1}).explain("executionStats"); -assert(isIndexOnly(plan.queryPlanner.winningPlan), +assert(isIndexOnly(db, plan.queryPlanner.winningPlan), "simple.3.7 - indexOnly should be true on covered query"); assert.eq(0, plan.executionStats.totalDocsExamined, @@ -85,7 +85,7 @@ coll.dropIndexes(); coll.ensureIndex({bar: 1}); var plan = coll.find({bar: {$nin: [5, 8]}}, {bar: 1, _id: 0}).hint({bar: 1}).explain("executionStats"); -assert(isIndexOnly(plan.queryPlanner.winningPlan), +assert(isIndexOnly(db, plan.queryPlanner.winningPlan), "simple.3.8 - indexOnly should be true on covered query"); assert.eq(0, plan.executionStats.totalDocsExamined, diff --git a/jstests/core/covered_index_simple_id.js b/jstests/core/covered_index_simple_id.js index c2550544abd..e7c25cc687a 100644 --- a/jstests/core/covered_index_simple_id.js +++ b/jstests/core/covered_index_simple_id.js @@ -14,7 +14,7 @@ coll.insert({_id: null}); // Test equality with int value var plan = coll.find({_id: 1}, {_id: 1}).hint({_id: 1}).explain("executionStats"); -assert(isIndexOnly(plan.queryPlanner.winningPlan), +assert(isIndexOnly(db, plan.queryPlanner.winningPlan), "simple.id.1 - indexOnly should be true on covered query"); assert.eq(0, plan.executionStats.totalDocsExamined, @@ -22,7 +22,7 @@ assert.eq(0, // Test equality with string value var plan = coll.find({_id: "string"}, {_id: 1}).hint({_id: 1}).explain("executionStats"); -assert(isIndexOnly(plan.queryPlanner.winningPlan), +assert(isIndexOnly(db, plan.queryPlanner.winningPlan), "simple.id.2 - indexOnly should be true on covered query"); assert.eq(0, plan.executionStats.totalDocsExamined, @@ -30,7 +30,7 @@ assert.eq(0, // Test equality with int value on a dotted field var plan = coll.find({_id: {bar: 1}}, {_id: 1}).hint({_id: 1}).explain("executionStats"); -assert(isIndexOnly(plan.queryPlanner.winningPlan), +assert(isIndexOnly(db, plan.queryPlanner.winningPlan), "simple.id.3 - indexOnly should be true on covered query"); assert.eq(0, plan.executionStats.totalDocsExamined, @@ -38,7 +38,7 @@ assert.eq(0, // Test no query var plan = coll.find({}, {_id: 1}).hint({_id: 1}).explain("executionStats"); -assert(isIndexOnly(plan.queryPlanner.winningPlan), +assert(isIndexOnly(db, plan.queryPlanner.winningPlan), "simple.id.4 - indexOnly should be true on covered query"); assert.eq(0, plan.executionStats.totalDocsExamined, @@ -46,7 +46,7 @@ assert.eq(0, // Test range query var plan = coll.find({_id: {$gt: 2, $lt: 6}}, {_id: 1}).hint({_id: 1}).explain("executionStats"); -assert(isIndexOnly(plan.queryPlanner.winningPlan), +assert(isIndexOnly(db, plan.queryPlanner.winningPlan), "simple.id.5 - indexOnly should be true on covered query"); assert.eq(0, plan.executionStats.totalDocsExamined, @@ -54,7 +54,7 @@ assert.eq(0, // Test in query var plan = coll.find({_id: {$in: [5, 8]}}, {_id: 1}).hint({_id: 1}).explain("executionStats"); -assert(isIndexOnly(plan.queryPlanner.winningPlan), +assert(isIndexOnly(db, plan.queryPlanner.winningPlan), "simple.id.6 - indexOnly should be true on covered query"); assert.eq(0, plan.executionStats.totalDocsExamined, diff --git a/jstests/core/covered_index_sort_1.js b/jstests/core/covered_index_sort_1.js index a65cc415ea4..1a7ac940efa 100644 --- a/jstests/core/covered_index_sort_1.js +++ b/jstests/core/covered_index_sort_1.js @@ -26,7 +26,7 @@ coll.ensureIndex({foo: 1}); // Test no query and sort ascending var plan = coll.find({}, {foo: 1, _id: 0}).sort({foo: 1}).hint({foo: 1}).explain("executionStats"); -assert(isIndexOnly(plan.queryPlanner.winningPlan), +assert(isIndexOnly(db, plan.queryPlanner.winningPlan), "sort.1.1 - indexOnly should be true on covered query"); assert.eq(0, plan.executionStats.totalDocsExamined, @@ -34,7 +34,7 @@ assert.eq(0, // Test no query and sort descending var plan = coll.find({}, {foo: 1, _id: 0}).sort({foo: -1}).hint({foo: 1}).explain("executionStats"); -assert(isIndexOnly(plan.queryPlanner.winningPlan), +assert(isIndexOnly(db, plan.queryPlanner.winningPlan), "sort.1.2 - indexOnly should be true on covered query"); assert.eq(0, plan.executionStats.totalDocsExamined, @@ -45,7 +45,7 @@ var plan = coll.find({foo: {$gt: 2}}, {foo: 1, _id: 0}) .sort({foo: -1}) .hint({foo: 1}) .explain("executionStats"); -assert(isIndexOnly(plan.queryPlanner.winningPlan), +assert(isIndexOnly(db, plan.queryPlanner.winningPlan), "sort.1.3 - indexOnly should be true on covered query"); assert.eq(0, plan.executionStats.totalDocsExamined, diff --git a/jstests/core/covered_index_sort_2.js b/jstests/core/covered_index_sort_2.js index 5ed3dc869c9..7f22b9c2867 100644 --- a/jstests/core/covered_index_sort_2.js +++ b/jstests/core/covered_index_sort_2.js @@ -14,7 +14,7 @@ coll.insert({_id: null}); // Test no query var plan = coll.find({}, {_id: 1}).sort({_id: -1}).hint({_id: 1}).explain("executionStats"); -assert(isIndexOnly(plan.queryPlanner.winningPlan), +assert(isIndexOnly(db, plan.queryPlanner.winningPlan), "sort.2.1 - indexOnly should be true on covered query"); assert.eq(0, plan.executionStats.totalDocsExamined, diff --git a/jstests/core/covered_index_sort_3.js b/jstests/core/covered_index_sort_3.js index aeeb6f70773..6a62bafacd2 100644 --- a/jstests/core/covered_index_sort_3.js +++ b/jstests/core/covered_index_sort_3.js @@ -21,7 +21,7 @@ var plan = coll.find({}, {b: 1, c: 1, _id: 0}) .sort({a: 1, b: -1, c: 1}) .hint({a: 1, b: -1, c: 1}) .explain("executionStats"); -assert(isIndexOnly(plan.queryPlanner.winningPlan), +assert(isIndexOnly(db, plan.queryPlanner.winningPlan), "sort.3.1 - indexOnly should be true on covered query"); assert.eq(0, plan.executionStats.totalDocsExamined, diff --git a/jstests/core/covered_multikey.js b/jstests/core/covered_multikey.js index db1aaf9c525..9607b48841d 100644 --- a/jstests/core/covered_multikey.js +++ b/jstests/core/covered_multikey.js @@ -25,11 +25,11 @@ assert.eq(1, coll.find({a: 1, b: 2}, {_id: 0, a: 1}).itcount()); assert.eq({a: 1}, coll.findOne({a: 1, b: 2}, {_id: 0, a: 1})); let explainRes = coll.explain("queryPlanner").find({a: 1, b: 2}, {_id: 0, a: 1}).finish(); - assert(isIxscan(explainRes.queryPlanner.winningPlan)); + assert(isIxscan(db, explainRes.queryPlanner.winningPlan)); if (isMMAPv1) { - assert(planHasStage(explainRes.queryPlanner.winningPlan, "FETCH")); + assert(planHasStage(db, explainRes.queryPlanner.winningPlan, "FETCH")); } else { - assert(!planHasStage(explainRes.queryPlanner.winningPlan, "FETCH")); + assert(!planHasStage(db, explainRes.queryPlanner.winningPlan, "FETCH")); } coll.drop(); @@ -46,9 +46,9 @@ .sort({c: -1, d: -1}) .finish(); if (isMMAPv1) { - assert(planHasStage(explainRes.queryPlanner.winningPlan, "FETCH")); + assert(planHasStage(db, explainRes.queryPlanner.winningPlan, "FETCH")); } else { - assert(!planHasStage(explainRes.queryPlanner.winningPlan, "FETCH")); + assert(!planHasStage(db, explainRes.queryPlanner.winningPlan, "FETCH")); } // Verify that a query cannot be covered over a path which is multikey due to an empty array. @@ -57,8 +57,8 @@ assert.commandWorked(coll.createIndex({a: 1})); assert.eq({a: []}, coll.findOne({a: []}, {_id: 0, a: 1})); explainRes = coll.explain("queryPlanner").find({a: []}, {_id: 0, a: 1}).finish(); - assert(planHasStage(explainRes.queryPlanner.winningPlan, "IXSCAN")); - assert(planHasStage(explainRes.queryPlanner.winningPlan, "FETCH")); + assert(planHasStage(db, explainRes.queryPlanner.winningPlan, "IXSCAN")); + assert(planHasStage(db, explainRes.queryPlanner.winningPlan, "FETCH")); let ixscanStage = getPlanStage(explainRes.queryPlanner.winningPlan, "IXSCAN"); assert.eq(true, ixscanStage.isMultiKey); @@ -69,8 +69,8 @@ assert.commandWorked(coll.createIndex({a: 1})); assert.eq({a: [2]}, coll.findOne({a: 2}, {_id: 0, a: 1})); explainRes = coll.explain("queryPlanner").find({a: 2}, {_id: 0, a: 1}).finish(); - assert(planHasStage(explainRes.queryPlanner.winningPlan, "IXSCAN")); - assert(planHasStage(explainRes.queryPlanner.winningPlan, "FETCH")); + assert(planHasStage(db, explainRes.queryPlanner.winningPlan, "IXSCAN")); + assert(planHasStage(db, explainRes.queryPlanner.winningPlan, "FETCH")); ixscanStage = getPlanStage(explainRes.queryPlanner.winningPlan, "IXSCAN"); assert.eq(true, ixscanStage.isMultiKey); @@ -82,8 +82,8 @@ assert.writeOK(coll.update({}, {$set: {a: [2]}})); assert.eq({a: [2]}, coll.findOne({a: 2}, {_id: 0, a: 1})); explainRes = coll.explain("queryPlanner").find({a: 2}, {_id: 0, a: 1}).finish(); - assert(planHasStage(explainRes.queryPlanner.winningPlan, "IXSCAN")); - assert(planHasStage(explainRes.queryPlanner.winningPlan, "FETCH")); + assert(planHasStage(db, explainRes.queryPlanner.winningPlan, "IXSCAN")); + assert(planHasStage(db, explainRes.queryPlanner.winningPlan, "FETCH")); ixscanStage = getPlanStage(explainRes.queryPlanner.winningPlan, "IXSCAN"); assert.eq(true, ixscanStage.isMultiKey); diff --git a/jstests/core/distinct_array1.js b/jstests/core/distinct_array1.js index 679395a064b..0ce6cf100f6 100644 --- a/jstests/core/distinct_array1.js +++ b/jstests/core/distinct_array1.js @@ -8,30 +8,28 @@ t.save({a: 9}); // Without index. res = t.distinct("a").sort(); -assert.eq("1,2,3,4,5,9", res.toString(), "A1"); +assert.eq("1,2,3,4,5,9", res.toString()); // Array element 0 without index. res = t.distinct("a.0").sort(); -assert.eq("1,2,3", res.toString(), "A2"); +assert.eq("1,2,3", res.toString()); // Array element 1 without index. res = t.distinct("a.1").sort(); -assert.eq("2,3,4", res.toString(), "A3"); +assert.eq("2,3,4", res.toString()); // With index. t.ensureIndex({a: 1}); res = t.distinct("a").sort(); -assert.eq("1,2,3,4,5,9", res.toString(), "A4"); +assert.eq("1,2,3,4,5,9", res.toString()); // Array element 0 with index. res = t.distinct("a.0").sort(); -assert.eq("1,2,3", res.toString(), "A5"); +assert.eq("1,2,3", res.toString()); // Array element 1 with index. res = t.distinct("a.1").sort(); -assert.eq("2,3,4", res.toString(), "A6"); - -// t.drop(); +assert.eq("2,3,4", res.toString()); t.save({a: [{b: "a"}, {b: "d"}], c: 12}); t.save({a: [{b: "b"}, {b: "d"}], c: 12}); @@ -42,21 +40,31 @@ t.save({a: {b: "z"}, c: 12}); // Without index. res = t.distinct("a.b").sort(); -assert.eq("a,b,c,d,e,f,z", res.toString(), "B1"); +assert.eq("a,b,c,d,e,f,z", res.toString()); // Array element 0 without index res = t.distinct("a.0.b").sort(); -assert.eq("a,b,c", res.toString(), "B2"); +assert.eq("a,b,c", res.toString()); // Array element 1 without index res = t.distinct("a.1.b").sort(); -assert.eq("d,e,f", res.toString(), "B3"); +assert.eq("d,e,f", res.toString()); // With index. t.ensureIndex({"a.b": 1}); res = t.distinct("a.b"); res.sort(); -assert.eq("a,b,c,d,e,f,z", res.toString(), "B4"); +// TODO SERVER-14832 The presence of an index may change results, but only if the index is not +// multikey. +// In a sharded scenario, an unlucky distribution of data will cause all the arrays to go to one +// shard, and one shard be left with only non-multikey documents, including one with {a: 9}, which +// will generate a null result. +if (res.includes(null)) { + // The default sorting of an array is by string, so null will appear in an odd position. + assert.eq(res, ["a", "b", "c", "d", "e", "f", null, "z"]); +} else { + assert.eq("a,b,c,d,e,f,z", res.toString()); +} // _id as an document containing an array t.save({_id: {a: [1, 2, 3]}}); @@ -66,25 +74,25 @@ t.save({_id: {a: 9}}); // Without index. res = t.distinct("_id.a").sort(); -assert.eq("1,2,3,4,5,9", res.toString(), "C1"); +assert.eq("1,2,3,4,5,9", res.toString()); // Array element 0 without index. res = t.distinct("_id.a.0").sort(); -assert.eq("1,2,3", res.toString(), "C2"); +assert.eq("1,2,3", res.toString()); // Array element 1 without index. res = t.distinct("_id.a.1").sort(); -assert.eq("2,3,4", res.toString(), "C3"); +assert.eq("2,3,4", res.toString()); // With index. t.ensureIndex({"_id.a": 1}); res = t.distinct("_id.a").sort(); -assert.eq("1,2,3,4,5,9", res.toString(), "C4"); +assert.eq("1,2,3,4,5,9", res.toString()); // Array element 0 with index. res = t.distinct("_id.a.0").sort(); -assert.eq("1,2,3", res.toString(), "C5"); +assert.eq("1,2,3", res.toString()); // Array element 1 with index. res = t.distinct("_id.a.1").sort(); -assert.eq("2,3,4", res.toString(), "C6"); +assert.eq("2,3,4", res.toString()); diff --git a/jstests/core/distinct_compound_index.js b/jstests/core/distinct_compound_index.js index 9b7c0226265..12dffab8bde 100644 --- a/jstests/core/distinct_compound_index.js +++ b/jstests/core/distinct_compound_index.js @@ -1,7 +1,8 @@ (function() { "use strict"; - load("jstests/libs/analyze_plan.js"); + load("jstests/libs/fixture_helpers.js"); // For FixtureHelpers. + load("jstests/libs/analyze_plan.js"); // For planHasStage. var coll = db.distinct_multikey_index; @@ -16,13 +17,17 @@ var explain_distinct_with_query = coll.explain("executionStats").distinct('b', {a: 1}); assert.commandWorked(explain_distinct_with_query); - assert(planHasStage(explain_distinct_with_query.queryPlanner.winningPlan, "DISTINCT_SCAN")); - assert(planHasStage(explain_distinct_with_query.queryPlanner.winningPlan, "PROJECTION")); - assert.eq(2, explain_distinct_with_query.executionStats.nReturned); + assert(planHasStage(db, explain_distinct_with_query.queryPlanner.winningPlan, "DISTINCT_SCAN")); + assert(planHasStage(db, explain_distinct_with_query.queryPlanner.winningPlan, "PROJECTION")); + // If the collection is sharded, we expect at most 2 distinct values per shard. If the + // collection is not sharded, we expect 2 returned. + assert.lte(2 * FixtureHelpers.numberOfShardsForCollection(coll), + explain_distinct_with_query.executionStats.nReturned); var explain_distinct_without_query = coll.explain("executionStats").distinct('b'); assert.commandWorked(explain_distinct_without_query); - assert(planHasStage(explain_distinct_without_query.queryPlanner.winningPlan, "COLLSCAN")); - assert(!planHasStage(explain_distinct_without_query.queryPlanner.winningPlan, "DISTINCT_SCAN")); + assert(planHasStage(db, explain_distinct_without_query.queryPlanner.winningPlan, "COLLSCAN")); + assert(!planHasStage( + db, explain_distinct_without_query.queryPlanner.winningPlan, "DISTINCT_SCAN")); assert.eq(40, explain_distinct_without_query.executionStats.nReturned); })(); diff --git a/jstests/core/distinct_index1.js b/jstests/core/distinct_index1.js index 63f61b8292c..34da35a2b60 100644 --- a/jstests/core/distinct_index1.js +++ b/jstests/core/distinct_index1.js @@ -20,42 +20,42 @@ for (i = 0; i < 1000; i++) { x = d("a"); // Collection scan looks at all 1000 documents and gets 1000 // distinct values. Looks at 0 index keys. -assert.eq(1000, x.executionStats.nReturned, "AA1"); -assert.eq(0, x.executionStats.totalKeysExamined, "AA2"); -assert.eq(1000, x.executionStats.totalDocsExamined, "AA3"); +assert.eq(1000, x.executionStats.nReturned); +assert.eq(0, x.executionStats.totalKeysExamined); +assert.eq(1000, x.executionStats.totalDocsExamined); x = d("a", {a: {$gt: 5}}); // Collection scan looks at all 1000 documents and gets 398 // distinct values which match the query. Looks at 0 index keys. -assert.eq(398, x.executionStats.nReturned, "AB1"); -assert.eq(0, x.executionStats.totalKeysExamined, "AB2"); -assert.eq(1000, x.executionStats.totalDocsExamined, "AB3"); +assert.eq(398, x.executionStats.nReturned); +assert.eq(0, x.executionStats.totalKeysExamined); +assert.eq(1000, x.executionStats.totalDocsExamined); x = d("b", {a: {$gt: 5}}); // Collection scan looks at all 1000 documents and gets 398 // distinct values which match the query. Looks at 0 index keys. -assert.eq(398, x.executionStats.nReturned, "AC1"); -assert.eq(0, x.executionStats.totalKeysExamined, "AC2"); -assert.eq(1000, x.executionStats.totalDocsExamined, "AC3"); +assert.eq(398, x.executionStats.nReturned); +assert.eq(0, x.executionStats.totalKeysExamined); +assert.eq(1000, x.executionStats.totalDocsExamined); t.ensureIndex({a: 1}); x = d("a"); // There are only 10 values. We use the fast distinct hack and only examine each value once. -assert.eq(10, x.executionStats.nReturned, "BA1"); -assert.eq(10, x.executionStats.totalKeysExamined, "BA2"); +assert.eq(10, x.executionStats.nReturned); +assert.lte(10, x.executionStats.totalKeysExamined); x = d("a", {a: {$gt: 5}}); // Only 4 values of a are >= 5 and we use the fast distinct hack. -assert.eq(4, x.executionStats.nReturned, "BB1"); -assert.eq(4, x.executionStats.totalKeysExamined, "BB2"); -assert.eq(0, x.executionStats.totalDocsExamined, "BB3"); +assert.eq(4, x.executionStats.nReturned); +assert.eq(4, x.executionStats.totalKeysExamined); +assert.eq(0, x.executionStats.totalDocsExamined); x = d("b", {a: {$gt: 5}}); // We can't use the fast distinct hack here because we're distinct-ing over 'b'. -assert.eq(398, x.executionStats.nReturned, "BC1"); -assert.eq(398, x.executionStats.totalKeysExamined, "BC2"); -assert.eq(398, x.executionStats.totalDocsExamined, "BC3"); +assert.eq(398, x.executionStats.nReturned); +assert.eq(398, x.executionStats.totalKeysExamined); +assert.eq(398, x.executionStats.totalDocsExamined); // Test that a distinct over a trailing field of the index can be covered. t.dropIndexes(); @@ -63,12 +63,12 @@ t.ensureIndex({a: 1, b: 1}); x = d("b", {a: {$gt: 5}, b: {$gt: 5}}); printjson(x); assert.lte(x.executionStats.nReturned, 171); -assert.eq(0, x.executionStats.totalDocsExamined, "BD3"); +assert.eq(0, x.executionStats.totalDocsExamined); // Should use an index scan over the hashed index. t.dropIndexes(); t.ensureIndex({a: "hashed"}); x = d("a", {$or: [{a: 3}, {a: 5}]}); -assert.eq(188, x.executionStats.nReturned, "DA1"); +assert.eq(188, x.executionStats.nReturned); var indexScanStage = getPlanStage(x.executionStats.executionStages, "IXSCAN"); assert.eq("hashed", indexScanStage.keyPattern.a); diff --git a/jstests/core/distinct_multikey.js b/jstests/core/distinct_multikey.js index ac9e69ff00f..e2250fbbe93 100644 --- a/jstests/core/distinct_multikey.js +++ b/jstests/core/distinct_multikey.js @@ -19,16 +19,16 @@ let result = coll.distinct("a"); assert.eq([1, 2, 3, 4, 5, 6, 7], result.sort()); let explain = coll.explain("queryPlanner").distinct("a"); - assert(planHasStage(explain.queryPlanner.winningPlan, "PROJECTION")); - assert(planHasStage(explain.queryPlanner.winningPlan, "DISTINCT_SCAN")); + assert(planHasStage(db, explain.queryPlanner.winningPlan, "PROJECTION")); + assert(planHasStage(db, explain.queryPlanner.winningPlan, "DISTINCT_SCAN")); // Test that distinct can correctly use a multikey index when there is a predicate. This query // should not be eligible for the distinct scan and cannot be covered. result = coll.distinct("a", {a: 3}); assert.eq([1, 2, 3, 4], result.sort()); explain = coll.explain("queryPlanner").distinct("a", {a: 3}); - assert(planHasStage(explain.queryPlanner.winningPlan, "FETCH")); - assert(planHasStage(explain.queryPlanner.winningPlan, "IXSCAN")); + assert(planHasStage(db, explain.queryPlanner.winningPlan, "FETCH")); + assert(planHasStage(db, explain.queryPlanner.winningPlan, "IXSCAN")); // Test distinct over a dotted multikey field, with a predicate. coll.drop(); @@ -39,8 +39,8 @@ result = coll.distinct("a.b", {"a.b": 3}); assert.eq([1, 2, 3, 4], result.sort()); explain = coll.explain("queryPlanner").distinct("a.b", {"a.b": 3}); - assert(planHasStage(explain.queryPlanner.winningPlan, "FETCH")); - assert(planHasStage(explain.queryPlanner.winningPlan, "IXSCAN")); + assert(planHasStage(db, explain.queryPlanner.winningPlan, "FETCH")); + assert(planHasStage(db, explain.queryPlanner.winningPlan, "IXSCAN")); // Test that the distinct scan can be used when there is a predicate and the index is not // multikey. @@ -53,8 +53,8 @@ result = coll.distinct("a", {a: {$gte: 2}}); assert.eq([2, 3], result.sort()); explain = coll.explain("queryPlanner").distinct("a", {a: {$gte: 2}}); - assert(planHasStage(explain.queryPlanner.winningPlan, "PROJECTION")); - assert(planHasStage(explain.queryPlanner.winningPlan, "DISTINCT_SCAN")); + assert(planHasStage(db, explain.queryPlanner.winningPlan, "PROJECTION")); + assert(planHasStage(db, explain.queryPlanner.winningPlan, "DISTINCT_SCAN")); // Test a distinct which can use a multikey index, where the field being distinct'ed is not // multikey. @@ -70,19 +70,19 @@ if (isMMAPv1) { // MMAPv1 does not support path-level multikey metadata tracking. It cannot use a distinct // scan since it does not know that the "a" field is not multikey. - assert(planHasStage(explain.queryPlanner.winningPlan, "FETCH")); - assert(planHasStage(explain.queryPlanner.winningPlan, "IXSCAN")); + assert(planHasStage(db, explain.queryPlanner.winningPlan, "FETCH")); + assert(planHasStage(db, explain.queryPlanner.winningPlan, "IXSCAN")); } else { - assert(planHasStage(explain.queryPlanner.winningPlan, "PROJECTION")); - assert(planHasStage(explain.queryPlanner.winningPlan, "DISTINCT_SCAN")); + assert(planHasStage(db, explain.queryPlanner.winningPlan, "PROJECTION")); + assert(planHasStage(db, explain.queryPlanner.winningPlan, "DISTINCT_SCAN")); } // Test distinct over a trailing multikey field. result = coll.distinct("b", {a: {$gte: 2}}); assert.eq([3, 4, 5], result.sort()); explain = coll.explain("queryPlanner").distinct("b", {a: {$gte: 2}}); - assert(planHasStage(explain.queryPlanner.winningPlan, "FETCH")); - assert(planHasStage(explain.queryPlanner.winningPlan, "IXSCAN")); + assert(planHasStage(db, explain.queryPlanner.winningPlan, "FETCH")); + assert(planHasStage(db, explain.queryPlanner.winningPlan, "IXSCAN")); // Test distinct over a trailing non-multikey field, where the leading field is multikey. coll.drop(); @@ -97,11 +97,11 @@ if (isMMAPv1) { // MMAPv1 does not support path-level multikey metadata tracking. It cannot use a distinct // scan since it does not know that the "a" field is not multikey. - assert(planHasStage(explain.queryPlanner.winningPlan, "FETCH")); - assert(planHasStage(explain.queryPlanner.winningPlan, "IXSCAN")); + assert(planHasStage(db, explain.queryPlanner.winningPlan, "FETCH")); + assert(planHasStage(db, explain.queryPlanner.winningPlan, "IXSCAN")); } else { - assert(planHasStage(explain.queryPlanner.winningPlan, "PROJECTION")); - assert(planHasStage(explain.queryPlanner.winningPlan, "DISTINCT_SCAN")); + assert(planHasStage(db, explain.queryPlanner.winningPlan, "PROJECTION")); + assert(planHasStage(db, explain.queryPlanner.winningPlan, "DISTINCT_SCAN")); } // Test distinct over a trailing non-multikey dotted path where the leading field is multikey. @@ -117,10 +117,10 @@ if (isMMAPv1) { // MMAPv1 does not support path-level multikey metadata tracking. It cannot use a distinct // scan since it does not know that the "a" field is not multikey. - assert(planHasStage(explain.queryPlanner.winningPlan, "FETCH")); - assert(planHasStage(explain.queryPlanner.winningPlan, "IXSCAN")); + assert(planHasStage(db, explain.queryPlanner.winningPlan, "FETCH")); + assert(planHasStage(db, explain.queryPlanner.winningPlan, "IXSCAN")); } else { - assert(planHasStage(explain.queryPlanner.winningPlan, "PROJECTION")); - assert(planHasStage(explain.queryPlanner.winningPlan, "DISTINCT_SCAN")); + assert(planHasStage(db, explain.queryPlanner.winningPlan, "PROJECTION")); + assert(planHasStage(db, explain.queryPlanner.winningPlan, "DISTINCT_SCAN")); } }()); diff --git a/jstests/core/explain2.js b/jstests/core/explain2.js index a0a65de4fe9..ec9a79b16c8 100644 --- a/jstests/core/explain2.js +++ b/jstests/core/explain2.js @@ -1,4 +1,7 @@ // Test calculation of the 'millis' field in explain output. +// This test makes assertions on how long a particular query will sleep. When the collection is +// sharded each shard may sleep for less time than is expected. +// @tags: [assumes_unsharded_collection] t = db.jstests_explain2; t.drop(); diff --git a/jstests/core/explain_count.js b/jstests/core/explain_count.js index 0351db29f9c..ee7cb31de94 100644 --- a/jstests/core/explain_count.js +++ b/jstests/core/explain_count.js @@ -15,13 +15,17 @@ function checkCountExplain(explain, nCounted) { printjson(explain); var execStages = explain.executionStats.executionStages; - // If passed through mongos, then the root stage should be the mongos SINGLE_SHARD stage, - // with COUNT as its child. If explaining directly on the shard, then COUNT is the root - // stage. - if ("SINGLE_SHARD" == execStages.stage) { - var countStage = execStages.shards[0].executionStages; - assert.eq(countStage.stage, "COUNT", "root stage on shard is not COUNT"); - assert.eq(countStage.nCounted, nCounted, "wrong count result"); + // If passed through mongos, then the root stage should be the mongos SINGLE_SHARD stage or + // SHARD_MERGE stages, with COUNT as the root stage on each shard. If explaining directly on the + // shard, then COUNT is the root stage. + if ("SINGLE_SHARD" == execStages.stage || "SHARD_MERGE" == execStages.stage) { + let totalCounted = 0; + for (let shardExplain of execStages.shards) { + const countStage = shardExplain.executionStages; + assert.eq(countStage.stage, "COUNT", "root stage on shard is not COUNT"); + totalCounted += countStage.nCounted; + } + assert.eq(totalCounted, nCounted, "wrong count result"); } else { assert.eq(execStages.stage, "COUNT", "root stage is not COUNT"); assert.eq(execStages.nCounted, nCounted, "wrong count result"); diff --git a/jstests/core/explain_delete.js b/jstests/core/explain_delete.js index ce8fd59363b..d65a4b1f748 100644 --- a/jstests/core/explain_delete.js +++ b/jstests/core/explain_delete.js @@ -19,13 +19,17 @@ function checkNWouldDelete(explain, nWouldDelete) { var executionStats = explain.executionStats; assert("executionStages" in executionStats); - // If passed through mongos, then DELETE should be below the SINGLE_SHARD mongos stage. - // Otherwise it is the root stage. + // If passed through mongos, then DELETE stage(s) should be below the SHARD_WRITE mongos stage. + // Otherwise the DELETE stage is the root stage. var execStages = executionStats.executionStages; if ("SHARD_WRITE" === execStages.stage) { - var deleteStage = execStages.shards[0].executionStages; - assert.eq(deleteStage.stage, "DELETE"); - assert.eq(deleteStage.nWouldDelete, nWouldDelete); + let totalToBeDeletedAcrossAllShards = 0; + execStages.shards.forEach(function(shardExplain) { + const rootStageName = shardExplain.executionStages.stage; + assert.eq(rootStageName, "DELETE", tojson(execStages)); + totalToBeDeletedAcrossAllShards += shardExplain.executionStages.nWouldDelete; + }); + assert.eq(totalToBeDeletedAcrossAllShards, nWouldDelete); } else { assert.eq(execStages.stage, "DELETE"); assert.eq(execStages.nWouldDelete, nWouldDelete); diff --git a/jstests/core/explain_distinct.js b/jstests/core/explain_distinct.js index 4cc9eab4eab..5f9fda44d18 100644 --- a/jstests/core/explain_distinct.js +++ b/jstests/core/explain_distinct.js @@ -28,7 +28,7 @@ // Collection doesn't exist. var explain = runDistinctExplain(coll, 'a', {}); assert.commandWorked(explain); - assert(planHasStage(explain.queryPlanner.winningPlan, "EOF")); + assert(planHasStage(db, explain.queryPlanner.winningPlan, "EOF")); // Insert the data to perform distinct() on. for (var i = 0; i < 10; i++) { @@ -50,7 +50,7 @@ var explain = runDistinctExplain(coll, 'b', {}); assert.commandWorked(explain); assert.eq(20, explain.executionStats.nReturned); - assert(isCollscan(explain.queryPlanner.winningPlan)); + assert(isCollscan(db, explain.queryPlanner.winningPlan)); assert.commandWorked(coll.createIndex({a: 1})); @@ -58,8 +58,8 @@ var explain = runDistinctExplain(coll, 'a', {}); assert.commandWorked(explain); assert.eq(2, explain.executionStats.nReturned); - assert(planHasStage(explain.queryPlanner.winningPlan, "PROJECTION")); - assert(planHasStage(explain.queryPlanner.winningPlan, "DISTINCT_SCAN")); + assert(planHasStage(db, explain.queryPlanner.winningPlan, "PROJECTION")); + assert(planHasStage(db, explain.queryPlanner.winningPlan, "DISTINCT_SCAN")); // Check that the DISTINCT_SCAN stage has the correct stats. var stage = getPlanStage(explain.queryPlanner.winningPlan, "DISTINCT_SCAN"); @@ -78,14 +78,14 @@ var explain = runDistinctExplain(coll, 'a', {a: 1}); assert.commandWorked(explain); assert.eq(1, explain.executionStats.nReturned); - assert(planHasStage(explain.queryPlanner.winningPlan, "PROJECTION")); - assert(planHasStage(explain.queryPlanner.winningPlan, "DISTINCT_SCAN")); + assert(planHasStage(db, explain.queryPlanner.winningPlan, "PROJECTION")); + assert(planHasStage(db, explain.queryPlanner.winningPlan, "DISTINCT_SCAN")); assert.eq([1], coll.distinct('b', {a: 1})); var explain = runDistinctExplain(coll, 'b', {a: 1}); assert.commandWorked(explain); assert.eq(1, explain.executionStats.nReturned); - assert(!planHasStage(explain.queryPlanner.winningPlan, "FETCH")); - assert(planHasStage(explain.queryPlanner.winningPlan, "PROJECTION")); - assert(planHasStage(explain.queryPlanner.winningPlan, "DISTINCT_SCAN")); + assert(!planHasStage(db, explain.queryPlanner.winningPlan, "FETCH")); + assert(planHasStage(db, explain.queryPlanner.winningPlan, "PROJECTION")); + assert(planHasStage(db, explain.queryPlanner.winningPlan, "DISTINCT_SCAN")); })(); diff --git a/jstests/core/explain_execution_error.js b/jstests/core/explain_execution_error.js index 98de41289df..ff999f48bb9 100644 --- a/jstests/core/explain_execution_error.js +++ b/jstests/core/explain_execution_error.js @@ -3,6 +3,8 @@ // Test that even when the execution of a query fails, explain reports query // planner information. +load("jstests/libs/fixture_helpers.js"); // For FixtureHelpers. + var t = db.explain_execution_error; t.drop(); @@ -12,36 +14,53 @@ var result; * Asserts that explain reports an error in its execution stats section. */ function assertExecError(explain) { - var errorObj; - - var execStats = explain.executionStats; - if (execStats.executionStages.stage == "SINGLE_SHARD") { - errorObj = execStats.executionStages.shards[0]; + // Gather the exec stats from all shards. + let allExecStats = []; + let topLevelExecStats = explain.executionStats; + if (topLevelExecStats.executionStages.stage == "SINGLE_SHARD" || + topLevelExecStats.executionStages.stage == "SHARD_MERGE_SORT") { + allExecStats = topLevelExecStats.executionStages.shards; } else { - errorObj = execStats; + allExecStats.push(topLevelExecStats); } - assert.eq(false, errorObj.executionSuccess); - assert("errorMessage" in errorObj); - assert("errorCode" in errorObj); + // In a sharded environment, we only know that at least one of the shards will fail, we can't + // expect all of them to fail, since there may be different amounts of data on each shard. + let haveSeenExecutionFailure = false; + for (let execStats of allExecStats) { + if (!execStats.executionSuccess) { + haveSeenExecutionFailure = true; + assert("errorMessage" in execStats, + `Expected "errorMessage" to be present in ${tojson(execStats)}`); + assert("errorCode" in execStats, + `Expected "errorCode" to be present in ${tojson(execStats)}`); + } + } + assert(haveSeenExecutionFailure, + `Expected at least one shard to have failed: ${tojson(explain)}`); } /** * Asserts that explain reports success in its execution stats section. */ function assertExecSuccess(explain) { - var errorObj; + let errorObjs = []; - var execStats = explain.executionStats; - if (execStats.executionStages.stage == "SINGLE_SHARD") { - errorObj = execStats.executionStages.shards[0]; + let execStats = explain.executionStats; + if (execStats.executionStages.stage == "SINGLE_SHARD" || + execStats.executionStages.stage == "SHARD_MERGE_SORT") { + errorObjs = execStats.executionStages.shards; } else { - errorObj = execStats; + errorObjs.push(execStats); } - assert.eq(true, errorObj.executionSuccess); - assert(!("errorMessage" in errorObj)); - assert(!("errorCode" in errorObj)); + for (let errorObj of errorObjs) { + assert.eq(true, errorObj.executionSuccess); + assert(!("errorMessage" in errorObj), + `Expected "errorMessage" not to be present in ${tojson(errorObj)}`); + assert(!("errorCode" in errorObj), + `Expected "errorCode" not to be present in ${tojson(errorObj)}`); + } } // Make a string that exceeds 1 MB. @@ -50,8 +69,9 @@ while (bigStr.length < (1024 * 1024)) { bigStr += bigStr; } -// Make a collection that is about 40 MB. -for (var i = 0; i < 40; i++) { +// Make a collection that is about 40 MB * number of shards. +const numShards = FixtureHelpers.numberOfShardsForCollection(t); +for (var i = 0; i < 40 * numShards; i++) { assert.writeOK(t.insert({a: bigStr, b: 1, c: i})); } diff --git a/jstests/core/explain_multikey.js b/jstests/core/explain_multikey.js index 9c8bcaff353..963e6c7b3dc 100644 --- a/jstests/core/explain_multikey.js +++ b/jstests/core/explain_multikey.js @@ -1,3 +1,8 @@ +// Tests the output of the multikey information in the explain output. +// +// This test examines the explain output to verify that certain indexes are multi-key, which may not +// be the case on all shards. +// @tags: [assumes_unsharded_collection] (function() { "use strict"; @@ -31,7 +36,7 @@ var explain = db.runCommand({explain: testOptions.commandObj}); assert.commandWorked(explain); - assert(planHasStage(explain.queryPlanner.winningPlan, testOptions.stage), + assert(planHasStage(db, explain.queryPlanner.winningPlan, testOptions.stage), "expected stage to be present: " + tojson(explain)); return getPlanStage(explain.queryPlanner.winningPlan, testOptions.stage); } diff --git a/jstests/core/explain_shell_helpers.js b/jstests/core/explain_shell_helpers.js index 6786e33c80b..ab06b8042e0 100644 --- a/jstests/core/explain_shell_helpers.js +++ b/jstests/core/explain_shell_helpers.js @@ -117,40 +117,40 @@ assert.commandWorked(explain); // .sort() explain = t.explain().find().sort({b: -1}).finish(); assert.commandWorked(explain); -assert(planHasStage(explain.queryPlanner.winningPlan, "SORT")); +assert(planHasStage(db, explain.queryPlanner.winningPlan, "SORT")); explain = t.find().sort({b: -1}).explain(); assert.commandWorked(explain); -assert(planHasStage(explain.queryPlanner.winningPlan, "SORT")); +assert(planHasStage(db, explain.queryPlanner.winningPlan, "SORT")); // .hint() explain = t.explain().find().hint({a: 1}).finish(); assert.commandWorked(explain); -assert(isIxscan(explain.queryPlanner.winningPlan)); +assert(isIxscan(db, explain.queryPlanner.winningPlan)); explain = t.explain().find().hint("a_1").finish(); assert.commandWorked(explain); -assert(isIxscan(explain.queryPlanner.winningPlan)); +assert(isIxscan(db, explain.queryPlanner.winningPlan)); explain = t.find().hint({a: 1}).explain(); assert.commandWorked(explain); -assert(isIxscan(explain.queryPlanner.winningPlan)); +assert(isIxscan(db, explain.queryPlanner.winningPlan)); explain = t.find().hint("a_1").explain(); assert.commandWorked(explain); -assert(isIxscan(explain.queryPlanner.winningPlan)); +assert(isIxscan(db, explain.queryPlanner.winningPlan)); // .min() explain = t.explain().find().min({a: 1}).finish(); assert.commandWorked(explain); -assert(isIxscan(explain.queryPlanner.winningPlan)); +assert(isIxscan(db, explain.queryPlanner.winningPlan)); explain = t.find().min({a: 1}).explain(); assert.commandWorked(explain); -assert(isIxscan(explain.queryPlanner.winningPlan)); +assert(isIxscan(db, explain.queryPlanner.winningPlan)); // .max() explain = t.explain().find().max({a: 1}).finish(); assert.commandWorked(explain); -assert(isIxscan(explain.queryPlanner.winningPlan)); +assert(isIxscan(db, explain.queryPlanner.winningPlan)); explain = t.find().max({a: 1}).explain(); assert.commandWorked(explain); -assert(isIxscan(explain.queryPlanner.winningPlan)); +assert(isIxscan(db, explain.queryPlanner.winningPlan)); // .showDiskLoc() explain = t.explain().find().showDiskLoc().finish(); @@ -234,7 +234,7 @@ assert("queryPlanner" in explain.stages[0].$cursor); // Basic count. explain = t.explain().count(); assert.commandWorked(explain); -assert(planHasStage(explain.queryPlanner.winningPlan, "COUNT")); +assert(planHasStage(db, explain.queryPlanner.winningPlan, "COUNT")); // Tests for applySkipLimit argument to .count. When we don't apply the skip, we // count one result. When we do apply the skip we count zero. @@ -254,14 +254,14 @@ assert.eq(0, stage.nCounted); // Count with hint. explain = t.explain().find({a: 3}).hint({a: 1}).count(); assert.commandWorked(explain); -assert(planHasStage(explain.queryPlanner.winningPlan, "COUNT")); -assert(planHasStage(explain.queryPlanner.winningPlan, "COUNT_SCAN")); +assert(planHasStage(db, explain.queryPlanner.winningPlan, "COUNT")); +assert(planHasStage(db, explain.queryPlanner.winningPlan, "COUNT_SCAN")); // Explainable count with hint. assert.commandWorked(t.ensureIndex({c: 1}, {sparse: true})); explain = t.explain().count({c: {$exists: false}}, {hint: "c_1"}); assert.commandWorked(explain); -assert(planHasStage(explain.queryPlanner.winningPlan, "IXSCAN")); +assert(planHasStage(db, explain.queryPlanner.winningPlan, "IXSCAN")); assert.eq(getPlanStage(explain.queryPlanner.winningPlan, "IXSCAN").indexName, "c_1"); assert.commandWorked(t.dropIndex({c: 1})); @@ -278,17 +278,17 @@ assert.commandWorked(explain); explain = t.explain().distinct('_id'); assert.commandWorked(explain); -assert(planHasStage(explain.queryPlanner.winningPlan, "PROJECTION")); -assert(planHasStage(explain.queryPlanner.winningPlan, "DISTINCT_SCAN")); +assert(planHasStage(db, explain.queryPlanner.winningPlan, "PROJECTION")); +assert(planHasStage(db, explain.queryPlanner.winningPlan, "DISTINCT_SCAN")); explain = t.explain().distinct('a'); assert.commandWorked(explain); -assert(planHasStage(explain.queryPlanner.winningPlan, "PROJECTION")); -assert(planHasStage(explain.queryPlanner.winningPlan, "DISTINCT_SCAN")); +assert(planHasStage(db, explain.queryPlanner.winningPlan, "PROJECTION")); +assert(planHasStage(db, explain.queryPlanner.winningPlan, "DISTINCT_SCAN")); explain = t.explain().distinct('b'); assert.commandWorked(explain); -assert(planHasStage(explain.queryPlanner.winningPlan, "COLLSCAN")); +assert(planHasStage(db, explain.queryPlanner.winningPlan, "COLLSCAN")); // // .remove() diff --git a/jstests/core/find1.js b/jstests/core/find1.js index a09c0822a47..e43b54dd398 100644 --- a/jstests/core/find1.js +++ b/jstests/core/find1.js @@ -1,6 +1,8 @@ +// This test makes assumptions about the order of collection scans using the snapshot query option. +// This order can change when the collection is sharded. +// @tags: [assumes_unsharded_collection] t = db.find1; t.drop(); - lookAtDocumentMetrics = false; // QUERY MIGRATION diff --git a/jstests/core/find5.js b/jstests/core/find5.js index 633b723fe5a..1771c1fbbdb 100644 --- a/jstests/core/find5.js +++ b/jstests/core/find5.js @@ -10,7 +10,7 @@ assert.eq(2, coll.find({}, {b: 1}).count(), "A"); function getIds(projection) { - return coll.find({}, projection).map(doc => doc._id); + return coll.find({}, projection).map(doc => doc._id).sort(); } assert.eq(Array.tojson(getIds(null)), Array.tojson(getIds({})), "B1 "); diff --git a/jstests/core/fts_mix.js b/jstests/core/fts_mix.js index f0098639e87..89195d8b710 100644 --- a/jstests/core/fts_mix.js +++ b/jstests/core/fts_mix.js @@ -1,5 +1,6 @@ load("jstests/libs/fts.js"); +load("jstests/aggregation/extras/utils.js"); // For resultsEq. // test collection tc = db.text_mix; @@ -84,19 +85,19 @@ tc.dropIndexes(); // now let's see about multiple fields, with specific weighting tc.ensureIndex({"title": "text", "text": "text"}, {weights: {"title": 10}}); -assert.eq([9, 7, 8], queryIDS(tc, "members physics")); +assert(resultsEq([9, 7, 8], queryIDS(tc, "members physics"))); tc.dropIndexes(); // test all-1 weighting with "$**" tc.ensureIndex({"$**": "text"}); -assert.eq([2, 8, 7], queryIDS(tc, "family tea estate")); +assert(resultsEq([2, 8, 7], queryIDS(tc, "family tea estate"))); tc.dropIndexes(); // non-1 weight on "$**" + other weight specified for some field tc.ensureIndex({"$**": "text"}, {weights: {"$**": 10, "text": 2}}); -assert.eq([7, 5], queryIDS(tc, "Olympic Games gold medal")); +assert(resultsEq([7, 5], queryIDS(tc, "Olympic Games gold medal"))); tc.dropIndexes(); @@ -111,12 +112,12 @@ tc.ensureIndex({"$**": "text"}, {weights: {"title": 10}}); // tests stemming for basic plural case res = tc.find({"$text": {"$search": "member"}}); res2 = tc.find({"$text": {"$search": "members"}}); -assert.eq(getIDS(res), getIDS(res2)); +assert(resultsEq(getIDS(res), getIDS(res2))); // "search" for something with potential 's bug. res = tc.find({"$text": {"$search": "magazine's"}}); res2 = tc.find({"$text": {"$search": "magazine"}}); -assert.eq(getIDS(res), getIDS(res2)); +assert(resultsEq(getIDS(res), getIDS(res2))); // -------------------------------------------- LANGUAGE ------------------------------------------- @@ -152,13 +153,10 @@ assert.doesNotThrow(function() { // ensure limit limits results assert.eq([2], queryIDS(tc, "rural river dam", null, null, 1)); -// ensure top results are the same regardless of limit -// make sure that this uses a case where it wouldn't be otherwise.. res = tc.find({"$text": {"$search": "united kingdom british princes"}}).limit(1); -res2 = tc.find({"$text": {"$search": "united kingdom british princes"}}); assert.eq(1, res.length()); +res2 = tc.find({"$text": {"$search": "united kingdom british princes"}}); assert.eq(4, res2.length()); -assert.eq(res[0]._id, res2[0]._id); // -------------------------------------------- PROJECTION ----------------------------------------- @@ -226,7 +224,3 @@ assert.eq([2], queryIDS(tc, "Mahim", {_id: {$lte: 4}})); // using regex conditional filtering assert.eq([9], queryIDS(tc, "members", {title: {$regex: /Phy.*/i}})); - -// ------------------------------------------------------------------------------------------------- - -assert(tc.validate().valid); diff --git a/jstests/core/fts_projection.js b/jstests/core/fts_projection.js index 0942c7563b9..f6b4773c2da 100644 --- a/jstests/core/fts_projection.js +++ b/jstests/core/fts_projection.js @@ -114,10 +114,10 @@ assert.neq(-1, let explainOutput = t.find({$text: {$search: "textual content -irrelevant"}}, { score: {$meta: "textScore"} }).explain(); -assert(planHasStage(explainOutput.queryPlanner.winningPlan, "TEXT_OR")); +assert(planHasStage(db, explainOutput.queryPlanner.winningPlan, "TEXT_OR")); explainOutput = t.find({$text: {$search: "textual content -irrelevant"}}).explain(); -assert(!planHasStage(explainOutput.queryPlanner.winningPlan, "TEXT_OR")); +assert(!planHasStage(db, explainOutput.queryPlanner.winningPlan, "TEXT_OR")); // Scores should exist. assert.eq(results.length, 2); diff --git a/jstests/core/getmore_invalidated_cursors.js b/jstests/core/getmore_invalidated_cursors.js index 527b6bf81e7..adf9b17fcd5 100644 --- a/jstests/core/getmore_invalidated_cursors.js +++ b/jstests/core/getmore_invalidated_cursors.js @@ -5,11 +5,12 @@ (function() { 'use strict'; + load('jstests/libs/fixture_helpers.js'); // For FixtureHelpers. + const testDB = db.getSiblingDB("getmore_invalidated_cursors"); const coll = testDB.test; const nDocs = 100; - const batchSize = nDocs - 1; function setupCollection() { coll.drop(); @@ -25,6 +26,10 @@ // code and message. setupCollection(); + // Make sure the batch size is small enough to ensure a getMore will need to be sent to at least + // one shard. + const batchSize = (nDocs / FixtureHelpers.numberOfShardsForCollection(coll)) - 1; + const isShardedCollection = coll.stats().sharded; const shellReadMode = testDB.getMongo().readMode(); diff --git a/jstests/core/hashindex1.js b/jstests/core/hashindex1.js index 2e05f9fe3da..b79deeee445 100644 --- a/jstests/core/hashindex1.js +++ b/jstests/core/hashindex1.js @@ -42,7 +42,7 @@ assert.eq(t.find({a: 3.1}).hint(goodspec).toArray()[0].a, 3.1); // Make sure we're using the hashed index. var explain = t.find({a: 1}).explain(); -assert(isIxscan(explain.queryPlanner.winningPlan), "not using hashed index"); +assert(isIxscan(db, explain.queryPlanner.winningPlan), "not using hashed index"); // SERVER-12222 // printjson( t.find({a : {$gte : 3 , $lte : 3}}).explain() ) @@ -50,20 +50,20 @@ assert(isIxscan(explain.queryPlanner.winningPlan), "not using hashed index"); // cursorname , // "not using hashed cursor"); var explain = t.find({c: 1}).explain(); -assert(!isIxscan(explain.queryPlanner.winningPlan), "using irrelevant hashed index"); +assert(!isIxscan(db, explain.queryPlanner.winningPlan), "using irrelevant hashed index"); // Hash index used with a $in set membership predicate. var explain = t.find({a: {$in: [1, 2]}}).explain(); printjson(explain); -assert(isIxscan(explain.queryPlanner.winningPlan), "not using hashed index"); +assert(isIxscan(db, explain.queryPlanner.winningPlan), "not using hashed index"); // Hash index used with a singleton $and predicate conjunction. var explain = t.find({$and: [{a: 1}]}).explain(); -assert(isIxscan(explain.queryPlanner.winningPlan), "not using hashed index"); +assert(isIxscan(db, explain.queryPlanner.winningPlan), "not using hashed index"); // Hash index used with a non singleton $and predicate conjunction. var explain = t.find({$and: [{a: {$in: [1, 2]}}, {a: {$gt: 1}}]}).explain(); -assert(isIxscan(explain.queryPlanner.winningPlan), "not using hashed index"); +assert(isIxscan(db, explain.queryPlanner.winningPlan), "not using hashed index"); // test creation of index based on hash of _id index var goodspec2 = {'_id': "hashed"}; diff --git a/jstests/core/idhack.js b/jstests/core/idhack.js index 0855a8ccbeb..2ef3041c759 100644 --- a/jstests/core/idhack.js +++ b/jstests/core/idhack.js @@ -31,23 +31,23 @@ var explain = t.find(query).explain(true); print("explain for " + tojson(query, "", true) + " = " + tojson(explain)); assert.eq(1, explain.executionStats.nReturned, "D1"); assert.eq(1, explain.executionStats.totalKeysExamined, "D2"); -assert(isIdhack(explain.queryPlanner.winningPlan), "D3"); +assert(isIdhack(db, explain.queryPlanner.winningPlan), "D3"); // ID hack cannot be used with hint(). t.ensureIndex({_id: 1, a: 1}); var hintExplain = t.find(query).hint({_id: 1, a: 1}).explain(); print("explain for hinted query = " + tojson(hintExplain)); -assert(!isIdhack(hintExplain.queryPlanner.winningPlan), "E1"); +assert(!isIdhack(db, hintExplain.queryPlanner.winningPlan), "E1"); // ID hack cannot be used with skip(). var skipExplain = t.find(query).skip(1).explain(); print("explain for skip query = " + tojson(skipExplain)); -assert(!isIdhack(skipExplain.queryPlanner.winningPlan), "F1"); +assert(!isIdhack(db, skipExplain.queryPlanner.winningPlan), "F1"); // Covered query returning _id field only can be handled by ID hack. var coveredExplain = t.find(query, {_id: 1}).explain(); print("explain for covered query = " + tojson(coveredExplain)); -assert(isIdhack(coveredExplain.queryPlanner.winningPlan), "G1"); +assert(isIdhack(db, coveredExplain.queryPlanner.winningPlan), "G1"); // Check doc from covered ID hack query. assert.eq({_id: {x: 2}}, t.findOne(query, {_id: 1}), "G2"); diff --git a/jstests/core/indexOtherNamespace.js b/jstests/core/indexOtherNamespace.js index f5919f721e8..86589714a35 100644 --- a/jstests/core/indexOtherNamespace.js +++ b/jstests/core/indexOtherNamespace.js @@ -8,12 +8,12 @@ otherDB.dropDatabase(); otherDB.foo.insert({a: 1}); assert.eq(1, otherDB.foo.getIndexes().length); -assert(isCollscan(otherDB.foo.find({a: 1}).explain().queryPlanner.winningPlan)); +assert(isCollscan(db, otherDB.foo.find({a: 1}).explain().queryPlanner.winningPlan)); assert.writeError( otherDB.randomNS.system.indexes.insert({ns: "indexOtherNS.foo", key: {a: 1}, name: "a_1"})); // Assert that index didn't actually get built assert.eq(1, otherDB.foo.getIndexes().length); -assert(isCollscan(otherDB.foo.find({a: 1}).explain().queryPlanner.winningPlan)); +assert(isCollscan(db, otherDB.foo.find({a: 1}).explain().queryPlanner.winningPlan)); otherDB.dropDatabase(); diff --git a/jstests/core/index_bounds_pipe.js b/jstests/core/index_bounds_pipe.js index e284d5c78f3..ee6cbd5b5f7 100644 --- a/jstests/core/index_bounds_pipe.js +++ b/jstests/core/index_bounds_pipe.js @@ -31,13 +31,17 @@ const explain = db.runCommand({explain: command}); assert.commandWorked(explain); - // Check that the query uses correct index bounds. - const ixscan = getPlanStage(explain.queryPlanner.winningPlan, 'IXSCAN'); - assert.neq(ixscan, null, 'Plan unexpectedly missing IXSCAN stage: ' + tojson(explain)); - assert.eq(ixscan.indexBounds._id, - params.bounds, - 'Expected bounds of ' + tojson(params.bounds) + ' but got ' + - tojson(ixscan.indexBounds._id)); + // Check that the query uses correct index bounds. When run against a sharded cluster, there + // may be multiple index scan stages, but each should have the same index bounds. + const ixscans = getPlanStages(explain.queryPlanner.winningPlan, 'IXSCAN'); + assert.gt(ixscans.length, 0, 'Plan unexpectedly missing IXSCAN stage: ' + tojson(explain)); + for (let i = 0; i < ixscans.length; i++) { + const ixscan = ixscans[i]; + assert.eq(ixscan.indexBounds._id, + params.bounds, + `Expected bounds of ${tojson(params.bounds)} but got ${ + tojson(ixscan.indexBounds._id)}. i=${i}, all output: ${tojson(explain)}`); + } // Check that the query regex matches expected strings. const results = db.runCommand(command); diff --git a/jstests/core/index_bounds_timestamp.js b/jstests/core/index_bounds_timestamp.js index 8acb842869a..a0114f2a4e3 100644 --- a/jstests/core/index_bounds_timestamp.js +++ b/jstests/core/index_bounds_timestamp.js @@ -10,8 +10,14 @@ // Helper function to get the nCounted from the COUNT stage in an explain plan's executionStats. function getCountFromExecutionStats(executionStats) { let stages = getPlanStages(executionStats.executionStages, "COUNT"); - assert.eq(1, stages.length, "executionStats should have 1 COUNT stage"); - return stages[0].nCounted; + // In a sharded cluster, there may be multiple COUNT stages. The sum of the 'nCounted' + // fields is what we're interested in here. + assert.gte(stages.length, 1, "executionStats should have at least 1 COUNT stage"); + let totalCounted = 0; + stages.forEach(function(countStage) { + totalCounted += countStage.nCounted; + }); + return totalCounted; } // Setup the test collection. @@ -38,19 +44,21 @@ // Check that count over (Timestamp(0, 0), Timestamp(2^32 - 1, 2^32 - 1)] is a covered query. plan = coll.explain("executionStats").find({ts: {$gt: Timestamp(0, 0)}}).count(); - assert(isIndexOnly(plan.queryPlanner.winningPlan), "ts $gt count should be a covered query"); + assert(isIndexOnly(db, plan.queryPlanner.winningPlan), + "ts $gt count should be a covered query"); assert.eq(5, getCountFromExecutionStats(plan.executionStats), "ts $gt count should be 5"); // Check that find over (Timestamp(0, 0), Timestamp(2^32 - 1, 2^32 - 1)] does not require a // FETCH stage when the query is covered by an index. plan = coll.explain("executionStats").find({ts: {$gt: Timestamp(0, 0)}}, {ts: 1, _id: 0}).finish(); - assert(isIndexOnly(plan.queryPlanner.winningPlan), + assert(isIndexOnly(db, plan.queryPlanner.winningPlan), "ts $gt find with project should be a covered query"); // Check that count over [Timestamp(0, 0), Timestamp(2^32 - 1, 2^32 - 1)] is a covered query. plan = coll.explain("executionStats").find({ts: {$gte: Timestamp(0, 0)}}).count(); - assert(isIndexOnly(plan.queryPlanner.winningPlan), "ts $gte count should be a covered query"); + assert(isIndexOnly(db, plan.queryPlanner.winningPlan), + "ts $gte count should be a covered query"); assert.eq(5, getCountFromExecutionStats(plan.executionStats), "ts $gte count should be 5"); // Check that find over [Timestamp(0, 0), Timestamp(2^32 - 1, 2^32 - 1)] does not require a @@ -58,24 +66,26 @@ plan = coll.explain("executionStats") .find({ts: {$gte: Timestamp(0, 0)}}, {ts: 1, _id: 0}) .finish(); - assert(isIndexOnly(plan.queryPlanner.winningPlan), + assert(isIndexOnly(db, plan.queryPlanner.winningPlan), "ts $gte find with project should be a covered query"); // Check that count over [Timestamp(0, 0), Timestamp(1, 0)) is a covered query. plan = coll.explain("executionStats").find({ts: {$lt: Timestamp(1, 0)}}).count(); - assert(isIndexOnly(plan.queryPlanner.winningPlan), "ts $lt count should be a covered query"); + assert(isIndexOnly(db, plan.queryPlanner.winningPlan), + "ts $lt count should be a covered query"); assert.eq(3, getCountFromExecutionStats(plan.executionStats), "ts $lt count should be 3"); // Check that find over [Timestamp(0, 0), Timestamp(1, 0)) does not require a FETCH stage when // the query is covered by an index. plan = coll.explain("executionStats").find({ts: {$lt: Timestamp(1, 0)}}, {ts: 1, _id: 0}).finish(); - assert(isIndexOnly(plan.queryPlanner.winningPlan), + assert(isIndexOnly(db, plan.queryPlanner.winningPlan), "ts $lt find with project should be a covered query"); // Check that count over [Timestamp(0, 0), Timestamp(1, 0)] is a covered query. plan = coll.explain("executionStats").find({ts: {$lte: Timestamp(1, 0)}}).count(); - assert(isIndexOnly(plan.queryPlanner.winningPlan), "ts $lte count should be a covered query"); + assert(isIndexOnly(db, plan.queryPlanner.winningPlan), + "ts $lte count should be a covered query"); assert.eq(4, getCountFromExecutionStats(plan.executionStats), "ts $lte count should be 4"); // Check that find over [Timestamp(0, 0), Timestamp(1, 0)] does not require a FETCH stage when @@ -83,14 +93,14 @@ plan = coll.explain("executionStats") .find({ts: {$lte: Timestamp(1, 0)}}, {ts: 1, _id: 0}) .finish(); - assert(isIndexOnly(plan.queryPlanner.winningPlan), + assert(isIndexOnly(db, plan.queryPlanner.winningPlan), "ts $lte find with project should be a covered query"); // Check that count over (Timestamp(0, 1), Timestamp(1, 0)) is a covered query. plan = coll.explain("executionStats") .find({ts: {$gt: Timestamp(0, 1), $lt: Timestamp(1, 0)}}) .count(); - assert(isIndexOnly(plan.queryPlanner.winningPlan), + assert(isIndexOnly(db, plan.queryPlanner.winningPlan), "ts $gt, $lt count should be a covered query"); assert.eq(2, getCountFromExecutionStats(plan.executionStats), "ts $gt, $lt count should be 2"); @@ -99,14 +109,14 @@ plan = coll.explain("executionStats") .find({ts: {$gt: Timestamp(0, 1), $lt: Timestamp(1, 0)}}, {ts: 1, _id: 0}) .finish(); - assert(isIndexOnly(plan.queryPlanner.winningPlan), + assert(isIndexOnly(db, plan.queryPlanner.winningPlan), "ts $gt, $lt find with project should be a covered query"); // Check that count over (Timestamp(0, 1), Timestamp(1, 0)] is a covered query. plan = coll.explain("executionStats") .find({ts: {$gt: Timestamp(0, 1), $lte: Timestamp(1, 0)}}) .count(); - assert(isIndexOnly(plan.queryPlanner.winningPlan), + assert(isIndexOnly(db, plan.queryPlanner.winningPlan), "ts $gt, $lte count should be a covered query"); assert.eq(3, getCountFromExecutionStats(plan.executionStats), "ts $gt, $lte count should be 3"); @@ -115,14 +125,14 @@ plan = coll.explain("executionStats") .find({ts: {$gt: Timestamp(0, 1), $lte: Timestamp(1, 0)}}, {ts: 1, _id: 0}) .finish(); - assert(isIndexOnly(plan.queryPlanner.winningPlan), + assert(isIndexOnly(db, plan.queryPlanner.winningPlan), "ts $gt, $lte find with project should be a covered query"); // Check that count over [Timestamp(0, 1), Timestamp(1, 0)) is a covered query. plan = coll.explain("executionStats") .find({ts: {$gte: Timestamp(0, 1), $lt: Timestamp(1, 0)}}) .count(); - assert(isIndexOnly(plan.queryPlanner.winningPlan), + assert(isIndexOnly(db, plan.queryPlanner.winningPlan), "ts $gte, $lt count should be a covered query"); assert.eq(3, getCountFromExecutionStats(plan.executionStats), "ts $gte, $lt count should be 3"); @@ -131,14 +141,14 @@ plan = coll.explain("executionStats") .find({ts: {$gte: Timestamp(0, 1), $lt: Timestamp(1, 0)}}, {ts: 1, _id: 0}) .finish(); - assert(isIndexOnly(plan.queryPlanner.winningPlan), + assert(isIndexOnly(db, plan.queryPlanner.winningPlan), "ts $gte, $lt find with project should be a covered query"); // Check that count over [Timestamp(0, 1), Timestamp(1, 0)] is a covered query. plan = coll.explain("executionStats") .find({ts: {$gte: Timestamp(0, 1), $lte: Timestamp(1, 0)}}) .count(); - assert(isIndexOnly(plan.queryPlanner.winningPlan), + assert(isIndexOnly(db, plan.queryPlanner.winningPlan), "ts $gte, $lte count should be a covered query"); assert.eq( 4, getCountFromExecutionStats(plan.executionStats), "ts $gte, $lte count should be 4"); @@ -148,6 +158,6 @@ plan = coll.explain("executionStats") .find({ts: {$gte: Timestamp(0, 1), $lte: Timestamp(1, 0)}}, {ts: 1, _id: 0}) .finish(); - assert(isIndexOnly(plan.queryPlanner.winningPlan), + assert(isIndexOnly(db, plan.queryPlanner.winningPlan), "ts $gte, $lte find with project should be a covered query"); })(); diff --git a/jstests/core/index_check2.js b/jstests/core/index_check2.js index 534784e6579..027cbf50ba9 100644 --- a/jstests/core/index_check2.js +++ b/jstests/core/index_check2.js @@ -35,9 +35,9 @@ assert.eq(120, t.find(q2).itcount(), "q2 a"); assert.eq(60, t.find(q3).itcount(), "q3 a"); // We expect these queries to use index scans over { tags: 1 }. -assert(isIxscan(t.find(q1).explain().queryPlanner.winningPlan), "e1"); -assert(isIxscan(t.find(q2).explain().queryPlanner.winningPlan), "e2"); -assert(isIxscan(t.find(q3).explain().queryPlanner.winningPlan), "e3"); +assert(isIxscan(db, t.find(q1).explain().queryPlanner.winningPlan), "e1"); +assert(isIxscan(db, t.find(q2).explain().queryPlanner.winningPlan), "e2"); +assert(isIxscan(db, t.find(q3).explain().queryPlanner.winningPlan), "e3"); scanned1 = t.find(q1).explain("executionStats").executionStats.totalKeysExamined; scanned2 = t.find(q2).explain("executionStats").executionStats.totalKeysExamined; diff --git a/jstests/core/index_check6.js b/jstests/core/index_check6.js index e85913aeec3..fd4a7177ffb 100644 --- a/jstests/core/index_check6.js +++ b/jstests/core/index_check6.js @@ -1,4 +1,6 @@ - +// This test makes assertions about how many keys are examined during query execution, which can +// change depending on whether/how many documents are filtered out by the SHARDING_FILTER stage. +// @tags: [assumes_unsharded_collection] t = db.index_check6; t.drop(); diff --git a/jstests/core/index_decimal.js b/jstests/core/index_decimal.js index 312ab106674..1fbb62332a2 100644 --- a/jstests/core/index_decimal.js +++ b/jstests/core/index_decimal.js @@ -38,7 +38,7 @@ assert.eq(qres, [{x: NumberDecimal('0.10')}], 'query for x equal to decimal 0.10 returns wrong value'); - assert(isIndexOnly(qplan.queryPlanner.winningPlan), + assert(isIndexOnly(db, qplan.queryPlanner.winningPlan), 'query on decimal should be covered: ' + tojson(qplan)); // Check that queries for exact floating point numbers don't return nearby decimals. diff --git a/jstests/core/index_diag.js b/jstests/core/index_diag.js index e458a590dda..3779d5f3c78 100644 --- a/jstests/core/index_diag.js +++ b/jstests/core/index_diag.js @@ -1,8 +1,9 @@ +load("jstests/libs/fixture_helpers.js"); t = db.index_diag; t.drop(); -t.ensureIndex({x: 1}); +assert.commandWorked(t.createIndex({x: 1})); all = []; ids = []; @@ -23,22 +24,28 @@ for (i = 1; i < 4; i++) { xs.push({x: -i}); } -assert.eq(all, t.find().sort({_id: 1}).toArray(), "A1"); -assert.eq(r(all), t.find().sort({_id: -1}).toArray(), "A2"); +assert.eq(all, t.find().sort({_id: 1}).toArray()); +assert.eq(r(all), t.find().sort({_id: -1}).toArray()); -assert.eq(all, t.find().sort({x: -1}).toArray(), "A3"); -assert.eq(r(all), t.find().sort({x: 1}).toArray(), "A4"); +assert.eq(all, t.find().sort({x: -1}).toArray()); +assert.eq(r(all), t.find().sort({x: 1}).toArray()); -assert.eq(ids, t.find().sort({_id: 1}).returnKey().toArray(), "B1"); -assert.eq(r(ids), t.find().sort({_id: -1}).returnKey().toArray(), "B2"); -assert.eq(xs, t.find().sort({x: -1}).returnKey().toArray(), "B3"); -assert.eq(r(xs), t.find().sort({x: 1}).returnKey().toArray(), "B4"); - -assert.eq(r(xs), t.find().hint({x: 1}).returnKey().toArray(), "B4"); +assert.eq(ids, t.find().sort({_id: 1}).returnKey().toArray()); +assert.eq(r(ids), t.find().sort({_id: -1}).returnKey().toArray()); +assert.eq(xs, t.find().sort({x: -1}).returnKey().toArray()); +assert.eq(r(xs), t.find().sort({x: 1}).returnKey().toArray()); // SERVER-4981 -t.ensureIndex({_id: 1, x: 1}); -assert.eq(all, t.find().hint({_id: 1, x: 1}).returnKey().toArray()); +if (FixtureHelpers.numberOfShardsForCollection(t) === 1) { + // With only one shard we can reliably assert on the order of results with a hint, since they + // will come straight off the index. However, without a sort specified, we cannot make this + // assertion when there are multiple shards, since mongos can merge results from each shard in + // whatever order it likes. + assert.eq(r(xs), t.find().hint({x: 1}).returnKey().toArray()); + assert.commandWorked(t.createIndex({_id: 1, x: 1})); + assert.eq(all, t.find().hint({_id: 1, x: 1}).returnKey().toArray()); +} +assert.commandWorked(t.ensureIndex({_id: 1, x: 1})); assert.eq(r(all), t.find().hint({_id: 1, x: 1}).sort({x: 1}).returnKey().toArray()); assert.eq([{}, {}, {}], t.find().hint({$natural: 1}).returnKey().toArray()); diff --git a/jstests/core/index_elemmatch2.js b/jstests/core/index_elemmatch2.js index ecd24035284..d87b26e7642 100644 --- a/jstests/core/index_elemmatch2.js +++ b/jstests/core/index_elemmatch2.js @@ -19,7 +19,7 @@ function assertIndexResults(coll, query, useIndex, nReturned) { const explainPlan = coll.find(query).explain("executionStats"); - assert.eq(isIxscan(explainPlan.queryPlanner.winningPlan), useIndex); + assert.eq(isIxscan(db, explainPlan.queryPlanner.winningPlan), useIndex); assert.eq(explainPlan.executionStats.nReturned, nReturned); } diff --git a/jstests/core/index_filter_commands.js b/jstests/core/index_filter_commands.js index cc50f65ca39..aefc40c72de 100644 --- a/jstests/core/index_filter_commands.js +++ b/jstests/core/index_filter_commands.js @@ -222,10 +222,10 @@ assert.commandWorked(t.runCommand('planCacheSetFilter', // pattern. explain = t.find(queryAA).explain(); -assert(isIxscan(explain.queryPlanner.winningPlan), "Expected index scan: " + tojson(explain)); +assert(isIxscan(db, explain.queryPlanner.winningPlan), "Expected index scan: " + tojson(explain)); explain = t.find(queryAA).collation(collationEN).explain(); -assert(isIxscan(explain.queryPlanner.winningPlan), "Expected index scan: " + tojson(explain)); +assert(isIxscan(db, explain.queryPlanner.winningPlan), "Expected index scan: " + tojson(explain)); // Ensure that index names in planCacheSetFilter only select matching names. @@ -233,7 +233,7 @@ assert.commandWorked( t.runCommand('planCacheSetFilter', {query: queryAA, collation: collationEN, indexes: ["a_1"]})); explain = t.find(queryAA).collation(collationEN).explain(); -assert(isCollscan(explain.queryPlanner.winningPlan), "Expected collscan: " + tojson(explain)); +assert(isCollscan(db, explain.queryPlanner.winningPlan), "Expected collscan: " + tojson(explain)); // // Test that planCacheSetFilter and planCacheClearFilters allow queries containing $expr. diff --git a/jstests/core/index_partial_read_ops.js b/jstests/core/index_partial_read_ops.js index fc8ab0233a7..f06ee85c621 100644 --- a/jstests/core/index_partial_read_ops.js +++ b/jstests/core/index_partial_read_ops.js @@ -24,21 +24,21 @@ load("jstests/libs/analyze_plan.js"); // find() operations that should use index. explain = coll.explain('executionStats').find({x: 6, a: 1}).finish(); assert.eq(1, explain.executionStats.nReturned); - assert(isIxscan(explain.queryPlanner.winningPlan)); + assert(isIxscan(db, explain.queryPlanner.winningPlan)); explain = coll.explain('executionStats').find({x: {$gt: 1}, a: 1}).finish(); assert.eq(1, explain.executionStats.nReturned); - assert(isIxscan(explain.queryPlanner.winningPlan)); + assert(isIxscan(db, explain.queryPlanner.winningPlan)); explain = coll.explain('executionStats').find({x: 6, a: {$lte: 1}}).finish(); assert.eq(1, explain.executionStats.nReturned); - assert(isIxscan(explain.queryPlanner.winningPlan)); + assert(isIxscan(db, explain.queryPlanner.winningPlan)); // find() operations that should not use index. explain = coll.explain('executionStats').find({x: 6, a: {$lt: 1.6}}).finish(); assert.eq(1, explain.executionStats.nReturned); - assert(isCollscan(explain.queryPlanner.winningPlan)); + assert(isCollscan(db, explain.queryPlanner.winningPlan)); explain = coll.explain('executionStats').find({x: 6}).finish(); assert.eq(1, explain.executionStats.nReturned); - assert(isCollscan(explain.queryPlanner.winningPlan)); + assert(isCollscan(db, explain.queryPlanner.winningPlan)); // // Verify basic functionality with the count command. @@ -46,11 +46,11 @@ load("jstests/libs/analyze_plan.js"); // Count operation that should use index. explain = coll.explain('executionStats').count({x: {$gt: 1}, a: 1}); - assert(isIxscan(explain.queryPlanner.winningPlan)); + assert(isIxscan(db, explain.queryPlanner.winningPlan)); // Count operation that should not use index. explain = coll.explain('executionStats').count({x: {$gt: 1}, a: 2}); - assert(isCollscan(explain.queryPlanner.winningPlan)); + assert(isCollscan(db, explain.queryPlanner.winningPlan)); // // Verify basic functionality with the aggregate command. @@ -58,11 +58,11 @@ load("jstests/libs/analyze_plan.js"); // Aggregate operation that should use index. explain = coll.aggregate([{$match: {x: {$gt: 1}, a: 1}}], {explain: true}).stages[0].$cursor; - assert(isIxscan(explain.queryPlanner.winningPlan)); + assert(isIxscan(db, explain.queryPlanner.winningPlan)); // Aggregate operation that should not use index. explain = coll.aggregate([{$match: {x: {$gt: 1}, a: 2}}], {explain: true}).stages[0].$cursor; - assert(isCollscan(explain.queryPlanner.winningPlan)); + assert(isCollscan(db, explain.queryPlanner.winningPlan)); // // Verify basic functionality with the findAndModify command. @@ -72,11 +72,11 @@ load("jstests/libs/analyze_plan.js"); explain = coll.explain('executionStats') .findAndModify({query: {x: {$gt: 1}, a: 1}, update: {$inc: {x: 1}}}); assert.eq(1, explain.executionStats.nReturned); - assert(isIxscan(explain.queryPlanner.winningPlan)); + assert(isIxscan(db, explain.queryPlanner.winningPlan)); // findAndModify operation that should not use index. explain = coll.explain('executionStats') .findAndModify({query: {x: {$gt: 1}, a: 2}, update: {$inc: {x: 1}}}); assert.eq(1, explain.executionStats.nReturned); - assert(isCollscan(explain.queryPlanner.winningPlan)); + assert(isCollscan(db, explain.queryPlanner.winningPlan)); })(); diff --git a/jstests/core/index_type_change.js b/jstests/core/index_type_change.js index 37e1d4c06e3..ad2525fe015 100644 --- a/jstests/core/index_type_change.js +++ b/jstests/core/index_type_change.js @@ -34,7 +34,7 @@ load("jstests/libs/analyze_plan.js"); // For 'isIndexOnly'. // First make sure it's actually using a covered index scan. var explain = coll.explain().find({a: 2}, {_id: 0, a: 1}); - assert(isIndexOnly(explain)); + assert(isIndexOnly(db, explain)); var updated = coll.findOne({a: 2}, {_id: 0, a: 1}); diff --git a/jstests/core/indexj.js b/jstests/core/indexj.js index 01754466a3a..4236ce0a535 100644 --- a/jstests/core/indexj.js +++ b/jstests/core/indexj.js @@ -1,4 +1,7 @@ // SERVER-726 +// This test makes assertions about how many keys are examined during query execution, which can +// change depending on whether/how many documents are filtered out by the SHARDING_FILTER stage. +// @tags: [assumes_unsharded_collection] t = db.jstests_indexj; t.drop(); diff --git a/jstests/core/maxscan.js b/jstests/core/maxscan.js index 37cbf4b5b2f..c8ba77b1173 100644 --- a/jstests/core/maxscan.js +++ b/jstests/core/maxscan.js @@ -1,6 +1,8 @@ (function() { "use strict"; + load("jstests/libs/fixture_helpers.js"); // For FixtureHelpers. + const coll = db.maxscan; coll.drop(); @@ -9,13 +11,17 @@ assert.writeOK(coll.insert({_id: i, x: i % 10})); } - assert.eq(N, coll.find().itcount(), "A"); - assert.eq(50, coll.find().maxScan(50).itcount(), "B"); + assert.eq(N, coll.find().itcount()); + // We should scan no more than 50 things on each shard. + assert.lte(coll.find().maxScan(50).itcount(), + 50 * FixtureHelpers.numberOfShardsForCollection(coll)); - assert.eq(10, coll.find({x: 2}).itcount(), "C"); - assert.eq(5, coll.find({x: 2}).sort({_id: 1}).maxScan(50).itcount(), "D"); + assert.eq(coll.find({x: 2}).itcount(), 10); + // We should scan no more than 50 things on each shard. + assert.lte(coll.find({x: 2}).sort({_id: 1}).maxScan(50).itcount(), + 5 * FixtureHelpers.numberOfShardsForCollection(coll)); 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"); + assert.eq(coll.find({x: 2}).sort({_id: 1}).hint({x: 1}).maxScan(N).itcount(), 10); + assert.eq(coll.find({x: 2}).sort({_id: 1}).hint({x: 1}).maxScan(1).itcount(), 0); }()); diff --git a/jstests/core/min_max_bounds.js b/jstests/core/min_max_bounds.js index 0139ec39851..1e4a80efbf6 100644 --- a/jstests/core/min_max_bounds.js +++ b/jstests/core/min_max_bounds.js @@ -3,6 +3,10 @@ */ (function() { 'use strict'; + + load('jstests/libs/fixture_helpers.js'); // For FixtureHelpers. + load('jstests/aggregation/extras/utils.js'); // For resultsEq. + var coll = db.query_bound_inclusion; coll.drop(); assert.writeOK(coll.insert({a: 1, b: 1})); @@ -25,8 +29,14 @@ res = coll.find().min({a: 1}).max({a: 3}).toArray(); assert.eq(res.length, 2); - assert.eq(res[0].a, 1); - assert.eq(res[1].a, 2); + if (FixtureHelpers.numberOfShardsForCollection(coll) === 1) { + assert.eq(res[0].a, 1); + assert.eq(res[1].a, 2); + } else { + // With more than one shard, we cannot assume the results will come back in order, since we + // did not request a sort. + assert(resultsEq(res.map((result) => result.a), [1, 2])); + } res = coll.find().min({a: 1}).max({a: 3}).sort({a: -1}).toArray(); assert.eq(res.length, 2); @@ -49,11 +59,17 @@ res = coll.find().min({b: 3}).max({b: 1}).toArray(); assert.eq(res.length, 2); - assert.eq(res[0].b, 3); - assert.eq(res[1].b, 2); + if (FixtureHelpers.numberOfShardsForCollection(coll) === 1) { + assert.eq(res[0].b, 3); + assert.eq(res[1].b, 2); + } else { + // With more than one shard, we cannot assume the results will come back in order, since we + // did not request a sort. + assert(resultsEq(res.map((result) => result.b), [3, 2])); + } res = coll.find().min({b: 3}).max({b: 1}).sort({b: 1}).toArray(); assert.eq(res.length, 2); assert.eq(res[0].b, 2); assert.eq(res[1].b, 3); -})();
\ No newline at end of file +})(); diff --git a/jstests/core/minmax.js b/jstests/core/minmax.js index 670c7d2f8b2..0b73142e136 100644 --- a/jstests/core/minmax.js +++ b/jstests/core/minmax.js @@ -1,4 +1,6 @@ // test min / max query parameters +load("jstests/libs/fixture_helpers.js"); // For FixtureHelpers. +load("jstests/aggregation/extras/utils.js"); // For resultsEq. addData = function() { t.save({a: 1, b: 1}); @@ -75,8 +77,14 @@ t.insert({a: 4}); t.insert({a: 5}); var cursor = t.find().min({a: 4}); -assert.eq(4, cursor.next()["a"]); -assert.eq(5, cursor.next()["a"]); +if (FixtureHelpers.numberOfShardsForCollection(t) === 1) { + assert.eq(4, cursor.next().a); + assert.eq(5, cursor.next().a); +} else { + // With more than one shard, we cannot assume the results will come back in order, since we + // did not request a sort. + assert(resultsEq([cursor.next().a, cursor.next().a], [4, 5])); +} assert(!cursor.hasNext()); cursor = t.find().max({a: 4}); @@ -88,8 +96,14 @@ t.dropIndexes(); t.ensureIndex({a: -1}); cursor = t.find().min({a: 4}); -assert.eq(4, cursor.next()["a"]); -assert.eq(3, cursor.next()["a"]); +if (FixtureHelpers.numberOfShardsForCollection(t) === 1) { + assert.eq(4, cursor.next().a); + assert.eq(3, cursor.next().a); +} else { + // With more than one shard, we cannot assume the results will come back in order, since we + // did not request a sort. + assert(resultsEq([cursor.next().a, cursor.next().a], [4, 3])); +} assert(!cursor.hasNext()); cursor = t.find().max({a: 4}); diff --git a/jstests/core/mr5.js b/jstests/core/mr5.js index 25aa387594a..b0bf29c4478 100644 --- a/jstests/core/mr5.js +++ b/jstests/core/mr5.js @@ -1,53 +1,63 @@ // @tags: [does_not_support_stepdowns] -t = db.mr5; -t.drop(); - -t.save({"partner": 1, "visits": 9}); -t.save({"partner": 2, "visits": 9}); -t.save({"partner": 1, "visits": 11}); -t.save({"partner": 1, "visits": 30}); -t.save({"partner": 2, "visits": 41}); -t.save({"partner": 2, "visits": 41}); - -m = function() { - emit(this.partner, {stats: [this.visits]}); -}; - -r = function(k, v) { - var stats = []; - var total = 0; - for (var i = 0; i < v.length; i++) { - for (var j in v[i].stats) { - stats.push(v[i].stats[j]); - total += v[i].stats[j]; +(function() { + "use strict"; + + load("jstests/aggregation/extras/utils.js"); // For resultsEq. + + const t = db.mr5; + t.drop(); + + assert.writeOK(t.insert({"partner": 1, "visits": 9})); + assert.writeOK(t.insert({"partner": 2, "visits": 9})); + assert.writeOK(t.insert({"partner": 1, "visits": 11})); + assert.writeOK(t.insert({"partner": 1, "visits": 30})); + assert.writeOK(t.insert({"partner": 2, "visits": 41})); + assert.writeOK(t.insert({"partner": 2, "visits": 41})); + + let mapper = function() { + emit(this.partner, {stats: [this.visits]}); + }; + + const reducer = function(k, v) { + var stats = []; + var total = 0; + for (var i = 0; i < v.length; i++) { + for (var j in v[i].stats) { + stats.push(v[i].stats[j]); + total += v[i].stats[j]; + } } - } - return {stats: stats, total: total}; -}; - -res = t.mapReduce(m, r, {out: "mr5_out", scope: {xx: 1}}); -// res.find().forEach( printjson ) - -z = res.convertToSingleObject(); -assert.eq(2, Object.keySet(z).length, "A1"); -assert.eq([9, 11, 30], z["1"].stats, "A2"); -assert.eq([9, 41, 41], z["2"].stats, "A3"); - -res.drop(); - -m = function() { - var x = "partner"; - var y = "visits"; - emit(this[x], {stats: [this[y]]}); -}; - -res = t.mapReduce(m, r, {out: "mr5_out", scope: {xx: 1}}); -// res.find().forEach( printjson ) - -z = res.convertToSingleObject(); -assert.eq(2, Object.keySet(z).length, "B1"); -assert.eq([9, 11, 30], z["1"].stats, "B2"); -assert.eq([9, 41, 41], z["2"].stats, "B3"); - -res.drop(); + return {stats: stats, total: total}; + }; + + let res = t.mapReduce(mapper, reducer, {out: "mr5_out", scope: {xx: 1}}); + + let resultAsObj = res.convertToSingleObject(); + assert.eq(2, + Object.keySet(resultAsObj).length, + `Expected 2 keys ("1" and "2") in object ${tojson(resultAsObj)}`); + // Use resultsEq() to avoid any assumptions about order. + assert(resultsEq([9, 11, 30], resultAsObj["1"].stats)); + assert(resultsEq([9, 41, 41], resultAsObj["2"].stats)); + + res.drop(); + + mapper = function() { + var x = "partner"; + var y = "visits"; + emit(this[x], {stats: [this[y]]}); + }; + + res = t.mapReduce(mapper, reducer, {out: "mr5_out", scope: {xx: 1}}); + + resultAsObj = res.convertToSingleObject(); + assert.eq(2, + Object.keySet(resultAsObj).length, + `Expected 2 keys ("1" and "2") in object ${tojson(resultAsObj)}`); + // Use resultsEq() to avoid any assumptions about order. + assert(resultsEq([9, 11, 30], resultAsObj["1"].stats)); + assert(resultsEq([9, 41, 41], resultAsObj["2"].stats)); + + res.drop(); +}()); diff --git a/jstests/core/mr_stored.js b/jstests/core/mr_stored.js index 3da288f0259..dd9853f0589 100644 --- a/jstests/core/mr_stored.js +++ b/jstests/core/mr_stored.js @@ -1,4 +1,6 @@ -// @tags: [does_not_support_stepdowns, requires_non_retryable_writes] +// This test expects a function stored in the system.js collection to be available for a map/reduce, +// which may not be the case if it is implicitly sharded in a passthrough. +// @tags: [does_not_support_stepdowns, requires_non_retryable_writes, assumes_unsharded_collection] t = db.mr_stored; t.drop(); diff --git a/jstests/core/or2.js b/jstests/core/or2.js index 2624c213fad..93f738a866d 100644 --- a/jstests/core/or2.js +++ b/jstests/core/or2.js @@ -47,7 +47,7 @@ doTest = function(index) { checkArrs([{_id: 0, x: 0, a: 1}], a1); if (index) { var explain = t.find({x: 0, $or: [{a: 1}]}).explain(); - assert(isIxscan(explain.queryPlanner.winningPlan)); + assert(isIxscan(db, explain.queryPlanner.winningPlan)); } a1b2 = t.find({x: 1, $or: [{a: 1}, {b: 2}]}).toArray(); @@ -55,7 +55,7 @@ doTest = function(index) { a1b2); if (index) { var explain = t.find({x: 0, $or: [{a: 1}]}).explain(); - assert(isIxscan(explain.queryPlanner.winningPlan)); + assert(isIxscan(db, explain.queryPlanner.winningPlan)); } /* diff --git a/jstests/core/or3.js b/jstests/core/or3.js index 50434965bae..0c831c8987b 100644 --- a/jstests/core/or3.js +++ b/jstests/core/or3.js @@ -51,7 +51,7 @@ doTest = function(index) { checkArrs(t.find({x: 1, a: {$ne: 1}, b: {$ne: 2}}).toArray(), an1bn2); if (index) { var explain = t.find({x: 1, $nor: [{a: 1}, {b: 2}]}).explain(); - assert(isIxscan(explain.queryPlanner.winningPlan)); + assert(isIxscan(db, explain.queryPlanner.winningPlan)); } an1b2 = t.find({$nor: [{a: 1}], $or: [{b: 2}]}).toArray(); diff --git a/jstests/core/ord.js b/jstests/core/ord.js index b398c225c6b..640f5de13cc 100644 --- a/jstests/core/ord.js +++ b/jstests/core/ord.js @@ -6,22 +6,26 @@ // behavior is changed. (function() { + "use strict"; + + load("jstests/libs/fixture_helpers.js"); // For FixtureHelpers. + const t = db.jstests_ord; t.drop(); t.ensureIndex({a: 1}); t.ensureIndex({b: 1}); - for (i = 0; i < 80; ++i) { + for (let i = 0; i < 80; ++i) { t.save({a: 1}); } - for (i = 0; i < 100; ++i) { + for (let i = 0; i < 100; ++i) { t.save({b: 1}); } const c = t.find({$or: [{a: 1}, {b: 1}]}).batchSize(100); - for (i = 0; i < 100; ++i) { + for (let i = 0; i < 100; ++i) { c.next(); } // At this point, our initial query has ended and there is a client cursor waiting @@ -32,7 +36,18 @@ // Dropping an index kills all cursors on the indexed namespace, not just those // cursors using the dropped index. - assert.throws(function() { - c.next(); - }); + if (FixtureHelpers.isMongos(db)) { + // mongos may have some data left from a previous batch stored in memory, so it might not + // return an error immediately, but it should eventually. + assert.soon(function() { + try { + c.next(); + return false; // We didn't throw an error yet. + } catch (e) { + return true; + } + }); + } else { + assert.throws(() => c.next()); + } })(); diff --git a/jstests/core/proj_key1.js b/jstests/core/proj_key1.js index 916ad8f1139..06e3c255a3f 100644 --- a/jstests/core/proj_key1.js +++ b/jstests/core/proj_key1.js @@ -12,8 +12,5 @@ for (i = 0; i < 10; i++) { t.ensureIndex({a: 1}); -// assert( t.find( {} , { a : 1 , _id : 0 } ).explain().indexOnly , "A4" ); // TODO: need to modify -// query optimier SERVER-2109 - -assert.eq(as, t.find({a: {$gte: 0}}, {a: 1, _id: 0}).toArray(), "B1"); -assert.eq(as, t.find({a: {$gte: 0}}, {a: 1, _id: 0}).batchSize(2).toArray(), "B1"); +assert.eq(as, t.find({a: {$gte: 0}}, {a: 1, _id: 0}).sort({a: 1}).toArray()); +assert.eq(as, t.find({a: {$gte: 0}}, {a: 1, _id: 0}).sort({a: 1}).batchSize(2).toArray()); diff --git a/jstests/core/projection_dotted_paths.js b/jstests/core/projection_dotted_paths.js index 9f7564a147e..a9316d70467 100644 --- a/jstests/core/projection_dotted_paths.js +++ b/jstests/core/projection_dotted_paths.js @@ -22,16 +22,16 @@ assert.eq(resultDoc, {a: 1, b: {c: 1, d: 1}, c: 1}); let explain = coll.find({a: 1}, {_id: 0, a: 1, "b.c": 1, "b.d": 1, c: 1}).explain("queryPlanner"); - assert(isIxscan(explain.queryPlanner.winningPlan)); - assert(isIndexOnly(explain.queryPlanner.winningPlan)); + assert(isIxscan(db, explain.queryPlanner.winningPlan)); + assert(isIndexOnly(db, explain.queryPlanner.winningPlan)); // Project a subset of the indexed fields. Verify that the projection is computed correctly and // that the plan is covered. resultDoc = coll.findOne({a: 1}, {_id: 0, "b.c": 1, c: 1}); assert.eq(resultDoc, {b: {c: 1}, c: 1}); explain = coll.find({a: 1}, {_id: 0, "b.c": 1, c: 1}).explain("queryPlanner"); - assert(isIxscan(explain.queryPlanner.winningPlan)); - assert(isIndexOnly(explain.queryPlanner.winningPlan)); + assert(isIxscan(db, explain.queryPlanner.winningPlan)); + assert(isIndexOnly(db, explain.queryPlanner.winningPlan)); // Project exactly the set of fields in the index but also include _id. Verify that the // projection is computed correctly and that the plan cannot be covered. @@ -39,29 +39,29 @@ assert.eq(resultDoc, {_id: 1, a: 1, b: {c: 1, d: 1}, c: 1}); explain = coll.find({a: 1}, {_id: 0, "b.c": 1, c: 1}).explain("queryPlanner"); explain = coll.find({a: 1}, {_id: 1, a: 1, "b.c": 1, "b.d": 1, c: 1}).explain("queryPlanner"); - assert(isIxscan(explain.queryPlanner.winningPlan)); - assert(!isIndexOnly(explain.queryPlanner.winningPlan)); + assert(isIxscan(db, explain.queryPlanner.winningPlan)); + assert(!isIndexOnly(db, explain.queryPlanner.winningPlan)); // Project a not-indexed field that exists in the collection. The plan should not be covered. resultDoc = coll.findOne({a: 1}, {_id: 0, "b.c": 1, "b.e": 1, c: 1}); assert.eq(resultDoc, {b: {c: 1, e: 1}, c: 1}); explain = coll.find({a: 1}, {_id: 0, "b.c": 1, "b.e": 1, c: 1}).explain("queryPlanner"); - assert(isIxscan(explain.queryPlanner.winningPlan)); - assert(!isIndexOnly(explain.queryPlanner.winningPlan)); + assert(isIxscan(db, explain.queryPlanner.winningPlan)); + assert(!isIndexOnly(db, explain.queryPlanner.winningPlan)); // Project a not-indexed field that does not exist in the collection. The plan should not be // covered. resultDoc = coll.findOne({a: 1}, {_id: 0, "b.c": 1, "b.z": 1, c: 1}); assert.eq(resultDoc, {b: {c: 1}, c: 1}); explain = coll.find({a: 1}, {_id: 0, "b.c": 1, "b.z": 1, c: 1}).explain("queryPlanner"); - assert(isIxscan(explain.queryPlanner.winningPlan)); - assert(!isIndexOnly(explain.queryPlanner.winningPlan)); + assert(isIxscan(db, explain.queryPlanner.winningPlan)); + assert(!isIndexOnly(db, explain.queryPlanner.winningPlan)); // Verify that the correct projection is computed with an idhack query. resultDoc = coll.findOne({_id: 1}, {_id: 0, "b.c": 1, "b.e": 1, c: 1}); assert.eq(resultDoc, {b: {c: 1, e: 1}, c: 1}); explain = coll.find({_id: 1}, {_id: 0, "b.c": 1, "b.e": 1, c: 1}).explain("queryPlanner"); - assert(isIdhack(explain.queryPlanner.winningPlan)); + assert(isIdhack(db, explain.queryPlanner.winningPlan)); // If we make a dotted path multikey, projections using that path cannot be covered. But // projections which do not include the multikey path can still be covered. @@ -70,17 +70,17 @@ resultDoc = coll.findOne({a: 2}, {_id: 0, "b.c": 1, "b.d": 1}); assert.eq(resultDoc, {b: {c: 1, d: [1, 2, 3]}}); explain = coll.find({a: 2}, {_id: 0, "b.c": 1, "b.d": 1}).explain("queryPlanner"); - assert(isIxscan(explain.queryPlanner.winningPlan)); - assert(!isIndexOnly(explain.queryPlanner.winningPlan)); + assert(isIxscan(db, explain.queryPlanner.winningPlan)); + assert(!isIndexOnly(db, explain.queryPlanner.winningPlan)); resultDoc = coll.findOne({a: 2}, {_id: 0, "b.c": 1}); assert.eq(resultDoc, {b: {c: 1}}); explain = coll.find({a: 2}, {_id: 0, "b.c": 1}).explain("queryPlanner"); - assert(isIxscan(explain.queryPlanner.winningPlan)); + assert(isIxscan(db, explain.queryPlanner.winningPlan)); if (jsTest.options().storageEngine !== "mmapv1") { // Storage engines other than MMAPv1 track path-level multikey info, and can use this info // to generate a covered plan. - assert(isIndexOnly(explain.queryPlanner.winningPlan)); + assert(isIndexOnly(db, explain.queryPlanner.winningPlan)); } // Verify that dotted projections work for multiple levels of nesting. @@ -89,8 +89,8 @@ resultDoc = coll.findOne({a: 3}, {_id: 0, "x.y.y": 1, "x.y.z": 1, "x.z": 1}); assert.eq(resultDoc, {x: {y: {y: 1, z: 1}, z: 1}}); explain = coll.find({a: 3}, {_id: 0, "x.y.y": 1, "x.y.z": 1, "x.z": 1}).explain("queryPlanner"); - assert(isIxscan(explain.queryPlanner.winningPlan)); - assert(isIndexOnly(explain.queryPlanner.winningPlan)); + assert(isIxscan(db, explain.queryPlanner.winningPlan)); + assert(isIndexOnly(db, explain.queryPlanner.winningPlan)); // If projected nested paths do not exist in the indexed document, then they will get filled in // with nulls. This is a bug tracked by SERVER-23229. diff --git a/jstests/core/record_store_count.js b/jstests/core/record_store_count.js index aae198fc03e..2b8a9ad71f0 100644 --- a/jstests/core/record_store_count.js +++ b/jstests/core/record_store_count.js @@ -20,23 +20,23 @@ load("jstests/libs/analyze_plan.js"); // For 'planHasStage'. // Logically empty predicates should use the record store's count. // var explain = coll.explain().count({}); - assert(!planHasStage(explain.queryPlanner.winningPlan, "COLLSCAN")); + assert(!planHasStage(db, explain.queryPlanner.winningPlan, "COLLSCAN")); explain = coll.explain().count({$comment: "hi"}); - assert(!planHasStage(explain.queryPlanner.winningPlan, "COLLSCAN")); + assert(!planHasStage(db, explain.queryPlanner.winningPlan, "COLLSCAN")); // // A non-empty query predicate should prevent the use of the record store's count. // explain = coll.explain().find({x: 0}).count(); - assert(planHasStage(explain.queryPlanner.winningPlan, "COUNT_SCAN")); + assert(planHasStage(db, explain.queryPlanner.winningPlan, "COUNT_SCAN")); explain = coll.explain().find({x: 0, $comment: "hi"}).count(); - assert(planHasStage(explain.queryPlanner.winningPlan, "COUNT_SCAN")); + assert(planHasStage(db, explain.queryPlanner.winningPlan, "COUNT_SCAN")); explain = coll.explain().find({x: 0}).hint({x: 1}).count(); - assert(planHasStage(explain.queryPlanner.winningPlan, "COUNT_SCAN")); + assert(planHasStage(db, explain.queryPlanner.winningPlan, "COUNT_SCAN")); explain = coll.explain().find({x: 0, $comment: "hi"}).hint({x: 1}).count(); - assert(planHasStage(explain.queryPlanner.winningPlan, "COUNT_SCAN")); + assert(planHasStage(db, explain.queryPlanner.winningPlan, "COUNT_SCAN")); })(); diff --git a/jstests/core/regex6.js b/jstests/core/regex6.js index b7e90664c4d..8872d1e4cca 100644 --- a/jstests/core/regex6.js +++ b/jstests/core/regex6.js @@ -1,4 +1,7 @@ // contributed by Andrew Kempe +// This test makes assertions about how many keys are examined during query execution, which can +// change depending on whether/how many documents are filtered out by the SHARDING_FILTER stage. +// @tags: [assumes_unsharded_collection] t = db.regex6; t.drop(); diff --git a/jstests/core/return_key.js b/jstests/core/return_key.js index b2442b43e6f..38843eaf0a3 100644 --- a/jstests/core/return_key.js +++ b/jstests/core/return_key.js @@ -32,9 +32,9 @@ load("jstests/libs/analyze_plan.js"); // Check that the plan is covered. explain = coll.find().hint({a: 1}).sort({a: 1}).returnKey().explain(); - assert(isIndexOnly(explain.queryPlanner.winningPlan)); + assert(isIndexOnly(db, explain.queryPlanner.winningPlan)); explain = coll.find().hint({a: 1}).sort({a: -1}).returnKey().explain(); - assert(isIndexOnly(explain.queryPlanner.winningPlan)); + assert(isIndexOnly(db, explain.queryPlanner.winningPlan)); // returnKey with an in-memory sort. results = coll.find().hint({a: 1}).sort({b: 1}).returnKey().toArray(); @@ -44,23 +44,23 @@ load("jstests/libs/analyze_plan.js"); // Check that the plan is not covered. explain = coll.find().hint({a: 1}).sort({b: 1}).returnKey().explain(); - assert(!isIndexOnly(explain.queryPlanner.winningPlan)); + assert(!isIndexOnly(db, explain.queryPlanner.winningPlan)); explain = coll.find().hint({a: 1}).sort({b: -1}).returnKey().explain(); - assert(!isIndexOnly(explain.queryPlanner.winningPlan)); + assert(!isIndexOnly(db, explain.queryPlanner.winningPlan)); // returnKey takes precedence over other a regular inclusion projection. Should still be // covered. results = coll.find({}, {b: 1}).hint({a: 1}).sort({a: -1}).returnKey().toArray(); assert.eq(results, [{a: 3}, {a: 2}, {a: 1}]); explain = coll.find({}, {b: 1}).hint({a: 1}).sort({a: -1}).returnKey().explain(); - assert(isIndexOnly(explain.queryPlanner.winningPlan)); + assert(isIndexOnly(db, explain.queryPlanner.winningPlan)); // returnKey takes precedence over other a regular exclusion projection. Should still be // covered. results = coll.find({}, {a: 0}).hint({a: 1}).sort({a: -1}).returnKey().toArray(); assert.eq(results, [{a: 3}, {a: 2}, {a: 1}]); explain = coll.find({}, {a: 0}).hint({a: 1}).sort({a: -1}).returnKey().explain(); - assert(isIndexOnly(explain.queryPlanner.winningPlan)); + assert(isIndexOnly(db, explain.queryPlanner.winningPlan)); // Unlike other projections, sortKey meta-projection can co-exist with returnKey. results = diff --git a/jstests/core/set_type_change.js b/jstests/core/set_type_change.js index ede59f7bcde..565da8be12e 100644 --- a/jstests/core/set_type_change.js +++ b/jstests/core/set_type_change.js @@ -7,9 +7,6 @@ * Tests that using the $set update modifier to change only the type of a field will actually update * the document, including any relevant indices. */ - -load("jstests/libs/analyze_plan.js"); // For 'isIndexOnly'. - (function() { "use strict"; diff --git a/jstests/core/snapshot_queries.js b/jstests/core/snapshot_queries.js index 33e50dc60fe..87364105d6d 100644 --- a/jstests/core/snapshot_queries.js +++ b/jstests/core/snapshot_queries.js @@ -1,4 +1,8 @@ -// @tags: [requires_getmore, requires_non_retryable_writes] +// This test uses multiple batches to pause a cursors progress, so requires getMores. This test uses +// a delete without a limit which is not retryable. It also makes assumptions about the order of +// collection scans using the snapshot query option. This order can change when the collection is +// sharded. +// @tags: [requires_getmore, requires_non_retryable_writes, assumes_unsharded_collection] // Regression test for edge cases in which .snapshot() queries could historically miss documents or // return the same document twice. diff --git a/jstests/core/sort_array.js b/jstests/core/sort_array.js index bdec23fe4ea..1343d60972e 100644 --- a/jstests/core/sort_array.js +++ b/jstests/core/sort_array.js @@ -23,7 +23,7 @@ let cursor = coll.find(filter, project).sort(sort); assert.eq(cursor.toArray(), expected); let explain = coll.find(filter, project).sort(sort).explain(); - assert(planHasStage(explain.queryPlanner.winningPlan, "SORT")); + assert(planHasStage(db, explain.queryPlanner.winningPlan, "SORT")); let pipeline = [ {$_internalInhibitOptimization: {}}, @@ -140,8 +140,8 @@ assert.commandWorked(coll.createIndex({a: 1, "b.c": 1})); assert.writeOK(coll.insert({a: [1, 2, 3], b: {c: 9}})); explain = coll.find({a: 2}).sort({"b.c": -1}).explain(); - assert(planHasStage(explain.queryPlanner.winningPlan, "IXSCAN")); - assert(!planHasStage(explain.queryPlanner.winningPlan, "SORT")); + assert(planHasStage(db, explain.queryPlanner.winningPlan, "IXSCAN")); + assert(!planHasStage(db, explain.queryPlanner.winningPlan, "SORT")); const pipeline = [ {$match: {a: 2}}, diff --git a/jstests/core/text_covered_matching.js b/jstests/core/text_covered_matching.js index 0ac73bcbab6..3e9846e3895 100644 --- a/jstests/core/text_covered_matching.js +++ b/jstests/core/text_covered_matching.js @@ -32,7 +32,7 @@ load("jstests/libs/analyze_plan.js"); // - we return exactly one document. let explainResult = coll.find({$text: {$search: "hello"}, b: 1}).explain("executionStats"); assert.commandWorked(explainResult); - assert(planHasStage(explainResult.queryPlanner.winningPlan, "OR")); + assert(planHasStage(db, explainResult.queryPlanner.winningPlan, "OR")); assert.eq(explainResult.executionStats.totalKeysExamined, 2, "Unexpected number of keys examined: " + tojson(explainResult)); @@ -49,7 +49,7 @@ load("jstests/libs/analyze_plan.js"); {a: 1, b: 1, c: 1, textScore: {$meta: "textScore"}}) .explain("executionStats"); assert.commandWorked(explainResult); - assert(planHasStage(explainResult.queryPlanner.winningPlan, "TEXT_OR")); + assert(planHasStage(db, explainResult.queryPlanner.winningPlan, "TEXT_OR")); assert.eq(explainResult.executionStats.totalKeysExamined, 2, "Unexpected number of keys examined: " + tojson(explainResult)); @@ -72,7 +72,7 @@ load("jstests/libs/analyze_plan.js"); // - we return exactly one document. explainResult = coll.find({$text: {$search: "hello"}, c: 1}).explain("executionStats"); assert.commandWorked(explainResult); - assert(planHasStage(explainResult.queryPlanner.winningPlan, "OR")); + assert(planHasStage(db, explainResult.queryPlanner.winningPlan, "OR")); assert.eq(explainResult.executionStats.totalKeysExamined, 2, "Unexpected number of keys examined: " + tojson(explainResult)); @@ -117,7 +117,7 @@ load("jstests/libs/analyze_plan.js"); // - we return exactly one document. explainResult = coll.find({$text: {$search: "hello"}, "b.d": 1}).explain("executionStats"); assert.commandWorked(explainResult); - assert(planHasStage(explainResult.queryPlanner.winningPlan, "OR")); + assert(planHasStage(db, explainResult.queryPlanner.winningPlan, "OR")); assert.eq(explainResult.executionStats.totalKeysExamined, 2, "Unexpected number of keys examined: " + tojson(explainResult)); @@ -134,7 +134,7 @@ load("jstests/libs/analyze_plan.js"); {a: 1, b: 1, c: 1, textScore: {$meta: "textScore"}}) .explain("executionStats"); assert.commandWorked(explainResult); - assert(planHasStage(explainResult.queryPlanner.winningPlan, "TEXT_OR")); + assert(planHasStage(db, explainResult.queryPlanner.winningPlan, "TEXT_OR")); assert.eq(explainResult.executionStats.totalKeysExamined, 2, "Unexpected number of keys examined: " + tojson(explainResult)); @@ -157,7 +157,7 @@ load("jstests/libs/analyze_plan.js"); // - we return exactly one document. explainResult = coll.find({$text: {$search: "hello"}, "c.e": 1}).explain("executionStats"); assert.commandWorked(explainResult); - assert(planHasStage(explainResult.queryPlanner.winningPlan, "OR")); + assert(planHasStage(db, explainResult.queryPlanner.winningPlan, "OR")); assert.eq(explainResult.executionStats.totalKeysExamined, 2, "Unexpected number of keys examined: " + tojson(explainResult)); diff --git a/jstests/core/ts1.js b/jstests/core/ts1.js index 342ff3215d7..79a2db95dca 100644 --- a/jstests/core/ts1.js +++ b/jstests/core/ts1.js @@ -1,37 +1,45 @@ -t = db.ts1; -t.drop(); - -N = 20; - -for (i = 0; i < N; i++) { - t.insert({_id: i, x: new Timestamp()}); - sleep(100); -} - -function get(i) { - return t.findOne({_id: i}).x; -} - -function cmp(a, b) { - if (a.t < b.t) - return -1; - if (a.t > b.t) - return 1; - - return a.i - b.i; -} - -for (i = 0; i < N - 1; i++) { - a = get(i); - b = get(i + 1); - // print( tojson(a) + "\t" + tojson(b) + "\t" + cmp(a,b) ); - assert.gt(0, cmp(a, b), "cmp " + i); -} - -assert.eq(N, t.find({x: {$type: 17}}).itcount(), "B1"); -assert.eq(0, t.find({x: {$type: 3}}).itcount(), "B2"); - -t.insert({_id: 100, x: new Timestamp(123456, 50)}); -x = t.findOne({_id: 100}).x; -assert.eq(123456, x.t, "C1"); -assert.eq(50, x.i, "C2"); +// Tests that timestamps are inserted in increasing order. This test assumes that timestamps +// inserted within the same second will have increasing increment values, which may not be the case +// if the inserts are into a sharded collection. +// @tags: [assumes_unsharded_collection] +(function() { + "use strict"; + const t = db.ts1; + t.drop(); + + const N = 20; + + for (let i = 0; i < N; i++) { + assert.writeOK(t.insert({_id: i, x: new Timestamp()})); + sleep(100); + } + + function get(i) { + return t.findOne({_id: i}).x; + } + + function cmp(a, b) { + if (a.t < b.t) + return -1; + if (a.t > b.t) + return 1; + + return a.i - b.i; + } + + for (let i = 0; i < N - 1; i++) { + const a = get(i); + const b = get(i + 1); + assert.gt(0, + cmp(a, b), + `Expected ${tojson(a)} to be smaller than ${tojson(b)} (at iteration ${i})`); + } + + assert.eq(N, t.find({x: {$type: 17}}).itcount()); + assert.eq(0, t.find({x: {$type: 3}}).itcount()); + + assert.writeOK(t.insert({_id: 100, x: new Timestamp(123456, 50)})); + const x = t.findOne({_id: 100}).x; + assert.eq(123456, x.t); + assert.eq(50, x.i); +}()); diff --git a/jstests/core/updatej.js b/jstests/core/updatej.js index 2c4f6246f11..b96233f448d 100644 --- a/jstests/core/updatej.js +++ b/jstests/core/updatej.js @@ -1,7 +1,9 @@ -// @tags: [requires_non_retryable_writes] - // Test that update validation failure terminates the update without modifying subsequent // documents. SERVER-4779 +// This test uses a multi-update, which is not retryable. The behavior it is testing is also not +// true of sharded clusters, since one shard may continue applying updates while the other +// encounters an error. +// @tags: [requires_non_retryable_writes, assumes_unsharded_collection] t = db.jstests_updatej; t.drop(); @@ -10,5 +12,5 @@ t.save({a: []}); t.save({a: 1}); t.save({a: []}); -t.update({}, {$push: {a: 2}}, false, true); +assert.writeError(t.update({}, {$push: {a: 2}}, false, true)); assert.eq(1, t.count({a: 2})); diff --git a/jstests/core/where4.js b/jstests/core/where4.js index df2f01a498e..974748f8dbc 100644 --- a/jstests/core/where4.js +++ b/jstests/core/where4.js @@ -1,4 +1,6 @@ -// @tags: [requires_non_retryable_writes] +// This test expects a function stored in the system.js collection to be available for a map/reduce, +// which may not be the case if it is implicitly sharded in a passthrough. +// @tags: [requires_non_retryable_writes, assumes_unsharded_collection] var myDB = db.getSiblingDB("where4"); @@ -31,4 +33,4 @@ assert.writeOK( myDB.where4.update({$where: "where4_addOne(this.x) == 2"}, {$inc: {y: 1}}, false, true)); assert.eq(3, myDB.where4.findOne({x: 1}).y); -assert.eq(1, myDB.where4.findOne({x: 2}).y);
\ No newline at end of file +assert.eq(1, myDB.where4.findOne({x: 2}).y); |