diff options
author | Anton Korshunov <anton.korshunov@mongodb.com> | 2021-03-02 13:03:45 +0000 |
---|---|---|
committer | Evergreen Agent <no-reply@evergreen.mongodb.com> | 2021-03-13 07:52:14 +0000 |
commit | d362ea53d66d85750b0cb63b69168e1b3a4a330e (patch) | |
tree | ad4a34c4c31ba25b252d52ddf854fd3f23550bbc /jstests/core | |
parent | a90fee27ae85894ae6e4522251fe0ea35ef473c7 (diff) | |
download | mongo-d362ea53d66d85750b0cb63b69168e1b3a4a330e.tar.gz |
SERVER-54322 Text query plans are not shown properly in SBE explain
Diffstat (limited to 'jstests/core')
-rw-r--r-- | jstests/core/fts_explain.js | 6 | ||||
-rw-r--r-- | jstests/core/fts_index3.js | 1 | ||||
-rw-r--r-- | jstests/core/fts_projection.js | 4 | ||||
-rw-r--r-- | jstests/core/hidden_index.js | 1 | ||||
-rw-r--r-- | jstests/core/index_bounds_pipe.js | 3 | ||||
-rw-r--r-- | jstests/core/index_filter_commands.js | 9 | ||||
-rw-r--r-- | jstests/core/profile_mapreduce.js | 1 | ||||
-rw-r--r-- | jstests/core/projection_dotted_paths.js | 44 | ||||
-rw-r--r-- | jstests/core/stages_text.js | 24 | ||||
-rw-r--r-- | jstests/core/text_covered_matching.js | 3 | ||||
-rw-r--r-- | jstests/core/wildcard_and_text_indexes.js | 9 | ||||
-rw-r--r-- | jstests/core/wildcard_index_cached_plans.js | 8 |
12 files changed, 43 insertions, 70 deletions
diff --git a/jstests/core/fts_explain.js b/jstests/core/fts_explain.js index 8e18393d15a..c5a56039b1e 100644 --- a/jstests/core/fts_explain.js +++ b/jstests/core/fts_explain.js @@ -2,6 +2,7 @@ // collection. // @tags: [ // assumes_no_implicit_index_creation, +// requires_fcv_49, // ] // Test $text explain. SERVER-12037. @@ -30,10 +31,9 @@ if ("SINGLE_SHARD" === stage.stage) { assert.eq(stage.stage, "PROJECTION_DEFAULT"); let textStage = stage.inputStage; -assert.eq(textStage.stage, "TEXT"); +assert.eq(textStage.stage, "TEXT_MATCH"); assert.gte(textStage.textIndexVersion, 1, "textIndexVersion incorrect or missing."); -assert.eq(textStage.inputStage.stage, "TEXT_MATCH"); -assert.eq(textStage.inputStage.inputStage.stage, "TEXT_OR"); +assert.eq(textStage.inputStage.stage, "TEXT_OR"); assert.eq(textStage.parsedTextQuery.terms, ["a"]); assert.eq(textStage.parsedTextQuery.negatedTerms, ["b"]); assert.eq(textStage.parsedTextQuery.phrases, ["a"]); diff --git a/jstests/core/fts_index3.js b/jstests/core/fts_index3.js index 0f57c7a9d80..c255b645d90 100644 --- a/jstests/core/fts_index3.js +++ b/jstests/core/fts_index3.js @@ -3,7 +3,6 @@ // key. // @tags: [ // assumes_unsharded_collection, -// sbe_incompatible, // ] // Test that updates to fields in a text-indexed document are correctly reflected in the text index. diff --git a/jstests/core/fts_projection.js b/jstests/core/fts_projection.js index 3d08cb953f0..a850f440195 100644 --- a/jstests/core/fts_projection.js +++ b/jstests/core/fts_projection.js @@ -107,10 +107,10 @@ assert.neq(-1, let explainOutput = t.find({$text: {$search: "textual content -irrelevant"}}, { score: {$meta: "textScore"} }).explain(); -assert(planHasStage(db, explainOutput.queryPlanner.winningPlan, "TEXT_OR")); +assert(planHasStage(db, getWinningPlan(explainOutput.queryPlanner), "TEXT_OR"), explainOutput); explainOutput = t.find({$text: {$search: "textual content -irrelevant"}}).explain(); -assert(!planHasStage(db, explainOutput.queryPlanner.winningPlan, "TEXT_OR")); +assert(!planHasStage(db, getWinningPlan(explainOutput.queryPlanner), "TEXT_OR"), explainOutput); // Scores should exist. assert.eq(results.length, 2); diff --git a/jstests/core/hidden_index.js b/jstests/core/hidden_index.js index 344a808fab6..287c7cc5d0a 100644 --- a/jstests/core/hidden_index.js +++ b/jstests/core/hidden_index.js @@ -5,7 +5,6 @@ * @tags: [ * # CollMod is not retryable. * requires_non_retryable_commands, - * sbe_incompatible, * ] */ diff --git a/jstests/core/index_bounds_pipe.js b/jstests/core/index_bounds_pipe.js index 88c9f9a9a7f..6edbfe824db 100644 --- a/jstests/core/index_bounds_pipe.js +++ b/jstests/core/index_bounds_pipe.js @@ -1,9 +1,6 @@ /** * Tests the tightness of index bounds when attempting to match a regex that contains escaped and * non-escaped pipe '|' characters. - * @tags: [ - * sbe_incompatible, - * ] */ (function() { 'use strict'; diff --git a/jstests/core/index_filter_commands.js b/jstests/core/index_filter_commands.js index 2369ac7e4fe..c87b3cfc2d7 100644 --- a/jstests/core/index_filter_commands.js +++ b/jstests/core/index_filter_commands.js @@ -230,10 +230,12 @@ assert.commandWorked(coll.runCommand('planCacheSetFilter', // pattern. explain = coll.find(queryAA).explain(); -assert(isIxscan(db, explain.queryPlanner.winningPlan), "Expected index scan: " + tojson(explain)); +assert(isIxscan(db, getWinningPlan(explain.queryPlanner)), + "Expected index scan: " + tojson(explain)); explain = coll.find(queryAA).collation(collationEN).explain(); -assert(isIxscan(db, explain.queryPlanner.winningPlan), "Expected index scan: " + tojson(explain)); +assert(isIxscan(db, getWinningPlan(explain.queryPlanner)), + "Expected index scan: " + tojson(explain)); // Ensure that index names in planCacheSetFilter only select matching names. @@ -241,7 +243,8 @@ assert.commandWorked(coll.runCommand('planCacheSetFilter', {query: queryAA, collation: collationEN, indexes: ["a_1"]})); explain = coll.find(queryAA).collation(collationEN).explain(); -assert(isCollscan(db, explain.queryPlanner.winningPlan), "Expected collscan: " + tojson(explain)); +assert(isCollscan(db, getWinningPlan(explain.queryPlanner)), + "Expected collscan: " + tojson(explain)); // // Test that planCacheSetFilter and planCacheClearFilters allow queries containing $expr. diff --git a/jstests/core/profile_mapreduce.js b/jstests/core/profile_mapreduce.js index 7f9b581acc1..608971f8684 100644 --- a/jstests/core/profile_mapreduce.js +++ b/jstests/core/profile_mapreduce.js @@ -3,7 +3,6 @@ // does_not_support_causal_consistency, // does_not_support_stepdowns, // requires_profiling, -// sbe_incompatible, // uses_map_reduce_with_temp_collections, // ] diff --git a/jstests/core/projection_dotted_paths.js b/jstests/core/projection_dotted_paths.js index 340189facdc..5fc12cdcac3 100644 --- a/jstests/core/projection_dotted_paths.js +++ b/jstests/core/projection_dotted_paths.js @@ -28,16 +28,16 @@ assert.commandWorked(coll.insert({_id: 1, a: 1, b: {c: 1, d: 1, e: 1}, c: 1, e: let resultDoc = coll.findOne({a: 1}, {_id: 0, a: 1, "b.c": 1, "b.d": 1, c: 1}); 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(db, explain.queryPlanner.winningPlan), explain); -assert(isIndexOnly(db, explain.queryPlanner.winningPlan), explain); +assert(isIxscan(db, getWinningPlan(explain.queryPlanner)), explain); +assert(isIndexOnly(db, getWinningPlan(explain.queryPlanner)), explain); // 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(db, explain.queryPlanner.winningPlan)); -assert(isIndexOnly(db, explain.queryPlanner.winningPlan)); +assert(isIxscan(db, getWinningPlan(explain.queryPlanner))); +assert(isIndexOnly(db, getWinningPlan(explain.queryPlanner))); // 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. @@ -45,32 +45,32 @@ resultDoc = coll.findOne({a: 1}, {_id: 1, a: 1, "b.c": 1, "b.d": 1, c: 1}); 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(db, explain.queryPlanner.winningPlan)); -assert(!isIndexOnly(db, explain.queryPlanner.winningPlan)); +assert(isIxscan(db, getWinningPlan(explain.queryPlanner))); +assert(!isIndexOnly(db, getWinningPlan(explain.queryPlanner))); // 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(db, explain.queryPlanner.winningPlan)); -assert(!isIndexOnly(db, explain.queryPlanner.winningPlan)); +assert(isIxscan(db, getWinningPlan(explain.queryPlanner))); +assert(!isIndexOnly(db, getWinningPlan(explain.queryPlanner))); // 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(db, explain.queryPlanner.winningPlan)); -assert(!isIndexOnly(db, explain.queryPlanner.winningPlan)); +assert(isIxscan(db, getWinningPlan(explain.queryPlanner))); +assert(!isIndexOnly(db, getWinningPlan(explain.queryPlanner))); // 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"); if (isSBEEnabled) { - assert(isIxscan(db, explain.queryPlanner.winningPlan), explain); + assert(isIxscan(db, getWinningPlan(explain.queryPlanner)), explain); } else { - assert(isIdhack(db, explain.queryPlanner.winningPlan), explain); + assert(isIdhack(db, getWinningPlan(explain.queryPlanner)), explain); } // If we make a dotted path multikey, projections using that path cannot be covered. But @@ -80,15 +80,15 @@ assert.commandWorked(coll.insert({a: 2, b: {c: 1, d: [1, 2, 3]}})); 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(db, explain.queryPlanner.winningPlan)); -assert(!isIndexOnly(db, explain.queryPlanner.winningPlan)); +assert(isIxscan(db, getWinningPlan(explain.queryPlanner))); +assert(!isIndexOnly(db, getWinningPlan(explain.queryPlanner))); 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(db, explain.queryPlanner.winningPlan)); +assert(isIxscan(db, getWinningPlan(explain.queryPlanner))); // Path-level multikey info allows for generating a covered plan. -assert(isIndexOnly(db, explain.queryPlanner.winningPlan)); +assert(isIndexOnly(db, getWinningPlan(explain.queryPlanner))); // Verify that dotted projections work for multiple levels of nesting. assert.commandWorked(coll.createIndex({a: 1, "x.y.y": 1, "x.y.z": 1, "x.z": 1})); @@ -96,8 +96,8 @@ assert.commandWorked(coll.insert({a: 3, x: {y: {y: 1, f: 1, z: 1}, f: 1, z: 1}}) 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(db, explain.queryPlanner.winningPlan)); -assert(isIndexOnly(db, explain.queryPlanner.winningPlan)); +assert(isIxscan(db, getWinningPlan(explain.queryPlanner))); +assert(isIndexOnly(db, getWinningPlan(explain.queryPlanner))); // 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. @@ -109,8 +109,8 @@ assert.eq(resultDoc, {x: {y: {y: null, z: null}, z: null}}); assert.commandWorked(coll.createIndex({"a.b.c": 1, "a.b": 1})); assert.commandWorked(coll.insert({a: {b: {c: 1, d: 1}}})); explain = coll.find({"a.b.c": 1}, {_id: 0, "a.b": 1}).explain(); - assert(isIxscan(db, explain.queryPlanner.winningPlan)); - assert(isIndexOnly(db, explain.queryPlanner.winningPlan)); + assert(isIxscan(db, getWinningPlan(explain.queryPlanner))); + assert(isIndexOnly(db, getWinningPlan(explain.queryPlanner))); assert.eq(coll.findOne({"a.b.c": 1}, {_id: 0, "a.b": 1}), {a: {b: {c: 1, d: 1}}}); } @@ -122,8 +122,8 @@ assert.eq(resultDoc, {x: {y: {y: null, z: null}, z: null}}); const filter = {"a.b": {c: 1, d: 1}}; explain = coll.find(filter, {_id: 0, "a.b": 1}).explain(); - assert(isIxscan(db, explain.queryPlanner.winningPlan)); - assert(isIndexOnly(db, explain.queryPlanner.winningPlan)); + assert(isIxscan(db, getWinningPlan(explain.queryPlanner))); + assert(isIndexOnly(db, getWinningPlan(explain.queryPlanner))); assert.eq(coll.findOne(filter, {_id: 0, "a.b": 1}), {a: {b: {c: 1, d: 1}}}); } }()); diff --git a/jstests/core/stages_text.js b/jstests/core/stages_text.js deleted file mode 100644 index 4b5379a0e4c..00000000000 --- a/jstests/core/stages_text.js +++ /dev/null @@ -1,24 +0,0 @@ -// @tags: [ -// does_not_support_stepdowns, -// uses_testing_only_commands, -// ] - -// Test very basic functionality of text stage - -t = db.stages_text; -t.drop(); -var collname = "stages_text"; - -t.save({x: "az b x"}); - -t.createIndex({x: "text"}); - -// We expect to retrieve 'b' -res = db.runCommand({stageDebug: {collection: collname, plan: {text: {args: {search: "b"}}}}}); -assert.eq(res.ok, 1); -assert.eq(res.results.length, 1); - -// I have not been indexed yet. -res = db.runCommand({stageDebug: {collection: collname, plan: {text: {args: {search: "hari"}}}}}); -assert.eq(res.ok, 1); -assert.eq(res.results.length, 0); diff --git a/jstests/core/text_covered_matching.js b/jstests/core/text_covered_matching.js index 92366237047..0fd75306fa9 100644 --- a/jstests/core/text_covered_matching.js +++ b/jstests/core/text_covered_matching.js @@ -10,7 +10,6 @@ // @tags: [ // assumes_balancer_off, // requires_fcv_49, -// sbe_incompatible, // ] load("jstests/libs/analyze_plan.js"); @@ -80,7 +79,7 @@ assert.docEq(filteringStage.filter, {"b": {"$eq": 1}}, "Incorrect filter on TEXT // underlying IXSCANs, but we should get an equivalent result. explainResult = coll.find({$text: {$search: "hello world"}, b: 1}).explain("executionStats"); assert.commandWorked(explainResult); -assert(planHasStage(db, explainResult.queryPlanner.winningPlan, "OR")); +assert(planHasStage(db, getWinningPlan(explainResult.queryPlanner), "OR")); assert.eq(explainResult.executionStats.totalKeysExamined, 4, "Unexpected number of keys examined: " + tojson(explainResult)); diff --git a/jstests/core/wildcard_and_text_indexes.js b/jstests/core/wildcard_and_text_indexes.js index 5fa3d0520a2..0835bd51b26 100644 --- a/jstests/core/wildcard_and_text_indexes.js +++ b/jstests/core/wildcard_and_text_indexes.js @@ -2,6 +2,7 @@ * Tests that a {$**: 1} index can coexist with a {$**: 'text'} index in the same collection. * @tags: [ * assumes_balancer_off, + * requires_fcv_49, * ] */ (function() { @@ -52,7 +53,7 @@ for (let textIndex of [{'$**': 'text'}, {a: 1, '$**': 'text'}]) { // when the query filter contains a compound field in the $text index. const textQuery = Object.assign(textIndex.a ? {a: 1} : {}, {$text: {$search: 'banana'}}); let explainOut = assert.commandWorked(coll.find(textQuery).explain("executionStats")); - assert(planHasStage(coll.getDB(), getWinningPlan(explainOut.queryPlanner), "TEXT")); + assert(planHasStage(coll.getDB(), getWinningPlan(explainOut.queryPlanner), "TEXT_MATCH")); assert.eq(getRejectedPlans(explainOut).length, 0); assert.eq(explainOut.executionStats.nReturned, 2); @@ -60,7 +61,7 @@ for (let textIndex of [{'$**': 'text'}, {a: 1, '$**': 'text'}]) { // where the query filter contains a field which is not present in the text index. explainOut = assert.commandWorked( coll.find(Object.assign({_fts: {$gt: 0, $lt: 4}}, textQuery)).explain("executionStats")); - assert(planHasStage(coll.getDB(), getWinningPlan(explainOut.queryPlanner), "TEXT")); + assert(planHasStage(coll.getDB(), getWinningPlan(explainOut.queryPlanner), "TEXT_MATCH")); assert.eq(getRejectedPlans(explainOut).length, 0); assert.eq(explainOut.executionStats.nReturned, 2); @@ -72,9 +73,9 @@ for (let textIndex of [{'$**': 'text'}, {a: 1, '$**': 'text'}]) { const textOrWildcard = getPlanStages(getWinningPlan(explainOut.queryPlanner), "OR").shift(); assert.eq(textOrWildcard.inputStages.length, 2); - const textBranch = (textOrWildcard.inputStages[0].stage === "TEXT" ? 0 : 1); + const textBranch = (textOrWildcard.inputStages[0].stage === "TEXT_MATCH" ? 0 : 1); const wildcardBranch = (textBranch + 1) % 2; - assert.eq(textOrWildcard.inputStages[textBranch].stage, "TEXT"); + assert.eq(textOrWildcard.inputStages[textBranch].stage, "TEXT_MATCH"); assert.eq(textOrWildcard.inputStages[wildcardBranch].stage, "IXSCAN"); assert.eq(textOrWildcard.inputStages[wildcardBranch].keyPattern, {$_path: 1, _fts: 1}); diff --git a/jstests/core/wildcard_index_cached_plans.js b/jstests/core/wildcard_index_cached_plans.js index 796686370bb..e124e2ad1e8 100644 --- a/jstests/core/wildcard_index_cached_plans.js +++ b/jstests/core/wildcard_index_cached_plans.js @@ -127,7 +127,7 @@ assert.commandWorked(coll.createIndex({"b.$**": 1})); // string bounds. const queryWithoutStringExplain = coll.explain().find({a: 5, b: 5}).collation({locale: "fr"}).finish(); -let ixScans = getPlanStages(queryWithoutStringExplain.queryPlanner.winningPlan, "IXSCAN"); +let ixScans = getPlanStages(getWinningPlan(queryWithoutStringExplain.queryPlanner), "IXSCAN"); assert.eq(ixScans.length, FixtureHelpers.numberOfShardsForCollection(coll)); assert.eq(ixScans[0].keyPattern, {$_path: 1, b: 1}); @@ -135,7 +135,7 @@ assert.eq(ixScans[0].keyPattern, {$_path: 1, b: 1}); // bounds. const queryWithStringExplain = coll.explain().find({a: 5, b: "a string"}).collation({locale: "fr"}).finish(); -ixScans = getPlanStages(queryWithStringExplain.queryPlanner.winningPlan, "IXSCAN"); +ixScans = getPlanStages(getWinningPlan(queryWithStringExplain.queryPlanner), "IXSCAN"); assert.eq(ixScans.length, 0); // Check that the shapes are different since the query which matches on a string will not @@ -151,13 +151,13 @@ assert.commandWorked(coll.createIndex({"$**": 1}, {partialFilterExpression: {a: // Run a query for a value included by the partial filter expression. const queryIndexedExplain = coll.find({a: 4}).explain(); -let ixScans = getPlanStages(queryIndexedExplain.queryPlanner.winningPlan, "IXSCAN"); +let ixScans = getPlanStages(getWinningPlan(queryIndexedExplain.queryPlanner), "IXSCAN"); assert.eq(ixScans.length, FixtureHelpers.numberOfShardsForCollection(coll)); assert.eq(ixScans[0].keyPattern, {$_path: 1, a: 1}); // Run a query which tries to get a value not included by the partial filter expression. const queryUnindexedExplain = coll.find({a: 100}).explain(); -ixScans = getPlanStages(queryUnindexedExplain.queryPlanner.winningPlan, "IXSCAN"); +ixScans = getPlanStages(getWinningPlan(queryUnindexedExplain.queryPlanner), "IXSCAN"); assert.eq(ixScans.length, 0); // Check that the shapes are different since the query which searches for a value not |