diff options
author | Dan Larkin-York <dan.larkin-york@mongodb.com> | 2022-01-07 14:55:06 +0000 |
---|---|---|
committer | Evergreen Agent <no-reply@evergreen.mongodb.com> | 2022-01-07 15:30:21 +0000 |
commit | 7a937480d7dec9564fdeb519e98555e315dfa970 (patch) | |
tree | b64e246d4c07be7f26cc87903379c3dc3f6b95e8 /jstests | |
parent | 6f8b7bd312e36b71990b32dda44d2511a27aa386 (diff) | |
download | mongo-7a937480d7dec9564fdeb519e98555e315dfa970.tar.gz |
SERVER-58716 Rewrite index spec hint for time-series collection
Diffstat (limited to 'jstests')
-rw-r--r-- | jstests/core/timeseries/timeseries_index.js | 109 | ||||
-rw-r--r-- | jstests/core/timeseries/timeseries_index_use.js | 667 | ||||
-rw-r--r-- | jstests/core/timeseries/timeseries_special_indexes_metadata.js | 36 |
3 files changed, 420 insertions, 392 deletions
diff --git a/jstests/core/timeseries/timeseries_index.js b/jstests/core/timeseries/timeseries_index.js index 0862b336ad8..000107273e3 100644 --- a/jstests/core/timeseries/timeseries_index.js +++ b/jstests/core/timeseries/timeseries_index.js @@ -50,14 +50,14 @@ TimeseriesTest.run((insert) => { * The second key pattern is what we can expect to use as a hint when querying the bucket * collection. */ - const runTest = function(keyForCreate, hint) { + const runTest = function(spec, bucketSpec) { const coll = db.getCollection(collNamePrefix + collCountPostfix++); const bucketsColl = db.getCollection('system.buckets.' + coll.getName()); coll.drop(); // implicitly drops bucketsColl. jsTestLog('Running test: collection: ' + coll.getFullName() + - ';\nindex spec key for createIndexes: ' + tojson(keyForCreate) + - ';\nindex spec key for query hint: ' + tojson(hint)); + ';\nindex spec key for createIndexes: ' + tojson(spec) + + ';\nindex spec for buckets collection: ' + tojson(bucketSpec)); assert.commandWorked(db.createCollection( coll.getName(), {timeseries: {timeField: timeFieldName, metaField: metaFieldName}})); @@ -74,12 +74,11 @@ TimeseriesTest.run((insert) => { // Insert data on the time-series collection and index it. assert.commandWorked(insert(coll, doc), 'failed to insert doc: ' + tojson(doc)); - assert.commandWorked(coll.createIndex(keyForCreate), - 'failed to create index: ' + tojson(keyForCreate)); + assert.commandWorked(coll.createIndex(spec), 'failed to create index: ' + tojson(spec)); // Check that the buckets collection was created, the index on it is usable and the document // is present in the expected format. - const bucketDocs = bucketsColl.find().hint(hint).toArray(); + const bucketDocs = bucketsColl.find().hint(bucketSpec).toArray(); assert.eq(1, bucketDocs.length, bucketDocs); const bucketDoc = bucketDocs[0]; @@ -94,92 +93,88 @@ TimeseriesTest.run((insert) => { let cursorDoc = assert.commandWorked(db.runCommand({listIndexes: coll.getName()})).cursor; assert.eq(coll.getFullName(), cursorDoc.ns, tojson(cursorDoc)); assert.eq(1 + numExtraIndexes, cursorDoc.firstBatch.length, tojson(cursorDoc)); - assert.contains(keyForCreate, cursorDoc.firstBatch.map(ix => ix.key), tojson(cursorDoc)); + assert.contains(spec, cursorDoc.firstBatch.map(ix => ix.key), tojson(cursorDoc)); // Check that listIndexes against the buckets collection returns the index as hinted cursorDoc = assert.commandWorked(db.runCommand({listIndexes: bucketsColl.getName()})).cursor; assert.eq(bucketsColl.getFullName(), cursorDoc.ns, tojson(cursorDoc)); assert.eq(1 + numExtraIndexes, cursorDoc.firstBatch.length, tojson(cursorDoc)); - assert.contains(hint, cursorDoc.firstBatch.map(ix => ix.key), tojson(cursorDoc)); + assert.contains(bucketSpec, cursorDoc.firstBatch.map(ix => ix.key), tojson(cursorDoc)); // Drop the index on the time-series collection and then check that the underlying buckets // collection index was dropped properly. - assert.commandWorked(coll.dropIndex(keyForCreate), - 'failed to drop index: ' + tojson(keyForCreate)); - assert.commandFailedWithCode(assert.throws(() => bucketsColl.find().hint(hint).toArray()), - ErrorCodes.BadValue); - assert.commandFailedWithCode(assert.throws(() => coll.find().hint(hint).toArray()), + assert.commandWorked(coll.dropIndex(spec), 'failed to drop index: ' + tojson(spec)); + assert.commandFailedWithCode( + assert.throws(() => bucketsColl.find().hint(bucketSpec).toArray()), + ErrorCodes.BadValue); + assert.commandFailedWithCode(assert.throws(() => coll.find().hint(spec).toArray()), ErrorCodes.BadValue); // Check that we are able to drop the index by name (single name and array of names). - assert.commandWorked(coll.createIndex(keyForCreate, {name: 'myindex1'}), - 'failed to create index: ' + tojson(keyForCreate)); + assert.commandWorked(coll.createIndex(spec, {name: 'myindex1'}), + 'failed to create index: ' + tojson(spec)); assert.commandWorked(coll.dropIndex('myindex1'), 'failed to drop index: myindex1'); - assert.commandWorked(coll.createIndex(keyForCreate, {name: 'myindex2'}), - 'failed to create index: ' + tojson(keyForCreate)); + assert.commandWorked(coll.createIndex(spec, {name: 'myindex2'}), + 'failed to create index: ' + tojson(spec)); assert.commandWorked(coll.dropIndexes(['myindex2']), 'failed to drop indexes: [myindex2]'); // Check that we are able to hide and unhide the index by name. - assert.commandWorked(coll.createIndex(keyForCreate, {name: 'hide1'}), - 'failed to create index: ' + tojson(keyForCreate)); - assert.eq(1, bucketsColl.find().hint(hint).toArray().length); - assert.eq(1, coll.find().hint(hint).toArray().length); + assert.commandWorked(coll.createIndex(spec, {name: 'hide1'}), + 'failed to create index: ' + tojson(spec)); + assert.eq(1, bucketsColl.find().hint(bucketSpec).toArray().length); + assert.eq(1, coll.find().hint(spec).toArray().length); assert.commandWorked(coll.hideIndex('hide1'), 'failed to hide index: hide1'); - assert.commandFailedWithCode(assert.throws(() => bucketsColl.find().hint(hint).toArray()), - ErrorCodes.BadValue); - assert.commandFailedWithCode(assert.throws(() => coll.find().hint(keyForCreate).toArray()), + assert.commandFailedWithCode( + assert.throws(() => bucketsColl.find().hint(bucketSpec).toArray()), + ErrorCodes.BadValue); + assert.commandFailedWithCode(assert.throws(() => coll.find().hint(spec).toArray()), ErrorCodes.BadValue); assert.commandWorked(coll.unhideIndex('hide1'), 'failed to unhide index: hide1'); - assert.eq(1, bucketsColl.find().hint(hint).toArray().length); - assert.eq(1, coll.find().hint(hint).toArray().length); + assert.eq(1, bucketsColl.find().hint(bucketSpec).toArray().length); + assert.eq(1, coll.find().hint(spec).toArray().length); assert.commandWorked(coll.dropIndex('hide1'), 'failed to drop index: hide1'); // Check that we are able to hide and unhide the index by key. - assert.commandWorked(coll.createIndex(keyForCreate, {name: 'hide2'}), - 'failed to create index: ' + tojson(keyForCreate)); - assert.eq(1, bucketsColl.find().hint(hint).toArray().length); - assert.eq(1, coll.find().hint(hint).toArray().length); - assert.commandWorked(coll.hideIndex(keyForCreate), 'failed to hide index: hide2'); - assert.commandFailedWithCode(assert.throws(() => bucketsColl.find().hint(hint).toArray()), - ErrorCodes.BadValue); - assert.commandFailedWithCode(assert.throws(() => coll.find().hint(hint).toArray()), + assert.commandWorked(coll.createIndex(spec, {name: 'hide2'}), + 'failed to create index: ' + tojson(spec)); + assert.eq(1, bucketsColl.find().hint(bucketSpec).toArray().length); + assert.eq(1, coll.find().hint(spec).toArray().length); + assert.commandWorked(coll.hideIndex(spec), 'failed to hide index: hide2'); + assert.commandFailedWithCode( + assert.throws(() => bucketsColl.find().hint(bucketSpec).toArray()), + ErrorCodes.BadValue); + assert.commandFailedWithCode(assert.throws(() => coll.find().hint(spec).toArray()), ErrorCodes.BadValue); - assert.commandWorked(coll.unhideIndex(keyForCreate), 'failed to unhide index: hide2'); - assert.eq(1, bucketsColl.find().hint(hint).toArray().length); - assert.eq(1, coll.find().hint(hint).toArray().length); + assert.commandWorked(coll.unhideIndex(spec), 'failed to unhide index: hide2'); + assert.eq(1, bucketsColl.find().hint(bucketSpec).toArray().length); + assert.eq(1, coll.find().hint(spec).toArray().length); assert.commandWorked(coll.dropIndex('hide2'), 'failed to drop index: hide2'); // Check that we are able to create the index as hidden. - assert.commandWorked(coll.createIndex(keyForCreate, {name: 'hide3', hidden: true}), - 'failed to create index: ' + tojson(keyForCreate)); - assert.commandFailedWithCode(assert.throws(() => bucketsColl.find().hint(hint).toArray()), - ErrorCodes.BadValue); - assert.commandFailedWithCode(assert.throws(() => coll.find().hint(hint).toArray()), + assert.commandWorked(coll.createIndex(spec, {name: 'hide3', hidden: true}), + 'failed to create index: ' + tojson(spec)); + assert.commandFailedWithCode( + assert.throws(() => bucketsColl.find().hint(bucketSpec).toArray()), + ErrorCodes.BadValue); + assert.commandFailedWithCode(assert.throws(() => coll.find().hint(spec).toArray()), ErrorCodes.BadValue); - assert.commandWorked(coll.unhideIndex(keyForCreate), 'failed to unhide index: hide3'); - assert.eq(1, bucketsColl.find().hint(hint).toArray().length); - assert.eq(1, coll.find().hint(hint).toArray().length); + assert.commandWorked(coll.unhideIndex(spec), 'failed to unhide index: hide3'); + assert.eq(1, bucketsColl.find().hint(bucketSpec).toArray().length); + assert.eq(1, coll.find().hint(spec).toArray().length); assert.commandWorked(coll.dropIndex('hide3'), 'failed to drop index: hide3'); // Check that user hints on queries will be allowed and will reference the indexes on the // buckets collection directly. - assert.commandWorked(coll.createIndex(keyForCreate, {name: 'index_for_hint_test'}), - 'failed to create index index_for_hint_test: ' + tojson(keyForCreate)); + assert.commandWorked(coll.createIndex(spec, {name: 'index_for_hint_test'}), + 'failed to create index index_for_hint_test: ' + tojson(spec)); // Specifying the index by name should work on both the time-series collection and the // underlying buckets collection. assert.eq(1, bucketsColl.find().hint('index_for_hint_test').toArray().length); assert.eq(1, coll.find().hint('index_for_hint_test').toArray().length); - // Specifying the index by key pattern should work when we use the underlying buckets - // collection's schema. - assert.eq(1, bucketsColl.find().hint(hint).toArray().length); - assert.eq(1, coll.find().hint(hint).toArray().length); - // Specifying the index by key pattern on the time-series collection should not work. - assert.commandFailedWithCode( - assert.throws(() => bucketsColl.find().hint(keyForCreate).toArray()), - ErrorCodes.BadValue); - assert.commandFailedWithCode(assert.throws(() => coll.find().hint(keyForCreate).toArray()), - ErrorCodes.BadValue); + // Specifying the index by key pattern should work. + assert.eq(1, bucketsColl.find().hint(bucketSpec).toArray().length); + assert.eq(1, coll.find().hint(spec).toArray().length); assert.commandWorked(coll.dropIndex('index_for_hint_test'), 'failed to drop index: index_for_hint_test'); }; diff --git a/jstests/core/timeseries/timeseries_index_use.js b/jstests/core/timeseries/timeseries_index_use.js index 1c431e323eb..71c3a99bbfb 100644 --- a/jstests/core/timeseries/timeseries_index_use.js +++ b/jstests/core/timeseries/timeseries_index_use.js @@ -15,329 +15,372 @@ load("jstests/core/timeseries/libs/timeseries.js"); load("jstests/libs/analyze_plan.js"); -TimeseriesTest.run((insert) => { - const testDB = db.getSiblingDB(jsTestName()); - assert.commandWorked(testDB.dropDatabase()); - - // Create timeseries collection. - const timeFieldName = 'ts'; - const metaFieldName = 'mm'; - const coll = testDB.getCollection('t'); - const bucketsColl = testDB.getCollection('system.buckets.' + coll.getName()); - - /** - * Sets up an empty time-series collection with options 'collOpts' on namespace 't' using - * 'timeFieldName' and 'metaFieldName'. Checks that the buckets collection is created, as well. - */ - function resetCollections(collOpts = {}) { - coll.drop(); // implicitly drops bucketsColl. - - assert.commandWorked(testDB.createCollection( - coll.getName(), - Object.assign({timeseries: {timeField: timeFieldName, metaField: metaFieldName}}, - collOpts))); - - const dbCollNames = testDB.getCollectionNames(); - assert.contains(bucketsColl.getName(), - dbCollNames, - "Failed to find namespace '" + bucketsColl.getName() + - "' amongst: " + tojson(dbCollNames)); - } - - /** - * Creates the index specified by the spec and options, then explains the query to ensure that - * the created index is used. Runs the query and verifies that the expected number of documents - * are matched. Finally, deletes the created index. - */ - const testQueryUsesIndex = function( - filter, numMatches, indexSpec, indexOpts = {}, queryOpts = {}) { - assert.commandWorked( - coll.createIndex(indexSpec, Object.assign({name: "testIndexName"}, indexOpts))); - - let query = coll.find(filter); - if (queryOpts.collation) - query = query.collation(queryOpts.collation); - - assert.eq(numMatches, query.itcount()); - - const explain = query.explain(); - const ixscan = getAggPlanStage(explain, "IXSCAN"); - assert.neq(null, ixscan, tojson(explain)); - assert.eq("testIndexName", ixscan.indexName, tojson(ixscan)); - assert.commandWorked(coll.dropIndex("testIndexName")); - }; +const generateTest = (useHint) => { + return (insert) => { + const testDB = db.getSiblingDB(jsTestName()); + assert.commandWorked(testDB.dropDatabase()); + + // Create timeseries collection. + const timeFieldName = 'ts'; + const metaFieldName = 'mm'; + const coll = testDB.getCollection('t'); + const bucketsColl = testDB.getCollection('system.buckets.' + coll.getName()); + + /** + * Sets up an empty time-series collection with options 'collOpts' on namespace 't' using + * 'timeFieldName' and 'metaFieldName'. Checks that the buckets collection is created, as + * well. + */ + function resetCollections(collOpts = {}) { + coll.drop(); // implicitly drops bucketsColl. + + assert.commandWorked(testDB.createCollection( + coll.getName(), + Object.assign({timeseries: {timeField: timeFieldName, metaField: metaFieldName}}, + collOpts))); + + const dbCollNames = testDB.getCollectionNames(); + assert.contains(bucketsColl.getName(), + dbCollNames, + "Failed to find namespace '" + bucketsColl.getName() + + "' amongst: " + tojson(dbCollNames)); + } - /** - * Creates the index specified by the spec and options, then explains the query to ensure that - * the created index is used. Runs the query and verifies that the expected number of documents - * are matched. Finally, deletes the created index. - */ - const testAggregationUsesIndex = function( - pipeline, numMatches, indexSpec, stageType = "IXSCAN", indexOpts = {}) { - assert.commandWorked( - coll.createIndex(indexSpec, Object.assign({name: "testIndexName"}, indexOpts))); - - let aggregation = coll.aggregate(pipeline); - assert.eq(numMatches, aggregation.itcount()); - - const explain = coll.explain().aggregate(pipeline); - const ixscan = getAggPlanStage(explain, stageType); - assert.neq(null, ixscan, tojson(explain)); - assert.eq("testIndexName", ixscan.indexName, tojson(ixscan)); - - assert.commandWorked(coll.dropIndex("testIndexName")); - }; + /** + * Creates the index specified by the spec and options, then explains the query to ensure + * that the created index is used. Runs the query and verifies that the expected number of + * documents are matched. Finally, deletes the created index. + */ + const testQueryUsesIndex = function( + filter, numMatches, indexSpec, indexOpts = {}, queryOpts = {}) { + assert.commandWorked( + coll.createIndex(indexSpec, Object.assign({name: "testIndexName"}, indexOpts))); + + let query = coll.find(filter); + if (useHint) + query = query.hint(indexSpec); + if (queryOpts.collation) + query = query.collation(queryOpts.collation); + + assert.eq(numMatches, query.itcount()); + + const explain = query.explain(); + const ixscan = getAggPlanStage(explain, "IXSCAN"); + assert.neq(null, ixscan, tojson(explain)); + assert.eq("testIndexName", ixscan.indexName, tojson(ixscan)); + assert.commandWorked(coll.dropIndex("testIndexName")); + }; + + /** + * Creates the index specified by the spec and options, then explains the query to ensure + * that the created index is used. Runs the query and verifies that the expected number of + * documents are matched. Finally, deletes the created index. + */ + const testAggregationUsesIndex = function( + pipeline, numMatches, indexSpec, stageType = "IXSCAN", indexOpts = {}) { + assert.commandWorked( + coll.createIndex(indexSpec, Object.assign({name: "testIndexName"}, indexOpts))); + + let aggregation = coll.aggregate(pipeline); + assert.eq(numMatches, aggregation.itcount()); + + const options = useHint ? {hint: indexSpec} : {}; + const explain = coll.explain().aggregate(pipeline, options); + const ixscan = getAggPlanStage(explain, stageType); + assert.neq(null, ixscan, tojson(explain)); + assert.eq("testIndexName", ixscan.indexName, tojson(ixscan)); + + assert.commandWorked(coll.dropIndex("testIndexName")); + }; + + /******************************* Tests scalar meta values *********************************/ + resetCollections(); + assert.commandWorked(insert(coll, [ + {_id: 0, [timeFieldName]: ISODate('1990-01-01 00:00:00.000Z'), [metaFieldName]: 2}, + {_id: 1, [timeFieldName]: ISODate('2000-01-01 00:00:00.000Z'), [metaFieldName]: 3}, + {_id: 2, [timeFieldName]: ISODate('2010-01-01 00:00:00.000Z'), [metaFieldName]: 2} + ])); + + const timeDate = ISODate('2005-01-01 00:00:00.000Z'); + + // Test ascending and descending index on timeField. + if (!FixtureHelpers.isSharded(bucketsColl)) { + // Skip if the collection is implicitly sharded: it may use the implicitly created + // index. + testQueryUsesIndex({[timeFieldName]: {$lte: timeDate}}, 2, {[timeFieldName]: 1}); + testQueryUsesIndex({[timeFieldName]: {$gte: timeDate}}, 1, {[timeFieldName]: -1}); + } + + // Test ascending and descending index on metaField. + testQueryUsesIndex({[metaFieldName]: {$eq: 3}}, 1, {[metaFieldName]: 1}); + testQueryUsesIndex({[metaFieldName]: {$lt: 3}}, 2, {[metaFieldName]: -1}); + + // Test compound indexes on metaField and timeField. + if (!FixtureHelpers.isSharded(bucketsColl)) { + // Skip if the collection is implicitly sharded: it may use the implicitly created + // index. + testQueryUsesIndex( + {[metaFieldName]: {$gte: 2}}, 3, {[metaFieldName]: 1, [timeFieldName]: 1}); + testQueryUsesIndex( + {[timeFieldName]: {$lt: timeDate}}, 2, {[timeFieldName]: 1, [metaFieldName]: 1}); + testQueryUsesIndex({[metaFieldName]: {$lte: 3}, [timeFieldName]: {$gte: timeDate}}, + 1, + {[metaFieldName]: 1, [timeFieldName]: 1}); + testQueryUsesIndex({[metaFieldName]: {$eq: 2}, [timeFieldName]: {$lte: timeDate}}, + 1, + {[metaFieldName]: -1, [timeFieldName]: -1}); + testQueryUsesIndex({[metaFieldName]: {$lt: 3}, [timeFieldName]: {$lte: timeDate}}, + 1, + {[timeFieldName]: 1, [metaFieldName]: -1}); + } - /********************************** Tests scalar meta values **********************************/ - resetCollections(); - assert.commandWorked(insert(coll, [ - {_id: 0, [timeFieldName]: ISODate('1990-01-01 00:00:00.000Z'), [metaFieldName]: 2}, - {_id: 1, [timeFieldName]: ISODate('2000-01-01 00:00:00.000Z'), [metaFieldName]: 3}, - {_id: 2, [timeFieldName]: ISODate('2010-01-01 00:00:00.000Z'), [metaFieldName]: 2} - ])); - - const timeDate = ISODate('2005-01-01 00:00:00.000Z'); - - // Test ascending and descending index on timeField. - if (!FixtureHelpers.isSharded(bucketsColl)) { - // Skip if the collection is implicitly sharded: it may use the implicitly created index. - testQueryUsesIndex({[timeFieldName]: {$lte: timeDate}}, 2, {[timeFieldName]: 1}); - testQueryUsesIndex({[timeFieldName]: {$gte: timeDate}}, 1, {[timeFieldName]: -1}); - } - - // Test ascending and descending index on metaField. - testQueryUsesIndex({[metaFieldName]: {$eq: 3}}, 1, {[metaFieldName]: 1}); - testQueryUsesIndex({[metaFieldName]: {$lt: 3}}, 2, {[metaFieldName]: -1}); - - // Test compound indexes on metaField and timeField. - if (!FixtureHelpers.isSharded(bucketsColl)) { - // Skip if the collection is implicitly sharded: it may use the implicitly created index. + /******************************** Tests object meta values ********************************/ + resetCollections(); + assert.commandWorked(insert(coll, [ + {_id: 0, [timeFieldName]: ISODate('1990-01-01 00:00:00.000Z'), [metaFieldName]: {a: 1}}, + { + _id: 1, + [timeFieldName]: ISODate('2000-01-01 00:00:00.000Z'), + [metaFieldName]: {a: 4, b: 5, loc: [1.0, 2.0]} + }, + { + _id: 2, + [timeFieldName]: ISODate('2010-01-01 00:00:00.000Z'), + [metaFieldName]: {a: "1", c: 0, loc: [-1.0, -2.0]} + } + ])); + + // Test indexes on subfields of metaField. + testQueryUsesIndex({[metaFieldName + '.a']: {$gt: 3}}, 1, {[metaFieldName + '.a']: 1}); testQueryUsesIndex( - {[metaFieldName]: {$gte: 2}}, 3, {[metaFieldName]: 1, [timeFieldName]: 1}); + {[metaFieldName + '.a']: {$type: 'string'}}, 1, {[metaFieldName + '.a']: -1}); testQueryUsesIndex( - {[timeFieldName]: {$lt: timeDate}}, 2, {[timeFieldName]: 1, [metaFieldName]: 1}); - testQueryUsesIndex({[metaFieldName]: {$lte: 3}, [timeFieldName]: {$gte: timeDate}}, + {[metaFieldName + '.b']: {$gte: 0}}, 1, {[metaFieldName + '.b']: 1}, {sparse: true}); + testQueryUsesIndex({[metaFieldName]: {$eq: {a: 1}}}, 1, {[metaFieldName]: 1}); + testQueryUsesIndex({[metaFieldName]: {$in: [{a: 1}, {a: 4, b: 5, loc: [1.0, 2.0]}]}}, + 2, + {[metaFieldName]: 1}); + + // Test compound indexes on multiple subfields of metaField. + testQueryUsesIndex({[metaFieldName + '.a']: {$lt: 3}}, 1, - {[metaFieldName]: 1, [timeFieldName]: 1}); - testQueryUsesIndex({[metaFieldName]: {$eq: 2}, [timeFieldName]: {$lte: timeDate}}, + {[metaFieldName + '.a']: 1, [metaFieldName + '.b']: -1}); + testQueryUsesIndex({[metaFieldName + '.a']: {$lt: 5}, [metaFieldName + '.b']: {$eq: 5}}, 1, - {[metaFieldName]: -1, [timeFieldName]: -1}); - testQueryUsesIndex({[metaFieldName]: {$lt: 3}, [timeFieldName]: {$lte: timeDate}}, + {[metaFieldName + '.a']: -1, [metaFieldName + '.b']: 1}); + testQueryUsesIndex( + {$or: [{[metaFieldName + '.a']: {$eq: 1}}, {[metaFieldName + '.a']: {$eq: "1"}}]}, + 2, + {[metaFieldName + '.a']: -1, [metaFieldName + '.b']: -1}); + testQueryUsesIndex({[metaFieldName + '.b']: {$lte: 5}}, 1, - {[timeFieldName]: 1, [metaFieldName]: -1}); - } - - /********************************** Tests object meta values **********************************/ - resetCollections(); - assert.commandWorked(insert(coll, [ - {_id: 0, [timeFieldName]: ISODate('1990-01-01 00:00:00.000Z'), [metaFieldName]: {a: 1}}, - { - _id: 1, - [timeFieldName]: ISODate('2000-01-01 00:00:00.000Z'), - [metaFieldName]: {a: 4, b: 5, loc: [1.0, 2.0]} - }, - { - _id: 2, - [timeFieldName]: ISODate('2010-01-01 00:00:00.000Z'), - [metaFieldName]: {a: "1", c: 0, loc: [-1.0, -2.0]} + {[metaFieldName + '.b']: 1, [metaFieldName + '.c']: 1}, + {sparse: true}); + testQueryUsesIndex({[metaFieldName + '.b']: {$lte: 5}, [metaFieldName + '.c']: {$lte: 4}}, + 0, + {[metaFieldName + '.b']: 1, [metaFieldName + '.c']: 1}, + {sparse: true}); + + // Test compound indexes on timeField and subfields of metaField. + if (!FixtureHelpers.isSharded(bucketsColl)) { + // Skip if the collection is implicitly sharded: it may use the implicitly created + // index. + testQueryUsesIndex({[metaFieldName + '.a']: {$gte: 2}}, + 1, + {[metaFieldName + '.a']: 1, [timeFieldName]: 1}); + testQueryUsesIndex({[timeFieldName]: {$lt: timeDate}}, + 2, + {[timeFieldName]: 1, [metaFieldName + '.a']: 1}); + testQueryUsesIndex( + {[metaFieldName + '.a']: {$lte: 4}, [timeFieldName]: {$lte: timeDate}}, + 2, + {[metaFieldName + '.a']: 1, [timeFieldName]: 1}); + testQueryUsesIndex( + {[metaFieldName + '.a']: {$lte: 4}, [timeFieldName]: {$lte: timeDate}}, + 2, + {[metaFieldName + '.a']: 1, [timeFieldName]: -1}); + testQueryUsesIndex( + {[metaFieldName + '.a']: {$eq: "1"}, [timeFieldName]: {$gt: timeDate}}, + 1, + {[timeFieldName]: -1, [metaFieldName + '.a']: 1}); } - ])); - - // Test indexes on subfields of metaField. - testQueryUsesIndex({[metaFieldName + '.a']: {$gt: 3}}, 1, {[metaFieldName + '.a']: 1}); - testQueryUsesIndex( - {[metaFieldName + '.a']: {$type: 'string'}}, 1, {[metaFieldName + '.a']: -1}); - testQueryUsesIndex( - {[metaFieldName + '.b']: {$gte: 0}}, 1, {[metaFieldName + '.b']: 1}, {sparse: true}); - testQueryUsesIndex({[metaFieldName]: {$eq: {a: 1}}}, 1, {[metaFieldName]: 1}); - testQueryUsesIndex( - {[metaFieldName]: {$in: [{a: 1}, {a: 4, b: 5, loc: [1.0, 2.0]}]}}, 2, {[metaFieldName]: 1}); - - // Test compound indexes on multiple subfields of metaField. - testQueryUsesIndex({[metaFieldName + '.a']: {$lt: 3}}, - 1, - {[metaFieldName + '.a']: 1, [metaFieldName + '.b']: -1}); - testQueryUsesIndex({[metaFieldName + '.a']: {$lt: 5}, [metaFieldName + '.b']: {$eq: 5}}, - 1, - {[metaFieldName + '.a']: -1, [metaFieldName + '.b']: 1}); - testQueryUsesIndex( - {$or: [{[metaFieldName + '.a']: {$eq: 1}}, {[metaFieldName + '.a']: {$eq: "1"}}]}, - 2, - {[metaFieldName + '.a']: -1, [metaFieldName + '.b']: -1}); - testQueryUsesIndex({[metaFieldName + '.b']: {$lte: 5}}, - 1, - {[metaFieldName + '.b']: 1, [metaFieldName + '.c']: 1}, - {sparse: true}); - testQueryUsesIndex({[metaFieldName + '.b']: {$lte: 5}, [metaFieldName + '.c']: {$lte: 4}}, - 0, - {[metaFieldName + '.b']: 1, [metaFieldName + '.c']: 1}, - {sparse: true}); - - // Test compound indexes on timeField and subfields of metaField. - if (!FixtureHelpers.isSharded(bucketsColl)) { - // Skip if the collection is implicitly sharded: it may use the implicitly created index. - testQueryUsesIndex({[metaFieldName + '.a']: {$gte: 2}}, - 1, - {[metaFieldName + '.a']: 1, [timeFieldName]: 1}); + + // Test wildcard indexes with metaField. + testQueryUsesIndex({[metaFieldName + '.a']: {$lt: 3}}, 1, {[metaFieldName + '.$**']: 1}); + testQueryUsesIndex({[metaFieldName + '.b']: {$gt: 3}}, 1, {[metaFieldName + '.$**']: 1}); + + // Test hashed indexes on metaField. + testQueryUsesIndex({[metaFieldName]: {$eq: {a: 1}}}, 1, {[metaFieldName]: "hashed"}); testQueryUsesIndex( - {[timeFieldName]: {$lt: timeDate}}, 2, {[timeFieldName]: 1, [metaFieldName + '.a']: 1}); - testQueryUsesIndex({[metaFieldName + '.a']: {$lte: 4}, [timeFieldName]: {$lte: timeDate}}, - 2, - {[metaFieldName + '.a']: 1, [timeFieldName]: 1}); - testQueryUsesIndex({[metaFieldName + '.a']: {$lte: 4}, [timeFieldName]: {$lte: timeDate}}, - 2, - {[metaFieldName + '.a']: 1, [timeFieldName]: -1}); - testQueryUsesIndex({[metaFieldName + '.a']: {$eq: "1"}, [timeFieldName]: {$gt: timeDate}}, + {[metaFieldName + '.a']: {$eq: 1}}, 1, {[metaFieldName + '.a']: "hashed"}); + testQueryUsesIndex({[metaFieldName + '.a']: {$eq: 1}}, 1, - {[timeFieldName]: -1, [metaFieldName + '.a']: 1}); - } - - // Test wildcard indexes with metaField. - testQueryUsesIndex({[metaFieldName + '.a']: {$lt: 3}}, 1, {[metaFieldName + '.$**']: 1}); - testQueryUsesIndex({[metaFieldName + '.b']: {$gt: 3}}, 1, {[metaFieldName + '.$**']: 1}); - - // Test hashed indexes on metaField. - testQueryUsesIndex({[metaFieldName]: {$eq: {a: 1}}}, 1, {[metaFieldName]: "hashed"}); - testQueryUsesIndex({[metaFieldName + '.a']: {$eq: 1}}, 1, {[metaFieldName + '.a']: "hashed"}); - testQueryUsesIndex({[metaFieldName + '.a']: {$eq: 1}}, - 1, - {[metaFieldName + '.a']: "hashed", [metaFieldName + '.b']: -1}); - testQueryUsesIndex({[metaFieldName + '.a']: {$eq: 1}, [metaFieldName + '.b']: {$gt: 0}}, - 0, - {[metaFieldName + '.a']: "hashed", [metaFieldName + '.b']: -1}); - testQueryUsesIndex({[metaFieldName + '.a']: {$eq: 1}, [metaFieldName + '.b']: {$gt: 0}}, - 0, - {[metaFieldName + '.b']: -1, [metaFieldName + '.a']: "hashed"}); - - // Test geo-type indexes on metaField. - testQueryUsesIndex({ - [metaFieldName + '.loc']: { - $geoWithin: { - $geometry: - {type: "Polygon", coordinates: [[[0, 0], [0, 3], [3, 3], [3, 0], [0, 0]]]} + {[metaFieldName + '.a']: "hashed", [metaFieldName + '.b']: -1}); + testQueryUsesIndex({[metaFieldName + '.a']: {$eq: 1}, [metaFieldName + '.b']: {$gt: 0}}, + 0, + {[metaFieldName + '.a']: "hashed", [metaFieldName + '.b']: -1}); + testQueryUsesIndex({[metaFieldName + '.a']: {$eq: 1}, [metaFieldName + '.b']: {$gt: 0}}, + 0, + {[metaFieldName + '.b']: -1, [metaFieldName + '.a']: "hashed"}); + + // Test geo-type indexes on metaField. + testQueryUsesIndex({ + [metaFieldName + '.loc']: { + $geoWithin: { + $geometry: + {type: "Polygon", coordinates: [[[0, 0], [0, 3], [3, 3], [3, 0], [0, 0]]]} + } } - } - }, - 1, - {[metaFieldName + '.loc']: '2dsphere'}); - testQueryUsesIndex({[metaFieldName + '.loc']: {$geoWithin: {$center: [[1.01, 2.01], 0.1]}}}, - 1, - {[metaFieldName + '.loc']: '2d'}); - testAggregationUsesIndex( - [ + }, + 1, + {[metaFieldName + '.loc']: '2dsphere'}); + testQueryUsesIndex({[metaFieldName + '.loc']: {$geoWithin: {$center: [[1.01, 2.01], 0.1]}}}, + 1, + {[metaFieldName + '.loc']: '2d'}); + testAggregationUsesIndex( + [ + { + $geoNear: { + near: {type: "Point", coordinates: [40.4, -70.4]}, + distanceField: "dist", + spherical: true, + key: metaFieldName + '.loc' + } + }, + {$limit: 1} + ], + 1, + {[metaFieldName + '.loc']: '2dsphere'}, + "GEO_NEAR_2DSPHERE"); + testAggregationUsesIndex( + [ + { + $geoNear: + {near: [40.4, -70.4], distanceField: "dist", key: metaFieldName + '.loc'} + }, + {$limit: 1} + ], + 1, + {[metaFieldName + '.loc']: '2d'}, + "GEO_NEAR_2D"); + + /********************************* Tests array meta values ********************************/ + resetCollections(); + assert.commandWorked(insert(coll, [ { - $geoNear: { - near: {type: "Point", coordinates: [40.4, -70.4]}, - distanceField: "dist", - spherical: true, - key: metaFieldName + '.loc' - } + _id: 0, + [timeFieldName]: ISODate('1990-01-01 00:00:00.000Z'), + [metaFieldName]: [1, 2, 3] }, - {$limit: 1} - ], - 1, - {[metaFieldName + '.loc']: '2dsphere'}, - "GEO_NEAR_2DSPHERE"); - testAggregationUsesIndex( - [ - {$geoNear: {near: [40.4, -70.4], distanceField: "dist", key: metaFieldName + '.loc'}}, - {$limit: 1} - ], - 1, - {[metaFieldName + '.loc']: '2d'}, - "GEO_NEAR_2D"); - - /*********************************** Tests array meta values **********************************/ - resetCollections(); - assert.commandWorked(insert(coll, [ - {_id: 0, [timeFieldName]: ISODate('1990-01-01 00:00:00.000Z'), [metaFieldName]: [1, 2, 3]}, - {_id: 1, [timeFieldName]: ISODate('2000-01-01 00:00:00.000Z'), [metaFieldName]: ['a', 'b']}, - { - _id: 2, - [timeFieldName]: ISODate('2010-01-01 00:00:00.000Z'), - [metaFieldName]: [{a: 1}, {b: 0}] - } - ])); - - // Test multikey indexes on metaField. - testQueryUsesIndex({[metaFieldName]: {$eq: {a: 1}}}, 1, {[metaFieldName]: 1}); - testQueryUsesIndex({[metaFieldName]: {$eq: 2}}, 1, {[metaFieldName]: 1}); - testQueryUsesIndex({[metaFieldName]: {$gte: {a: 1}}}, 1, {[metaFieldName]: -1}); - - resetCollections(); - assert.commandWorked(insert(coll, [ - { - _id: 0, - [timeFieldName]: ISODate('1990-01-01 00:00:00.000Z'), - [metaFieldName]: {a: [1, 2, 3], b: 0} - }, - { - _id: 1, - [timeFieldName]: ISODate('2000-01-01 00:00:00.000Z'), - [metaFieldName]: {a: ['a', 'b'], b: 1} - }, - { - _id: 2, - [timeFieldName]: ISODate('2010-01-01 00:00:00.000Z'), - [metaFieldName]: {a: [{b: 1}, {c: 0}]} - } - ])); - - // Test multikey indexes on subfields of metaFields. - testQueryUsesIndex({[metaFieldName + '.a']: {$eq: {b: 1}}}, 1, {[metaFieldName + '.a']: 1}); - testQueryUsesIndex({[metaFieldName + '.a']: {$eq: 2}}, 1, {[metaFieldName + '.a']: 1}); - testQueryUsesIndex({[metaFieldName + '.a']: {$gte: {a: 1}}}, 1, {[metaFieldName + '.a']: -1}); - testQueryUsesIndex({[metaFieldName + '.a']: {$gte: 1}, [metaFieldName + '.b']: {$exists: 1}}, - 1, - {[metaFieldName + '.a']: -1, [metaFieldName + '.b']: 1}); - - /*********************************** Tests string meta values *********************************/ - const collation = {collation: {locale: 'en', strength: 1, numericOrdering: true}}; - - // Create timeseries collection with a collation. - resetCollections(collation); - assert.commandWorked(insert(coll, [ - { - _id: 0, - [timeFieldName]: ISODate('1990-01-01 00:00:00.000Z'), - [metaFieldName]: "hello hello" - }, - { - _id: 1, - [timeFieldName]: ISODate('2000-01-01 00:00:00.000Z'), - [metaFieldName]: "hello world" - }, - {_id: 2, [timeFieldName]: ISODate('2010-01-01 00:00:00.000Z'), [metaFieldName]: "bye bye"} - ])); - - // Test index on metaField when collection collation matches query collation. - testQueryUsesIndex({[metaFieldName]: {$eq: "bye bye"}}, 1, {[metaFieldName]: 1}, {}, collation); - testQueryUsesIndex( - {[metaFieldName]: {$gte: "hello hello"}}, 2, {[metaFieldName]: -1}, {}, collation); - - resetCollections(collation); - assert.commandWorked(insert(coll, [ - { - _id: 0, - [timeFieldName]: ISODate('1990-01-01 00:00:00.000Z'), - [metaFieldName]: {a: "hello hello", b: "hello"} - }, - { - _id: 1, - [timeFieldName]: ISODate('2000-01-01 00:00:00.000Z'), - [metaFieldName]: {a: "hello world", b: "hello"} - }, - { - _id: 2, - [timeFieldName]: ISODate('2010-01-01 00:00:00.000Z'), - [metaFieldName]: {a: "bye bye", b: "bye"} - } - ])); - - // Test index on subfields of metaField when collection collation matches query collation. - testQueryUsesIndex( - {[metaFieldName + '.a']: {$eq: "bye bye"}}, 1, {[metaFieldName + '.a']: 1}, {}, collation); - testQueryUsesIndex( - {[metaFieldName + '.b']: {$gt: "bye bye"}}, 2, {[metaFieldName + '.b']: -1}, {}, collation); -}); + { + _id: 1, + [timeFieldName]: ISODate('2000-01-01 00:00:00.000Z'), + [metaFieldName]: ['a', 'b'] + }, + { + _id: 2, + [timeFieldName]: ISODate('2010-01-01 00:00:00.000Z'), + [metaFieldName]: [{a: 1}, {b: 0}] + } + ])); + + // Test multikey indexes on metaField. + testQueryUsesIndex({[metaFieldName]: {$eq: {a: 1}}}, 1, {[metaFieldName]: 1}); + testQueryUsesIndex({[metaFieldName]: {$eq: 2}}, 1, {[metaFieldName]: 1}); + testQueryUsesIndex({[metaFieldName]: {$gte: {a: 1}}}, 1, {[metaFieldName]: -1}); + + resetCollections(); + assert.commandWorked(insert(coll, [ + { + _id: 0, + [timeFieldName]: ISODate('1990-01-01 00:00:00.000Z'), + [metaFieldName]: {a: [1, 2, 3], b: 0} + }, + { + _id: 1, + [timeFieldName]: ISODate('2000-01-01 00:00:00.000Z'), + [metaFieldName]: {a: ['a', 'b'], b: 1} + }, + { + _id: 2, + [timeFieldName]: ISODate('2010-01-01 00:00:00.000Z'), + [metaFieldName]: {a: [{b: 1}, {c: 0}]} + } + ])); + + // Test multikey indexes on subfields of metaFields. + testQueryUsesIndex({[metaFieldName + '.a']: {$eq: {b: 1}}}, 1, {[metaFieldName + '.a']: 1}); + testQueryUsesIndex({[metaFieldName + '.a']: {$eq: 2}}, 1, {[metaFieldName + '.a']: 1}); + testQueryUsesIndex( + {[metaFieldName + '.a']: {$gte: {a: 1}}}, 1, {[metaFieldName + '.a']: -1}); + testQueryUsesIndex( + {[metaFieldName + '.a']: {$gte: 1}, [metaFieldName + '.b']: {$exists: 1}}, + 1, + {[metaFieldName + '.a']: -1, [metaFieldName + '.b']: 1}); + + /********************************* Tests string meta values *******************************/ + const collation = {collation: {locale: 'en', strength: 1, numericOrdering: true}}; + + // Create timeseries collection with a collation. + resetCollections(collation); + assert.commandWorked(insert(coll, [ + { + _id: 0, + [timeFieldName]: ISODate('1990-01-01 00:00:00.000Z'), + [metaFieldName]: "hello hello" + }, + { + _id: 1, + [timeFieldName]: ISODate('2000-01-01 00:00:00.000Z'), + [metaFieldName]: "hello world" + }, + { + _id: 2, + [timeFieldName]: ISODate('2010-01-01 00:00:00.000Z'), + [metaFieldName]: "bye bye" + } + ])); + + // Test index on metaField when collection collation matches query collation. + testQueryUsesIndex( + {[metaFieldName]: {$eq: "bye bye"}}, 1, {[metaFieldName]: 1}, {}, collation); + testQueryUsesIndex( + {[metaFieldName]: {$gte: "hello hello"}}, 2, {[metaFieldName]: -1}, {}, collation); + + resetCollections(collation); + assert.commandWorked(insert(coll, [ + { + _id: 0, + [timeFieldName]: ISODate('1990-01-01 00:00:00.000Z'), + [metaFieldName]: {a: "hello hello", b: "hello"} + }, + { + _id: 1, + [timeFieldName]: ISODate('2000-01-01 00:00:00.000Z'), + [metaFieldName]: {a: "hello world", b: "hello"} + }, + { + _id: 2, + [timeFieldName]: ISODate('2010-01-01 00:00:00.000Z'), + [metaFieldName]: {a: "bye bye", b: "bye"} + } + ])); + + // Test index on subfields of metaField when collection collation matches query collation. + testQueryUsesIndex({[metaFieldName + '.a']: {$eq: "bye bye"}}, + 1, + {[metaFieldName + '.a']: 1}, + {}, + collation); + testQueryUsesIndex({[metaFieldName + '.b']: {$gt: "bye bye"}}, + 2, + {[metaFieldName + '.b']: -1}, + {}, + collation); + }; +}; + +// Run the test twice, once without hinting the index, and again hinting the index by spec. +TimeseriesTest.run(generateTest(false)); +TimeseriesTest.run(generateTest(true)); })(); diff --git a/jstests/core/timeseries/timeseries_special_indexes_metadata.js b/jstests/core/timeseries/timeseries_special_indexes_metadata.js index 35b4d5d5ab9..17600465193 100644 --- a/jstests/core/timeseries/timeseries_special_indexes_metadata.js +++ b/jstests/core/timeseries/timeseries_special_indexes_metadata.js @@ -60,8 +60,8 @@ TimeseriesTest.run((insert) => { ", buckets: " + tojson(bucketsIndexSpec)); // Check that the index is usable. - assert.gt(timeseriescoll.find(timeseriesFindQuery).hint(bucketsIndexSpec).toArray().length, - 0); + assert.gt( + timeseriescoll.find(timeseriesFindQuery).hint(timeseriesIndexSpec).toArray().length, 0); assert.gt(bucketscoll.find(bucketsFindQuery).hint(bucketsIndexSpec).toArray().length, 0); // Check that listIndexes returns expected results. @@ -71,7 +71,7 @@ TimeseriesTest.run((insert) => { // hint. assert.commandWorked(timeseriescoll.hideIndex(timeseriesIndexSpec)); assert.commandFailedWithCode( - assert.throws(() => timeseriescoll.find().hint(bucketsIndexSpec).toArray()), + assert.throws(() => timeseriescoll.find().hint(timeseriesIndexSpec).toArray()), ErrorCodes.BadValue); assert.commandFailedWithCode( assert.throws(() => bucketscoll.find().hint(bucketsIndexSpec).toArray()), @@ -82,8 +82,8 @@ TimeseriesTest.run((insert) => { // Unhide the index and check that the find cmd with 'bucketsIndexSpec' works again. assert.commandWorked(timeseriescoll.unhideIndex(timeseriesIndexSpec)); - assert.gt(timeseriescoll.find(timeseriesFindQuery).hint(bucketsIndexSpec).toArray().length, - 0); + assert.gt( + timeseriescoll.find(timeseriesFindQuery).hint(timeseriesIndexSpec).toArray().length, 0); assert.gt(bucketscoll.find(bucketsFindQuery).hint(bucketsIndexSpec).toArray().length, 0); } @@ -137,17 +137,13 @@ TimeseriesTest.run((insert) => { hideUnhideListIndexes(sparseTimeseriesIndexSpec, sparseBucketsIndexSpec); - // Check that only 1 of the 2 entries are returned. Note: index hints on a time-series - // collection only work with the underlying buckets collection's index spec. + // Check that only 1 of the 2 entries are returned. assert.eq(1, - timeseriescoll.find().hint(sparseBucketsIndexSpec).toArray().length, - "Failed to use index: " + tojson(sparseBucketsIndexSpec)); + timeseriescoll.find().hint(sparseTimeseriesIndexSpec).toArray().length, + "Failed to use index: " + tojson(sparseTimeseriesIndexSpec)); assert.eq(1, bucketscoll.find().hint(sparseBucketsIndexSpec).toArray().length, "Failed to use index: " + tojson(sparseBucketsIndexSpec)); - assert.commandFailedWithCode( - assert.throws(() => timeseriescoll.find().hint(sparseTimeseriesIndexSpec).toArray()), - ErrorCodes.BadValue); assert.eq(2, timeseriescoll.find().toArray().length, "Failed to see all time-series documents"); /** @@ -332,18 +328,12 @@ TimeseriesTest.run((insert) => { const wildcardBucketsResults = bucketscoll.find({'meta.c.d': 1}).hint(wildcardBucketsIndexSpec).toArray(); assert.eq(2, wildcardBucketsResults.length, "Query results: " + tojson(wildcardBucketsResults)); - const wildcardTimeseriesResults = - timeseriescoll.find({[metaFieldName + '.c.d']: 1}).hint(wildcardBucketsIndexSpec).toArray(); + const wildcardTimeseriesResults = timeseriescoll.find({[metaFieldName + '.c.d']: 1}) + .hint(wildcardTimeseriesIndexSpec) + .toArray(); assert.eq( 2, wildcardTimeseriesResults.length, "Query results: " + tojson(wildcardTimeseriesResults)); - // The time-series index spec does not work as a hint. - assert.commandFailedWithCode( - assert.throws(() => timeseriescoll.find({[metaFieldName + '.c.d']: 1}) - .hint(wildcardTimeseriesIndexSpec) - .toArray()), - ErrorCodes.BadValue); - hideUnhideListIndexes(wildcardTimeseriesIndexSpec, wildcardBucketsIndexSpec, {[metaFieldName + '.c.d']: 1} /* timeseriesFindQuery */, @@ -366,8 +356,8 @@ TimeseriesTest.run((insert) => { }; assert.commandWorked(timeseriescoll.insert(wildcardMultikeyDoc)); - assert.eq(1, - timeseriescoll.find({'mm.d.zip': '01234'}).hint(wildcardBucketsIndexSpec).itcount()); + assert.eq( + 1, timeseriescoll.find({'mm.d.zip': '01234'}).hint(wildcardTimeseriesIndexSpec).itcount()); const wildcardFindExplain = assert.commandWorked( bucketscoll.find({'meta.d.zip': '01234'}).hint(wildcardBucketsIndexSpec).explain()); const planWildcardStage = |