diff options
Diffstat (limited to 'jstests/core/timeseries/timeseries_match_pushdown.js')
-rw-r--r-- | jstests/core/timeseries/timeseries_match_pushdown.js | 411 |
1 files changed, 411 insertions, 0 deletions
diff --git a/jstests/core/timeseries/timeseries_match_pushdown.js b/jstests/core/timeseries/timeseries_match_pushdown.js new file mode 100644 index 00000000000..7fe13e878a6 --- /dev/null +++ b/jstests/core/timeseries/timeseries_match_pushdown.js @@ -0,0 +1,411 @@ +/** + * Tests that the $match stage followed by unpacking stage has been pushed down with correct + * predicates. + * + * @tags: [ + * requires_timeseries, + * requires_fcv_62, + * does_not_support_stepdowns, + * directly_against_shardsvrs_incompatible, + * ] + */ +(function() { +"use strict"; + +load("jstests/libs/analyze_plan.js"); // For getAggPlanStages + +const coll = db.timeseries_match_pushdown; +coll.drop(); + +const timeField = 'time'; +const metaField = 'meta'; +const measureField = 'a'; +assert.commandWorked(db.createCollection(coll.getName(), {timeseries: {timeField, metaField}})); + +// Insert documents into the collection. The bucketing is designed so that some buckets match the +// query entirely, some buckets match the query partially, and some with no matches. +assert.commandWorked(coll.insert([ + {[timeField]: ISODate('2022-01-01T00:00:01'), [measureField]: 1, [metaField]: 0}, + {[timeField]: ISODate('2022-01-01T00:00:02'), [measureField]: 2, [metaField]: 1}, + {[timeField]: ISODate('2022-01-01T00:00:03'), [measureField]: 3, [metaField]: 1}, + {[timeField]: ISODate('2022-01-01T00:00:04'), [measureField]: 4, [metaField]: 1}, + {[timeField]: ISODate('2022-01-01T00:00:05'), [measureField]: 5, [metaField]: 2}, + {[timeField]: ISODate('2022-01-01T00:00:06'), [measureField]: 6, [metaField]: 3}, + {[timeField]: ISODate('2022-01-01T00:00:07'), [measureField]: 7, [metaField]: 3}, + {[timeField]: ISODate('2022-01-01T00:00:08'), [measureField]: 8, [metaField]: 3}, + {[timeField]: ISODate('2022-01-01T00:00:09'), [measureField]: 9, [metaField]: 4}, +])); +const aTime = ISODate('2022-01-01T00:00:03'); +const bTime = ISODate('2022-01-01T00:00:07'); +const bMeta = 3; +const aMeasure = 3; + +/** + * Runs a $match query with the specified 'eventFilter' or a 'pipeline'. + * Assert the 'wholeBucketFilter' is attached correctly to the unpacking stage, and has the expected + * result 'expectedDocs'. + */ +const runTest = function({pipeline, eventFilter, wholeBucketFilter, expectedDocs}) { + if (!pipeline) { + pipeline = [{$match: eventFilter}]; + } + const explain = assert.commandWorked(coll.explain().aggregate(pipeline)); + const unpackStages = getAggPlanStages(explain, '$_internalUnpackBucket'); + assert.eq(1, + unpackStages.length, + "Should only have a single $_internalUnpackBucket stage: " + tojson(explain)); + const unpackStage = unpackStages[0].$_internalUnpackBucket; + assert.docEq(unpackStage.eventFilter, eventFilter, "Incorrect eventFilter: " + tojson(explain)); + if (wholeBucketFilter) { + assert.docEq(unpackStage.wholeBucketFilter, + wholeBucketFilter, + "Incorrect wholeBucketFilter: " + tojson(explain)); + } else { + assert(!unpackStage.wholeBucketFilter, "Incorrect wholeBucketFilter: " + tojson(explain)); + } + + const docs = coll.aggregate([...pipeline, {$sort: {time: 1}}]).toArray(); + assert.eq(docs.length, expectedDocs.length, "Incorrect docs: " + tojson(docs)); + expectedDocs.forEach((doc, i) => { + assert.docEq(doc, expectedDocs[i], "Incorrect docs: " + tojson(docs)); + }); +}; + +const minTimeField = `control.min.${timeField}`; +const maxTimeField = `control.max.${timeField}`; + +// $gt on time +runTest({ + eventFilter: {[timeField]: {$gt: aTime}}, + wholeBucketFilter: {[minTimeField]: {$gt: aTime}}, + expectedDocs: [ + {[timeField]: ISODate('2022-01-01T00:00:04'), [measureField]: 4, [metaField]: 1}, + {[timeField]: ISODate('2022-01-01T00:00:05'), [measureField]: 5, [metaField]: 2}, + {[timeField]: ISODate('2022-01-01T00:00:06'), [measureField]: 6, [metaField]: 3}, + {[timeField]: ISODate('2022-01-01T00:00:07'), [measureField]: 7, [metaField]: 3}, + {[timeField]: ISODate('2022-01-01T00:00:08'), [measureField]: 8, [metaField]: 3}, + {[timeField]: ISODate('2022-01-01T00:00:09'), [measureField]: 9, [metaField]: 4}, + ], +}); + +// $gt on measurement +runTest({ + eventFilter: {[measureField]: {$gt: aMeasure}}, + expectedDocs: [ + {[timeField]: ISODate('2022-01-01T00:00:04'), [measureField]: 4, [metaField]: 1}, + {[timeField]: ISODate('2022-01-01T00:00:05'), [measureField]: 5, [metaField]: 2}, + {[timeField]: ISODate('2022-01-01T00:00:06'), [measureField]: 6, [metaField]: 3}, + {[timeField]: ISODate('2022-01-01T00:00:07'), [measureField]: 7, [metaField]: 3}, + {[timeField]: ISODate('2022-01-01T00:00:08'), [measureField]: 8, [metaField]: 3}, + {[timeField]: ISODate('2022-01-01T00:00:09'), [measureField]: 9, [metaField]: 4}, + ], +}); + +// $gt in $expr on time +runTest({ + pipeline: [{$match: {$expr: {$gt: [`$${timeField}`, {$const: aTime}]}}}], + eventFilter: { + $and: [ + {[timeField]: {$_internalExprGt: aTime}}, + {$expr: {$gt: [`$${timeField}`, {$const: aTime}]}}, + ] + }, + wholeBucketFilter: { + $and: [ + {[minTimeField]: {$_internalExprGt: aTime}}, + {[minTimeField]: {$_internalExprGt: aTime}}, + ] + }, + expectedDocs: [ + {[timeField]: ISODate('2022-01-01T00:00:04'), [measureField]: 4, [metaField]: 1}, + {[timeField]: ISODate('2022-01-01T00:00:05'), [measureField]: 5, [metaField]: 2}, + {[timeField]: ISODate('2022-01-01T00:00:06'), [measureField]: 6, [metaField]: 3}, + {[timeField]: ISODate('2022-01-01T00:00:07'), [measureField]: 7, [metaField]: 3}, + {[timeField]: ISODate('2022-01-01T00:00:08'), [measureField]: 8, [metaField]: 3}, + {[timeField]: ISODate('2022-01-01T00:00:09'), [measureField]: 9, [metaField]: 4}, + ], +}); + +// $gte on time +runTest({ + eventFilter: {[timeField]: {$gte: aTime}}, + wholeBucketFilter: {[minTimeField]: {$gte: aTime}}, + expectedDocs: [ + {[timeField]: ISODate('2022-01-01T00:00:03'), [measureField]: 3, [metaField]: 1}, + {[timeField]: ISODate('2022-01-01T00:00:04'), [measureField]: 4, [metaField]: 1}, + {[timeField]: ISODate('2022-01-01T00:00:05'), [measureField]: 5, [metaField]: 2}, + {[timeField]: ISODate('2022-01-01T00:00:06'), [measureField]: 6, [metaField]: 3}, + {[timeField]: ISODate('2022-01-01T00:00:07'), [measureField]: 7, [metaField]: 3}, + {[timeField]: ISODate('2022-01-01T00:00:08'), [measureField]: 8, [metaField]: 3}, + {[timeField]: ISODate('2022-01-01T00:00:09'), [measureField]: 9, [metaField]: 4}, + ], +}); + +// $gte on measurement +runTest({ + eventFilter: {[measureField]: {$gte: aMeasure}}, + expectedDocs: [ + {[timeField]: ISODate('2022-01-01T00:00:03'), [measureField]: 3, [metaField]: 1}, + {[timeField]: ISODate('2022-01-01T00:00:04'), [measureField]: 4, [metaField]: 1}, + {[timeField]: ISODate('2022-01-01T00:00:05'), [measureField]: 5, [metaField]: 2}, + {[timeField]: ISODate('2022-01-01T00:00:06'), [measureField]: 6, [metaField]: 3}, + {[timeField]: ISODate('2022-01-01T00:00:07'), [measureField]: 7, [metaField]: 3}, + {[timeField]: ISODate('2022-01-01T00:00:08'), [measureField]: 8, [metaField]: 3}, + {[timeField]: ISODate('2022-01-01T00:00:09'), [measureField]: 9, [metaField]: 4}, + ], +}); + +// $gte in $expr on time +runTest({ + pipeline: [{$match: {$expr: {$gte: [`$${timeField}`, {$const: aTime}]}}}], + eventFilter: { + $and: [ + {[timeField]: {$_internalExprGte: aTime}}, + {$expr: {$gte: [`$${timeField}`, {$const: aTime}]}}, + ] + }, + wholeBucketFilter: { + $and: [ + {[minTimeField]: {$_internalExprGte: aTime}}, + {[minTimeField]: {$_internalExprGte: aTime}}, + ] + }, + expectedDocs: [ + {[timeField]: ISODate('2022-01-01T00:00:03'), [measureField]: 3, [metaField]: 1}, + {[timeField]: ISODate('2022-01-01T00:00:04'), [measureField]: 4, [metaField]: 1}, + {[timeField]: ISODate('2022-01-01T00:00:05'), [measureField]: 5, [metaField]: 2}, + {[timeField]: ISODate('2022-01-01T00:00:06'), [measureField]: 6, [metaField]: 3}, + {[timeField]: ISODate('2022-01-01T00:00:07'), [measureField]: 7, [metaField]: 3}, + {[timeField]: ISODate('2022-01-01T00:00:08'), [measureField]: 8, [metaField]: 3}, + {[timeField]: ISODate('2022-01-01T00:00:09'), [measureField]: 9, [metaField]: 4}, + ], +}); + +// $lt on time +runTest({ + eventFilter: {[timeField]: {$lt: aTime}}, + wholeBucketFilter: {[maxTimeField]: {$lt: aTime}}, + expectedDocs: [ + {[timeField]: ISODate('2022-01-01T00:00:01'), [measureField]: 1, [metaField]: 0}, + {[timeField]: ISODate('2022-01-01T00:00:02'), [measureField]: 2, [metaField]: 1}, + ], +}); + +// $lt on measurement +runTest({ + eventFilter: {[measureField]: {$lt: aMeasure}}, + expectedDocs: [ + {[timeField]: ISODate('2022-01-01T00:00:01'), [measureField]: 1, [metaField]: 0}, + {[timeField]: ISODate('2022-01-01T00:00:02'), [measureField]: 2, [metaField]: 1}, + ], +}); + +// $lt in $expr on time +runTest({ + pipeline: [{$match: {$expr: {$lt: [`$${timeField}`, {$const: aTime}]}}}], + eventFilter: { + $and: [ + {[timeField]: {$_internalExprLt: aTime}}, + {$expr: {$lt: [`$${timeField}`, {$const: aTime}]}}, + ] + }, + wholeBucketFilter: { + $and: [ + {[maxTimeField]: {$_internalExprLt: aTime}}, + {[maxTimeField]: {$_internalExprLt: aTime}}, + ] + }, + expectedDocs: [ + {[timeField]: ISODate('2022-01-01T00:00:01'), [measureField]: 1, [metaField]: 0}, + {[timeField]: ISODate('2022-01-01T00:00:02'), [measureField]: 2, [metaField]: 1}, + ], +}); + +// $lte on time +runTest({ + eventFilter: {[timeField]: {$lte: aTime}}, + wholeBucketFilter: {[maxTimeField]: {$lte: aTime}}, + expectedDocs: [ + {[timeField]: ISODate('2022-01-01T00:00:01'), [measureField]: 1, [metaField]: 0}, + {[timeField]: ISODate('2022-01-01T00:00:02'), [measureField]: 2, [metaField]: 1}, + {[timeField]: ISODate('2022-01-01T00:00:03'), [measureField]: 3, [metaField]: 1}, + ], +}); + +// $lte in $expr on time +runTest({ + pipeline: [{$match: {$expr: {$lte: [`$${timeField}`, {$const: aTime}]}}}], + eventFilter: { + $and: [ + {[timeField]: {$_internalExprLte: aTime}}, + {$expr: {$lte: [`$${timeField}`, {$const: aTime}]}}, + ] + }, + wholeBucketFilter: { + $and: [ + {[maxTimeField]: {$_internalExprLte: aTime}}, + {[maxTimeField]: {$_internalExprLte: aTime}}, + ] + }, + expectedDocs: [ + {[timeField]: ISODate('2022-01-01T00:00:01'), [measureField]: 1, [metaField]: 0}, + {[timeField]: ISODate('2022-01-01T00:00:02'), [measureField]: 2, [metaField]: 1}, + {[timeField]: ISODate('2022-01-01T00:00:03'), [measureField]: 3, [metaField]: 1}, + ], +}); + +// $lte on measurement +runTest({ + eventFilter: {[measureField]: {$lte: aMeasure}}, + expectedDocs: [ + {[timeField]: ISODate('2022-01-01T00:00:01'), [measureField]: 1, [metaField]: 0}, + {[timeField]: ISODate('2022-01-01T00:00:02'), [measureField]: 2, [metaField]: 1}, + {[timeField]: ISODate('2022-01-01T00:00:03'), [measureField]: 3, [metaField]: 1}, + ], +}); + +// $eq on time +runTest({ + eventFilter: {[timeField]: {$eq: aTime}}, + wholeBucketFilter: {$and: [{[minTimeField]: {$eq: aTime}}, {[maxTimeField]: {$eq: aTime}}]}, + expectedDocs: [ + {[timeField]: ISODate('2022-01-01T00:00:03'), [measureField]: 3, [metaField]: 1}, + ], +}); + +// $eq in $expr on time +runTest({ + pipeline: [{$match: {$expr: {$eq: [`$${timeField}`, {$const: aTime}]}}}], + eventFilter: { + $and: [ + {[timeField]: {$_internalExprEq: aTime}}, + {$expr: {$eq: [`$${timeField}`, {$const: aTime}]}}, + ] + }, + wholeBucketFilter: { + $and: [ + {[minTimeField]: {$_internalExprEq: aTime}}, + {[maxTimeField]: {$_internalExprEq: aTime}}, + {[minTimeField]: {$_internalExprEq: aTime}}, + {[maxTimeField]: {$_internalExprEq: aTime}}, + ] + }, + expectedDocs: [ + {[timeField]: ISODate('2022-01-01T00:00:03'), [measureField]: 3, [metaField]: 1}, + ], +}); + +// $eq on measurement +runTest({ + eventFilter: {[measureField]: {$eq: aMeasure}}, + expectedDocs: [ + {[timeField]: ISODate('2022-01-01T00:00:03'), [measureField]: 3, [metaField]: 1}, + ], +}); + +// $and on time +runTest({ + eventFilter: {$and: [{[timeField]: {$gt: aTime}}, {[timeField]: {$lt: bTime}}]}, + wholeBucketFilter: { + $and: [ + {[minTimeField]: {$gt: aTime}}, + {[maxTimeField]: {$lt: bTime}}, + ] + }, + expectedDocs: [ + {[timeField]: ISODate('2022-01-01T00:00:04'), [measureField]: 4, [metaField]: 1}, + {[timeField]: ISODate('2022-01-01T00:00:05'), [measureField]: 5, [metaField]: 2}, + {[timeField]: ISODate('2022-01-01T00:00:06'), [measureField]: 6, [metaField]: 3}, + ], +}); + +// $or on time +runTest({ + eventFilter: {$or: [{[timeField]: {$lte: aTime}}, {[timeField]: {$gte: bTime}}]}, + wholeBucketFilter: { + $or: [ + {[maxTimeField]: {$lte: aTime}}, + {[minTimeField]: {$gte: bTime}}, + ] + }, + expectedDocs: [ + {[timeField]: ISODate('2022-01-01T00:00:01'), [measureField]: 1, [metaField]: 0}, + {[timeField]: ISODate('2022-01-01T00:00:02'), [measureField]: 2, [metaField]: 1}, + {[timeField]: ISODate('2022-01-01T00:00:03'), [measureField]: 3, [metaField]: 1}, + {[timeField]: ISODate('2022-01-01T00:00:07'), [measureField]: 7, [metaField]: 3}, + {[timeField]: ISODate('2022-01-01T00:00:08'), [measureField]: 8, [metaField]: 3}, + {[timeField]: ISODate('2022-01-01T00:00:09'), [measureField]: 9, [metaField]: 4}, + ], +}); + +// $match on time and meta +runTest({ + pipeline: [{$match: {$and: [{[timeField]: {$gt: aTime}}, {[metaField]: {$lte: bMeta}}]}}], + eventFilter: {[timeField]: {$gt: aTime}}, + wholeBucketFilter: { + [minTimeField]: {$gt: aTime}, + }, + expectedDocs: [ + {[timeField]: ISODate('2022-01-01T00:00:04'), [measureField]: 4, [metaField]: 1}, + {[timeField]: ISODate('2022-01-01T00:00:05'), [measureField]: 5, [metaField]: 2}, + {[timeField]: ISODate('2022-01-01T00:00:06'), [measureField]: 6, [metaField]: 3}, + {[timeField]: ISODate('2022-01-01T00:00:07'), [measureField]: 7, [metaField]: 3}, + {[timeField]: ISODate('2022-01-01T00:00:08'), [measureField]: 8, [metaField]: 3}, + ], +}); + +// $match on time or meta +runTest({ + eventFilter: {$or: [{[timeField]: {$lte: aTime}}, {[metaField]: {$gt: bMeta}}]}, + wholeBucketFilter: { + $or: [ + {[maxTimeField]: {$lte: aTime}}, + {[metaField]: {$gt: bMeta}}, + ] + }, + expectedDocs: [ + {[timeField]: ISODate('2022-01-01T00:00:01'), [measureField]: 1, [metaField]: 0}, + {[timeField]: ISODate('2022-01-01T00:00:02'), [measureField]: 2, [metaField]: 1}, + {[timeField]: ISODate('2022-01-01T00:00:03'), [measureField]: 3, [metaField]: 1}, + {[timeField]: ISODate('2022-01-01T00:00:09'), [measureField]: 9, [metaField]: 4}, + ], +}); + +// double $match +runTest({ + pipeline: [{$match: {[timeField]: {$gt: aTime}}}, {$match: {[timeField]: {$lt: bTime}}}], + eventFilter: {$and: [{[timeField]: {$gt: aTime}}, {[timeField]: {$lt: bTime}}]}, + wholeBucketFilter: { + $and: [ + {[minTimeField]: {$gt: aTime}}, + {[maxTimeField]: {$lt: bTime}}, + ] + }, + expectedDocs: [ + {[timeField]: ISODate('2022-01-01T00:00:04'), [measureField]: 4, [metaField]: 1}, + {[timeField]: ISODate('2022-01-01T00:00:05'), [measureField]: 5, [metaField]: 2}, + {[timeField]: ISODate('2022-01-01T00:00:06'), [measureField]: 6, [metaField]: 3}, + ], +}); + +// triple $match +runTest({ + pipeline: [ + {$match: {[timeField]: {$gt: aTime}}}, + {$match: {[timeField]: {$lt: bTime}}}, + {$match: {[timeField]: {$lt: aTime}}}, + ], + eventFilter: { + $and: + [{[timeField]: {$gt: aTime}}, {[timeField]: {$lt: bTime}}, {[timeField]: {$lt: aTime}}] + }, + wholeBucketFilter: { + $and: [ + {[minTimeField]: {$gt: aTime}}, + {[maxTimeField]: {$lt: bTime}}, + {[maxTimeField]: {$lt: aTime}}, + ] + }, + expectedDocs: [], +}); +})(); |