diff options
author | Bernard Gorman <bernard.gorman@gmail.com> | 2018-10-03 17:38:54 +0100 |
---|---|---|
committer | Bernard Gorman <bernard.gorman@gmail.com> | 2018-10-03 22:36:02 +0100 |
commit | b73a042894580032505768ea94c58348c5e4bc79 (patch) | |
tree | 1a09f75d0a9ab5336df587d1db2a71c02d383afb | |
parent | 3077fb640b0df0b8d769aafd0d38e67d3307198b (diff) | |
download | mongo-b73a042894580032505768ea94c58348c5e4bc79.tar.gz |
SERVER-36198 Remove 'internalQueryAllowAllPathsIndexes' server parameter
28 files changed, 693 insertions, 960 deletions
diff --git a/etc/perf.yml b/etc/perf.yml index aee01faf84f..0f9fdbb3a2e 100644 --- a/etc/perf.yml +++ b/etc/perf.yml @@ -526,8 +526,6 @@ buildvariants: compile_flags: --ssl MONGO_DISTMOD=rhel62 -j$(grep -c ^processor /proc/cpuinfo) --release --variables-files=etc/scons/mongodbtoolchain_gcc.vars mongod_exec_wrapper: &exec_wrapper "numactl --physcpubind=4,5,6,7 -i 1" perf_exec_wrapper: &perf_wrapper "numactl --physcpubind=1,2,3 -i 0" - # TODO SERVER-36198: Remove the `--setParameter internalQueryAllowAllPathsIndexes=true` line - # when wildcard indexes are enabled by default. mongod_flags: >- --auth --fork @@ -537,7 +535,6 @@ buildvariants: --networkMessageCompressors noop --setParameter diagnosticDataCollectionEnabled=false --setParameter enableTestCommands=1 - --setParameter internalQueryAllowAllPathsIndexes=true --setParameter ttlMonitorEnabled=false --storageEngine inMemory --syncdelay 0 @@ -569,8 +566,6 @@ buildvariants: expansions: mongod_exec_wrapper: *exec_wrapper perf_exec_wrapper: *perf_wrapper - # TODO SERVER-36198: Remove the `--setParameter internalQueryAllowAllPathsIndexes=true` line - # when wildcard indexes are enabled by default. mongod_flags: >- --auth --fork @@ -582,7 +577,6 @@ buildvariants: --replSet test --setParameter diagnosticDataCollectionEnabled=false --setParameter enableTestCommands=1 - --setParameter internalQueryAllowAllPathsIndexes=true --setParameter ttlMonitorEnabled=false --storageEngine inMemory --syncdelay 0 diff --git a/jstests/concurrency/fsm_workloads/create_index_background_wildcard.js b/jstests/concurrency/fsm_workloads/create_index_background_wildcard.js index 631d5411fa3..5ad4f86e2d8 100644 --- a/jstests/concurrency/fsm_workloads/create_index_background_wildcard.js +++ b/jstests/concurrency/fsm_workloads/create_index_background_wildcard.js @@ -5,7 +5,6 @@ */ load('jstests/concurrency/fsm_libs/extend_workload.js'); // For extendWorkload. load('jstests/concurrency/fsm_workloads/create_index_background.js'); // For $config. -load('jstests/libs/discover_topology.js'); // For findDataBearingNodes(). var $config = extendWorkload($config, function($config, $super) { $config.data.getIndexSpec = function() { @@ -25,16 +24,6 @@ var $config = extendWorkload($config, function($config, $super) { }; $config.setup = function setup() { - // Enable the test flag required in order to build $** indexes on all data bearing nodes. - // TODO SERVER-36198: Remove this. - const hosts = DiscoverTopology.findDataBearingNodes(db.getMongo()); - for (let host of hosts) { - const conn = new Mongo(host); - const adminDB = conn.getDB("admin"); - assert.commandWorked( - adminDB.adminCommand({setParameter: 1, internalQueryAllowAllPathsIndexes: true})); - } - $super.setup.apply(this, arguments); }; diff --git a/jstests/concurrency/fsm_workloads/indexed_insert_wildcard.js b/jstests/concurrency/fsm_workloads/indexed_insert_wildcard.js index aaa0a1140f9..c1f08b7a876 100644 --- a/jstests/concurrency/fsm_workloads/indexed_insert_wildcard.js +++ b/jstests/concurrency/fsm_workloads/indexed_insert_wildcard.js @@ -8,20 +8,9 @@ */ load('jstests/concurrency/fsm_libs/extend_workload.js'); // For extendWorkload(). load('jstests/concurrency/fsm_workloads/indexed_insert_base.js'); // For $config(). -load('jstests/libs/discover_topology.js'); // For findDataBearingNodes(). var $config = extendWorkload($config, function($config, $super) { $config.setup = function init(db, collName) { - // Enable the test flag required in order to build $** indexes on all data bearing nodes. - // TODO SERVER-36198: Remove this. - const hosts = DiscoverTopology.findDataBearingNodes(db.getMongo()); - for (let host of hosts) { - const conn = new Mongo(host); - const adminDB = conn.getDB("admin"); - assert.commandWorked( - adminDB.adminCommand({setParameter: 1, internalQueryAllowAllPathsIndexes: true})); - } - $super.setup.apply(this, arguments); }; diff --git a/jstests/concurrency/fsm_workloads/reindex.js b/jstests/concurrency/fsm_workloads/reindex.js index 32ed49b8e69..ff9d0604ed5 100644 --- a/jstests/concurrency/fsm_workloads/reindex.js +++ b/jstests/concurrency/fsm_workloads/reindex.js @@ -96,24 +96,5 @@ var $config = (function() { query: {reIndex: 0.5, query: 0.5} }; - function setup(db, collName, cluster) { - // Enable the test flag required in order to build $** indexes on all data bearing nodes. - // TODO SERVER-36198: Remove this. - const hosts = DiscoverTopology.findDataBearingNodes(db.getMongo()); - for (let host of hosts) { - const conn = new Mongo(host); - const adminDB = conn.getDB("admin"); - assert.commandWorked( - adminDB.adminCommand({setParameter: 1, internalQueryAllowAllPathsIndexes: true})); - } - } - - return { - threadCount: 15, - iterations: 10, - states: states, - transitions: transitions, - data: data, - setup: setup - }; + return {threadCount: 15, iterations: 10, states: states, transitions: transitions, data: data}; })(); diff --git a/jstests/libs/discover_topology.js b/jstests/libs/discover_topology.js index bd99c240438..7fb1c79c427 100644 --- a/jstests/libs/discover_topology.js +++ b/jstests/libs/discover_topology.js @@ -126,36 +126,6 @@ var DiscoverTopology = (function() { } return findConnectedNodesViaMongos(conn, options); - }, - - /** - * Return a list of nodes that store data. - * TODO SERVER-36198: Remove this. - */ - findDataBearingNodes: function findDataBearingNodes(conn) { - const topology = DiscoverTopology.findConnectedNodes(conn); - const hostList = []; - if (topology.type === Topology.kStandalone) { - hostList.push(topology.mongod); - } else if (topology.type === Topology.kReplicaSet) { - hostList.push(...topology.nodes); - } else if (topology.type === Topology.kShardedCluster) { - for (let shardName of Object.keys(topology.shards)) { - const shard = topology.shards[shardName]; - - if (shard.type === Topology.kStandalone) { - hostList.push(shard.mongod); - } else if (shard.type === Topology.kReplicaSet) { - hostList.push(...shard.nodes); - } else { - throw new Error('Unrecognized topology format: ' + tojson(topology)); - } - } - } else { - throw new Error('Unrecognized topology format: ' + tojson(topology)); - } - - return hostList; } }; })(); diff --git a/jstests/multiVersion/wildcard_index_feature_compatability_version.js b/jstests/multiVersion/wildcard_index_feature_compatability_version.js index 315c5452ca8..22af526ac1b 100644 --- a/jstests/multiVersion/wildcard_index_feature_compatability_version.js +++ b/jstests/multiVersion/wildcard_index_feature_compatability_version.js @@ -16,11 +16,8 @@ TestData.skipCheckDBHashes = true; // Skip db hashes when restarting the replset. - const nodeOptions40 = {binVersion: "last-stable", setParameter: {}}; - const nodeOptions42 = { - binVersion: "latest", - setParameter: {internalQueryAllowAllPathsIndexes: 1} - }; + const nodeOptions40 = {binVersion: "last-stable"}; + const nodeOptions42 = {binVersion: "latest"}; // Set up a new replSet consisting of 3 nodes, initially running on 4.0 binaries. const rst = new ReplSetTest({nodes: 3, nodeOptions: nodeOptions40}); diff --git a/jstests/noPassthroughWithMongod/wildcard_and_text_indexes.js b/jstests/noPassthroughWithMongod/wildcard_and_text_indexes.js index 58677209ddf..945ef3502b7 100644 --- a/jstests/noPassthroughWithMongod/wildcard_and_text_indexes.js +++ b/jstests/noPassthroughWithMongod/wildcard_and_text_indexes.js @@ -39,61 +39,51 @@ assert.commandWorked(coll.insert({_id: 2, a: 1, _fts: 2, textToSearch: "bananas"})); assert.commandWorked(coll.insert({_id: 3, a: 1, _fts: 3})); - // Required in order to build $** indexes. - assert.commandWorked( - db.adminCommand({setParameter: 1, internalQueryAllowAllPathsIndexes: true})); - try { - // Build a wildcard index, and verify that it can be used to query for the field '_fts'. - assert.commandWorked(coll.createIndex({"$**": 1})); - assertWildcardQuery({_fts: {$gt: 0, $lt: 4}}, '_fts'); + // Build a wildcard index, and verify that it can be used to query for the field '_fts'. + assert.commandWorked(coll.createIndex({"$**": 1})); + assertWildcardQuery({_fts: {$gt: 0, $lt: 4}}, '_fts'); - // Perform the tests below for simple and compound $text indexes. - for (let textIndex of[{'$**': 'text'}, {a: 1, '$**': 'text'}]) { - // Build the appropriate text index. - assert.commandWorked(coll.createIndex(textIndex, {name: "textIndex"})); + // Perform the tests below for simple and compound $text indexes. + for (let textIndex of[{'$**': 'text'}, {a: 1, '$**': 'text'}]) { + // Build the appropriate text index. + assert.commandWorked(coll.createIndex(textIndex, {name: "textIndex"})); - // Confirm that the $** index can still be used to query for the '_fts' field outside of - // a $text query. - assertWildcardQuery({_fts: {$gt: 0, $lt: 4}}, '_fts'); + // Confirm that the $** index can still be used to query for the '_fts' field outside of + // $text queries. + assertWildcardQuery({_fts: {$gt: 0, $lt: 4}}, '_fts'); - // Confirm that $** does not generate a candidate plan for $text search, including cases - // 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(), explainOut.queryPlanner.winningPlan, "TEXT")); - assert.eq(explainOut.queryPlanner.rejectedPlans.length, 0); - assert.eq(explainOut.executionStats.nReturned, 2); + // Confirm that $** does not generate a candidate plan for $text search, including cases + // 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(), explainOut.queryPlanner.winningPlan, "TEXT")); + assert.eq(explainOut.queryPlanner.rejectedPlans.length, 0); + assert.eq(explainOut.executionStats.nReturned, 2); - // Confirm that $** does not generate a candidate plan for $text search, including cases - // 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(), explainOut.queryPlanner.winningPlan, "TEXT")); - assert.eq(explainOut.queryPlanner.rejectedPlans.length, 0); - assert.eq(explainOut.executionStats.nReturned, 2); + // Confirm that $** does not generate a candidate plan for $text search, including cases + // 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(), explainOut.queryPlanner.winningPlan, "TEXT")); + assert.eq(explainOut.queryPlanner.rejectedPlans.length, 0); + assert.eq(explainOut.executionStats.nReturned, 2); - // Confirm that the $** index can be used alongside a $text predicate in an $or. - explainOut = assert.commandWorked( - coll.find({$or: [{_fts: 3}, textQuery]}).explain("executionStats")); - assert.eq(explainOut.queryPlanner.rejectedPlans.length, 0); - assert.eq(explainOut.executionStats.nReturned, 3); + // Confirm that the $** index can be used alongside a $text predicate in an $or. + explainOut = assert.commandWorked( + coll.find({$or: [{_fts: 3}, textQuery]}).explain("executionStats")); + assert.eq(explainOut.queryPlanner.rejectedPlans.length, 0); + assert.eq(explainOut.executionStats.nReturned, 3); - const textOrWildcard = getPlanStages(explainOut.queryPlanner.winningPlan, "OR").shift(); - assert.eq(textOrWildcard.inputStages.length, 2); - const textBranch = (textOrWildcard.inputStages[0].stage === "TEXT" ? 0 : 1); - const wildcardBranch = (textBranch + 1) % 2; - assert.eq(textOrWildcard.inputStages[textBranch].stage, "TEXT"); - assert.eq(textOrWildcard.inputStages[wildcardBranch].stage, "IXSCAN"); - assert.eq(textOrWildcard.inputStages[wildcardBranch].keyPattern, {$_path: 1, _fts: 1}); + const textOrWildcard = getPlanStages(explainOut.queryPlanner.winningPlan, "OR").shift(); + assert.eq(textOrWildcard.inputStages.length, 2); + const textBranch = (textOrWildcard.inputStages[0].stage === "TEXT" ? 0 : 1); + const wildcardBranch = (textBranch + 1) % 2; + assert.eq(textOrWildcard.inputStages[textBranch].stage, "TEXT"); + assert.eq(textOrWildcard.inputStages[wildcardBranch].stage, "IXSCAN"); + assert.eq(textOrWildcard.inputStages[wildcardBranch].keyPattern, {$_path: 1, _fts: 1}); - // Drop the index so that a different text index can be created. - assert.commandWorked(coll.dropIndex("textIndex")); - } - } finally { - // Disable $** indexes once the tests have either completed or failed. - assert.commandWorked( - db.adminCommand({setParameter: 1, internalQueryAllowAllPathsIndexes: false})); + // Drop the index so that a different text index can be created. + assert.commandWorked(coll.dropIndex("textIndex")); } })(); diff --git a/jstests/noPassthroughWithMongod/wildcard_index_basic_index_bounds.js b/jstests/noPassthroughWithMongod/wildcard_index_basic_index_bounds.js index 54fdcdfba58..776c9d9fc50 100644 --- a/jstests/noPassthroughWithMongod/wildcard_index_basic_index_bounds.js +++ b/jstests/noPassthroughWithMongod/wildcard_index_basic_index_bounds.js @@ -196,33 +196,23 @@ } } - // Required in order to build $** indexes. - assert.commandWorked( - db.adminCommand({setParameter: 1, internalQueryAllowAllPathsIndexes: true})); - - try { - // Test a $** index that indexes the entire document. - runWildcardIndexTest({'$**': 1}, null, ['a', 'b.c', 'b.d.e', 'b.f']); - - // Test a $** index on a single subtree. - runWildcardIndexTest({'a.$**': 1}, null, ['a']); - runWildcardIndexTest({'b.$**': 1}, null, ['b.c', 'b.d.e', 'b.f']); - runWildcardIndexTest({'b.d.$**': 1}, null, ['b.d.e']); - - // Test a $** index which includes a subset of paths. - runWildcardIndexTest({'$**': 1}, {a: 1}, ['a']); - runWildcardIndexTest({'$**': 1}, {b: 1}, ['b.c', 'b.d.e', 'b.f']); - runWildcardIndexTest({'$**': 1}, {'b.d': 1}, ['b.d.e']); - runWildcardIndexTest({'$**': 1}, {a: 1, 'b.d': 1}, ['a', 'b.d.e']); - - // Test a $** index which excludes a subset of paths. - runWildcardIndexTest({'$**': 1}, {a: 0}, ['b.c', 'b.d.e', 'b.f']); - runWildcardIndexTest({'$**': 1}, {b: 0}, ['a']); - runWildcardIndexTest({'$**': 1}, {'b.d': 0}, ['a', 'b.c', 'b.f']); - runWildcardIndexTest({'$**': 1}, {a: 0, 'b.d': 0}, ['b.c', 'b.f']); - } finally { - // Disable $** indexes once the tests have either completed or failed. - assert.commandWorked( - db.adminCommand({setParameter: 1, internalQueryAllowAllPathsIndexes: false})); - } + // Test a $** index that indexes the entire document. + runWildcardIndexTest({'$**': 1}, null, ['a', 'b.c', 'b.d.e', 'b.f']); + + // Test a $** index on a single subtree. + runWildcardIndexTest({'a.$**': 1}, null, ['a']); + runWildcardIndexTest({'b.$**': 1}, null, ['b.c', 'b.d.e', 'b.f']); + runWildcardIndexTest({'b.d.$**': 1}, null, ['b.d.e']); + + // Test a $** index which includes a subset of paths. + runWildcardIndexTest({'$**': 1}, {a: 1}, ['a']); + runWildcardIndexTest({'$**': 1}, {b: 1}, ['b.c', 'b.d.e', 'b.f']); + runWildcardIndexTest({'$**': 1}, {'b.d': 1}, ['b.d.e']); + runWildcardIndexTest({'$**': 1}, {a: 1, 'b.d': 1}, ['a', 'b.d.e']); + + // Test a $** index which excludes a subset of paths. + runWildcardIndexTest({'$**': 1}, {a: 0}, ['b.c', 'b.d.e', 'b.f']); + runWildcardIndexTest({'$**': 1}, {b: 0}, ['a']); + runWildcardIndexTest({'$**': 1}, {'b.d': 0}, ['a', 'b.c', 'b.f']); + runWildcardIndexTest({'$**': 1}, {a: 0, 'b.d': 0}, ['b.c', 'b.f']); })(); diff --git a/jstests/noPassthroughWithMongod/wildcard_index_cached_plans.js b/jstests/noPassthroughWithMongod/wildcard_index_cached_plans.js index dea4b0733d1..704ce77cd7e 100644 --- a/jstests/noPassthroughWithMongod/wildcard_index_cached_plans.js +++ b/jstests/noPassthroughWithMongod/wildcard_index_cached_plans.js @@ -7,9 +7,6 @@ load('jstests/libs/analyze_plan.js'); // For getPlanStage(). - assert.commandWorked( - db.adminCommand({setParameter: 1, internalQueryAllowAllPathsIndexes: true})); - const coll = db.wildcard_cached_plans; coll.drop(); assert.commandWorked(coll.createIndex({"b.$**": 1})); diff --git a/jstests/noPassthroughWithMongod/wildcard_index_collation.js b/jstests/noPassthroughWithMongod/wildcard_index_collation.js index 92addd75de6..6f31c12bafb 100644 --- a/jstests/noPassthroughWithMongod/wildcard_index_collation.js +++ b/jstests/noPassthroughWithMongod/wildcard_index_collation.js @@ -49,80 +49,71 @@ tojson(collation) + " not found: " + tojson(indexSpecs)); } - try { - // Required in order to build $** indexes. - assert.commandWorked( - db.adminCommand({setParameter: 1, internalQueryAllowAllPathsIndexes: true})); - - // Recreate the collection with a default case-insensitive collation. - assert.commandWorked( - db.createCollection(coll.getName(), {collation: {locale: "en_US", strength: 1}})); - - // Confirm that the $** index inherits the collection's default collation. - assert.commandWorked(coll.createIndex({"$**": 1})); - assertIndexHasCollation({"$**": 1}, { - locale: "en_US", - caseLevel: false, - caseFirst: "off", - strength: 1, - numericOrdering: false, - alternate: "non-ignorable", - maxVariable: "punct", - normalization: false, - backwards: false, - version: "57.1", - }); - - // Insert a series of documents whose fieldnames and values differ only by case. - assert.commandWorked(coll.insert({a: {b: "string", c: "STRING"}, d: "sTrInG", e: 5})); - assert.commandWorked(coll.insert({a: {b: "STRING", c: "string"}, d: "StRiNg", e: 5})); - assert.commandWorked(coll.insert({A: {B: "string", C: "STRING"}, d: "sTrInG", E: 5})); - assert.commandWorked(coll.insert({A: {B: "STRING", C: "string"}, d: "StRiNg", E: 5})); - - // Confirm that only the document's values adhere to the case-insensitive collation. The - // field paths, which are also present in the $** index keys, are evaluated using simple - // binary comparison; so for instance, path "a.b" does *not* match path "A.B". - assertWildcardIndexAnswersQuery({"a.b": "string"}, [ - {a: {b: "string", c: "STRING"}, d: "sTrInG", e: 5}, - {a: {b: "STRING", c: "string"}, d: "StRiNg", e: 5} - ]); - assertWildcardIndexAnswersQuery({"A.B": "string"}, [ - {A: {B: "string", C: "STRING"}, d: "sTrInG", E: 5}, - {A: {B: "STRING", C: "string"}, d: "StRiNg", E: 5} - ]); - - // All documents in the collection are returned if we query over both upper- and lower-case - // fieldnames, or when the fieldname has a consistent case across all documents. - const allDocs = coll.find({}, {_id: 0}).toArray(); - assertWildcardIndexAnswersQuery({$or: [{"a.c": "string"}, {"A.C": "string"}]}, allDocs); - assertWildcardIndexAnswersQuery({d: "string"}, allDocs); - - // Confirm that the $** index also differentiates between upper and lower fieldname case - // when querying fields which do not contain string values. - assertWildcardIndexAnswersQuery({e: 5}, [ - {a: {b: "string", c: "STRING"}, d: "sTrInG", e: 5}, - {a: {b: "STRING", c: "string"}, d: "StRiNg", e: 5} - ]); - assertWildcardIndexAnswersQuery({E: 5}, [ - {A: {B: "string", C: "STRING"}, d: "sTrInG", E: 5}, - {A: {B: "STRING", C: "string"}, d: "StRiNg", E: 5} - ]); - - // Confirm that the $** index produces a covered plan for a query on non-string, non-object, - // non-array values. - assert(isIndexOnly(coll.getDB(), winningPlan({e: 5}, {_id: 0, e: 1}))); - assert(isIndexOnly(coll.getDB(), winningPlan({E: 5}, {_id: 0, E: 1}))); - - // Confirm that the $** index differentiates fieldname case when attempting to cover. - assert(!isIndexOnly(coll.getDB(), winningPlan({e: 5}, {_id: 0, E: 1}))); - assert(!isIndexOnly(coll.getDB(), winningPlan({E: 5}, {_id: 0, e: 1}))); - - // Confirm that attempting to project the virtual $_path field which is present in $** index - // keys produces a non-covered solution, which nonetheless returns the correct results. - assert(!isIndexOnly(coll.getDB(), winningPlan({e: 5}, {_id: 0, e: 1, $_path: 1}))); - assertWildcardIndexAnswersQuery({e: 5}, [{e: 5}, {e: 5}], {_id: 0, e: 1, $_path: 1}); - } finally { - // Disable $** indexes once the tests have either completed or failed. - db.adminCommand({setParameter: 1, internalQueryAllowAllPathsIndexes: false}); - } + // Recreate the collection with a default case-insensitive collation. + assert.commandWorked( + db.createCollection(coll.getName(), {collation: {locale: "en_US", strength: 1}})); + + // Confirm that the $** index inherits the collection's default collation. + assert.commandWorked(coll.createIndex({"$**": 1})); + assertIndexHasCollation({"$**": 1}, { + locale: "en_US", + caseLevel: false, + caseFirst: "off", + strength: 1, + numericOrdering: false, + alternate: "non-ignorable", + maxVariable: "punct", + normalization: false, + backwards: false, + version: "57.1", + }); + + // Insert a series of documents whose fieldnames and values differ only by case. + assert.commandWorked(coll.insert({a: {b: "string", c: "STRING"}, d: "sTrInG", e: 5})); + assert.commandWorked(coll.insert({a: {b: "STRING", c: "string"}, d: "StRiNg", e: 5})); + assert.commandWorked(coll.insert({A: {B: "string", C: "STRING"}, d: "sTrInG", E: 5})); + assert.commandWorked(coll.insert({A: {B: "STRING", C: "string"}, d: "StRiNg", E: 5})); + + // Confirm that only the document's values adhere to the case-insensitive collation. The field + // paths, which are also present in the $** index keys, are evaluated using simple binary + // comparison; so for instance, path "a.b" does *not* match path "A.B". + assertWildcardIndexAnswersQuery({"a.b": "string"}, [ + {a: {b: "string", c: "STRING"}, d: "sTrInG", e: 5}, + {a: {b: "STRING", c: "string"}, d: "StRiNg", e: 5} + ]); + assertWildcardIndexAnswersQuery({"A.B": "string"}, [ + {A: {B: "string", C: "STRING"}, d: "sTrInG", E: 5}, + {A: {B: "STRING", C: "string"}, d: "StRiNg", E: 5} + ]); + + // All documents in the collection are returned if we query over both upper- and lower-case + // fieldnames, or when the fieldname has a consistent case across all documents. + const allDocs = coll.find({}, {_id: 0}).toArray(); + assertWildcardIndexAnswersQuery({$or: [{"a.c": "string"}, {"A.C": "string"}]}, allDocs); + assertWildcardIndexAnswersQuery({d: "string"}, allDocs); + + // Confirm that the $** index also differentiates between upper and lower fieldname case when + // querying fields which do not contain string values. + assertWildcardIndexAnswersQuery({e: 5}, [ + {a: {b: "string", c: "STRING"}, d: "sTrInG", e: 5}, + {a: {b: "STRING", c: "string"}, d: "StRiNg", e: 5} + ]); + assertWildcardIndexAnswersQuery({E: 5}, [ + {A: {B: "string", C: "STRING"}, d: "sTrInG", E: 5}, + {A: {B: "STRING", C: "string"}, d: "StRiNg", E: 5} + ]); + + // Confirm that the $** index produces a covered plan for a query on non-string, non-object, + // non-array values. + assert(isIndexOnly(coll.getDB(), winningPlan({e: 5}, {_id: 0, e: 1}))); + assert(isIndexOnly(coll.getDB(), winningPlan({E: 5}, {_id: 0, E: 1}))); + + // Confirm that the $** index differentiates fieldname case when attempting to cover. + assert(!isIndexOnly(coll.getDB(), winningPlan({e: 5}, {_id: 0, E: 1}))); + assert(!isIndexOnly(coll.getDB(), winningPlan({E: 5}, {_id: 0, e: 1}))); + + // Confirm that attempting to project the virtual $_path field which is present in $** index + // keys produces a non-covered solution, which nonetheless returns the correct results. + assert(!isIndexOnly(coll.getDB(), winningPlan({e: 5}, {_id: 0, e: 1, $_path: 1}))); + assertWildcardIndexAnswersQuery({e: 5}, [{e: 5}, {e: 5}], {_id: 0, e: 1, $_path: 1}); })();
\ No newline at end of file diff --git a/jstests/noPassthroughWithMongod/wildcard_index_covered_queries.js b/jstests/noPassthroughWithMongod/wildcard_index_covered_queries.js index ecb182d45af..b70a0d1e5a3 100644 --- a/jstests/noPassthroughWithMongod/wildcard_index_covered_queries.js +++ b/jstests/noPassthroughWithMongod/wildcard_index_covered_queries.js @@ -44,51 +44,41 @@ coll.find(query, proj).hint({$natural: 1}).toArray()); } - try { - // Required in order to build $** indexes. - assert.commandWorked( - db.adminCommand({setParameter: 1, internalQueryAllowAllPathsIndexes: true})); - - // Create a new collection and build a $** index on it. - const bulk = coll.initializeUnorderedBulkOp(); - for (let i = 0; i < 200; i++) { - bulk.insert({a: {b: i, c: `${(i+1)}`}, d: (i + 2)}); - } - assert.commandWorked(bulk.execute()); - assert.commandWorked(coll.createIndex({"$**": 1})); - - // Verify that the $** index can cover an exact match on an integer value. - assertWildcardProvidesCoveredSolution({"a.b": 10}, {_id: 0, "a.b": 1}); - - // Verify that the $** index can cover an exact match on a string value. - assertWildcardProvidesCoveredSolution({"a.c": "10"}, {_id: 0, "a.c": 1}); - - // Verify that the $** index can cover a range query for integer values. - assertWildcardProvidesCoveredSolution({"a.b": {$gt: 10, $lt: 99}}, {_id: 0, "a.b": 1}); - - // Verify that the $** index can cover a range query for string values. - assertWildcardProvidesCoveredSolution({"a.c": {$gt: "10", $lt: "99"}}, {_id: 0, "a.c": 1}); - - // Verify that the $** index can cover an $in query for integer values. - assertWildcardProvidesCoveredSolution({"a.b": {$in: [0, 50, 100, 150]}}, - {_id: 0, "a.b": 1}); - - // Verify that the $** index can cover an $in query for string values. - assertWildcardProvidesCoveredSolution({"a.c": {$in: ["0", "50", "100", "150"]}}, - {_id: 0, "a.c": 1}); - - // Verify that attempting to project the virtual $_path field from the $** keyPattern will - // fail to do so and will instead produce a non-covered query. However, this query will - // nonetheless output the correct results. - const shouldFailToCover = true; - assertWildcardProvidesCoveredSolution( - {d: {$in: [0, 25, 50, 75, 100]}}, {_id: 0, d: 1, $_path: 1}, shouldFailToCover); - - // Verify that predicates which produce inexact-fetch bounds are not covered by a $** index. - assertWildcardProvidesCoveredSolution( - {d: {$elemMatch: {$eq: 50}}}, {_id: 0, d: 1}, shouldFailToCover); - } finally { - // Disable $** indexes once the tests have either completed or failed. - db.adminCommand({setParameter: 1, internalQueryAllowAllPathsIndexes: false}); + // Create a new collection and build a $** index on it. + const bulk = coll.initializeUnorderedBulkOp(); + for (let i = 0; i < 200; i++) { + bulk.insert({a: {b: i, c: `${(i+1)}`}, d: (i + 2)}); } + assert.commandWorked(bulk.execute()); + assert.commandWorked(coll.createIndex({"$**": 1})); + + // Verify that the $** index can cover an exact match on an integer value. + assertWildcardProvidesCoveredSolution({"a.b": 10}, {_id: 0, "a.b": 1}); + + // Verify that the $** index can cover an exact match on a string value. + assertWildcardProvidesCoveredSolution({"a.c": "10"}, {_id: 0, "a.c": 1}); + + // Verify that the $** index can cover a range query for integer values. + assertWildcardProvidesCoveredSolution({"a.b": {$gt: 10, $lt: 99}}, {_id: 0, "a.b": 1}); + + // Verify that the $** index can cover a range query for string values. + assertWildcardProvidesCoveredSolution({"a.c": {$gt: "10", $lt: "99"}}, {_id: 0, "a.c": 1}); + + // Verify that the $** index can cover an $in query for integer values. + assertWildcardProvidesCoveredSolution({"a.b": {$in: [0, 50, 100, 150]}}, {_id: 0, "a.b": 1}); + + // Verify that the $** index can cover an $in query for string values. + assertWildcardProvidesCoveredSolution({"a.c": {$in: ["0", "50", "100", "150"]}}, + {_id: 0, "a.c": 1}); + + // Verify that attempting to project the virtual $_path field from the $** keyPattern will fail + // to do so and will instead produce a non-covered query. However, this query will nonetheless + // output the correct results. + const shouldFailToCover = true; + assertWildcardProvidesCoveredSolution( + {d: {$in: [0, 25, 50, 75, 100]}}, {_id: 0, d: 1, $_path: 1}, shouldFailToCover); + + // Verify that predicates which produce inexact-fetch bounds are not covered by a $** index. + assertWildcardProvidesCoveredSolution( + {d: {$elemMatch: {$eq: 50}}}, {_id: 0, d: 1}, shouldFailToCover); })();
\ No newline at end of file diff --git a/jstests/noPassthroughWithMongod/wildcard_index_empty_arrays.js b/jstests/noPassthroughWithMongod/wildcard_index_empty_arrays.js index 77b20b5ba8e..7b5e763bbad 100644 --- a/jstests/noPassthroughWithMongod/wildcard_index_empty_arrays.js +++ b/jstests/noPassthroughWithMongod/wildcard_index_empty_arrays.js @@ -11,40 +11,31 @@ const assertArrayEq = (l, r) => assert(arrayEq(l, r), tojson(l) + " != " + tojson(r)); - // Required in order to build $** indexes. - assert.commandWorked( - db.adminCommand({setParameter: 1, internalQueryAllowAllPathsIndexes: true})); - - try { - const indexWildcard = {"$**": 1}; - assert.commandWorked(coll.createIndex(indexWildcard)); - - assert.commandWorked(coll.insert({a: 1, b: 1, c: [], d: {e: [5, 6]}})); - assert.commandWorked(coll.insert({a: 2, b: 2, c: [1, 2], d: {e: []}})); - assert.commandWorked(coll.insert({a: 1, b: 2, c: [3, 4], d: {e: [7, 8]}, f: [{g: []}]})); - assert.commandWorked(coll.insert({a: 2, b: [[]], c: 1, d: 4})); - - // $** index matches empty array. - assertArrayEq(coll.find({c: []}, {_id: 0}).hint(indexWildcard).toArray(), - [{a: 1, b: 1, c: [], d: {e: [5, 6]}}]); - - // $** index supports equality to array offset. - assertArrayEq(coll.find({"c.0": 1}, {_id: 0}).hint(indexWildcard).toArray(), - [{a: 2, b: 2, c: [1, 2], d: {e: []}}]); - - // $** index matches empty array nested in object. - assertArrayEq(coll.find({"d.e": []}, {_id: 0}).hint(indexWildcard).toArray(), - [{a: 2, b: 2, c: [1, 2], d: {e: []}}]); - - // $** index matches empty array nested within an array of objects. - assertArrayEq(coll.find({"f.0.g": []}, {_id: 0}).hint(indexWildcard).toArray(), - [{a: 1, b: 2, c: [3, 4], d: {e: [7, 8]}, f: [{g: []}]}]); - - // $** index matches empty array nested within an array. - assertArrayEq(coll.find({"b": []}, {_id: 0}).hint(indexWildcard).toArray(), - [{a: 2, b: [[]], c: 1, d: 4}]); - } finally { - // Disable $** indexes once the tests have either completed or failed. - db.adminCommand({setParameter: 1, internalQueryAllowAllPathsIndexes: false}); - } + const indexWildcard = {"$**": 1}; + assert.commandWorked(coll.createIndex(indexWildcard)); + + assert.commandWorked(coll.insert({a: 1, b: 1, c: [], d: {e: [5, 6]}})); + assert.commandWorked(coll.insert({a: 2, b: 2, c: [1, 2], d: {e: []}})); + assert.commandWorked(coll.insert({a: 1, b: 2, c: [3, 4], d: {e: [7, 8]}, f: [{g: []}]})); + assert.commandWorked(coll.insert({a: 2, b: [[]], c: 1, d: 4})); + + // $** index matches empty array. + assertArrayEq(coll.find({c: []}, {_id: 0}).hint(indexWildcard).toArray(), + [{a: 1, b: 1, c: [], d: {e: [5, 6]}}]); + + // $** index supports equality to array offset. + assertArrayEq(coll.find({"c.0": 1}, {_id: 0}).hint(indexWildcard).toArray(), + [{a: 2, b: 2, c: [1, 2], d: {e: []}}]); + + // $** index matches empty array nested in object. + assertArrayEq(coll.find({"d.e": []}, {_id: 0}).hint(indexWildcard).toArray(), + [{a: 2, b: 2, c: [1, 2], d: {e: []}}]); + + // $** index matches empty array nested within an array of objects. + assertArrayEq(coll.find({"f.0.g": []}, {_id: 0}).hint(indexWildcard).toArray(), + [{a: 1, b: 2, c: [3, 4], d: {e: [7, 8]}, f: [{g: []}]}]); + + // $** index matches empty array nested within an array. + assertArrayEq(coll.find({"b": []}, {_id: 0}).hint(indexWildcard).toArray(), + [{a: 2, b: [[]], c: 1, d: 4}]); })();
\ No newline at end of file diff --git a/jstests/noPassthroughWithMongod/wildcard_index_equality_to_empty_obj.js b/jstests/noPassthroughWithMongod/wildcard_index_equality_to_empty_obj.js index cdc0d34a28f..10e1b1186ce 100644 --- a/jstests/noPassthroughWithMongod/wildcard_index_equality_to_empty_obj.js +++ b/jstests/noPassthroughWithMongod/wildcard_index_equality_to_empty_obj.js @@ -23,69 +23,56 @@ {_id: 10, a: [0, {b: {}}]}, ])); - // Required in order to build $** indexes. - assert.commandWorked( - db.adminCommand({setParameter: 1, internalQueryAllowAllPathsIndexes: true})); - try { - assert.commandWorked(coll.createIndex({"$**": 1})); + assert.commandWorked(coll.createIndex({"$**": 1})); - // Test that a comparison to empty object query returns the expected results when the $** - // index is hinted. - let results = coll.find({a: {}}, {_id: 1}).sort({_id: 1}).hint({"$**": 1}).toArray(); - assert.eq(results, [{_id: 3}, {_id: 4}, {_id: 6}]); + // Test that a comparison to empty object query returns the expected results when the $** index + // is hinted. + let results = coll.find({a: {}}, {_id: 1}).sort({_id: 1}).hint({"$**": 1}).toArray(); + assert.eq(results, [{_id: 3}, {_id: 4}, {_id: 6}]); - // Result set should be the same as when hinting a COLLSCAN and with no hint. - assert.eq(results, - coll.find({a: {}}, {_id: 1}).sort({_id: 1}).hint({$natural: 1}).toArray()); - assert.eq(results, coll.find({a: {}}, {_id: 1}).sort({_id: 1}).toArray()); + // Result set should be the same as when hinting a COLLSCAN and with no hint. + assert.eq(results, coll.find({a: {}}, {_id: 1}).sort({_id: 1}).hint({$natural: 1}).toArray()); + assert.eq(results, coll.find({a: {}}, {_id: 1}).sort({_id: 1}).toArray()); - // Repeat the above query, but express it using $lte:{}, which is a synonym for $eq:{}. - results = coll.find({a: {$lte: {}}}, {_id: 1}).sort({_id: 1}).hint({"$**": 1}).toArray(); - assert.eq(results, [{_id: 3}, {_id: 4}, {_id: 6}]); - assert.eq( - results, - coll.find({a: {$lte: {}}}, {_id: 1}).sort({_id: 1}).hint({$natural: 1}).toArray()); - assert.eq(results, coll.find({a: {$lte: {}}}, {_id: 1}).sort({_id: 1}).toArray()); + // Repeat the above query, but express it using $lte:{}, which is a synonym for $eq:{}. + results = coll.find({a: {$lte: {}}}, {_id: 1}).sort({_id: 1}).hint({"$**": 1}).toArray(); + assert.eq(results, [{_id: 3}, {_id: 4}, {_id: 6}]); + assert.eq(results, + coll.find({a: {$lte: {}}}, {_id: 1}).sort({_id: 1}).hint({$natural: 1}).toArray()); + assert.eq(results, coll.find({a: {$lte: {}}}, {_id: 1}).sort({_id: 1}).toArray()); - // Test that an inequality to empty object query results in an error when the $** index is - // hinted. - assert.throws( - () => coll.find({a: {$gte: {}}}, {_id: 1}).sort({_id: 1}).hint({"$**": 1}).toArray()); + // Test that an inequality to empty object query results in an error when the $** index is + // hinted. + assert.throws( + () => coll.find({a: {$gte: {}}}, {_id: 1}).sort({_id: 1}).hint({"$**": 1}).toArray()); - // Test that an inequality to empty object query returns the expected results in the - // presence of the $** index. - results = coll.find({a: {$gte: {}}}, {_id: 1}).sort({_id: 1}).toArray(); - assert.eq(results, [{_id: 3}, {_id: 4}, {_id: 6}, {_id: 7}, {_id: 9}, {_id: 10}]); + // Test that an inequality to empty object query returns the expected results in the presence of + // the $** index. + results = coll.find({a: {$gte: {}}}, {_id: 1}).sort({_id: 1}).toArray(); + assert.eq(results, [{_id: 3}, {_id: 4}, {_id: 6}, {_id: 7}, {_id: 9}, {_id: 10}]); - // Result set should be the same as when hinting a COLLSCAN and with no hint. - assert.eq( - results, - coll.find({a: {$gte: {}}}, {_id: 1}).sort({_id: 1}).hint({$natural: 1}).toArray()); - assert.eq(results, coll.find({a: {$gte: {}}}, {_id: 1}).sort({_id: 1}).toArray()); + // Result set should be the same as when hinting a COLLSCAN and with no hint. + assert.eq(results, + coll.find({a: {$gte: {}}}, {_id: 1}).sort({_id: 1}).hint({$natural: 1}).toArray()); + assert.eq(results, coll.find({a: {$gte: {}}}, {_id: 1}).sort({_id: 1}).toArray()); - // Test that an $in with an empty object returns the expected results when the $** index is - // hinted. - results = - coll.find({a: {$in: [3, {}]}}, {_id: 1}).sort({_id: 1}).hint({"$**": 1}).toArray(); - assert.eq(results, [{_id: 3}, {_id: 4}, {_id: 6}, {_id: 8}]); + // Test that an $in with an empty object returns the expected results when the $** index is + // hinted. + results = coll.find({a: {$in: [3, {}]}}, {_id: 1}).sort({_id: 1}).hint({"$**": 1}).toArray(); + assert.eq(results, [{_id: 3}, {_id: 4}, {_id: 6}, {_id: 8}]); - // Result set should be the same as when hinting a COLLSCAN and with no hint. - assert.eq( - results, - coll.find({a: {$in: [3, {}]}}, {_id: 1}).sort({_id: 1}).hint({$natural: 1}).toArray()); - assert.eq(results, coll.find({a: {$in: [3, {}]}}, {_id: 1}).sort({_id: 1}).toArray()); + // Result set should be the same as when hinting a COLLSCAN and with no hint. + assert.eq( + results, + coll.find({a: {$in: [3, {}]}}, {_id: 1}).sort({_id: 1}).hint({$natural: 1}).toArray()); + assert.eq(results, coll.find({a: {$in: [3, {}]}}, {_id: 1}).sort({_id: 1}).toArray()); - // Test that a wildcard index can support equality to an empty object on a dotted field. - results = coll.find({"a.b": {$eq: {}}}, {_id: 1}).sort({_id: 1}).hint({"$**": 1}).toArray(); - assert.eq(results, [{_id: 9}, {_id: 10}]); + // Test that a wildcard index can support equality to an empty object on a dotted field. + results = coll.find({"a.b": {$eq: {}}}, {_id: 1}).sort({_id: 1}).hint({"$**": 1}).toArray(); + assert.eq(results, [{_id: 9}, {_id: 10}]); - // Result set should be the same as when hinting a COLLSCAN and with no hint. - assert.eq( - results, - coll.find({"a.b": {$eq: {}}}, {_id: 1}).sort({_id: 1}).hint({$natural: 1}).toArray()); - assert.eq(results, coll.find({"a.b": {$eq: {}}}, {_id: 1}).sort({_id: 1}).toArray()); - } finally { - // Disable $** indexes once the tests have either completed or failed. - db.adminCommand({setParameter: 1, internalQueryAllowAllPathsIndexes: false}); - } + // Result set should be the same as when hinting a COLLSCAN and with no hint. + assert.eq(results, + coll.find({"a.b": {$eq: {}}}, {_id: 1}).sort({_id: 1}).hint({$natural: 1}).toArray()); + assert.eq(results, coll.find({"a.b": {$eq: {}}}, {_id: 1}).sort({_id: 1}).toArray()); }()); diff --git a/jstests/noPassthroughWithMongod/wildcard_index_filter.js b/jstests/noPassthroughWithMongod/wildcard_index_filter.js index 3cd0d535e91..f8dd7182267 100644 --- a/jstests/noPassthroughWithMongod/wildcard_index_filter.js +++ b/jstests/noPassthroughWithMongod/wildcard_index_filter.js @@ -44,58 +44,45 @@ assert.eq(planStage.indexName, expectedIndexName, tojson(planStage)); } - // Required in order to build $** indexes. - assert.commandWorked( - db.adminCommand({setParameter: 1, internalQueryAllowAllPathsIndexes: true})); + const indexWildcard = {"$**": 1}; + const indexA = {"a": 1}; + assert.commandWorked(coll.createIndex(indexWildcard)); + assert.commandWorked(coll.createIndex(indexA)); - try { - const indexWildcard = {"$**": 1}; - const indexA = {"a": 1}; - assert.commandWorked(coll.createIndex(indexWildcard)); - assert.commandWorked(coll.createIndex(indexA)); + assert.commandWorked(coll.insert({a: "a"})); - assert.commandWorked(coll.insert({a: "a"})); + // Filtering on $** index. $** index is used over another index. + assertExpectedIndexAnswersQueryWithFilter({a: "a"}, [indexWildcard], {a: "a"}, "$**_1"); - // Filtering on $** index. $** index is used over another index. - assertExpectedIndexAnswersQueryWithFilter({a: "a"}, [indexWildcard], {a: "a"}, "$**_1"); + // Filtering on regular index. $** index is not used over another index. + assertExpectedIndexAnswersQueryWithFilter({a: "a"}, [indexA], {a: "a"}, "a_1"); - // Filtering on regular index. $** index is not used over another index. - assertExpectedIndexAnswersQueryWithFilter({a: "a"}, [indexA], {a: "a"}, "a_1"); + assert.commandWorked(coll.insert({a: "a", b: "b"})); - assert.commandWorked(coll.insert({a: "a", b: "b"})); + const indexAB = {"a": 1, "b": 1}; + assert.commandWorked(coll.createIndex(indexAB)); - const indexAB = {"a": 1, "b": 1}; - assert.commandWorked(coll.createIndex(indexAB)); + // Filtering on $** index. $** index is used over another index for compound query. + assertExpectedIndexAnswersQueryWithFilter( + {a: "a", b: "b"}, [indexWildcard], {a: "a", b: "b"}, "$**_1"); - // Filtering on $** index. $** index is used over another index for compound query. - assertExpectedIndexAnswersQueryWithFilter( - {a: "a", b: "b"}, [indexWildcard], {a: "a", b: "b"}, "$**_1"); + // Filtering on regular compound index. Check that $** index is not used over another index + // for compound query. + assertExpectedIndexAnswersQueryWithFilter( + {a: "a", b: "b"}, [indexAB], {a: "a", b: "b"}, "a_1_b_1"); - // Filtering on regular compound index. Check that $** index is not used over another index - // for compound query. - assertExpectedIndexAnswersQueryWithFilter( - {a: "a", b: "b"}, [indexAB], {a: "a", b: "b"}, "a_1_b_1"); + // Filtering on $** index while hinting on another index. Index filter is prioritized. + assertExpectedIndexAnswersQueryWithFilter({a: "a"}, [indexWildcard], {a: "a"}, "$**_1", indexA); - // Filtering on $** index while hinting on another index. Index filter is prioritized. - assertExpectedIndexAnswersQueryWithFilter( - {a: "a"}, [indexWildcard], {a: "a"}, "$**_1", indexA); + // Filtering on regular index while hinting on $** index. Index filter is prioritized. + assertExpectedIndexAnswersQueryWithFilter({a: "a"}, [indexA], {a: "a"}, "a_1", indexWildcard); - // Filtering on regular index while hinting on $** index. Index filter is prioritized. - assertExpectedIndexAnswersQueryWithFilter( - {a: "a"}, [indexA], {a: "a"}, "a_1", indexWildcard); + // Index filter for $** index does not apply when query does not match filter query shape. + assertExpectedIndexAnswersQueryWithFilter({b: "b"}, [indexWildcard], {a: "a"}, "a_1", indexA); - // Index filter for $** index does not apply when query does not match filter query shape. - assertExpectedIndexAnswersQueryWithFilter( - {b: "b"}, [indexWildcard], {a: "a"}, "a_1", indexA); + const indexAWildcard = {"a.$**": 1}; + assert.commandWorked(coll.createIndex(indexAWildcard)); - const indexAWildcard = {"a.$**": 1}; - assert.commandWorked(coll.createIndex(indexAWildcard)); - - // Filtering on a path specified $** index. Check that the $** is used over other indices. - assertExpectedIndexAnswersQueryWithFilter({a: "a"}, [indexAWildcard], {a: "a"}, "a.$**_1"); - } finally { - // Disable $** indexes once the tests have either completed or failed. - assert.commandWorked( - db.adminCommand({setParameter: 1, internalQueryAllowAllPathsIndexes: false})); - } + // Filtering on a path specified $** index. Check that the $** is used over other indices. + assertExpectedIndexAnswersQueryWithFilter({a: "a"}, [indexAWildcard], {a: "a"}, "a.$**_1"); })();
\ No newline at end of file diff --git a/jstests/noPassthroughWithMongod/wildcard_index_hint.js b/jstests/noPassthroughWithMongod/wildcard_index_hint.js index f4a8d8fb878..9f88e248c3d 100644 --- a/jstests/noPassthroughWithMongod/wildcard_index_hint.js +++ b/jstests/noPassthroughWithMongod/wildcard_index_hint.js @@ -30,92 +30,77 @@ assertArrayEq(wildcardResults, expectedResults); } - assert.commandWorked( - db.adminCommand({setParameter: 1, internalQueryAllowAllPathsIndexes: true})); - - try { - assert.commandWorked(db.createCollection(coll.getName())); - - // Check that error is thrown if the hinted index doesn't exist. - assert.commandFailedWithCode( - db.runCommand({find: coll.getName(), filter: {"a": 1}, hint: {"$**": 1}}), - ErrorCodes.BadValue); - - assert.commandWorked(coll.createIndex({"$**": 1})); - - assert.commandWorked(coll.insert({_id: 10, a: 1, b: 1, c: {d: 1, e: 1}})); - assert.commandWorked(coll.insert({a: 1, b: 2, c: {d: 2, e: 2}})); - assert.commandWorked(coll.insert({a: 2, b: 2, c: {d: 1, e: 2}})); - assert.commandWorked(coll.insert({a: 2, b: 1, c: {d: 2, e: 2}})); - assert.commandWorked(coll.insert({a: 2, b: 2, c: {e: 2}})); - - // Hint a $** index without a competing index. - assertExpectedIndexAnswersQueryWithHint( - {"a": 1}, - {"$**": 1}, - "$**_1", - [{a: 1, b: 1, c: {d: 1, e: 1}}, {a: 1, b: 2, c: {d: 2, e: 2}}]); - - assert.commandWorked(coll.createIndex({"a": 1})); - - // Hint a $** index with a competing index. - assertExpectedIndexAnswersQueryWithHint( - {"a": 1}, - {"$**": 1}, - "$**_1", - [{a: 1, b: 1, c: {d: 1, e: 1}}, {a: 1, b: 2, c: {d: 2, e: 2}}]); - - // Hint a $** index with a competing _id index. - assertExpectedIndexAnswersQueryWithHint( - {"a": 1, "_id": 10}, {"$**": 1}, "$**_1", [{a: 1, b: 1, c: {d: 1, e: 1}}]); - - // Hint a regular index with a competing $** index. - assertExpectedIndexAnswersQueryWithHint( - {"a": 1}, - {"a": 1}, - "a_1", - [{a: 1, b: 1, c: {d: 1, e: 1}}, {a: 1, b: 2, c: {d: 2, e: 2}}]); - - // Query on fields that not all documents in the collection have with $** index hint. - assertExpectedIndexAnswersQueryWithHint( - {"c.d": 1}, - {"$**": 1}, - "$**_1", - [{a: 1, b: 1, c: {d: 1, e: 1}}, {a: 2, b: 2, c: {d: 1, e: 2}}]); - - // Adding another wildcard index with a path specified. - assert.commandWorked(coll.createIndex({"c.$**": 1})); - - // Hint on path that is not in query argument. - assert.commandFailedWithCode( - db.runCommand({find: coll.getName(), filter: {"a": 1}, hint: {"c.$**": 1}}), - ErrorCodes.BadValue); - - // Hint on a path specified $** index. - assertExpectedIndexAnswersQueryWithHint( - {"c.d": 1}, - {"c.$**": 1}, - "c.$**_1", - [{a: 2, b: 2, c: {d: 1, e: 2}}, {a: 1, b: 1, c: {d: 1, e: 1}}]); - - // Min/max with $** index hint. - assert.commandFailedWithCode( - db.runCommand( - {find: coll.getName(), filter: {"b": 1}, min: {"a": 1}, hint: {"$**": 1}}), - ErrorCodes.BadValue); - - // Hint a $** index on a query with compound fields. - assertExpectedIndexAnswersQueryWithHint( - {"a": 1, "c.e": 1}, {"$**": 1}, "$**_1", [{a: 1, b: 1, c: {d: 1, e: 1}}]); - - // Hint a $** index by name. - assertExpectedIndexAnswersQueryWithHint( - {"a": 1}, - "$**_1", - "$**_1", - [{a: 1, b: 1, c: {d: 1, e: 1}}, {a: 1, b: 2, c: {d: 2, e: 2}}]); - } finally { - // Disable $** indexes once the tests have either completed or failed. - db.adminCommand({setParameter: 1, internalQueryAllowAllPathsIndexes: false}); - } + assert.commandWorked(db.createCollection(coll.getName())); + + // Check that error is thrown if the hinted index doesn't exist. + assert.commandFailedWithCode( + db.runCommand({find: coll.getName(), filter: {"a": 1}, hint: {"$**": 1}}), + ErrorCodes.BadValue); + + assert.commandWorked(coll.createIndex({"$**": 1})); + + assert.commandWorked(coll.insert({_id: 10, a: 1, b: 1, c: {d: 1, e: 1}})); + assert.commandWorked(coll.insert({a: 1, b: 2, c: {d: 2, e: 2}})); + assert.commandWorked(coll.insert({a: 2, b: 2, c: {d: 1, e: 2}})); + assert.commandWorked(coll.insert({a: 2, b: 1, c: {d: 2, e: 2}})); + assert.commandWorked(coll.insert({a: 2, b: 2, c: {e: 2}})); + + // Hint a $** index without a competing index. + assertExpectedIndexAnswersQueryWithHint( + {"a": 1}, + {"$**": 1}, + "$**_1", + [{a: 1, b: 1, c: {d: 1, e: 1}}, {a: 1, b: 2, c: {d: 2, e: 2}}]); + + assert.commandWorked(coll.createIndex({"a": 1})); + + // Hint a $** index with a competing index. + assertExpectedIndexAnswersQueryWithHint( + {"a": 1}, + {"$**": 1}, + "$**_1", + [{a: 1, b: 1, c: {d: 1, e: 1}}, {a: 1, b: 2, c: {d: 2, e: 2}}]); + + // Hint a $** index with a competing _id index. + assertExpectedIndexAnswersQueryWithHint( + {"a": 1, "_id": 10}, {"$**": 1}, "$**_1", [{a: 1, b: 1, c: {d: 1, e: 1}}]); + + // Hint a regular index with a competing $** index. + assertExpectedIndexAnswersQueryWithHint( + {"a": 1}, {"a": 1}, "a_1", [{a: 1, b: 1, c: {d: 1, e: 1}}, {a: 1, b: 2, c: {d: 2, e: 2}}]); + + // Query on fields that not all documents in the collection have with $** index hint. + assertExpectedIndexAnswersQueryWithHint( + {"c.d": 1}, + {"$**": 1}, + "$**_1", + [{a: 1, b: 1, c: {d: 1, e: 1}}, {a: 2, b: 2, c: {d: 1, e: 2}}]); + + // Adding another wildcard index with a path specified. + assert.commandWorked(coll.createIndex({"c.$**": 1})); + + // Hint on path that is not in query argument. + assert.commandFailedWithCode( + db.runCommand({find: coll.getName(), filter: {"a": 1}, hint: {"c.$**": 1}}), + ErrorCodes.BadValue); + + // Hint on a path specified $** index. + assertExpectedIndexAnswersQueryWithHint( + {"c.d": 1}, + {"c.$**": 1}, + "c.$**_1", + [{a: 2, b: 2, c: {d: 1, e: 2}}, {a: 1, b: 1, c: {d: 1, e: 1}}]); + + // Min/max with $** index hint. + assert.commandFailedWithCode( + db.runCommand({find: coll.getName(), filter: {"b": 1}, min: {"a": 1}, hint: {"$**": 1}}), + ErrorCodes.BadValue); + + // Hint a $** index on a query with compound fields. + assertExpectedIndexAnswersQueryWithHint( + {"a": 1, "c.e": 1}, {"$**": 1}, "$**_1", [{a: 1, b: 1, c: {d: 1, e: 1}}]); + + // Hint a $** index by name. + assertExpectedIndexAnswersQueryWithHint( + {"a": 1}, "$**_1", "$**_1", [{a: 1, b: 1, c: {d: 1, e: 1}}, {a: 1, b: 2, c: {d: 2, e: 2}}]); })();
\ No newline at end of file diff --git a/jstests/noPassthroughWithMongod/wildcard_index_minmax.js b/jstests/noPassthroughWithMongod/wildcard_index_minmax.js index 006e92069c1..85b4eca86d0 100644 --- a/jstests/noPassthroughWithMongod/wildcard_index_minmax.js +++ b/jstests/noPassthroughWithMongod/wildcard_index_minmax.js @@ -11,85 +11,71 @@ const assertArrayEq = (l, r) => assert(arrayEq(l, r), tojson(l) + " != " + tojson(r)); - // Required in order to build $** indexes. - assert.commandWorked( - db.adminCommand({setParameter: 1, internalQueryAllowAllPathsIndexes: true})); - - try { - assert.commandWorked(coll.createIndex({"$**": 1})); - - assert.commandWorked(coll.insert({a: 1, b: 1})); - assert.commandWorked(coll.insert({a: 1, b: 2})); - assert.commandWorked(coll.insert({a: 2, b: 1})); - assert.commandWorked(coll.insert({a: 2, b: 2})); - - // Throws error for $** index min. - assert.commandFailedWithCode( - db.runCommand({find: coll.getName(), min: {"a": 0.5}, hint: {"$**": 1}}), - ErrorCodes.BadValue); - - // Throws error for $** index max. - assert.commandFailedWithCode( - db.runCommand({find: coll.getName(), max: {"a": 1.5}, hint: {"$**": 1}}), - ErrorCodes.BadValue); - - // Throws error for $** index min/max. - assert.commandFailedWithCode( - db.runCommand( - {find: coll.getName(), min: {"a": 0.5}, max: {"a": 1.5}, hint: {"$**": 1}}), - ErrorCodes.BadValue); - - // Throws error for $** index min with filter of a different value. - assert.commandFailedWithCode( - db.runCommand( - {find: coll.getName(), filter: {"a": 2}, min: {"a": 1}, hint: {"$**": 1}}), - ErrorCodes.BadValue); - - // Throws error for $** index max with filter of a different value. - assert.commandFailedWithCode( - db.runCommand( - {find: coll.getName(), filter: {"a": 1}, max: {"a": 1.5}, hint: {"$**": 1}}), - ErrorCodes.BadValue); - - // Throws error for $** index min and max with filter of a different value. - assert.commandFailedWithCode(db.runCommand({ - find: coll.getName(), - filter: {"a": 1}, - min: {"a": 0.5}, - max: {"a": 1.5}, - hint: {"$**": 1} - }), - ErrorCodes.BadValue); - - // Throws error for $** index min with filter of the same value. - assert.commandFailedWithCode( - db.runCommand( - {find: coll.getName(), filter: {"a": 1}, min: {"a": 1}, hint: {"$**": 1}}), - ErrorCodes.BadValue); - - // Throws error for $** index max with filter of the same value. - assert.commandFailedWithCode( - db.runCommand( - {find: coll.getName(), filter: {"a": 1}, max: {"a": 1}, hint: {"$**": 1}}), - ErrorCodes.BadValue); - - // Throws error for $** index min and max with filter of the same value. - assert.commandFailedWithCode(db.runCommand({ - find: coll.getName(), - filter: {"a": 1}, - min: {"a": 1}, - max: {"a": 1}, - hint: {"$**": 1} - }), - ErrorCodes.BadValue); - - assert.commandWorked(coll.createIndex({"a": 1})); - - // $** index does not interfere with valid min/max. - assertArrayEq(coll.find({}, {_id: 0}).min({"a": 0.5}).max({"a": 1.5}).toArray(), - [{a: 1, b: 1}, {a: 1, b: 2}]); - } finally { - // Disable $** indexes once the tests have either completed or failed. - db.adminCommand({setParameter: 1, internalQueryAllowAllPathsIndexes: false}); - } + assert.commandWorked(coll.createIndex({"$**": 1})); + + assert.commandWorked(coll.insert({a: 1, b: 1})); + assert.commandWorked(coll.insert({a: 1, b: 2})); + assert.commandWorked(coll.insert({a: 2, b: 1})); + assert.commandWorked(coll.insert({a: 2, b: 2})); + + // Throws error for $** index min. + assert.commandFailedWithCode( + db.runCommand({find: coll.getName(), min: {"a": 0.5}, hint: {"$**": 1}}), + ErrorCodes.BadValue); + + // Throws error for $** index max. + assert.commandFailedWithCode( + db.runCommand({find: coll.getName(), max: {"a": 1.5}, hint: {"$**": 1}}), + ErrorCodes.BadValue); + + // Throws error for $** index min/max. + assert.commandFailedWithCode( + db.runCommand({find: coll.getName(), min: {"a": 0.5}, max: {"a": 1.5}, hint: {"$**": 1}}), + ErrorCodes.BadValue); + + // Throws error for $** index min with filter of a different value. + assert.commandFailedWithCode( + db.runCommand({find: coll.getName(), filter: {"a": 2}, min: {"a": 1}, hint: {"$**": 1}}), + ErrorCodes.BadValue); + + // Throws error for $** index max with filter of a different value. + assert.commandFailedWithCode( + db.runCommand({find: coll.getName(), filter: {"a": 1}, max: {"a": 1.5}, hint: {"$**": 1}}), + ErrorCodes.BadValue); + + // Throws error for $** index min and max with filter of a different value. + assert.commandFailedWithCode(db.runCommand({ + find: coll.getName(), + filter: {"a": 1}, + min: {"a": 0.5}, + max: {"a": 1.5}, + hint: {"$**": 1} + }), + ErrorCodes.BadValue); + + // Throws error for $** index min with filter of the same value. + assert.commandFailedWithCode( + db.runCommand({find: coll.getName(), filter: {"a": 1}, min: {"a": 1}, hint: {"$**": 1}}), + ErrorCodes.BadValue); + + // Throws error for $** index max with filter of the same value. + assert.commandFailedWithCode( + db.runCommand({find: coll.getName(), filter: {"a": 1}, max: {"a": 1}, hint: {"$**": 1}}), + ErrorCodes.BadValue); + + // Throws error for $** index min and max with filter of the same value. + assert.commandFailedWithCode(db.runCommand({ + find: coll.getName(), + filter: {"a": 1}, + min: {"a": 1}, + max: {"a": 1}, + hint: {"$**": 1} + }), + ErrorCodes.BadValue); + + assert.commandWorked(coll.createIndex({"a": 1})); + + // $** index does not interfere with valid min/max. + assertArrayEq(coll.find({}, {_id: 0}).min({"a": 0.5}).max({"a": 1.5}).toArray(), + [{a: 1, b: 1}, {a: 1, b: 2}]); })();
\ No newline at end of file diff --git a/jstests/noPassthroughWithMongod/wildcard_index_multikey.js b/jstests/noPassthroughWithMongod/wildcard_index_multikey.js index 7c5e7c93fea..d0887e810c9 100644 --- a/jstests/noPassthroughWithMongod/wildcard_index_multikey.js +++ b/jstests/noPassthroughWithMongod/wildcard_index_multikey.js @@ -101,152 +101,143 @@ } } - // Required in order to build $** indexes. + // Test a $** index that indexes the entire document. + runWildcardIndexTest({'$**': 1}, null, ['a', 'b.c', 'b.d.e']); + // Test a $** index on a single subtree. + runWildcardIndexTest({'a.$**': 1}, null, ['a']); + runWildcardIndexTest({'b.$**': 1}, null, ['b.c', 'b.d.e']); + runWildcardIndexTest({'b.c.$**': 1}, null, ['b.c']); + runWildcardIndexTest({'b.d.$**': 1}, null, ['b.d.e']); + // Test a $** index which includes a subset of paths. + runWildcardIndexTest({'$**': 1}, {a: 1}, ['a']); + runWildcardIndexTest({'$**': 1}, {b: 1}, ['b.c', 'b.d.e']); + runWildcardIndexTest({'$**': 1}, {'b.d': 1}, ['b.d.e']); + runWildcardIndexTest({'$**': 1}, {a: 1, 'b.d': 1}, ['a', 'b.d.e']); + // Test a $** index which excludes a subset of paths. + runWildcardIndexTest({'$**': 1}, {a: 0}, ['b.c', 'b.d.e']); + runWildcardIndexTest({'$**': 1}, {b: 0}, ['a']); + runWildcardIndexTest({'$**': 1}, {'b.c': 0}, ['a', 'b.d.e']); + runWildcardIndexTest({'$**': 1}, {a: 0, 'b.c': 0}, ['b.d.e']); + + // Sanity check that a few queries which need to be planned specially in the multikey case + // return the correct results. + coll.drop(); + assert.commandWorked(coll.createIndex({"$**": 1})); + assert.commandWorked(coll.insert({a: [-5, 15]})); + assert.eq(1, coll.find({a: {$gt: 0, $lt: 9}}).itcount()); + assert.eq(1, coll.find({a: {$gt: 0, $lt: 9}}).hint({$natural: 1}).itcount()); + assert.eq(0, coll.find({a: {$elemMatch: {$gt: 0, $lt: 9}}}).itcount()); + assert.eq(0, coll.find({a: {$elemMatch: {$gt: 0, $lt: 9}}}).hint({$natural: 1}).itcount()); + + assert.commandWorked(coll.insert({b: {c: {d: [{e: {f: -5}}, {e: {f: 15}}]}}})); + assert.eq(1, coll.find({"b.c.d.e.f": {$gt: 0, $lt: 9}}).itcount()); + assert.eq(1, coll.find({"b.c.d.e.f": {$gt: 0, $lt: 9}}).hint({$natural: 1}).itcount()); + assert.eq(0, coll.find({"b.c.d": {$elemMatch: {"e.f": {$gt: 0, $lt: 9}}}}).itcount()); + assert.eq(0, + coll.find({"b.c.d": {$elemMatch: {"e.f": {$gt: 0, $lt: 9}}}}) + .hint({$natural: 1}) + .itcount()); + + // Fieldname-or-array-index query tests. + assert(coll.drop()); + assert.commandWorked(coll.createIndex({"$**": 1})); + + // Insert some documents that exhibit a mix of numeric fieldnames and array indices. + assert.commandWorked(coll.insert({_id: 1, a: [{b: [{c: 1}]}]})); + assert.commandWorked(coll.insert({_id: 2, a: [{b: [{c: 0}, {c: 1}]}]})); + assert.commandWorked(coll.insert({_id: 3, a: {'0': [{b: {'1': {c: 1}}}, {d: 1}]}})); + assert.commandWorked(coll.insert({_id: 4, a: [{b: [{1: {c: 1}}]}]})); assert.commandWorked( - db.adminCommand({setParameter: 1, internalQueryAllowAllPathsIndexes: true})); - try { - // Test a $** index that indexes the entire document. - runWildcardIndexTest({'$**': 1}, null, ['a', 'b.c', 'b.d.e']); - // Test a $** index on a single subtree. - runWildcardIndexTest({'a.$**': 1}, null, ['a']); - runWildcardIndexTest({'b.$**': 1}, null, ['b.c', 'b.d.e']); - runWildcardIndexTest({'b.c.$**': 1}, null, ['b.c']); - runWildcardIndexTest({'b.d.$**': 1}, null, ['b.d.e']); - // Test a $** index which includes a subset of paths. - runWildcardIndexTest({'$**': 1}, {a: 1}, ['a']); - runWildcardIndexTest({'$**': 1}, {b: 1}, ['b.c', 'b.d.e']); - runWildcardIndexTest({'$**': 1}, {'b.d': 1}, ['b.d.e']); - runWildcardIndexTest({'$**': 1}, {a: 1, 'b.d': 1}, ['a', 'b.d.e']); - // Test a $** index which excludes a subset of paths. - runWildcardIndexTest({'$**': 1}, {a: 0}, ['b.c', 'b.d.e']); - runWildcardIndexTest({'$**': 1}, {b: 0}, ['a']); - runWildcardIndexTest({'$**': 1}, {'b.c': 0}, ['a', 'b.d.e']); - runWildcardIndexTest({'$**': 1}, {a: 0, 'b.c': 0}, ['b.d.e']); - - // Sanity check that a few queries which need to be planned specially in the multikey case - // return the correct results. - coll.drop(); - assert.commandWorked(coll.createIndex({"$**": 1})); - assert.commandWorked(coll.insert({a: [-5, 15]})); - assert.eq(1, coll.find({a: {$gt: 0, $lt: 9}}).itcount()); - assert.eq(1, coll.find({a: {$gt: 0, $lt: 9}}).hint({$natural: 1}).itcount()); - assert.eq(0, coll.find({a: {$elemMatch: {$gt: 0, $lt: 9}}}).itcount()); - assert.eq(0, coll.find({a: {$elemMatch: {$gt: 0, $lt: 9}}}).hint({$natural: 1}).itcount()); - - assert.commandWorked(coll.insert({b: {c: {d: [{e: {f: -5}}, {e: {f: 15}}]}}})); - assert.eq(1, coll.find({"b.c.d.e.f": {$gt: 0, $lt: 9}}).itcount()); - assert.eq(1, coll.find({"b.c.d.e.f": {$gt: 0, $lt: 9}}).hint({$natural: 1}).itcount()); - assert.eq(0, coll.find({"b.c.d": {$elemMatch: {"e.f": {$gt: 0, $lt: 9}}}}).itcount()); - assert.eq(0, - coll.find({"b.c.d": {$elemMatch: {"e.f": {$gt: 0, $lt: 9}}}}) - .hint({$natural: 1}) - .itcount()); - - // Fieldname-or-array-index query tests. - assert(coll.drop()); - assert.commandWorked(coll.createIndex({"$**": 1})); - - // Insert some documents that exhibit a mix of numeric fieldnames and array indices. - assert.commandWorked(coll.insert({_id: 1, a: [{b: [{c: 1}]}]})); - assert.commandWorked(coll.insert({_id: 2, a: [{b: [{c: 0}, {c: 1}]}]})); - assert.commandWorked(coll.insert({_id: 3, a: {'0': [{b: {'1': {c: 1}}}, {d: 1}]}})); - assert.commandWorked(coll.insert({_id: 4, a: [{b: [{1: {c: 1}}]}]})); - assert.commandWorked( - coll.insert({_id: 5, a: [{b: [{'1': {c: {'2': {d: [0, 1, 2, 3, {e: 1}]}}}}]}]})); - - /* - * Multikey Metadata Keys: - * {'': 1, '': 'a'} - * {'': 1, '': 'a.0'} - * {'': 1, '': 'a.b'} - * {'': 1, '': 'a.b.1.c.2.d'} - * Keys: - * {'': 'a.b.c', '': 1} // _id: 1, a,b multikey - * {'': 'a.b.c', '': 0} // _id: 2, a,b multikey - * {'': 'a.b.c', '': 1} // _id: 2, a,b multikey - * {'': 'a.0.b.1.c', '': 1} // _id: 3, '0, 1' are fieldnames, a.0 multikey - * {'': 'a.0.d', '': 1} // _id: 3, '0' is fieldname, a.0 multikey - * {'': 'a.b.1.c', '': 1} // _id: 4, '1' is fieldname, a,b multikey - * {'': 'a.b.1.c.2.d', '': 0} // _id: 5, a,b,a.b.1.c.2.d multikey, '1' is fieldname - * {'': 'a.b.1.c.2.d', '': 1} // _id: 5 - * {'': 'a.b.1.c.2.d', '': 2} // _id: 5 - * {'': 'a.b.1.c.2.d', '': 3} // _id: 5 - * {'': 'a.b.1.c.2.d.e', '': 1} // _id: 5 - */ - - // Test that a query with multiple numeric path components returns all relevant documents, - // whether the numeric path component refers to a fieldname or array index in each doc: - // - // _id:1 will be captured by the special fieldname-or-array-index bounds 'a.b.c', but will - // be filtered out by the INEXACT_FETCH since it has no array index or fieldname 'b.1'. - // _id:2 will match both 'a.0' and 'b.1' by array index. - // _id:3 will match both 'a.0' and 'b.1' by fieldname. - // _id:4 will match 'a.0' by array index and 'b.1' by fieldname. - // _id:5 is not captured by the special fieldname-or-array-index bounds. - // - // We examine the solution's 'nReturned' versus 'totalDocsExamined' to confirm this. - // totalDocsExamined: [_id:1, _id:2, _id:3, _id:4], nReturned: [_id:2, _id:3, _id:4] - assertWildcardQuery({'a.0.b.1.c': 1}, - 'a.0.b.1.c', - {'executionStats.nReturned': 3, 'executionStats.totalDocsExamined': 4}); - - // Test that we can query a specific field of an array whose fieldname is itself numeric. - assertWildcardQuery({'a.0.1.d': 1}, - 'a.0.1.d', - {'executionStats.nReturned': 1, 'executionStats.totalDocsExamined': 1}); - - // Test that we can query a primitive value at a specific array index. - assertWildcardQuery({'a.0.b.1.c.2.d.3': 3}, - 'a.0.b.1.c.2.d.3', - {'executionStats.nReturned': 1, 'executionStats.totalDocsExamined': 1}); - - // Test that a $** index can't be used for a query through more than 8 nested array indices. - assert.commandWorked( - coll.insert({_id: 6, a: [{b: [{c: [{d: [{e: [{f: [{g: [{h: [{i: [1]}]}]}]}]}]}]}]}]})); - // We can query up to a depth of 8 arrays via specific indices, but not through 9 or more. - assertWildcardQuery({'a.0.b.0.c.0.d.0.e.0.f.0.g.0.h.0.i': 1}, - 'a.0.b.0.c.0.d.0.e.0.f.0.g.0.h.0.i'); - assertWildcardQuery({'a.0.b.0.c.0.d.0.e.0.f.0.g.0.h.0.i.0': 1}, null); - - // Test that fieldname-or-array-index queries do not inappropriately trim predicates; that - // is, all predicates on the field are added to a FETCH filter above the IXSCAN. - assert(coll.drop()); - assert.commandWorked(coll.createIndex({"$**": 1})); - - assert.commandWorked(coll.insert({_id: 1, a: [0, 1, 2]})); - assert.commandWorked(coll.insert({_id: 2, a: [1, 2, 3]})); - assert.commandWorked(coll.insert({_id: 3, a: [2, 3, 4], b: [5, 6, 7]})); - assert.commandWorked(coll.insert({_id: 4, a: [3, 4, 5], b: [6, 7, 8], c: {'0': 9}})); - assert.commandWorked(coll.insert({_id: 5, a: [4, 5, 6], b: [7, 8, 9], c: {'0': 10}})); - assert.commandWorked(coll.insert({_id: 6, a: [5, 6, 7], b: [8, 9, 10], c: {'0': 11}})); - - assertWildcardQuery({"a.0": {$gt: 1, $lt: 4}}, 'a.0', {'executionStats.nReturned': 2}); - assertWildcardQuery({"a.1": {$gte: 1, $lte: 4}}, 'a.1', {'executionStats.nReturned': 4}); - assertWildcardQuery({"b.2": {$in: [5, 9]}}, 'b.2', {'executionStats.nReturned': 1}); - assertWildcardQuery({"c.0": {$in: [10, 11]}}, 'c.0', {'executionStats.nReturned': 2}); - - // Test that the $** index doesn't trim predicates when planning across multiple nested - // $and/$or expressions on various fieldname-or-array-index paths. - const trimTestQuery = { - $or: [ - {"a.0": {$gte: 0, $lt: 3}, "a.1": {$in: [2, 3, 4]}}, - {"b.1": {$gt: 6, $lte: 9}, "c.0": {$gt: 9, $lt: 12}} - ] - }; - const trimTestExplain = coll.find(trimTestQuery).explain("executionStats"); - // Verify that the expected number of documents were matched, and the $** index was used. - // Matched documents: [_id:2, _id:3, _id:5, _id:6] - assert.eq(trimTestExplain.executionStats.nReturned, 4); - const trimTestIxScans = getPlanStages(trimTestExplain.queryPlanner.winningPlan, "IXSCAN"); - for (let ixScan of trimTestIxScans) { - assert.eq(ixScan.keyPattern["$_path"], 1); - } - // Finally, confirm that a collection scan produces the same results. - assertArrayEq(coll.find(trimTestQuery).toArray(), - coll.find(trimTestQuery).hint({$natural: 1}).toArray()); - } finally { - // Disable $** indexes once the tests have either completed or failed. - assert.commandWorked( - db.adminCommand({setParameter: 1, internalQueryAllowAllPathsIndexes: false})); + coll.insert({_id: 5, a: [{b: [{'1': {c: {'2': {d: [0, 1, 2, 3, {e: 1}]}}}}]}]})); + + /* + * Multikey Metadata Keys: + * {'': 1, '': 'a'} + * {'': 1, '': 'a.0'} + * {'': 1, '': 'a.b'} + * {'': 1, '': 'a.b.1.c.2.d'} + * Keys: + * {'': 'a.b.c', '': 1} // _id: 1, a,b multikey + * {'': 'a.b.c', '': 0} // _id: 2, a,b multikey + * {'': 'a.b.c', '': 1} // _id: 2, a,b multikey + * {'': 'a.0.b.1.c', '': 1} // _id: 3, '0, 1' are fieldnames, a.0 multikey + * {'': 'a.0.d', '': 1} // _id: 3, '0' is fieldname, a.0 multikey + * {'': 'a.b.1.c', '': 1} // _id: 4, '1' is fieldname, a,b multikey + * {'': 'a.b.1.c.2.d', '': 0} // _id: 5, a,b,a.b.1.c.2.d multikey, '1' is fieldname + * {'': 'a.b.1.c.2.d', '': 1} // _id: 5 + * {'': 'a.b.1.c.2.d', '': 2} // _id: 5 + * {'': 'a.b.1.c.2.d', '': 3} // _id: 5 + * {'': 'a.b.1.c.2.d.e', '': 1} // _id: 5 + */ + + // Test that a query with multiple numeric path components returns all relevant documents, + // whether the numeric path component refers to a fieldname or array index in each doc: + // + // _id:1 will be captured by the special fieldname-or-array-index bounds 'a.b.c', but will be + // filtered out by the INEXACT_FETCH since it has no array index or fieldname 'b.1'. + // _id:2 will match both 'a.0' and 'b.1' by array index. + // _id:3 will match both 'a.0' and 'b.1' by fieldname. + // _id:4 will match 'a.0' by array index and 'b.1' by fieldname. + // _id:5 is not captured by the special fieldname-or-array-index bounds. + // + // We examine the solution's 'nReturned' versus 'totalDocsExamined' to confirm this. + // totalDocsExamined: [_id:1, _id:2, _id:3, _id:4], nReturned: [_id:2, _id:3, _id:4] + assertWildcardQuery({'a.0.b.1.c': 1}, + 'a.0.b.1.c', + {'executionStats.nReturned': 3, 'executionStats.totalDocsExamined': 4}); + + // Test that we can query a specific field of an array whose fieldname is itself numeric. + assertWildcardQuery({'a.0.1.d': 1}, + 'a.0.1.d', + {'executionStats.nReturned': 1, 'executionStats.totalDocsExamined': 1}); + + // Test that we can query a primitive value at a specific array index. + assertWildcardQuery({'a.0.b.1.c.2.d.3': 3}, + 'a.0.b.1.c.2.d.3', + {'executionStats.nReturned': 1, 'executionStats.totalDocsExamined': 1}); + + // Test that a $** index can't be used for a query through more than 8 nested array indices. + assert.commandWorked( + coll.insert({_id: 6, a: [{b: [{c: [{d: [{e: [{f: [{g: [{h: [{i: [1]}]}]}]}]}]}]}]}]})); + // We can query up to a depth of 8 arrays via specific indices, but not through 9 or more. + assertWildcardQuery({'a.0.b.0.c.0.d.0.e.0.f.0.g.0.h.0.i': 1}, + 'a.0.b.0.c.0.d.0.e.0.f.0.g.0.h.0.i'); + assertWildcardQuery({'a.0.b.0.c.0.d.0.e.0.f.0.g.0.h.0.i.0': 1}, null); + + // Test that fieldname-or-array-index queries do not inappropriately trim predicates; that is, + // all predicates on the field are added to a FETCH filter above the IXSCAN. + assert(coll.drop()); + assert.commandWorked(coll.createIndex({"$**": 1})); + + assert.commandWorked(coll.insert({_id: 1, a: [0, 1, 2]})); + assert.commandWorked(coll.insert({_id: 2, a: [1, 2, 3]})); + assert.commandWorked(coll.insert({_id: 3, a: [2, 3, 4], b: [5, 6, 7]})); + assert.commandWorked(coll.insert({_id: 4, a: [3, 4, 5], b: [6, 7, 8], c: {'0': 9}})); + assert.commandWorked(coll.insert({_id: 5, a: [4, 5, 6], b: [7, 8, 9], c: {'0': 10}})); + assert.commandWorked(coll.insert({_id: 6, a: [5, 6, 7], b: [8, 9, 10], c: {'0': 11}})); + + assertWildcardQuery({"a.0": {$gt: 1, $lt: 4}}, 'a.0', {'executionStats.nReturned': 2}); + assertWildcardQuery({"a.1": {$gte: 1, $lte: 4}}, 'a.1', {'executionStats.nReturned': 4}); + assertWildcardQuery({"b.2": {$in: [5, 9]}}, 'b.2', {'executionStats.nReturned': 1}); + assertWildcardQuery({"c.0": {$in: [10, 11]}}, 'c.0', {'executionStats.nReturned': 2}); + + // Test that the $** index doesn't trim predicates when planning across multiple nested $and/$or + // expressions on various fieldname-or-array-index paths. + const trimTestQuery = { + $or: [ + {"a.0": {$gte: 0, $lt: 3}, "a.1": {$in: [2, 3, 4]}}, + {"b.1": {$gt: 6, $lte: 9}, "c.0": {$gt: 9, $lt: 12}} + ] + }; + const trimTestExplain = coll.find(trimTestQuery).explain("executionStats"); + // Verify that the expected number of documents were matched, and the $** index was used. + // Matched documents: [_id:2, _id:3, _id:5, _id:6] + assert.eq(trimTestExplain.executionStats.nReturned, 4); + const trimTestIxScans = getPlanStages(trimTestExplain.queryPlanner.winningPlan, "IXSCAN"); + for (let ixScan of trimTestIxScans) { + assert.eq(ixScan.keyPattern["$_path"], 1); } + // Finally, confirm that a collection scan produces the same results. + assertArrayEq(coll.find(trimTestQuery).toArray(), + coll.find(trimTestQuery).hint({$natural: 1}).toArray()); })(); diff --git a/jstests/noPassthroughWithMongod/wildcard_index_nonblocking_sort.js b/jstests/noPassthroughWithMongod/wildcard_index_nonblocking_sort.js index b9c7edb3921..09aaba0fd90 100644 --- a/jstests/noPassthroughWithMongod/wildcard_index_nonblocking_sort.js +++ b/jstests/noPassthroughWithMongod/wildcard_index_nonblocking_sort.js @@ -6,10 +6,6 @@ const coll = db.wildcard_nonblocking_sort; - // Required in order to build $** indexes. - assert.commandWorked( - db.adminCommand({setParameter: 1, internalQueryAllowAllPathsIndexes: true})); - assert.commandWorked(coll.createIndex({"$**": 1}, {wildcardProjection: {"excludedField": 0}})); for (let i = 0; i < 50; i++) { diff --git a/jstests/noPassthroughWithMongod/wildcard_index_partial_index.js b/jstests/noPassthroughWithMongod/wildcard_index_partial_index.js index 4e8d8ec090f..fe809c6030b 100644 --- a/jstests/noPassthroughWithMongod/wildcard_index_partial_index.js +++ b/jstests/noPassthroughWithMongod/wildcard_index_partial_index.js @@ -42,17 +42,9 @@ assert(isCollscan(db, explain.queryPlanner.winningPlan)); } - try { - // Required in order to build $** indexes. - assert.commandWorked( - db.adminCommand({setParameter: 1, internalQueryAllowAllPathsIndexes: true})); - - testPartialWildcardIndex({"$**": 1}, {partialFilterExpression: {a: {$lte: 1.5}}}); - - // Case where the partial filter expression is on a field not included in the index. - testPartialWildcardIndex({"x.$**": 1}, {partialFilterExpression: {a: {$lte: 1.5}}}); - } finally { - // Disable $** indexes once the tests have either completed or failed. - db.adminCommand({setParameter: 1, internalQueryAllowAllPathsIndexes: false}); - } + // Case where the partial filter expression is on a field in the index. + testPartialWildcardIndex({"$**": 1}, {partialFilterExpression: {a: {$lte: 1.5}}}); + + // Case where the partial filter expression is on a field not included in the index. + testPartialWildcardIndex({"x.$**": 1}, {partialFilterExpression: {a: {$lte: 1.5}}}); })(); diff --git a/jstests/noPassthroughWithMongod/wildcard_index_return_key.js b/jstests/noPassthroughWithMongod/wildcard_index_return_key.js index 382aa9cfb98..7f8ed54e741 100644 --- a/jstests/noPassthroughWithMongod/wildcard_index_return_key.js +++ b/jstests/noPassthroughWithMongod/wildcard_index_return_key.js @@ -13,58 +13,48 @@ const assertArrayEq = (l, r) => assert(arrayEq(l, r), tojson(l) + " != " + tojson(r)); const assertArrayNotEq = (l, r) => assert(!arrayEq(l, r), tojson(l) + " == " + tojson(r)); - // Required in order to build $** indexes. - assert.commandWorked( - db.adminCommand({setParameter: 1, internalQueryAllowAllPathsIndexes: true})); + assert.commandWorked(coll.createIndex({"$**": 1})); - try { - assert.commandWorked(coll.createIndex({"$**": 1})); + assert.commandWorked(coll.insert({a: 1, b: 2, c: {d: 2, e: 1}})); + assert.commandWorked(coll.insert({a: 2, b: 2, c: {d: 1, e: 2}})); + assert.commandWorked(coll.insert({a: 2, b: 1, c: {d: 2, e: 2}})); + assert.commandWorked(coll.insert({a: 1, b: 1, c: {e: 2}})); - assert.commandWorked(coll.insert({a: 1, b: 2, c: {d: 2, e: 1}})); - assert.commandWorked(coll.insert({a: 2, b: 2, c: {d: 1, e: 2}})); - assert.commandWorked(coll.insert({a: 2, b: 1, c: {d: 2, e: 2}})); - assert.commandWorked(coll.insert({a: 1, b: 1, c: {e: 2}})); + // $** index return key with one field argument. + assertArrayEq(coll.find({a: 1}).returnKey().toArray(), + [{"$_path": "a", a: 1}, {"$_path": "a", a: 1}]); - // $** index return key with one field argument. - assertArrayEq(coll.find({a: 1}).returnKey().toArray(), - [{"$_path": "a", a: 1}, {"$_path": "a", a: 1}]); + // $** index return key with dot path argument. + assertArrayEq(coll.find({"c.e": 1}).returnKey().toArray(), [{"$_path": "c.e", "c.e": 1}]); - // $** index return key with dot path argument. - assertArrayEq(coll.find({"c.e": 1}).returnKey().toArray(), [{"$_path": "c.e", "c.e": 1}]); + assert.commandWorked(coll.createIndex({"a": 1})); - assert.commandWorked(coll.createIndex({"a": 1})); + // $** index return key with competing regular index. + assertArrayEq(coll.find({a: 1}).hint({"$**": 1}).returnKey().toArray(), + [{"$_path": "a", a: 1}, {"$_path": "a", a: 1}]); - // $** index return key with competing regular index. - assertArrayEq(coll.find({a: 1}).hint({"$**": 1}).returnKey().toArray(), - [{"$_path": "a", a: 1}, {"$_path": "a", a: 1}]); + assert.commandWorked(coll.createIndex({"a": 1, "b": 1})); - assert.commandWorked(coll.createIndex({"a": 1, "b": 1})); + // $** index return key with competing compound index. + assertArrayNotEq(coll.find({a: 1, b: 1}).hint({"$**": 1}).returnKey().toArray(), + [{a: 1, b: 1}]); - // $** index return key with competing compound index. - assertArrayNotEq(coll.find({a: 1, b: 1}).hint({"$**": 1}).returnKey().toArray(), - [{a: 1, b: 1}]); + assert.commandWorked(coll.insert({a: 2, b: 2, c: {e: 2}, f: [1, 2, 3]})); + assert.commandWorked(coll.insert({a: 2, b: 2, c: {e: 2}, g: [{h: 1}, {i: 2}]})); - assert.commandWorked(coll.insert({a: 2, b: 2, c: {e: 2}, f: [1, 2, 3]})); - assert.commandWorked(coll.insert({a: 2, b: 2, c: {e: 2}, g: [{h: 1}, {i: 2}]})); + // Multikey path $** index return key. + assertArrayEq(coll.find({f: 1}).returnKey().toArray(), [{"$_path": "f", f: 1}]); - // Multikey path $** index return key. - assertArrayEq(coll.find({f: 1}).returnKey().toArray(), [{"$_path": "f", f: 1}]); + // Multikey subobject $** index return key. + assertArrayEq(coll.find({"g.h": 1}).returnKey().toArray(), [{"$_path": "g.h", "g.h": 1}]); - // Multikey subobject $** index return key. - assertArrayEq(coll.find({"g.h": 1}).returnKey().toArray(), [{"$_path": "g.h", "g.h": 1}]); + assert.commandWorked(coll.dropIndexes()); + assert.commandWorked(coll.createIndex({"c.$**": 1})); - assert.commandWorked(coll.dropIndexes()); - assert.commandWorked(coll.createIndex({"c.$**": 1})); + // Path specified $** index return key. + assertArrayEq(coll.find({"c.d": 1}).returnKey().toArray(), [{"$_path": "c.d", "c.d": 1}]); - // Path specified $** index return key. - assertArrayEq(coll.find({"c.d": 1}).returnKey().toArray(), [{"$_path": "c.d", "c.d": 1}]); - - // Path specified $** index return key with irrelevant query. We expect this query to be - // answered with a collection scan, in which case returnKey is expected to return empty - // objects. - assertArrayEq(coll.find({a: 1, b: 1}).returnKey().toArray(), [{}]); - } finally { - // Disable $** indexes once the tests have either completed or failed. - db.adminCommand({setParameter: 1, internalQueryAllowAllPathsIndexes: false}); - } + // Path specified $** index return key with irrelevant query. We expect this query to be + // answered with a COLLSCAN, in which case returnKey is expected to return empty objects. + assertArrayEq(coll.find({a: 1, b: 1}).returnKey().toArray(), [{}]); })();
\ No newline at end of file diff --git a/jstests/noPassthroughWithMongod/wildcard_index_validindex.js b/jstests/noPassthroughWithMongod/wildcard_index_validindex.js index eb3b92ba94e..6759a900762 100644 --- a/jstests/noPassthroughWithMongod/wildcard_index_validindex.js +++ b/jstests/noPassthroughWithMongod/wildcard_index_validindex.js @@ -30,138 +30,122 @@ 1); }; - assert.commandWorked( - db.adminCommand({setParameter: 1, internalQueryAllowAllPathsIndexes: true})); - try { - // Can create a valid wildcard index. - createIndexAndVerifyWithDrop({"$**": 1}, {name: kIndexName}); - - // Can create a valid wildcard index with subpaths. - createIndexAndVerifyWithDrop({"a.$**": 1}, {name: kIndexName}); - - // Can create a wildcard index with partialFilterExpression. - createIndexAndVerifyWithDrop({"$**": 1}, - {name: kIndexName, partialFilterExpression: {a: {"$gt": 0}}}); - - // Can create a wildcard index with foreground & background construction. - createIndexAndVerifyWithDrop({"$**": 1}, {background: false, name: kIndexName}); - createIndexAndVerifyWithDrop({"$**": 1}, {background: true, name: kIndexName}); - - // Can create a wildcard index with index level collation. - createIndexAndVerifyWithDrop({"$**": 1}, {collation: {locale: "fr"}, name: kIndexName}); - - // Can create a wildcard index with an inclusion projection. - createIndexAndVerifyWithDrop({"$**": 1}, - {wildcardProjection: {a: 1, b: 1, c: 1}, name: kIndexName}); - // Can create a wildcard index with an exclusion projection. - createIndexAndVerifyWithDrop({"$**": 1}, - {wildcardProjection: {a: 0, b: 0, c: 0}, name: kIndexName}); - // Can include _id in an exclusion. - createIndexAndVerifyWithDrop( - {"$**": 1}, {wildcardProjection: {_id: 1, a: 0, b: 0, c: 0}, name: kIndexName}); - // Can exclude _id in an exclusion. - createIndexAndVerifyWithDrop( - {"$**": 1}, {wildcardProjection: {_id: 0, a: 1, b: 1, c: 1}, name: kIndexName}); - - // Cannot create a wildcard index with a non-positive numeric key value. - coll.dropIndexes(); - assert.commandFailedWithCode(coll.createIndex({"$**": 0}), ErrorCodes.CannotCreateIndex); - assert.commandFailedWithCode(coll.createIndex({"$**": -1}), ErrorCodes.CannotCreateIndex); - assert.commandFailedWithCode(coll.createIndex({"$**": -2}), ErrorCodes.CannotCreateIndex); - - // Cannot create a wildcard index with sparse option. - assert.commandFailedWithCode(coll.createIndex({"$**": 1}, {sparse: true}), - ErrorCodes.CannotCreateIndex); - - // Cannot create a wildcard index with a v0 or v1 index. - assert.commandFailedWithCode(coll.createIndex({"$**": 1}, {v: 0}), - ErrorCodes.CannotCreateIndex); - assert.commandFailedWithCode(coll.createIndex({"$**": 1}, {v: 1}), - ErrorCodes.CannotCreateIndex); - - // Cannot create a unique index. - assert.commandFailedWithCode(coll.createIndex({"$**": 1}, {unique: true}), - ErrorCodes.CannotCreateIndex); - - // Cannot create a hashed wildcard index. - assert.commandFailedWithCode(coll.createIndex({"$**": "hashed"}), - ErrorCodes.CannotCreateIndex); - - // Cannot create a TTL wildcard index. - assert.commandFailedWithCode(coll.createIndex({"$**": 1}, {expireAfterSeconds: 3600}), - ErrorCodes.CannotCreateIndex); - - // Cannot create a geoSpatial wildcard index. - assert.commandFailedWithCode(coll.createIndex({"$**": "2dsphere"}), - ErrorCodes.CannotCreateIndex); - assert.commandFailedWithCode(coll.createIndex({"$**": "2d"}), ErrorCodes.CannotCreateIndex); - - // Cannot create a text wildcard index using single sub-path syntax. - assert.commandFailedWithCode(coll.createIndex({"a.$**": "text"}), - ErrorCodes.CannotCreateIndex); - - // Cannot specify plugin by string. - assert.commandFailedWithCode(coll.createIndex({"a": "wildcard"}), - ErrorCodes.CannotCreateIndex); - assert.commandFailedWithCode(coll.createIndex({"$**": "wildcard"}), - ErrorCodes.CannotCreateIndex); - - // Cannot create a compound wildcard index. - assert.commandFailedWithCode(coll.createIndex({"$**": 1, "a": 1}), - ErrorCodes.CannotCreateIndex); - assert.commandFailedWithCode(coll.createIndex({"a": 1, "$**": 1}), - ErrorCodes.CannotCreateIndex); - - // Cannot create an wildcard index with an invalid spec. - assert.commandFailedWithCode(coll.createIndex({"a.$**.$**": 1}), - ErrorCodes.CannotCreateIndex); - assert.commandFailedWithCode(coll.createIndex({"$**.$**": 1}), - ErrorCodes.CannotCreateIndex); - assert.commandFailedWithCode(coll.createIndex({"$**": "hello"}), - ErrorCodes.CannotCreateIndex); - - // Cannot create an wildcard index with mixed inclusion exclusion. - assert.commandFailedWithCode( - createIndexHelper({"$**": 1}, {name: kIndexName, wildcardProjection: {a: 1, b: 0}}), - 40178); - // Cannot create an wildcard index with computed fields. - assert.commandFailedWithCode( - createIndexHelper({"$**": 1}, - {name: kIndexName, wildcardProjection: {a: 1, b: "string"}}), - ErrorCodes.FailedToParse); - // Cannot create an wildcard index with an empty projection. - assert.commandFailedWithCode( - createIndexHelper({"$**": 1}, {name: kIndexName, wildcardProjection: {}}), - ErrorCodes.FailedToParse); - // Cannot create another index type with "wildcardProjection" projection. - assert.commandFailedWithCode( - createIndexHelper({"a": 1}, {name: kIndexName, wildcardProjection: {a: 1, b: 1}}), - ErrorCodes.BadValue); - // Cannot create a text index with a "wildcardProjection" projection. - assert.commandFailedWithCode( - createIndexHelper({"$**": "text"}, - {name: kIndexName, wildcardProjection: {a: 1, b: 1}}), - ErrorCodes.BadValue); - // Cannot create an wildcard index with a non-object "wildcardProjection" projection. - assert.commandFailedWithCode( - createIndexHelper({"a.$**": 1}, {name: kIndexName, wildcardProjection: "string"}), - ErrorCodes.TypeMismatch); - // Cannot exclude an subfield of _id in an inclusion. - assert.commandFailedWithCode(createIndexHelper({"_id.id": 0, a: 1, b: 1, c: 1}), - ErrorCodes.CannotCreateIndex); - // Cannot include an subfield of _id in an exclusion. - assert.commandFailedWithCode(createIndexHelper({"_id.id": 1, a: 0, b: 0, c: 0}), - ErrorCodes.CannotCreateIndex); - - // Cannot specify both a subpath and a projection. - assert.commandFailedWithCode( - createIndexHelper({"a.$**": 1}, {name: kIndexName, wildcardProjection: {a: 1}}), - ErrorCodes.FailedToParse); - assert.commandFailedWithCode( - createIndexHelper({"a.$**": 1}, {name: kIndexName, wildcardProjection: {b: 0}}), - ErrorCodes.FailedToParse); - } finally { - assert.commandWorked( - db.adminCommand({"setParameter": 1, "internalQueryAllowAllPathsIndexes": false})); - } + // Can create a valid wildcard index. + createIndexAndVerifyWithDrop({"$**": 1}, {name: kIndexName}); + + // Can create a valid wildcard index with subpaths. + createIndexAndVerifyWithDrop({"a.$**": 1}, {name: kIndexName}); + + // Can create a wildcard index with partialFilterExpression. + createIndexAndVerifyWithDrop({"$**": 1}, + {name: kIndexName, partialFilterExpression: {a: {"$gt": 0}}}); + + // Can create a wildcard index with foreground & background construction. + createIndexAndVerifyWithDrop({"$**": 1}, {background: false, name: kIndexName}); + createIndexAndVerifyWithDrop({"$**": 1}, {background: true, name: kIndexName}); + + // Can create a wildcard index with index level collation. + createIndexAndVerifyWithDrop({"$**": 1}, {collation: {locale: "fr"}, name: kIndexName}); + + // Can create a wildcard index with an inclusion projection. + createIndexAndVerifyWithDrop({"$**": 1}, + {wildcardProjection: {a: 1, b: 1, c: 1}, name: kIndexName}); + // Can create a wildcard index with an exclusion projection. + createIndexAndVerifyWithDrop({"$**": 1}, + {wildcardProjection: {a: 0, b: 0, c: 0}, name: kIndexName}); + // Can include _id in an exclusion. + createIndexAndVerifyWithDrop( + {"$**": 1}, {wildcardProjection: {_id: 1, a: 0, b: 0, c: 0}, name: kIndexName}); + // Can exclude _id in an exclusion. + createIndexAndVerifyWithDrop( + {"$**": 1}, {wildcardProjection: {_id: 0, a: 1, b: 1, c: 1}, name: kIndexName}); + + // Cannot create a wildcard index with a non-positive numeric key value. + coll.dropIndexes(); + assert.commandFailedWithCode(coll.createIndex({"$**": 0}), ErrorCodes.CannotCreateIndex); + assert.commandFailedWithCode(coll.createIndex({"$**": -1}), ErrorCodes.CannotCreateIndex); + assert.commandFailedWithCode(coll.createIndex({"$**": -2}), ErrorCodes.CannotCreateIndex); + + // Cannot create a wildcard index with sparse option. + assert.commandFailedWithCode(coll.createIndex({"$**": 1}, {sparse: true}), + ErrorCodes.CannotCreateIndex); + + // Cannot create a wildcard index with a v0 or v1 index. + assert.commandFailedWithCode(coll.createIndex({"$**": 1}, {v: 0}), + ErrorCodes.CannotCreateIndex); + assert.commandFailedWithCode(coll.createIndex({"$**": 1}, {v: 1}), + ErrorCodes.CannotCreateIndex); + + // Cannot create a unique index. + assert.commandFailedWithCode(coll.createIndex({"$**": 1}, {unique: true}), + ErrorCodes.CannotCreateIndex); + + // Cannot create a hashed wildcard index. + assert.commandFailedWithCode(coll.createIndex({"$**": "hashed"}), ErrorCodes.CannotCreateIndex); + + // Cannot create a TTL wildcard index. + assert.commandFailedWithCode(coll.createIndex({"$**": 1}, {expireAfterSeconds: 3600}), + ErrorCodes.CannotCreateIndex); + + // Cannot create a geoSpatial wildcard index. + assert.commandFailedWithCode(coll.createIndex({"$**": "2dsphere"}), + ErrorCodes.CannotCreateIndex); + assert.commandFailedWithCode(coll.createIndex({"$**": "2d"}), ErrorCodes.CannotCreateIndex); + + // Cannot create a text wildcard index using single sub-path syntax. + assert.commandFailedWithCode(coll.createIndex({"a.$**": "text"}), ErrorCodes.CannotCreateIndex); + + // Cannot specify plugin by string. + assert.commandFailedWithCode(coll.createIndex({"a": "wildcard"}), ErrorCodes.CannotCreateIndex); + assert.commandFailedWithCode(coll.createIndex({"$**": "wildcard"}), + ErrorCodes.CannotCreateIndex); + + // Cannot create a compound wildcard index. + assert.commandFailedWithCode(coll.createIndex({"$**": 1, "a": 1}), + ErrorCodes.CannotCreateIndex); + assert.commandFailedWithCode(coll.createIndex({"a": 1, "$**": 1}), + ErrorCodes.CannotCreateIndex); + + // Cannot create an wildcard index with an invalid spec. + assert.commandFailedWithCode(coll.createIndex({"a.$**.$**": 1}), ErrorCodes.CannotCreateIndex); + assert.commandFailedWithCode(coll.createIndex({"$**.$**": 1}), ErrorCodes.CannotCreateIndex); + assert.commandFailedWithCode(coll.createIndex({"$**": "hello"}), ErrorCodes.CannotCreateIndex); + + // Cannot create an wildcard index with mixed inclusion exclusion. + assert.commandFailedWithCode( + createIndexHelper({"$**": 1}, {name: kIndexName, wildcardProjection: {a: 1, b: 0}}), 40178); + // Cannot create an wildcard index with computed fields. + assert.commandFailedWithCode( + createIndexHelper({"$**": 1}, {name: kIndexName, wildcardProjection: {a: 1, b: "string"}}), + ErrorCodes.FailedToParse); + // Cannot create an wildcard index with an empty projection. + assert.commandFailedWithCode( + createIndexHelper({"$**": 1}, {name: kIndexName, wildcardProjection: {}}), + ErrorCodes.FailedToParse); + // Cannot create another index type with "wildcardProjection" projection. + assert.commandFailedWithCode( + createIndexHelper({"a": 1}, {name: kIndexName, wildcardProjection: {a: 1, b: 1}}), + ErrorCodes.BadValue); + // Cannot create a text index with a "wildcardProjection" projection. + assert.commandFailedWithCode( + createIndexHelper({"$**": "text"}, {name: kIndexName, wildcardProjection: {a: 1, b: 1}}), + ErrorCodes.BadValue); + // Cannot create an wildcard index with a non-object "wildcardProjection" projection. + assert.commandFailedWithCode( + createIndexHelper({"a.$**": 1}, {name: kIndexName, wildcardProjection: "string"}), + ErrorCodes.TypeMismatch); + // Cannot exclude an subfield of _id in an inclusion. + assert.commandFailedWithCode(createIndexHelper({"_id.id": 0, a: 1, b: 1, c: 1}), + ErrorCodes.CannotCreateIndex); + // Cannot include an subfield of _id in an exclusion. + assert.commandFailedWithCode(createIndexHelper({"_id.id": 1, a: 0, b: 0, c: 0}), + ErrorCodes.CannotCreateIndex); + + // Cannot specify both a subpath and a projection. + assert.commandFailedWithCode( + createIndexHelper({"a.$**": 1}, {name: kIndexName, wildcardProjection: {a: 1}}), + ErrorCodes.FailedToParse); + assert.commandFailedWithCode( + createIndexHelper({"a.$**": 1}, {name: kIndexName, wildcardProjection: {b: 0}}), + ErrorCodes.FailedToParse); })(); diff --git a/src/mongo/db/catalog/index_key_validate.cpp b/src/mongo/db/catalog/index_key_validate.cpp index d8bda8fec86..f5b090b40a6 100644 --- a/src/mongo/db/catalog/index_key_validate.cpp +++ b/src/mongo/db/catalog/index_key_validate.cpp @@ -121,13 +121,6 @@ Status validateKeyPattern(const BSONObj& key, IndexDescriptor::IndexVersion inde code, mongoutils::str::stream() << "Unknown index plugin '" << pluginName << '\''); } - if (pluginName == IndexNames::WILDCARD && !internalQueryAllowAllPathsIndexes.load()) { - // TODO: SERVER-36198 remove this check once wildcard indexes are complete. - return Status( - ErrorCodes::NotImplemented, - "Cannot use a wildcard index without enabling internalQueryAllowAllPathsIndexes"); - } - BSONObjIterator it(key); while (it.more()) { BSONElement keyElement = it.next(); diff --git a/src/mongo/db/catalog/index_key_validate_test.cpp b/src/mongo/db/catalog/index_key_validate_test.cpp index 371cdc62633..1800e6d06d5 100644 --- a/src/mongo/db/catalog/index_key_validate_test.cpp +++ b/src/mongo/db/catalog/index_key_validate_test.cpp @@ -56,19 +56,14 @@ public: TestCommandQueryKnobGuard() { _prevEnabled = getTestCommandsEnabled(); setTestCommandsEnabled(true); - - _prevKnobEnabled = internalQueryAllowAllPathsIndexes.load(); - internalQueryAllowAllPathsIndexes.store(true); } ~TestCommandQueryKnobGuard() { setTestCommandsEnabled(_prevEnabled); - internalQueryAllowAllPathsIndexes.store(_prevKnobEnabled); } private: bool _prevEnabled; - bool _prevKnobEnabled; }; TEST(IndexKeyValidateTest, KeyElementValueOfSmallPositiveIntSucceeds) { diff --git a/src/mongo/db/catalog/index_spec_validate_test.cpp b/src/mongo/db/catalog/index_spec_validate_test.cpp index 9481460593c..32255f51ab3 100644 --- a/src/mongo/db/catalog/index_spec_validate_test.cpp +++ b/src/mongo/db/catalog/index_spec_validate_test.cpp @@ -68,21 +68,15 @@ public: // TODO: Remove test command enabling/disabling in SERVER-36198 _prevEnabled = getTestCommandsEnabled(); setTestCommandsEnabled(true); - - // TODO: Remove knob enabling/disabling in SERVER-36198. - _prevKnobEnabled = internalQueryAllowAllPathsIndexes.load(); - internalQueryAllowAllPathsIndexes.store(true); } ~TestCommandFcvGuard() { serverGlobalParams.featureCompatibility.setVersion(_prevVersion); setTestCommandsEnabled(_prevEnabled); - internalQueryAllowAllPathsIndexes.store(_prevKnobEnabled); } private: bool _prevEnabled; - bool _prevKnobEnabled; ServerGlobalParams::FeatureCompatibility::Version _prevVersion; }; diff --git a/src/mongo/db/query/query_knobs.cpp b/src/mongo/db/query/query_knobs.cpp index 4d59a177c2e..403cc2ba873 100644 --- a/src/mongo/db/query/query_knobs.cpp +++ b/src/mongo/db/query/query_knobs.cpp @@ -116,6 +116,4 @@ MONGO_EXPORT_SERVER_PARAMETER(internalQueryPlannerGenerateCoveredWholeIndexScans MONGO_EXPORT_SERVER_PARAMETER(internalQueryIgnoreUnknownJSONSchemaKeywords, bool, false); MONGO_EXPORT_SERVER_PARAMETER(internalQueryProhibitBlockingMergeOnMongoS, bool, false); - -MONGO_EXPORT_SERVER_PARAMETER(internalQueryAllowAllPathsIndexes, bool, false); } // namespace mongo diff --git a/src/mongo/db/query/query_knobs.h b/src/mongo/db/query/query_knobs.h index d7f6ab8d1c4..a7bc4a39bee 100644 --- a/src/mongo/db/query/query_knobs.h +++ b/src/mongo/db/query/query_knobs.h @@ -137,10 +137,4 @@ extern AtomicInt32 internalDocumentSourceCursorBatchSizeBytes; extern AtomicInt32 internalDocumentSourceLookupCacheSizeBytes; extern AtomicBool internalQueryProhibitBlockingMergeOnMongoS; - -// -// In-progress features. -// -// TODO: Remove in SERVER-36198. -extern AtomicBool internalQueryAllowAllPathsIndexes; } // namespace mongo diff --git a/src/mongo/dbtests/validate_tests.cpp b/src/mongo/dbtests/validate_tests.cpp index ddc62bffc0e..f060ed785c1 100644 --- a/src/mongo/dbtests/validate_tests.cpp +++ b/src/mongo/dbtests/validate_tests.cpp @@ -66,9 +66,6 @@ public: _db(nullptr) { _client.createCollection(_ns); { - _origWildcardKnob = internalQueryAllowAllPathsIndexes.load(); - internalQueryAllowAllPathsIndexes.store(true); - AutoGetCollection autoGetCollection(&_opCtx, _nss, MODE_X); _isInRecordIdOrder = autoGetCollection.getCollection()->getRecordStore()->isInRecordIdOrder(); @@ -78,7 +75,6 @@ public: ~ValidateBase() { _client.dropCollection(_ns); getGlobalServiceContext()->unsetKillAllOperations(); - internalQueryAllowAllPathsIndexes.store(_origWildcardKnob); } protected: @@ -140,7 +136,6 @@ protected: unique_ptr<AutoGetDb> _autoDb; Database* _db; bool _isInRecordIdOrder; - bool _origWildcardKnob{false}; }; template <bool full, bool background> diff --git a/src/mongo/dbtests/wildcard_multikey_persistence_test.cpp b/src/mongo/dbtests/wildcard_multikey_persistence_test.cpp index 6d31c903035..860fec6bec3 100644 --- a/src/mongo/dbtests/wildcard_multikey_persistence_test.cpp +++ b/src/mongo/dbtests/wildcard_multikey_persistence_test.cpp @@ -61,13 +61,10 @@ std::vector<InsertStatement> toInserts(std::vector<BSONObj> docs) { class WildcardMultikeyPersistenceTestFixture : public unittest::Test { public: WildcardMultikeyPersistenceTestFixture() { - _origWildcardKnob = internalQueryAllowAllPathsIndexes.load(); - internalQueryAllowAllPathsIndexes.store(true); _opCtx = cc().makeOperationContext(); } virtual ~WildcardMultikeyPersistenceTestFixture() { - internalQueryAllowAllPathsIndexes.store(_origWildcardKnob); _opCtx.reset(); } |