summaryrefslogtreecommitdiff
path: root/jstests/core/timeseries
diff options
context:
space:
mode:
Diffstat (limited to 'jstests/core/timeseries')
-rw-r--r--jstests/core/timeseries/bucket_unpacking_with_sort.js3
-rw-r--r--jstests/core/timeseries/bucket_unpacking_with_sort_extended_range.js269
-rw-r--r--jstests/core/timeseries/bucket_unpacking_with_sort_plan_cache.js2
-rw-r--r--jstests/core/timeseries/libs/timeseries.js33
-rw-r--r--jstests/core/timeseries/nondefault_collation.js325
-rw-r--r--jstests/core/timeseries/partialFilterExpression_with_internalBucketGeoWithin.js7
-rw-r--r--jstests/core/timeseries/timeseries_bucket_rename.js28
-rw-r--r--jstests/core/timeseries/timeseries_collection_uuid.js42
-rw-r--r--jstests/core/timeseries/timeseries_collmod.js13
-rw-r--r--jstests/core/timeseries/timeseries_computed_field.js856
-rw-r--r--jstests/core/timeseries/timeseries_create_collection.js9
-rw-r--r--jstests/core/timeseries/timeseries_delete_hint.js6
-rw-r--r--jstests/core/timeseries/timeseries_field_parsed_as_bson.js70
-rw-r--r--jstests/core/timeseries/timeseries_filter_extended_range.js185
-rw-r--r--jstests/core/timeseries/timeseries_geonear_lookup.js40
-rw-r--r--jstests/core/timeseries/timeseries_geonear_measurements.js4
-rw-r--r--jstests/core/timeseries/timeseries_graph_lookup.js1
-rw-r--r--jstests/core/timeseries/timeseries_groupby_reorder.js233
-rw-r--r--jstests/core/timeseries/timeseries_index.js9
-rw-r--r--jstests/core/timeseries/timeseries_index_partial.js33
-rw-r--r--jstests/core/timeseries/timeseries_index_spec.js5
-rw-r--r--jstests/core/timeseries/timeseries_index_use.js25
-rw-r--r--jstests/core/timeseries/timeseries_insert_mixed_schema_bucket.js75
-rw-r--r--jstests/core/timeseries/timeseries_internal_bucket_geo_within.js6
-rw-r--r--jstests/core/timeseries/timeseries_lastpoint_top.js1
-rw-r--r--jstests/core/timeseries/timeseries_lookup.js299
-rw-r--r--jstests/core/timeseries/timeseries_match_pushdown.js634
-rw-r--r--jstests/core/timeseries/timeseries_match_pushdown_with_project.js131
-rw-r--r--jstests/core/timeseries/timeseries_merge.js1
-rw-r--r--jstests/core/timeseries/timeseries_metric_index_2dsphere.js3
-rw-r--r--jstests/core/timeseries/timeseries_metric_index_ascending_descending.js3
-rw-r--r--jstests/core/timeseries/timeseries_metric_index_compound.js3
-rw-r--r--jstests/core/timeseries/timeseries_predicates.js204
-rw-r--r--jstests/core/timeseries/timeseries_project.js185
-rw-r--r--jstests/core/timeseries/timeseries_project_pushdown.js196
-rw-r--r--jstests/core/timeseries/timeseries_resume_after.js21
-rw-r--r--jstests/core/timeseries/timeseries_sparse_index.js3
-rw-r--r--jstests/core/timeseries/timeseries_streaming_group.js159
-rw-r--r--jstests/core/timeseries/timeseries_union_with.js5
-rw-r--r--jstests/core/timeseries/timeseries_update_mixed_schema_bucket.js79
40 files changed, 324 insertions, 3882 deletions
diff --git a/jstests/core/timeseries/bucket_unpacking_with_sort.js b/jstests/core/timeseries/bucket_unpacking_with_sort.js
index 695e94daec9..029697be74a 100644
--- a/jstests/core/timeseries/bucket_unpacking_with_sort.js
+++ b/jstests/core/timeseries/bucket_unpacking_with_sort.js
@@ -17,9 +17,6 @@
* requires_timeseries,
* # Explain of a resolved view must be executed by mongos.
* directly_against_shardsvrs_incompatible,
- * # Time-series hint by index key did not exist in 5.0.
- * # Also, the 5.0 version of this feature does not support reverse collscan.
- * requires_fcv_60,
* ]
*/
(function() {
diff --git a/jstests/core/timeseries/bucket_unpacking_with_sort_extended_range.js b/jstests/core/timeseries/bucket_unpacking_with_sort_extended_range.js
deleted file mode 100644
index e74d1c3e914..00000000000
--- a/jstests/core/timeseries/bucket_unpacking_with_sort_extended_range.js
+++ /dev/null
@@ -1,269 +0,0 @@
-/**
- * Test that sort queries work properly on dates ouside the 32 bit epoch range,
- * [1970-01-01 00:00:00 UTC - 2038-01-29 03:13:07 UTC], when a collection scan is used.
- *
- * @tags: [
- * # Explain of a resolved view must be executed by mongos.
- * directly_against_shardsvrs_incompatible,
- * # This complicates aggregation extraction.
- * do_not_wrap_aggregations_in_facets,
- * # Refusing to run a test that issues an aggregation command with explain because it may
- * # return incomplete results if interrupted by a stepdown.
- * does_not_support_stepdowns,
- * # We need a timeseries collection.
- * requires_timeseries,
- * # Cannot insert into a time-series collection in a multi-document transaction.
- * does_not_support_transactions,
- * # Time-series hint by index key did not exist in 5.0
- * requires_fcv_60,
- * ]
- */
-
-(function() {
-"use strict";
-
-load("jstests/aggregation/extras/utils.js"); // For getExplainedPipelineFromAggregation.
-load("jstests/core/timeseries/libs/timeseries.js");
-load('jstests/libs/analyze_plan.js');
-
-if (!TimeseriesTest.bucketUnpackWithSortEnabled(db.getMongo())) {
- jsTestLog("Skipping test because 'BucketUnpackWithSort' is disabled.");
- return;
-}
-
-const timeFieldName = "t";
-
-// Create unindexed collection
-const coll = db.timeseries_internal_bounded_sort_extended_range;
-const buckets = db['system.buckets.' + coll.getName()];
-coll.drop();
-assert.commandWorked(db.createCollection(coll.getName(), {timeseries: {timeField: 't'}}));
-
-// Create collection indexed on time
-const collIndexed = db.timeseries_internal_bounded_sort_extended_range_with_index;
-const bucketsIndexed = db['system.buckets.' + collIndexed.getName()];
-collIndexed.drop();
-assert.commandWorked(db.createCollection(collIndexed.getName(), {timeseries: {timeField: 't'}}));
-assert.commandWorked(collIndexed.createIndex({'t': 1}));
-
-jsTestLog(collIndexed.getIndexes());
-jsTestLog(bucketsIndexed.getIndexes());
-
-function numShards() {
- return db.getSiblingDB('config').shards.count();
-}
-for (const collection of [buckets, bucketsIndexed]) {
- if (FixtureHelpers.isSharded(collection) && numShards() >= 2) {
- // Split and move data to create an interesting scenario: we have some data on each shard,
- // but all the extended-range data is on a non-primary shard. This means view resolution is
- // unaware of the extended-range data, because that happens on the primary shard.
-
- const shards = db.getSiblingDB('config').shards.find().toArray();
- const [shardName0, shardName1] = shards.map(doc => doc._id);
-
- assert.commandWorked(db.adminCommand({movePrimary: db.getName(), to: shardName0}));
- const collName = collection.getFullName();
- // Our example data has documents between 2000-2003, and these dates are non-wrapping.
- // So this goes on the primary shard, and everything else goes on the non-primary.
- assert.commandWorked(sh.splitAt(collName, {'control.min.t': ISODate('2000-01-01')}));
- assert.commandWorked(sh.splitAt(collName, {'control.min.t': ISODate('2003-01-01')}));
- assert.commandWorked(
- sh.moveChunk(collName, {'control.min.t': ISODate('1969-01-01')}, shardName1));
- assert.commandWorked(
- sh.moveChunk(collName, {'control.min.t': ISODate('2000-01-01')}, shardName0));
- assert.commandWorked(
- sh.moveChunk(collName, {'control.min.t': ISODate('2003-01-01')}, shardName1));
- }
-}
-
-const intervalMillis = 60000;
-function insertBucket(start) {
- jsTestLog("Inserting bucket starting with " + Date(start).toString());
-
- const batchSize = 1000;
-
- const batch =
- Array.from({length: batchSize}, (_, j) => ({t: new Date(start + j * intervalMillis)}));
-
- assert.commandWorked(coll.insert(batch));
- assert.commandWorked(collIndexed.insert(batch));
-}
-
-// Insert some data. We'll insert 5 buckets in each range, with values < 0,
-// values between 0 and FFFFFFFF(unsigned), and values > FFFFFFFF. It turns out, however,
-// that Javascript's Date doesn't handle dates beyond 2039 either, so we rely on lower dates
-// to test for unexpected behavior.
-function insertDocuments() {
- // We want to choose the underflow and overflow lower bits in such a way that we
- // encourage wrong results when the upper bytes are removed.
- const underflowMin = new Date("1969-01-01").getTime(); // Year before the 32 bit epoch
- const normalMin = new Date("2002-01-01").getTime(); // Middle of the 32 bit epoch
-
- insertBucket(underflowMin);
-
- var numBatches = 5;
-
- const batchOffset = Math.floor(intervalMillis / (numBatches + 1));
- for (let i = 0; i < numBatches; ++i) {
- const start = normalMin + i * batchOffset;
- insertBucket(start);
- }
- assert.gt(buckets.aggregate([{$count: 'n'}]).next().n, 1, 'Expected more than one bucket');
-}
-
-insertDocuments();
-
-const unpackStage = getAggPlanStages(coll.explain().aggregate(), '$_internalUnpackBucket')[0];
-
-function assertSorted(result, ascending) {
- let prev = ascending ? {t: -Infinity} : {t: Infinity};
- for (const doc of result) {
- if (ascending) {
- assert.lt(+prev.t,
- +doc.t,
- 'Found two docs not in ascending time order: ' + tojson({prev, doc}));
- } else {
- assert.gt(+prev.t,
- +doc.t,
- 'Found two docs not in descending time order: ' + tojson({prev, doc}));
- }
-
- prev = doc;
- }
-}
-
-function checkAgainstReferenceBoundedSortUnexpected(
- collection, reference, pipeline, hint, sortOrder) {
- const options = hint ? {hint: hint} : {};
-
- const bucket = db['system.buckets.' + coll.getName()];
-
- const plan = collection.explain().aggregate(pipeline, options);
- if (FixtureHelpers.isSharded(buckets) && numShards() >= 2) {
- // With a sharded collection, some shards might not have any extended-range data,
- // so they might still use $_internalBoundedSort. But we know at least one
- // shard has extended-range data, so we know at least one shard has to
- // use a blocking sort.
- const bounded = getAggPlanStages(plan, "$_internalBoundedSort");
- const blocking = getAggPlanStages(plan, "$sort");
- assert.gt(blocking.length, 0, {bounded, blocking, plan});
- assert.lt(bounded.length,
- FixtureHelpers.numberOfShardsForCollection(buckets),
- {bounded, blocking, plan});
- } else {
- const stages = getAggPlanStages(plan, "$_internalBoundedSort");
- assert.eq([], stages, plan);
- }
-
- const opt = collection.aggregate(pipeline, options).toArray();
- assertSorted(opt, sortOrder);
-
- assert.eq(reference.length, opt.length);
- for (var i = 0; i < opt.length; ++i) {
- assert.docEq(reference[i], opt[i]);
- }
-}
-
-function checkAgainstReferenceBoundedSortExpected(
- collection, reference, pipeline, hint, sortOrder) {
- const options = hint ? {hint: hint} : {};
-
- const plan = collection.explain().aggregate(pipeline, options);
- const stages = getAggPlanStages(plan, "$_internalBoundedSort");
- assert.neq([], stages, plan);
-
- const opt = collection.aggregate(pipeline, options).toArray();
- assertSorted(opt, sortOrder);
-
- assert.eq(reference.length, opt.length);
- for (var i = 0; i < opt.length; ++i) {
- assert.docEq(reference[i], opt[i]);
- }
-}
-
-function runTest(ascending) {
- const reference = buckets
- .aggregate([
- unpackStage,
- {$_internalInhibitOptimization: {}},
- {$sort: {t: ascending ? 1 : -1}},
- ])
- .toArray();
- assertSorted(reference, ascending);
-
- // Check plan using collection scan
- checkAgainstReferenceBoundedSortUnexpected(coll,
- reference,
- [
- {$sort: {t: ascending ? 1 : -1}},
- ],
- {},
- ascending);
-
- // Check plan using hinted collection scan
- checkAgainstReferenceBoundedSortUnexpected(coll,
- reference,
- [
- {$sort: {t: ascending ? 1 : -1}},
- ],
- {"$natural": ascending ? 1 : -1},
- ascending);
-
- const referenceIndexed = bucketsIndexed
- .aggregate([
- unpackStage,
- {$_internalInhibitOptimization: {}},
- {$sort: {t: ascending ? 1 : -1}},
- ])
- .toArray();
- assertSorted(referenceIndexed, ascending);
-
- // Check plan using index scan. If we've inserted a date before 1-1-1970, we round the min
- // up towards 1970, rather then down, which has the effect of increasing the control.min.t.
- // This means the minimum time in the bucket is likely to be lower than indicated and thus,
- // actual dates may be out of order relative to what's indicated by the bucket bounds.
- checkAgainstReferenceBoundedSortUnexpected(collIndexed,
- referenceIndexed,
- [
- {$sort: {t: ascending ? 1 : -1}},
- ],
- {},
- ascending);
-
- // Check plan using hinted index scan
- checkAgainstReferenceBoundedSortUnexpected(collIndexed,
- referenceIndexed,
- [
- {$sort: {t: ascending ? 1 : -1}},
- ],
- {"t": 1},
- ascending);
-
- // Check plan using hinted collection scan
- checkAgainstReferenceBoundedSortUnexpected(collIndexed,
- referenceIndexed,
- [
- {$sort: {t: ascending ? 1 : -1}},
- ],
- {"$natural": ascending ? 1 : -1},
- ascending);
-
- // The workaround in all cases is to create a reverse index on the time field, though
- // it's necessary to force use of the reversed index.
- const reverseIdxName = "reverseIdx";
- collIndexed.createIndex({t: -1}, {name: reverseIdxName});
-
- checkAgainstReferenceBoundedSortExpected(collIndexed,
- referenceIndexed,
- [
- {$sort: {t: ascending ? 1 : -1}},
- ],
- {"t": -1},
- ascending);
-
- collIndexed.dropIndex(reverseIdxName);
-}
-
-runTest(false); // descending
-runTest(true); // ascending
-})();
diff --git a/jstests/core/timeseries/bucket_unpacking_with_sort_plan_cache.js b/jstests/core/timeseries/bucket_unpacking_with_sort_plan_cache.js
index e69a8d7a860..25e1c99312e 100644
--- a/jstests/core/timeseries/bucket_unpacking_with_sort_plan_cache.js
+++ b/jstests/core/timeseries/bucket_unpacking_with_sort_plan_cache.js
@@ -21,8 +21,6 @@
* directly_against_shardsvrs_incompatible,
* # We use the profiler to get info in order to force replanning.
* requires_profiling,
- * # The 5.0 version of this feature does not support reverse collscan.
- * requires_fcv_60,
* ]
*/
(function() {
diff --git a/jstests/core/timeseries/libs/timeseries.js b/jstests/core/timeseries/libs/timeseries.js
index 540e0bcea55..ea8d71c87a1 100644
--- a/jstests/core/timeseries/libs/timeseries.js
+++ b/jstests/core/timeseries/libs/timeseries.js
@@ -11,20 +11,6 @@ var TimeseriesTest = class {
return FeatureFlagUtil.isEnabled(conn, "TimeseriesBucketCompression");
}
- static bucketsMayHaveMixedSchemaData(coll) {
- const catalog = coll.aggregate([{$listCatalog: {}}]).toArray()[0];
- const tsMixedSchemaOptionNewFormat = catalog.md.options.storageEngine &&
- catalog.md.options.storageEngine.wiredTiger &&
- catalog.md.options.storageEngine.wiredTiger.configString;
- // TODO SERVER-92533 Simplify once SERVER-91195 is backported to all supported branches
- if (tsMixedSchemaOptionNewFormat !== undefined) {
- return tsMixedSchemaOptionNewFormat ==
- "app_metadata=(timeseriesBucketsMayHaveMixedSchemaData=true)";
- } else {
- return catalog.md.timeseriesBucketsMayHaveMixedSchemaData;
- }
- }
-
/**
* Returns whether time-series updates and deletes are supported.
*/
@@ -66,16 +52,9 @@ var TimeseriesTest = class {
}
static bucketUnpackWithSortEnabled(conn) {
- const resp50 = conn.adminCommand({getParameter: 1, featureFlagBucketUnpackWithSort50: 1});
- const resp = conn.adminCommand({getParameter: 1, featureFlagBucketUnpackWithSort: 1});
- assert(Boolean(resp50.ok) ^ Boolean(resp.ok),
- `Expected exactly one of these feature flags to exist: ${tojson(resp50)} vs ${
- tojson(resp)}`);
- if (resp50.ok) {
- return assert.commandWorked(resp50).featureFlagBucketUnpackWithSort50.value;
- } else {
- return assert.commandWorked(resp).featureFlagBucketUnpackWithSort.value;
- }
+ return assert
+ .commandWorked(conn.adminCommand({getParameter: 1, featureFlagBucketUnpackWithSort: 1}))
+ .featureFlagBucketUnpackWithSort.value;
}
/**
@@ -220,7 +199,7 @@ var TimeseriesTest = class {
static ensureDataIsDistributedIfSharded(coll, splitPointDate) {
const db = coll.getDB();
- const buckets = db[this.getBucketsCollName(coll.getName())];
+ const buckets = db["system.buckets." + coll.getName()];
if (FixtureHelpers.isSharded(buckets)) {
const timeFieldName =
db.getCollectionInfos({name: coll.getName()})[0].options.timeseries.timeField;
@@ -269,8 +248,4 @@ var TimeseriesTest = class {
}
}
}
-
- static getBucketsCollName(collName) {
- return `system.buckets.${collName}`;
- }
};
diff --git a/jstests/core/timeseries/nondefault_collation.js b/jstests/core/timeseries/nondefault_collation.js
index 01f9b8c635c..3ff1c63d330 100644
--- a/jstests/core/timeseries/nondefault_collation.js
+++ b/jstests/core/timeseries/nondefault_collation.js
@@ -1,13 +1,8 @@
/**
- * Correctness tests for TS collections with collation that might not match the explicit collation,
- * specified in the query.
- *
- * Queries on timeseries attempt various optimizations to avoid unpacking of buckets. These rely on
- * the meta field and the control data (currently, min and max), computed for each bucket.
- * Collection's collation might affect the computed control values.
+ * Test ensures that users can specify non-default collation when querying on time-series
+ * collections.
*
* @tags: [
- * assumes_unsharded_collection,
* requires_non_retryable_writes,
* requires_pipeline_optimization,
* requires_getmore,
@@ -26,221 +21,123 @@ load("jstests/libs/analyze_plan.js");
const coll = db.timeseries_nondefault_collation;
const bucketsColl = db.getCollection('system.buckets.' + coll.getName());
+coll.drop(); // implicitly drops bucketsColl.
+
+const timeFieldName = 'time';
+const metaFieldName = 'meta';
+
const numericOrdering = {
- locale: "en_US",
- numericOrdering: true,
- strength: 1 // case and diacritics ignored
+ collation: {locale: "en_US", numericOrdering: true}
};
+
const caseSensitive = {
- locale: "en_US",
- strength: 1,
- caseLevel: true
+ collation: {locale: "en_US", strength: 1, caseLevel: true, numericOrdering: true}
};
+
const diacriticSensitive = {
- locale: "en_US",
- strength: 2,
- caseLevel: false
+ collation: {locale: "en_US", strength: 2}
};
-const insensitive = {
- locale: "en_US",
+
+const englishCollation = {
+ locale: 'en',
strength: 1
};
-// Find on meta field isn't different from a find on any other view, but let's check it anyway.
-(function testFind_MetaField() {
- coll.drop();
-
- assert.commandWorked(db.createCollection(
- coll.getName(),
- {timeseries: {timeField: 'time', metaField: 'meta'}, collation: numericOrdering}));
- assert.contains(bucketsColl.getName(), db.getCollectionNames());
-
- assert.commandWorked(coll.insert({time: ISODate(), meta: "1", value: 42}));
- assert.commandWorked(coll.insert({time: ISODate(), meta: "10", value: 42}));
- assert.commandWorked(coll.insert({time: ISODate(), meta: "5", value: 42}));
-
- // Use the collection's collation with numeric ordering.
- let res1 = coll.find({meta: {$gt: "4"}});
- assert.eq(2, res1.itcount(), res1.toArray()); // should match "5" and "10"
-
- // Use explicit collation with lexicographic ordering.
- let res2 = coll.find({meta: {$gt: "4"}}).collation(insensitive);
- assert.eq(1, res2.itcount(), res2.toArray()); // should match only "5"
-}());
-
-// For the measurement fields each bucket computes additional "control values", such as min/max and
-// might use them to avoid unpacking.
-(function testFind_MeasurementField() {
- coll.drop();
-
- assert.commandWorked(db.createCollection(
- coll.getName(),
- {timeseries: {timeField: 'time', metaField: 'meta'}, collation: numericOrdering}));
- assert.contains(bucketsColl.getName(), db.getCollectionNames());
-
- // The 'numericOrdering' on the collection means that the max of the bucket with the three docs
- // below is "10" (while the lexicographic max is "5").
- assert.commandWorked(coll.insert({time: ISODate(), meta: 42, value: "1"}));
- assert.commandWorked(coll.insert({time: ISODate(), meta: 42, value: "10"}));
- assert.commandWorked(coll.insert({time: ISODate(), meta: 42, value: "5"}));
-
- // A query with default collation would use the bucket's min/max and find the matches. We are
- // not checking the unpacking optimizations here as it's not a concern of collation per se.
- let res1 = coll.find({value: {$gt: "4"}});
- assert.eq(2, res1.itcount(), res1.toArray()); // should match "5" and "10"
-
- // If a query with 'insensitive' collation, which doesn't do numeric ordering, used the bucket's
- // min/max it would miss the bucket. Check, that it doesn't.
- let res2 = coll.find({value: {$gt: "4"}}).collation(insensitive);
- assert.eq(1, res2.itcount(), res2.toArray()); // should match only "5"
-}());
-
-(function testFind_OnlyQueryHasCollation() {
- coll.drop();
-
- assert.commandWorked(
- db.createCollection(coll.getName(), {timeseries: {timeField: 'time', metaField: 'meta'}}));
-
- // This should generate a bucket with control.min.value = 'C' and control.max.value = 'c'.
- assert.commandWorked(coll.insert({time: ISODate(), meta: 42, value: "C"}));
- assert.commandWorked(coll.insert({time: ISODate(), meta: 42, value: "b"}));
- assert.commandWorked(coll.insert({time: ISODate(), meta: 42, value: "c"}));
-
- // A query with default collation would use the bucket's min/max and find the two matches.
- const resWithNoCollation = coll.find({value: {$lt: "c"}});
- assert.eq(2,
- resWithNoCollation.itcount(),
- resWithNoCollation.toArray()); // should match "C" and "b".
-
- // If a query with 'insensitive' collation used the bucket's min/max it would miss the bucket.
- // Check, that it doesn't.
- const resWithCollation_find = coll.find({value: {$lt: "c"}}).collation(insensitive);
- assert.eq(1,
- resWithCollation_find.itcount(),
- resWithCollation_find.toArray()); // should match only "b".
-
- // Run the same test with aggregate command.
- const resWithCollation_agg =
- coll.aggregate([{$match: {value: {$lt: "c"}}}], {collation: insensitive}).toArray();
- assert.eq(1, resWithCollation_agg.length, resWithCollation_agg);
-}());
-
-(function testAgg_GroupByMetaField() {
- coll.drop();
-
- assert.commandWorked(db.createCollection(
- coll.getName(),
- {timeseries: {timeField: 'time', metaField: 'meta'}, collation: numericOrdering}));
- assert.contains(bucketsColl.getName(), db.getCollectionNames());
-
- assert.commandWorked(coll.insert({time: ISODate(), meta: "1", val: 1}));
- assert.commandWorked(coll.insert({time: ISODate(), meta: "5", val: 1}));
-
- // Using collection's collation with numeric ordering.
- let res1 =
- coll.aggregate([{$bucket: {groupBy: "$meta", boundaries: ["1", "10", "50"]}}]).toArray();
- assert.eq(1, res1.length);
- assert.eq({_id: "1", count: 2}, res1[0]);
-
- // Using explicit collation with lexicographic ordering.
- let res2 = coll.aggregate([{$bucket: {groupBy: "$meta", boundaries: ["1", "10", "50"]}}],
- {collation: insensitive})
- .toArray();
- assert.eq(2, res2.length);
- assert.eq({_id: "1", count: 1}, res2[0]); // "1" goes here
- assert.eq({_id: "10", count: 1}, res2[1]); // "5" goes here
-}());
-
-(function testAgg_GroupByMeasurementField() {
- coll.drop();
-
- assert.commandWorked(db.createCollection(
- coll.getName(),
- {timeseries: {timeField: 'time', metaField: 'meta'}, collation: insensitive}));
- assert.contains(bucketsColl.getName(), db.getCollectionNames());
-
- // Cause two different buckets with various case/diacritics in each for the measurement 'name'.
- assert.commandWorked(coll.insert({time: ISODate(), meta: "a", name: 'A'}));
- assert.commandWorked(coll.insert({time: ISODate(), meta: "a", name: 'a'}));
- assert.commandWorked(coll.insert({time: ISODate(), meta: "a", name: 'á'}));
- assert.commandWorked(coll.insert({time: ISODate(), meta: "b", name: 'A'}));
- assert.commandWorked(coll.insert({time: ISODate(), meta: "b", name: 'a'}));
- assert.commandWorked(coll.insert({time: ISODate(), meta: "b", name: 'ä'}));
-
- // Test with the collection's collation, which is case and diacritic insensitive.
- assert.eq(1, coll.aggregate([{$sortByCount: "$name"}]).itcount());
-
- // Test with explicit collation that is different from the collection's.
- assert.eq(2, coll.aggregate([{$sortByCount: "$name"}], {collation: caseSensitive}).itcount());
- assert.eq(3,
- coll.aggregate([{$sortByCount: "$name"}], {collation: diacriticSensitive}).itcount());
-}());
-
-// For $group queries that would put whole buckets into the same group, it might be possible to
-// avoid unpacking if the information the group is computing is exposed in the control data of each
-// bucket. Currently, we only do this optimization for min/max with the meta as the group key.
-(function testAgg_MinMaxOptimization() {
- coll.drop();
-
- assert.commandWorked(db.createCollection(
- coll.getName(),
- {timeseries: {timeField: 'time', metaField: 'meta'}, collation: numericOrdering}));
- assert.contains(bucketsColl.getName(), db.getCollectionNames());
-
- // These two docs will be placed in the same bucket, and the max for the bucket will be computed
- // using collection's collation, that is, it should be "10".
- assert.commandWorked(coll.insert({time: ISODate(), meta: 42, val: "10"}));
- assert.commandWorked(coll.insert({time: ISODate(), meta: 42, val: "5"}));
-
- // Let's check our understanding of what happens with the bucketing as otherwise the tests below
- // won't be testing what we think they are.
- let buckets = bucketsColl.find().toArray();
- assert.eq(1, buckets.length, "All docs should be placed into the same bucket");
- assert.eq("10", buckets[0].control.max.val, "Computed max control for 'val' measurement");
-
- // Use the collection's collation with numeric ordering.
- let res1 = coll.aggregate([{$group: {_id: "$meta", v: {$max: "$val"}}}]).toArray();
- assert.eq("10", res1[0].v, "max val in numeric ordering per the collection's collation");
-
- // Use the collection's collation with lexicographic ordering.
- let res2 =
- coll.aggregate([{$group: {_id: "$meta", v: {$max: "$val"}}}], {collation: insensitive})
- .toArray();
- assert.eq("5", res2[0].v, "max val in lexicographic ordering per the query collation");
-}());
-
-(function testFind_IndexWithDifferentCollation() {
- coll.drop();
-
- assert.commandWorked(db.createCollection(
- coll.getName(),
- {timeseries: {timeField: 'time', metaField: 'meta'}, collation: diacriticSensitive}));
- assert.contains(bucketsColl.getName(), db.getCollectionNames());
-
- // Create index with a different collation.
- assert.commandWorked(coll.createIndex({meta: 1}, {collation: insensitive}));
-
- // We only check that the correct plan is chosen so the contents of the collection don't matter
- // as long as it's not empty.
- assert.commandWorked(coll.insert({time: ISODate(), meta: 42}));
- assert.commandWorked(coll.insert({time: ISODate(), meta: "the answer"}));
-
- // Queries that don't specify explicit collation should use the collection's default collation
- // which isn't compatible with the index, so the index should NOT be used.
- let query = coll.find({meta: "str"}).explain();
- assert(!aggPlanHasStage(query, "IXSCAN"), query);
-
- // Queries with an explicit collation which isn't compatible with the index, should NOT do
- // index scan.
- query = coll.find({meta: "str"}).collation(caseSensitive).explain();
- assert(!aggPlanHasStage(query, "IXSCAN"), query);
-
- // Queries with the same collation as in the index, should do index scan.
- query = coll.find({meta: "str"}).collation(insensitive).explain();
- assert(aggPlanHasStage(query, "IXSCAN"), query);
+const simpleCollation = {
+ collation: {locale: "simple"}
+};
- // Numeric queries that don't rely on collation should do index scan.
- query = coll.find({meta: 1}).explain();
- assert(aggPlanHasStage(query, "IXSCAN"), query);
-}());
+assert.commandWorked(db.createCollection(coll.getName(), {
+ timeseries: {timeField: timeFieldName, metaField: metaFieldName},
+ collation: englishCollation
+}));
+assert.contains(bucketsColl.getName(), db.getCollectionNames());
+
+assert.commandWorked(
+ coll.insert({[timeFieldName]: ISODate(), [metaFieldName]: "1", name: 'A', name2: "á"}));
+assert.commandWorked(
+ coll.insert({[timeFieldName]: ISODate(), [metaFieldName]: "2", name: 'a', name2: "á"}));
+assert.commandWorked(
+ coll.insert({[timeFieldName]: ISODate(), [metaFieldName]: "5", name: 'A', name2: "á"}));
+assert.commandWorked(
+ coll.insert({[timeFieldName]: ISODate(), [metaFieldName]: "10", name: 'a', name2: "á"}));
+assert.commandWorked(
+ coll.insert({[timeFieldName]: ISODate(), [metaFieldName]: "20", name: 'A', name2: "a"}));
+assert.commandWorked(
+ coll.insert({[timeFieldName]: ISODate(), [metaFieldName]: "50", name: 'B', name2: "a"}));
+assert.commandWorked(
+ coll.insert({[timeFieldName]: ISODate(), [metaFieldName]: "100", name: 'b', name2: "a"}));
+assert.commandWorked(
+ coll.insert({[timeFieldName]: ISODate(), [metaFieldName]: "200", name: 'B', name2: "a"}));
+assert.commandWorked(
+ coll.insert({[timeFieldName]: ISODate(), [metaFieldName]: "500", name: 'b', name2: "a"}));
+
+// Default collation is case and diacretic insensitive.
+assert.eq(2, coll.aggregate([{$sortByCount: "$name"}]).itcount());
+assert.eq(1, coll.aggregate([{$sortByCount: "$name2"}]).itcount());
+
+// Test that a explicit collation different from collection's default passes for a timeseries
+// collection.
+let results =
+ coll.aggregate([{$bucket: {groupBy: "$meta", boundaries: ["1", "10", "100", "1000"]}}],
+ numericOrdering)
+ .toArray();
+assert.eq(3, results.length);
+assert.eq({_id: "1", count: 3}, results[0]);
+assert.eq({_id: "10", count: 3}, results[1]);
+assert.eq({_id: "100", count: 3}, results[2]);
+
+assert.eq(4, coll.aggregate([{$sortByCount: "$name"}], caseSensitive).itcount());
+assert.eq(2, coll.aggregate([{$sortByCount: "$name2"}], diacriticSensitive).itcount());
+
+coll.drop();
+const defaultCollation = {
+ locale: "en",
+ numericOrdering: true,
+ caseLevel: true,
+ strength: 2
+};
+assert.commandWorked(db.createCollection(coll.getName(), {
+ timeseries: {timeField: timeFieldName, metaField: metaFieldName},
+ collation: defaultCollation
+}));
+assert.contains(bucketsColl.getName(), db.getCollectionNames());
+assert.commandWorked(coll.createIndex({[metaFieldName]: 1}, {collation: {locale: "simple"}}));
+
+assert.commandWorked(coll.insert(
+ {[timeFieldName]: ISODate(), [metaFieldName]: 1, name: 'A', name2: "á", value: "1"}));
+assert.commandWorked(coll.insert(
+ {[timeFieldName]: ISODate(), [metaFieldName]: 2, name: 'a', name2: "á", value: "11"}));
+assert.commandWorked(coll.insert(
+ {[timeFieldName]: ISODate(), [metaFieldName]: 1, name: 'A', name2: "á", value: "50"}));
+assert.commandWorked(coll.insert(
+ {[timeFieldName]: ISODate(), [metaFieldName]: 1, name: 'a', name2: "á", value: "100"}));
+assert.commandWorked(coll.insert(
+ {[timeFieldName]: ISODate(), [metaFieldName]: "2", name: 'A', name2: "a", value: "3"}));
+assert.commandWorked(coll.insert(
+ {[timeFieldName]: ISODate(), [metaFieldName]: "5", name: 'B', name2: "a", value: "-100"}));
+assert.commandWorked(coll.insert(
+ {[timeFieldName]: ISODate(), [metaFieldName]: "1", name: 'b', name2: "a", value: "-200"}));
+assert.commandWorked(coll.insert(
+ {[timeFieldName]: ISODate(), [metaFieldName]: "2", name: 'B', name2: "a", value: "1000"}));
+assert.commandWorked(coll.insert(
+ {[timeFieldName]: ISODate(), [metaFieldName]: "5", name: 'b', name2: "a", value: "4"}));
+
+// This collection has been created using non simple collation. The collection was then indexed on
+// its metadata using simple collation. These tests confirm that queries on the indexed field using
+// nondefault (simple) collation use the index. They also confirm that queries that don't involve
+// strings but do use default collation, on indexed fields, also use the index.
+const nonDefaultCollationQuery = coll.find({meta: 2}, {collation: englishCollation}).explain();
+assert(aggPlanHasStage(nonDefaultCollationQuery, "IXSCAN"), nonDefaultCollationQuery);
+
+const simpleNonDefaultCollationQuery = coll.find({meta: 2}, simpleCollation).explain();
+assert(aggPlanHasStage(simpleNonDefaultCollationQuery, "IXSCAN"), simpleNonDefaultCollationQuery);
+
+const defaultCollationQuery = coll.find({meta: 1}, {collation: defaultCollation}).explain();
+assert(aggPlanHasStage(defaultCollationQuery, "IXSCAN"), defaultCollationQuery);
+
+// This test guarantees that the bucket's min/max matches the query's min/max regardless of
+// collation.
+results = coll.find({value: {$gt: "4"}}, simpleCollation);
+assert.eq(4, results.itcount());
}());
diff --git a/jstests/core/timeseries/partialFilterExpression_with_internalBucketGeoWithin.js b/jstests/core/timeseries/partialFilterExpression_with_internalBucketGeoWithin.js
index 61521e7f86e..5be75d79e28 100644
--- a/jstests/core/timeseries/partialFilterExpression_with_internalBucketGeoWithin.js
+++ b/jstests/core/timeseries/partialFilterExpression_with_internalBucketGeoWithin.js
@@ -10,11 +10,12 @@
*/
load("jstests/libs/analyze_plan.js");
-load("jstests/libs/feature_flag_util.js");
load('jstests/noPassthrough/libs/index_build.js');
-
(function() {
-if (FeatureFlagUtil.isEnabled(db, "TimeseriesMetricIndexes")) {
+const isFeatureEnabled = db.adminCommand({getParameter: 1, featureFlagTimeseriesMetricIndexes: 1})
+ .featureFlagTimeseriesMetricIndexes.value;
+
+if (isFeatureEnabled) {
const timeFieldName = "timestamp";
const coll = db.partialFilterExpression_with_internalBucketGeoWithin;
diff --git a/jstests/core/timeseries/timeseries_bucket_rename.js b/jstests/core/timeseries/timeseries_bucket_rename.js
new file mode 100644
index 00000000000..98a0b73b810
--- /dev/null
+++ b/jstests/core/timeseries/timeseries_bucket_rename.js
@@ -0,0 +1,28 @@
+/**
+ * Tests that a system.buckets collection cannot be renamed.
+ *
+ * @tags: [
+ * does_not_support_stepdowns,
+ * does_not_support_transactions,
+ * requires_getmore,
+ * ]
+ */
+(function() {
+'use strict';
+
+const coll = db.timeseries_bucket_rename;
+const bucketsColl = db.getCollection('system.buckets.' + coll.getName());
+
+const timeFieldName = 'time';
+
+coll.drop();
+assert.commandWorked(db.createCollection(coll.getName(), {timeseries: {timeField: timeFieldName}}));
+assert.contains(bucketsColl.getName(), db.getCollectionNames());
+
+assert.commandFailedWithCode(db.adminCommand({
+ renameCollection: bucketsColl.getFullName(),
+ to: db.getName() + ".otherColl",
+ dropTarget: false
+}),
+ ErrorCodes.IllegalOperation);
+})();
diff --git a/jstests/core/timeseries/timeseries_collection_uuid.js b/jstests/core/timeseries/timeseries_collection_uuid.js
deleted file mode 100644
index 2ba80cc8f16..00000000000
--- a/jstests/core/timeseries/timeseries_collection_uuid.js
+++ /dev/null
@@ -1,42 +0,0 @@
-/**
- * Tests using the collectionUUID parameter when operating on a time-series collection.
- *
- * @tags: [
- * does_not_support_transactions,
- * requires_fcv_60,
- * requires_timeseries,
- * ]
- */
-
-(function() {
-"use strict";
-
-const dbName = jsTestName();
-const collName = "coll";
-
-const testDB = db.getSiblingDB(dbName);
-testDB.dropDatabase();
-
-assert.commandWorked(
- testDB.createCollection(collName, {timeseries: {timeField: "t", metaField: "m"}}));
-const coll = testDB[collName];
-const bucketsColl = testDB["system.buckets." + collName];
-
-const nonexistentUUID = UUID();
-const bucketsCollUUID = testDB.getCollectionInfos({name: bucketsColl.getName()})[0].info.uuid;
-
-const testInsert = function(uuid, ordered) {
- assert.commandFailedWithCode(testDB.runCommand({
- insert: collName,
- documents: [{t: ISODate()}],
- collectionUUID: uuid,
- ordered: ordered,
- }),
- ErrorCodes.CollectionUUIDMismatch);
-};
-
-testInsert(nonexistentUUID, true);
-testInsert(nonexistentUUID, false);
-testInsert(bucketsCollUUID, true);
-testInsert(bucketsCollUUID, false);
-}());
diff --git a/jstests/core/timeseries/timeseries_collmod.js b/jstests/core/timeseries/timeseries_collmod.js
index 07c9166b632..69fd7dbeb82 100644
--- a/jstests/core/timeseries/timeseries_collmod.js
+++ b/jstests/core/timeseries/timeseries_collmod.js
@@ -19,17 +19,6 @@ assert.commandWorked(
db.createCollection(collName, {timeseries: {timeField: "time", granularity: 'seconds'}}));
assert.commandWorked(coll.createIndex({"time": 1}));
-// Setting prepareUnique should return an error on a time-series collection.
-assert.commandFailedWithCode(
- db.runCommand(
- {"collMod": collName, "index": {"keyPattern": {"time": 1}, "prepareUnique": true}}),
- ErrorCodes.InvalidOptions);
-
-assert.commandFailedWithCode(
- db.runCommand(
- {"collMod": collName, "index": {"keyPattern": {"time": 1}, "prepareUnique": false}}),
- ErrorCodes.InvalidOptions);
-
// Tries to convert a time-series secondary index to TTL index.
assert.commandFailedWithCode(
db.runCommand(
@@ -67,4 +56,4 @@ assert.commandWorked(db.runCommand({"collMod": collName, "expireAfterSeconds": 6
// Successfully sets the granularity for a time-series collection.
assert.commandWorked(
db.runCommand({"collMod": collName, "timeseries": {"granularity": "minutes"}}));
-})();
+})(); \ No newline at end of file
diff --git a/jstests/core/timeseries/timeseries_computed_field.js b/jstests/core/timeseries/timeseries_computed_field.js
deleted file mode 100644
index fab03d1a3b0..00000000000
--- a/jstests/core/timeseries/timeseries_computed_field.js
+++ /dev/null
@@ -1,856 +0,0 @@
-/**
- * Test use of computed fields in aggregations on time series collections.
- * @tags: [
- * requires_timeseries,
- * does_not_support_stepdowns,
- * directly_against_shardsvrs_incompatible,
- * # "Explain of a resolved view must be executed by mongos"
- * directly_against_shardsvrs_incompatible,
- * # Some suites use mixed-binary cluster setup where some nodes might have the flag enabled while
- * # others -- not. For this test we need control over whether the flag is set on the node that
- * # ends up executing the query.
- * assumes_standalone_mongod
- * ]
- */
-load("jstests/core/timeseries/libs/timeseries.js");
-
-TimeseriesTest.run((insert) => {
- const datePrefix = 1680912440;
-
- let coll = db.timeseries_computed_field;
- const bucketsColl = db.getCollection('system.buckets.' + coll.getName());
-
- const timeFieldName = 'time';
- const metaFieldName = 'measurement';
-
- coll.drop();
- assert.commandWorked(db.createCollection(coll.getName(), {
- timeseries: {timeField: timeFieldName, metaField: metaFieldName},
- }));
- assert.contains(bucketsColl.getName(), db.getCollectionNames());
-
- insert(coll, {
- _id: 0,
- [timeFieldName]: new Date(datePrefix + 100), // ISODate("1970-01-20T10:55:12.540Z")
- [metaFieldName]: "cpu",
- topLevelScalar: 123,
- topLevelScalarDouble: 123.8778645,
- topLevelLargeNumber: 12345678912345,
- topLevelArray: [1, 2, 3, 4],
- arrOfObj: [{x: 1}, {x: 2}, {x: 3}, {x: 4}],
- obj: {a: 123},
- length: 0,
- });
- insert(coll, {
- _id: 1,
- [timeFieldName]: new Date(datePrefix + 200), // ISODate("1970-01-20T10:55:12.640Z")
- [metaFieldName]: "cpu",
- topLevelScalar: 456,
- topLevelScalarDouble: 546.76858699,
- topLevelArray: [101, 102, 103, 104],
- arrOfObj: [{x: 101}, {x: 102}, {x: 103}, {x: 104}],
- obj: {a: 456},
- length: 23,
- });
- // Insert a document that will be placed in a different bucket.
- insert(coll, {
- _id: 2,
- [timeFieldName]: new Date(datePrefix + 300),
- [metaFieldName]: "gpu",
- length: -2,
- });
-
- // Computing a field on a dotted path which is an array, then grouping on it. Note that the
- // semantics for setting a computed field on a dotted array path are particularly strange, but
- // should be preserved for backwards compatibility.
- {
- const res = coll.aggregate([
- {$addFields: {"arrOfObj.x": {$trim: {input: "test string"}}}},
- {$match: {topLevelScalar: {$gte: 0}}},
- {$group: {_id: null, max: {$max: "$arrOfObj.x"}}}
- ])
- .toArray();
- assert.eq(res.length, 1, res);
- assert.eq(res[0].max, ["test string", "test string", "test string", "test string"], res);
- }
-
- {
- const res = coll.aggregate([
- {$addFields: {"arrOfObj.x": {$add: ["$topLevelScalar", 1]}}},
- {$match: {topLevelScalar: {$gte: 0}}},
- {$group: {_id: null, max: {$max: "$arrOfObj.x"}}}
- ])
- .toArray();
- assert.eq(res.length, 1, res);
- assert.eq(res[0].max, [457, 457, 457, 457], res);
- }
-
- // Computing a field and then filtering by it.
- {
- const res = coll.aggregate([
- {$addFields: {"arrOfObj.x": {$trim: {input: "test string"}}}},
- {$match: {"arrOfObj.x": "test string"}},
- {$group: {_id: null, max: {$max: "$arrOfObj.x"}}}
- ])
- .toArray();
- assert.eq(res.length, 1, res);
- assert.eq(res[0].max, ["test string", "test string", "test string", "test string"], res);
- }
-
- {
- // Computing a field based on a dotted path which does not traverse arrays.
- const res = coll.aggregate([
- {$addFields: {"computedA": {$add: ["$obj.a", 1]}}},
- // Only one document should have a value where obj.a was 457
- // (456 + 1).
- {$match: {"computedA": 457}},
- {$count: "count"}
- ])
- .toArray();
- assert.eq(res.length, 1, res);
- assert.eq(res[0].count, 1, res);
- }
-
- {
- // Include an unnecessary computed field before counting the number of documents.
- const res = coll.aggregate([{$addFields: {"a": 1}}, {$count: "count"}]).toArray();
- assert.eq(res.length, 1, res);
- assert.eq(res[0].count, 3, res);
- }
-
- // mathematical expressions
- {
- let pipeline = [
- {$addFields: {"computedA": {$add: ["$topLevelScalar", 1]}}},
- {$match: {"computedA": 457}},
- {$count: "count"}
- ];
-
- const res = coll.aggregate(pipeline).toArray();
- assert.eq(res.length, 1, res);
- assert.eq(res[0].count, 1, res);
- }
- {
- let pipeline = [
- {$addFields: {"computedA": {$subtract: ["$topLevelScalar", 1]}}},
- {$match: {"computedA": 455}},
- {$count: "count"}
- ];
-
- const res = coll.aggregate(pipeline).toArray();
- assert.eq(res.length, 1, res);
- assert.eq(res[0].count, 1, res);
- }
- {
- let pipeline = [
- {$addFields: {"computedA": {$multiply: ["$topLevelScalar", 10]}}},
- {$match: {"computedA": 4560}},
- {$count: "count"}
- ];
-
- const res = coll.aggregate(pipeline).toArray();
- assert.eq(res.length, 1, res);
- assert.eq(res[0].count, 1, res);
- }
- {
- let pipeline = [
- {$addFields: {"computedA": {$divide: ["$topLevelScalar", 2]}}},
- {$match: {"computedA": 228}},
- {$count: "count"}
- ];
-
- const res = coll.aggregate(pipeline).toArray();
- assert.eq(res.length, 1, res);
- assert.eq(res[0].count, 1, res);
- }
- {
- let pipeline = [
- {$addFields: {"computedA": {$add: [1, "$topLevelScalar"]}}},
- {$match: {"computedA": 457}},
- {$count: "count"}
- ];
-
- const res = coll.aggregate(pipeline).toArray();
- assert.eq(res.length, 1, res);
- assert.eq(res[0].count, 1, res);
- }
- {
- let pipeline = [
- {$addFields: {"computedA": {$subtract: [200, "$topLevelScalar"]}}},
- {$match: {"computedA": 77}},
- {$count: "count"}
- ];
-
- const res = coll.aggregate(pipeline).toArray();
- assert.eq(res.length, 1, res);
- assert.eq(res[0].count, 1, res);
- }
- {
- let pipeline = [
- {$addFields: {"computedA": {$multiply: [10, "$topLevelScalar"]}}},
- {$match: {"computedA": 4560}},
- {$count: "count"}
- ];
-
- const res = coll.aggregate(pipeline).toArray();
- assert.eq(res.length, 1, res);
- assert.eq(res[0].count, 1, res);
- }
- {
- let pipeline = [
- {$addFields: {"computedA": {$divide: [4560, "$topLevelScalar"]}}},
- {$match: {"computedA": 10}},
- {$count: "count"}
- ];
-
- const res = coll.aggregate(pipeline).toArray();
- assert.eq(res.length, 1, res);
- assert.eq(res[0].count, 1, res);
- }
-
- {
- let pipeline = [
- {$addFields: {"computedA": {$add: ["$topLevelScalar", "$topLevelScalar"]}}},
- {$match: {"computedA": 912}},
- {$count: "count"}
- ];
-
- const res = coll.aggregate(pipeline).toArray();
- assert.eq(res.length, 1, res);
- assert.eq(res[0].count, 1, res);
- }
- {
- let pipeline = [
- {$addFields: {"computedA": {$subtract: ["$topLevelScalar", "$topLevelScalar"]}}},
- {$match: {"computedA": 0}},
- {$count: "count"}
- ];
-
- const res = coll.aggregate(pipeline).toArray();
- assert.eq(res.length, 1, res);
- assert.eq(res[0].count, 2, res);
- }
- {
- let pipeline = [
- {$addFields: {"computedA": {$multiply: ["$topLevelScalar", "$topLevelScalar"]}}},
- {$match: {"computedA": 15129}},
- {$count: "count"}
- ];
-
- const res = coll.aggregate(pipeline).toArray();
- assert.eq(res.length, 1, res);
- assert.eq(res[0].count, 1, res);
- }
- {
- let pipeline = [
- {$addFields: {"computedA": {$divide: ["$topLevelScalar", "$topLevelScalar"]}}},
- {$match: {"computedA": 1}},
- {$count: "count"}
- ];
-
- const res = coll.aggregate(pipeline).toArray();
- assert.eq(res.length, 1, res);
- assert.eq(res[0].count, 2, res);
- }
-
- {
- let pipeline = [
- {
- $addFields: {
- "hourDiff": {
- $dateDiff: {
- "startDate": "$time",
- "endDate": new Date("1970-01-21"),
- "unit": "hour"
- }
- }
- }
- },
- {$match: {"hourDiff": {$gte: 12}}},
- {
- $group: {
- "_id": {"time": {"$dateTrunc": {"date": "$time", "unit": "minute"}}},
- "open": {"$first": "$topLevelScalar"},
- }
- }
- ];
-
- assert.docEq([{"_id": {"time": ISODate("1970-01-20T10:55:00Z")}, "open": 123}],
- coll.aggregate(pipeline).toArray());
- }
-
- {
- let pipeline = [
- {$addFields: {"mt": {$multiply: ["$topLevelScalar", 10]}}},
- {$match: {"mt": {$gte: 2000}}},
- {
- $group: {
- "_id": {"time": {"$dateTrunc": {"date": "$time", "unit": "minute"}}},
- "open": {"$first": "$topLevelScalar"},
- }
- }
- ];
-
- assert.docEq([{"_id": {"time": ISODate("1970-01-20T10:55:00Z")}, "open": 456}],
- coll.aggregate(pipeline).toArray());
- }
-
- {
- let pipeline = [
- {
- $addFields: {
- "hourDiff": {
- $dateDiff: {
- "startDate": "$time",
- "endDate": new Date("1970-01-21"),
- "unit": "hour"
- }
- }
- }
- },
- {$match: {$or: [{topLevelScalar: {$gt: 200, $lt: 800}}, {hourDiff: {$gte: 12}}]}},
- {
- $group: {
- "_id": {"time": {"$dateTrunc": {"date": "$time", "unit": "minute"}}},
- "open": {"$first": "$topLevelScalar"},
- }
- }
- ];
-
- assert.docEq([{"_id": {"time": ISODate("1970-01-20T10:55:00Z")}, "open": 123}],
- coll.aggregate(pipeline).toArray());
- }
-
- {
- // Try a project stage which adds and remove subfields.
- const res = coll.aggregate([
- {$project: {"obj.newField": "$topLevelScalar"}},
- {$project: {"_id": 0, "obj.a": 0}}
- ])
- .toArray();
- assert.eq(res.length, 3, res);
- assert.eq(res[0], {"obj": {"newField": 123}}, res);
- assert.eq(res[1], {"obj": {"newField": 456}}, res);
- assert.eq(res[2], {"obj": {}}, res);
- }
-
- {
- // Try a replaceRoot stage which remove all fields.
- const res = coll.aggregate([
- {$match: {"time": {$gte: new Date(datePrefix + 200)}}},
- {$addFields: {}},
- {$project: {"measurement0": {$floor: "$topLevelScalar"}}},
- {$replaceRoot: {newRoot: {}}}
- ])
- .toArray();
- assert.eq(res.length, 2, res);
- assert.eq(res[0], {}, res);
- assert.eq(res[1], {}, res);
- }
-
- {
- // Try a $match that works on fields that are not projected.
- const res =
- coll.aggregate([
- {$project: {"obj.a": 1}},
- {$match: {$or: [{"topLevelScalar": {$gt: 10}}, {$expr: {$literal: true}}]}},
- {$sort: {_id: 1}},
- {$count: "count"}
- ])
- .toArray();
- assert.eq(res.length, 1, res);
- assert.eq(res[0].count, 3, res);
- }
-
- {
- const res = coll.aggregate([{
- "$project": {
- "t": {
- "$dateDiff": {
- "startDate": "$time",
- "endDate": new Date(datePrefix + 150),
- "unit": "millisecond"
- }
- }
- }
- }])
- .toArray();
- assert.eq(res.length, 3, res);
- assert.docEq(res[0], {_id: 0, "t": NumberLong(50)}, res);
- assert.docEq(res[1], {_id: 1, "t": NumberLong(-50)}, res);
- assert.docEq(res[2], {_id: 2, "t": NumberLong(-150)}, res);
- }
-
- {
- const res = coll.aggregate([{
- "$project": {
- "t": {
- "$dateDiff": {
- "startDate": new Date(datePrefix - 10),
- "endDate": "$time",
- "unit": "millisecond"
- }
- }
- }
- }])
- .toArray();
- assert.eq(res.length, 3, res);
- assert.docEq(res[0], {_id: 0, "t": NumberLong(110)}, res);
- assert.docEq(res[1], {_id: 1, "t": NumberLong(210)}, res);
- assert.docEq(res[2], {_id: 2, "t": NumberLong(310)}, res);
- }
-
- {
- const res = coll.aggregate([
- {
- $addFields: {
- "computedField": {
- $dateTrunc: {
- date: "$time",
- unit: "second",
- }
- }
- }
- },
- {$match: {computedField: new Date(datePrefix - 440)}},
- {$count: "count"}
- ])
- .toArray();
- assert.eq(res.length, 1, res);
- assert.eq(res[0].count, 3, res);
- }
-
- {
- const res = coll.aggregate([{$match: {topLevelScalar: {$exists: true}}}, {$count: "count"}])
- .toArray();
- assert.eq(res.length, 1, res);
- assert.eq(res[0].count, 2, res);
- }
-
- {
- const res =
- coll.aggregate([{$match: {topLevelScalar: {$lt: 456}}}, {$count: "count"}]).toArray();
- assert.eq(res.length, 1, res);
- assert.eq(res[0].count, 1, res);
- }
-
- {
- const res =
- coll.aggregate([{$match: {topLevelScalar: {$lte: 456}}}, {$count: "count"}]).toArray();
- assert.eq(res.length, 1, res);
- assert.eq(res[0].count, 2, res);
- }
-
- {
- const res =
- coll.aggregate([{$match: {topLevelScalar: {$gt: 123}}}, {$count: "count"}]).toArray();
- assert.eq(res.length, 1, res);
- assert.eq(res[0].count, 1, res);
- }
-
- {
- const res =
- coll.aggregate([{$match: {topLevelScalar: {$gte: 123}}}, {$count: "count"}]).toArray();
- assert.eq(res.length, 1, res);
- assert.eq(res[0].count, 2, res);
- }
-
- {
- const res =
- coll.aggregate([{$match: {topLevelScalar: {$eq: 123}}}, {$count: "count"}]).toArray();
- assert.eq(res.length, 1, res);
- assert.eq(res[0].count, 1, res);
- }
-
- {
- const res =
- coll.aggregate([{$match: {topLevelScalar: {$ne: 123}}}, {$count: "count"}]).toArray();
- assert.eq(res.length, 1, res);
- assert.eq(res[0].count, 2, res);
- }
-
- {
- const res = coll.aggregate([
- {
- $match: {
- $or: [
- {"topLevelScalar": 123},
- {"topLevelScalar": 456},
- ]
- }
- },
- {$count: "count"}
- ])
- .toArray();
- assert.eq(res.length, 1, res);
- assert.eq(res[0].count, 2, res);
- }
-
- {
- const res = coll.aggregate([
- {$addFields: {"computedField": {$and: ["$topLevelScalar", true]}}},
- {$match: {computedField: true}},
- {$count: "count"}
- ])
- .toArray();
- assert.eq(res.length, 1, res);
- assert.eq(res[0].count, 2, res);
- }
-
- {
- const res = coll.aggregate([
- {$addFields: {"computedField": {$and: ["$topLevelScalar", "$obj.a"]}}},
- {$match: {computedField: true}},
- {$count: "count"}
- ])
- .toArray();
- assert.eq(res.length, 1, res);
- assert.eq(res[0].count, 2, res);
- }
-
- {
- const res = coll.aggregate([
- {$addFields: {"computedField": {$or: ["$topLevelScalar", false]}}},
- {$match: {computedField: true}},
- {$count: "count"}
- ])
- .toArray();
- assert.eq(res.length, 1, res);
- assert.eq(res[0].count, 2, res);
- }
-
- {
- const res = coll.aggregate([
- {$addFields: {"computedField": {$or: ["$topLevelScalar", "$obj.a"]}}},
- {$match: {computedField: true}},
- {$count: "count"}
- ])
- .toArray();
- assert.eq(res.length, 1, res);
- assert.eq(res[0].count, 2, res);
- }
-
- {
- const res = coll.aggregate([
- {$addFields: {"computedField": {$not: ["$topLevelScalar"]}}},
- {$match: {computedField: true}},
- {$count: "count"}
- ])
- .toArray();
- assert.eq(res.length, 1, res);
- assert.eq(res[0].count, 1, res);
- }
-
- {
- const res =
- coll.aggregate([
- {$addFields: {"computedField": {$anyElementTrue: [["$topLevelScalar"]]}}},
- {$match: {computedField: true}},
- {$count: "count"}
- ])
- .toArray();
- assert.eq(res.length, 1, res);
- assert.eq(res[0].count, 2, res);
- }
-
- {
- const res =
- coll.aggregate([
- {$match: {length: {$ne: 0}}},
- {$set: {"computedA": {$multiply: ["$topLevelScalar", "$topLevelScalar"]}}},
- {$addFields: {"ratio": {$divide: ["$computedA", "$length"]}}},
- {$match: {ratio: {$gt: 0}}},
- ])
- .toArray();
- assert.eq(res.length, 1, res);
- assert.eq(res[0]._id, 1, res);
- }
-
- {
- let pipeline = [
- {$addFields: {"computedA": {$round: ["$topLevelScalarDouble", 2]}}},
- {$match: {"computedA": {$gte: 100}}},
- {
- $group: {
- "_id": {"time": {"$dateTrunc": {"date": "$time", "unit": "minute"}}},
- "value": {$sum: "$computedA"},
- }
- }
- ];
-
- const res = coll.aggregate(pipeline).toArray();
- assert.docEq([{"_id": {"time": ISODate("1970-01-20T10:55:00Z")}, "value": 670.65}], res);
- }
- {
- let pipeline = [
- {$addFields: {"computedA": {$round: ["$topLevelScalarDouble"]}}},
- {$match: {"computedA": {$gte: 100}}},
- {
- $group: {
- "_id": {"time": {"$dateTrunc": {"date": "$time", "unit": "minute"}}},
- "value": {$sum: "$computedA"},
- }
- }
- ];
-
- const res = coll.aggregate(pipeline).toArray();
- assert.docEq([{"_id": {"time": ISODate("1970-01-20T10:55:00Z")}, "value": 671}], res);
- }
- {
- let pipeline = [
- {$addFields: {"computedA": {$round: [2, "$topLevelScalarDouble"]}}},
- {$match: {"computedA": {$gte: 100}}},
- {
- $group: {
- "_id": {"time": {"$dateTrunc": {"date": "$time", "unit": "minute"}}},
- "value": {$sum: "$computedA"},
- }
- }
- ];
-
- assert.throws(() => coll.aggregate(pipeline));
- }
- {
- let pipeline = [
- {$addFields: {"computedA": {$round: ["$topLevelScalarDouble", "$topLevelScalar"]}}},
- {$match: {"computedA": {$gte: 100}}},
- {
- $group: {
- "_id": {"time": {"$dateTrunc": {"date": "$time", "unit": "minute"}}},
- "value": {$sum: "$computedA"},
- }
- }
- ];
-
- assert.throws(() => coll.aggregate(pipeline));
- }
-
- {
- let pipeline = [
- {$addFields: {"computedA": {$trunc: ["$topLevelScalarDouble", 2]}}},
- {$match: {"computedA": {$gte: 100}}},
- {
- $group: {
- "_id": {"time": {"$dateTrunc": {"date": "$time", "unit": "minute"}}},
- "value": {$sum: "$computedA"},
- }
- }
- ];
-
- const res = coll.aggregate(pipeline).toArray();
- assert.docEq([{"_id": {"time": ISODate("1970-01-20T10:55:00Z")}, "value": 670.63}], res);
- }
- {
- let pipeline = [
- {$addFields: {"computedA": {$trunc: ["$topLevelScalarDouble"]}}},
- {$match: {"computedA": {$gte: 100}}},
- {
- $group: {
- "_id": {"time": {"$dateTrunc": {"date": "$time", "unit": "minute"}}},
- "value": {$sum: "$computedA"},
- }
- }
- ];
-
- const res = coll.aggregate(pipeline).toArray();
- assert.docEq([{"_id": {"time": ISODate("1970-01-20T10:55:00Z")}, "value": 669}], res);
- }
- {
- let pipeline = [
- {$addFields: {"computedA": {$trunc: [2, "$topLevelScalarDouble"]}}},
- {$match: {"computedA": {$gte: 100}}},
- {
- $group: {
- "_id": {"time": {"$dateTrunc": {"date": "$time", "unit": "minute"}}},
- "value": {$sum: "$computedA"},
- }
- }
- ];
-
- assert.throws(() => coll.aggregate(pipeline));
- }
- {
- let pipeline = [
- {$addFields: {"computedA": {$trunc: ["$topLevelScalarDouble", "$topLevelScalar"]}}},
- {$match: {"computedA": {$gte: 100}}},
- {
- $group: {
- "_id": {"time": {"$dateTrunc": {"date": "$time", "unit": "minute"}}},
- "value": {$sum: "$computedA"},
- }
- }
- ];
-
- assert.throws(() => coll.aggregate(pipeline));
- }
-
- {
- let pipeline = [
- {$addFields: {md: {$mod: ["$topLevelScalar", 5]}}},
- {
- $group: {
- "_id": {$dateTrunc: {"date": "$time", "unit": "minute"}},
- "total1": {$sum: "$md"},
- "total2": {$sum: "$topLevelScalar"}
- }
- }
- ];
-
- assert.docEq([{"_id": ISODate("1970-01-20T10:55:00Z"), "total1": 4, "total2": 579}],
- coll.aggregate(pipeline).toArray());
- }
-
- {
- let pipeline = [
- {$addFields: {md: {$mod: [500, "$topLevelScalar"]}}},
- {
- $group: {
- "_id": {$dateTrunc: {"date": "$time", "unit": "minute"}},
- "total1": {$sum: "$md"},
- "total2": {$sum: "$topLevelScalar"}
- }
- }
- ];
-
- assert.docEq([{"_id": ISODate("1970-01-20T10:55:00Z"), "total1": 52, "total2": 579}],
- coll.aggregate(pipeline).toArray());
- }
-
- {
- let pipeline = [
- {$addFields: {md: {$mod: ["$topLevelScalar", "$topLevelScalar"]}}},
- {
- $group: {
- "_id": {$dateTrunc: {"date": "$time", "unit": "minute"}},
- "total1": {$sum: "$md"},
- "total2": {$sum: "$topLevelScalar"}
- }
- }
- ];
-
- assert.docEq([{"_id": ISODate("1970-01-20T10:55:00Z"), "total1": 0, "total2": 579}],
- coll.aggregate(pipeline).toArray());
- }
-
- {
- let pipeline = [
- {$addFields: {md: {$mod: ["$topLevelScalar", 5]}}},
- {$match: {md: {$mod: [4, 1]}}},
- {
- $group: {
- "_id": {$dateTrunc: {"date": "$time", "unit": "minute"}},
- "total1": {$sum: "$md"},
- "total2": {$sum: "$topLevelScalar"}
- }
- }
- ];
-
- assert.docEq([{"_id": ISODate("1970-01-20T10:55:00Z"), "total1": 1, "total2": 456}],
- coll.aggregate(pipeline).toArray());
- }
- {
- let pipeline = [{$match: {topLevelLargeNumber: {$mod: [1, 0]}}}, {$count: "count"}];
-
- const res = coll.aggregate(pipeline).toArray();
-
- assert.eq(res.length, 1, res);
- assert.eq(res[0].count, 1, res);
- }
- {
- const res =
- coll.aggregate([
- {
- $addFields: {
- "computedField":
- {$dateAdd: {startDate: "$time", unit: "millisecond", amount: 100}}
- }
- },
- {$match: {computedField: new Date(datePrefix + 400)}},
- {$count: "count"}
- ])
- .toArray();
- assert.eq(res.length, 1, res);
- assert.eq(res[0].count, 1, res);
- }
-
- {
- const res = coll.aggregate([
- {
- $addFields: {
- "computedField": {
- $dateSubtract:
- {startDate: "$time", unit: "millisecond", amount: 100}
- }
- }
- },
- {$match: {computedField: new Date(datePrefix)}},
- {$count: "count"}
- ])
- .toArray();
- assert.eq(res.length, 1, res);
- assert.eq(res[0].count, 1, res);
- }
-
- {
- // Test the case where a computed meta field is computed to missing.
- const res =
- coll.aggregate([{"$project": {"_id": 0, [metaFieldName]: "$$REMOVE"}}]).toArray();
- assert.eq(res.length, coll.count(), res);
- for (let doc of res) {
- assert.eq(doc, {}, res);
- }
- }
-
- {
- // Test the case where a computed meta field is computed to missing because of a project
- // out.
- const res = coll.aggregate([
- {"$project": {[metaFieldName]: 0}},
- {"$project": {[metaFieldName]: "$" + metaFieldName}},
- {$match: {a: 1}}
- ])
- .toArray();
- assert.eq(res.length, 0, res);
- }
-
- {
- // Test the case where a field is projected out and then projected back in.
- const res = coll.aggregate([
- {"$project": {"_id": 0, [metaFieldName]: 1}},
- {"$project": {"_id": 0, [timeFieldName]: "$" + timeFieldName}},
- {"$project": {"_id": 1, [timeFieldName]: 1}}
- ])
- .toArray();
- assert.eq(res.length, coll.count(), res);
- for (let doc of res) {
- assert.eq(doc, {}, res);
- }
- }
-
- {
- // Test the case where a field is projected out and then projected back in.
- const res = coll.aggregate([
- {"$project": {"_id": 0, [metaFieldName]: 1}},
- {"$project": {"_id": 0, [timeFieldName]: 1}}
- ])
- .toArray();
- assert.eq(res.length, coll.count(), res);
- for (let doc of res) {
- assert.eq(doc, {}, res);
- }
- }
-
- {
- // Test the case where a field is projected out and then projected back in.
- const res = coll.aggregate([
- {"$project": {"_id": 0, [metaFieldName]: 0}},
- {"$project": {"_id": 0, [metaFieldName]: 1}},
- {"$project": {"_id": 0, [timeFieldName]: 1}}
- ])
- .toArray();
- assert.eq(res.length, coll.count(), res);
- for (let doc of res) {
- assert.eq(doc, {}, res);
- }
- }
-});
diff --git a/jstests/core/timeseries/timeseries_create_collection.js b/jstests/core/timeseries/timeseries_create_collection.js
index 99616605163..feb2f443d26 100644
--- a/jstests/core/timeseries/timeseries_create_collection.js
+++ b/jstests/core/timeseries/timeseries_create_collection.js
@@ -17,15 +17,6 @@ assert.commandWorked(testDB.dropDatabase());
const timeFieldName = 'time';
const coll = testDB.t;
-// Fails to create a time-series collection with null-embedded timeField or metaField.
-assert.commandFailedWithCode(
- testDB.createCollection(coll.getName(), {timeseries: {timeField: '\0time'}}),
- ErrorCodes.BadValue);
-assert.commandFailedWithCode(
- testDB.createCollection(coll.getName(),
- {timeseries: {timeField: timeFieldName, metaField: 't\0ag'}}),
- ErrorCodes.BadValue);
-
// Create a timeseries collection, listCollection should show view and bucket collection
assert.commandWorked(
testDB.createCollection(coll.getName(), {timeseries: {timeField: timeFieldName}}));
diff --git a/jstests/core/timeseries/timeseries_delete_hint.js b/jstests/core/timeseries/timeseries_delete_hint.js
index 69fb2bc5a91..0facca816e1 100644
--- a/jstests/core/timeseries/timeseries_delete_hint.js
+++ b/jstests/core/timeseries/timeseries_delete_hint.js
@@ -18,11 +18,11 @@
(function() {
"use strict";
+load("jstests/core/timeseries/libs/timeseries.js");
load("jstests/libs/curop_helpers.js");
-load("jstests/libs/feature_flag_util.js");
load('jstests/libs/parallel_shell_helpers.js');
-if (!FeatureFlagUtil.isEnabled(db, "TimeseriesUpdatesAndDeletes")) {
+if (!TimeseriesTest.timeseriesUpdatesAndDeletesEnabled(db.getMongo())) {
jsTestLog("Skipping test because the time-series updates and deletes feature flag is disabled");
return;
}
@@ -71,7 +71,7 @@ const validateDeleteIndex = (docsToInsert,
: assert.commandWorked(
testDB.runCommand({delete: coll.getName(), deletes: deleteQuery}));
assert.eq(res["n"], expectedNRemoved);
- assert.sameMembers(coll.find({}, {_id: 0}).toArray(), expectedRemainingDocs);
+ assert.docEq(coll.find({}, {_id: 0}).toArray(), expectedRemainingDocs);
assert(coll.drop());
},
docsToInsert,
diff --git a/jstests/core/timeseries/timeseries_field_parsed_as_bson.js b/jstests/core/timeseries/timeseries_field_parsed_as_bson.js
deleted file mode 100644
index 70210a90072..00000000000
--- a/jstests/core/timeseries/timeseries_field_parsed_as_bson.js
+++ /dev/null
@@ -1,70 +0,0 @@
-/**
- * Tests that timeseries timeField is parsed as bson.
- *
- * @tags: [
- * # We need a timeseries collection.
- * requires_timeseries,
- * # Cannot insert into a time-series collection in a multi-document transaction
- * does_not_support_transactions,
- * multiversion_incompatible
- * ]
- */
-
-(function() {
-'use strict';
-
-const collName = "timeseries_field_parsed_as_bson";
-const coll = db.getCollection(collName);
-
-coll.drop();
-const timeField = "badInput']}}}}}}";
-assert.commandWorked(db.createCollection(collName, {timeseries: {timeField: timeField}}));
-
-const timeseriesCollInfo = db.getCollectionInfos({name: "system.buckets." + collName})[0];
-jsTestLog("Timeseries system collection info: " + tojson(timeseriesCollInfo));
-const properties = {};
-properties[timeField] = {
- "bsonType": "date"
-};
-const expectedValidator = {
- "$jsonSchema": {
- "bsonType": "object",
- "required": ["_id", "control", "data"],
- "properties": {
- "_id": {"bsonType": "objectId"},
- "control": {
- "bsonType": "object",
- "required": ["version", "min", "max"],
- "properties": {
- "version": {"bsonType": "number"},
- "min":
- {"bsonType": "object", "required": [timeField], "properties": properties},
- "max":
- {"bsonType": "object", "required": [timeField], "properties": properties},
- "closed": {"bsonType": "bool"},
- "count": {"bsonType": "number", "minimum": 1}
- },
- "additionalProperties": false
- },
- "data": {"bsonType": "object"},
- "meta": {}
- },
- "additionalProperties": false
- }
-};
-
-assert(timeseriesCollInfo.options);
-assert.eq(timeseriesCollInfo.options.validator, expectedValidator);
-
-const doc = {
- a: 1,
- [timeField]: new Date("2021-01-01")
-};
-assert.commandWorked(coll.insert(doc));
-assert.docEq([doc], coll.aggregate([{$match: {}}, {$project: {_id: 0}}]).toArray());
-
-coll.drop();
-assert.commandWorked(db.createCollection(collName, {timeseries: {timeField: "\\"}}));
-coll.drop();
-assert.commandWorked(db.createCollection(collName, {timeseries: {timeField: "\\\\"}}));
-})();
diff --git a/jstests/core/timeseries/timeseries_filter_extended_range.js b/jstests/core/timeseries/timeseries_filter_extended_range.js
deleted file mode 100644
index 2b86e29c943..00000000000
--- a/jstests/core/timeseries/timeseries_filter_extended_range.js
+++ /dev/null
@@ -1,185 +0,0 @@
-/**
- * Test that find/match type queries work properly on dates ouside the 32 bit epoch range,
- * [1970-01-01 00:00:00 UTC - 2038-01-29 03:13:07 UTC].
- *
- * @tags: [
- * # Refusing to run a test that issues an aggregation command with explain because it may
- * # return incomplete results if interrupted by a stepdown.
- * does_not_support_stepdowns,
- * # We need a timeseries collection.
- * requires_timeseries,
- * # Cannot insert into a time-series collection in a multi-document transaction
- * does_not_support_transactions,
- * # Explain of a resolved view must be executed by mongos.
- * directly_against_shardsvrs_incompatible,
- * ]
- */
-
-(function() {
-"use strict";
-const timeFieldName = "time";
-
-/*
- * Creates a collection, populates it, runs the `query` and ensures that the result set
- * is equal to `results`.
- *
- * If overflow is set we create a document with dates above the 32 bit range (year 2040)
- * If underflow is set, we create a document with dates below the 32 bit range (year 1965)
- */
-function runTest(underflow, overflow, query, results) {
- // Setup our DB & our collections.
- const tsColl = db.getCollection(jsTestName());
- tsColl.drop();
-
- assert.commandWorked(
- db.createCollection(tsColl.getName(), {timeseries: {timeField: timeFieldName}}));
-
- const dates = [
- // If underflow, we want to insert a date that would fall below the epoch
- // i.e. 1970-01-01 00:00:00 UTC. Otherwise we use a date within the epoch.
- {[timeFieldName]: underflow ? new Date("1965-01-01") : new Date("1971-01-01")},
- {[timeFieldName]: new Date("1975-01-01")},
- {[timeFieldName]: new Date("1980-01-01")},
- {[timeFieldName]: new Date("1995-01-01")},
- // If overflow, we want to insert a date that would use more than 32 bit milliseconds after
- // the epoch. This overflow will occur 2038-01-29 03:13:07 UTC. Otherwise we go slightly
- // before the end of the 32 bit epoch.
- {[timeFieldName]: overflow ? new Date("2040-01-01") : new Date("2030-01-01")}
- ];
- assert.commandWorked(tsColl.insert(dates));
-
- // Make sure the expected results are in the correct order for comparison below.
- function cmpTimeFields(a, b) {
- return (b[timeFieldName].getTime() - a[timeFieldName].getTime());
- }
- results.sort(cmpTimeFields);
-
- const pipeline = [{$match: query}, {$project: {_id: 0, [timeFieldName]: 1}}];
-
- const plan = tsColl.explain().aggregate(pipeline);
-
- // Verify agg pipeline. We don't want to go through a plan that encourages a sort order to
- // avoid BUS and index selection, so we sort after gathering the results.
- const aggActuals = tsColl.aggregate(pipeline).toArray();
- aggActuals.sort(cmpTimeFields);
- assert.docEq(results, aggActuals, JSON.stringify(plan, null, 4));
-
- // Verify the equivalent find command. We again don't want to go through a plan that
- // encourages a sort order to avoid BUS and index selection, so we sort after gathering the
- // results.
- let findActuals = tsColl.find(query, {_id: 0, [timeFieldName]: 1}).toArray();
- findActuals.sort(cmpTimeFields);
- assert.docEq(findActuals, results);
-}
-
-runTest(false,
- false,
- {[timeFieldName]: {$eq: new Date("1980-01-01")}},
- [{[timeFieldName]: new Date("1980-01-01")}]);
-runTest(false,
- true,
- {[timeFieldName]: {$eq: new Date("2040-01-01")}},
- [{[timeFieldName]: new Date("2040-01-01")}]);
-runTest(true,
- false,
- {[timeFieldName]: {$eq: new Date("1965-01-01")}},
- [{[timeFieldName]: new Date("1965-01-01")}]);
-
-runTest(false,
- false,
- {[timeFieldName]: {$lt: new Date("1980-01-01")}},
- [{[timeFieldName]: new Date("1971-01-01")}, {[timeFieldName]: new Date("1975-01-01")}]);
-runTest(false,
- true,
- {[timeFieldName]: {$lt: new Date("1980-01-01")}},
- [{[timeFieldName]: new Date("1971-01-01")}, {[timeFieldName]: new Date("1975-01-01")}]);
-runTest(true,
- false,
- {[timeFieldName]: {$lt: new Date("1980-01-01")}},
- [{[timeFieldName]: new Date("1965-01-01")}, {[timeFieldName]: new Date("1975-01-01")}]);
-runTest(true,
- true,
- {[timeFieldName]: {$lt: new Date("1980-01-01")}},
- [{[timeFieldName]: new Date("1965-01-01")}, {[timeFieldName]: new Date("1975-01-01")}]);
-
-runTest(false,
- false,
- {[timeFieldName]: {$gt: new Date("1980-01-01")}},
- [{[timeFieldName]: new Date("1995-01-01")}, {[timeFieldName]: new Date("2030-01-01")}]);
-runTest(false,
- true,
- {[timeFieldName]: {$gt: new Date("1980-01-01")}},
- [{[timeFieldName]: new Date("1995-01-01")}, {[timeFieldName]: new Date("2040-01-01")}]);
-runTest(true,
- false,
- {[timeFieldName]: {$gt: new Date("1980-01-01")}},
- [{[timeFieldName]: new Date("1995-01-01")}, {[timeFieldName]: new Date("2030-01-01")}]);
-runTest(true,
- true,
- {[timeFieldName]: {$gt: new Date("1980-01-01")}},
- [{[timeFieldName]: new Date("1995-01-01")}, {[timeFieldName]: new Date("2040-01-01")}]);
-
-runTest(false, false, {[timeFieldName]: {$lte: new Date("1980-01-01")}}, [
- {[timeFieldName]: new Date("1971-01-01")},
- {[timeFieldName]: new Date("1975-01-01")},
- {[timeFieldName]: new Date("1980-01-01")}
-]);
-runTest(false, true, {[timeFieldName]: {$lte: new Date("1980-01-01")}}, [
- {[timeFieldName]: new Date("1971-01-01")},
- {[timeFieldName]: new Date("1975-01-01")},
- {[timeFieldName]: new Date("1980-01-01")}
-]);
-runTest(true, false, {[timeFieldName]: {$lte: new Date("1980-01-01")}}, [
- {[timeFieldName]: new Date("1965-01-01")},
- {[timeFieldName]: new Date("1975-01-01")},
- {[timeFieldName]: new Date("1980-01-01")}
-]);
-runTest(true, true, {[timeFieldName]: {$lte: new Date("1980-01-01")}}, [
- {[timeFieldName]: new Date("1965-01-01")},
- {[timeFieldName]: new Date("1975-01-01")},
- {[timeFieldName]: new Date("1980-01-01")}
-]);
-
-runTest(false, false, {[timeFieldName]: {$gte: new Date("1980-01-01")}}, [
- {[timeFieldName]: new Date("1980-01-01")},
- {[timeFieldName]: new Date("1995-01-01")},
- {[timeFieldName]: new Date("2030-01-01")}
-]);
-runTest(false, true, {[timeFieldName]: {$gte: new Date("1980-01-01")}}, [
- {[timeFieldName]: new Date("1980-01-01")},
- {[timeFieldName]: new Date("1995-01-01")},
- {[timeFieldName]: new Date("2040-01-01")}
-]);
-runTest(true, false, {[timeFieldName]: {$gte: new Date("1980-01-01")}}, [
- {[timeFieldName]: new Date("1980-01-01")},
- {[timeFieldName]: new Date("1995-01-01")},
- {[timeFieldName]: new Date("2030-01-01")}
-]);
-runTest(true, true, {[timeFieldName]: {$gte: new Date("1980-01-01")}}, [
- {[timeFieldName]: new Date("1980-01-01")},
- {[timeFieldName]: new Date("1995-01-01")},
- {[timeFieldName]: new Date("2040-01-01")}
-]);
-
-// Verify ranges that straddle the lower and upper epoch boundaries work properly.
-runTest(
- true, false, {[timeFieldName]: {$gt: new Date("1920-01-01"), $lt: new Date("1980-01-01")}}, [
- {[timeFieldName]: new Date("1965-01-01")},
- {[timeFieldName]: new Date("1975-01-01")},
- ]);
-runTest(
- false, true, {[timeFieldName]: {$gt: new Date("1980-01-01"), $lt: new Date("2050-01-01")}}, [
- {[timeFieldName]: new Date("1995-01-01")},
- {[timeFieldName]: new Date("2040-01-01")},
- ]);
-runTest(
- false, false, {[timeFieldName]: {$gt: new Date("1920-01-01"), $lt: new Date("1980-01-01")}}, [
- {[timeFieldName]: new Date("1971-01-01")},
- {[timeFieldName]: new Date("1975-01-01")},
- ]);
-runTest(
- false, false, {[timeFieldName]: {$gt: new Date("1980-01-01"), $lt: new Date("2050-01-01")}}, [
- {[timeFieldName]: new Date("1995-01-01")},
- {[timeFieldName]: new Date("2030-01-01")},
- ]);
-})();
diff --git a/jstests/core/timeseries/timeseries_geonear_lookup.js b/jstests/core/timeseries/timeseries_geonear_lookup.js
deleted file mode 100644
index 78f68eb014e..00000000000
--- a/jstests/core/timeseries/timeseries_geonear_lookup.js
+++ /dev/null
@@ -1,40 +0,0 @@
-/**
- * Test that time-series works as expected with $geoNear within a $lookup.
- *
- * @tags: [
- * does_not_support_transactions,
- * requires_pipeline_optimization,
- * # We need a timeseries collection.
- * requires_timeseries,
- * references_foreign_collection,
- * ]
- */
-
-// create a timeseries collection
-const timeFieldName = "time";
-const metaFieldName = "tags";
-const testDB = db;
-
-const tsColl = db.getCollection("ts_coll");
-
-const kMaxDistance = Math.PI * 2.0;
-
-tsColl.drop();
-assert.commandWorked(testDB.createCollection(
- tsColl.getName(), {timeseries: {timeField: timeFieldName, metaField: metaFieldName}}));
-
-assert.commandWorked(tsColl.createIndex({'tags.loc': '2dsphere'}));
-
-tsColl.insert({time: ISODate(), tags: {loc: [40, 40], descr: 0}, value: 0});
-
-const coll2 = db.getCollection("store_min_max_values");
-coll2.drop();
-assert.commandWorked(coll2.insert({_id: 0, minimumDist: 0.0, maximumDist: kMaxDistance}));
-
-coll2.aggregate([{$lookup: {from: tsColl.getName(),
-let: {minVal: "$minimumDist",maxVal:"$maximumDist"},
-pipeline: [
- {$geoNear: {near: {type: "Point", coordinates: [0, 0]},
- key: 'tags.loc',
- distanceField: "tags.distance"}}],
- as: 'output'}}]);
diff --git a/jstests/core/timeseries/timeseries_geonear_measurements.js b/jstests/core/timeseries/timeseries_geonear_measurements.js
index abbf7db9596..5c5427a02d1 100644
--- a/jstests/core/timeseries/timeseries_geonear_measurements.js
+++ b/jstests/core/timeseries/timeseries_geonear_measurements.js
@@ -23,10 +23,10 @@
(function() {
"use strict";
+load("jstests/core/timeseries/libs/timeseries.js");
load("jstests/libs/analyze_plan.js");
-load("jstests/libs/feature_flag_util.js");
-if (!FeatureFlagUtil.isEnabled(db, "TimeseriesMetricIndexes")) {
+if (!TimeseriesTest.timeseriesMetricIndexesEnabled(db.getMongo())) {
jsTestLog(
"Skipped test as the featureFlagTimeseriesMetricIndexes feature flag is not enabled.");
return;
diff --git a/jstests/core/timeseries/timeseries_graph_lookup.js b/jstests/core/timeseries/timeseries_graph_lookup.js
index 25f47aa2cef..7b152cc6b1f 100644
--- a/jstests/core/timeseries/timeseries_graph_lookup.js
+++ b/jstests/core/timeseries/timeseries_graph_lookup.js
@@ -5,7 +5,6 @@
* does_not_support_transactions,
* requires_timeseries,
* requires_fcv_51,
- * references_foreign_collection,
* ]
*/
(function() {
diff --git a/jstests/core/timeseries/timeseries_groupby_reorder.js b/jstests/core/timeseries/timeseries_groupby_reorder.js
deleted file mode 100644
index 774d867d3f6..00000000000
--- a/jstests/core/timeseries/timeseries_groupby_reorder.js
+++ /dev/null
@@ -1,233 +0,0 @@
-/**
- * Test the behavior of $group on time-series collections. Specifically, we are targeting rewrites
- * that replace bucket unpacking with $group over the buckets collection. Currently, only $min/$max
- * are supported for the rewrites.
- *
- * @tags: [
- * directly_against_shardsvrs_incompatible,
- * does_not_support_stepdowns,
- * does_not_support_transactions,
- * requires_fcv_60,
- * ]
- */
-(function() {
-"use strict";
-
-load("jstests/libs/fixture_helpers.js");
-load("jstests/core/timeseries/libs/timeseries.js");
-
-const coll = db.timeseries_groupby_reorder;
-coll.drop();
-
-// We will only check correctness of the results here as checking the plan in JSTests is brittle and
-// is better done in document_source_internal_unpack_bucket_test/group_reorder_test.cpp. For the
-// cases when the re-write isn't applicable, the used datasets should yield wrong result if the
-// re-write is applied.
-function runGroupRewriteTest(docs, pipeline, expectedResults) {
- coll.drop();
- db.createCollection(coll.getName(), {timeseries: {metaField: "meta", timeField: "time"}});
- coll.insertMany(docs);
- assert.docEq(expectedResults, coll.aggregate(pipeline).toArray(), () => {
- return `Pipeline: ${tojson(pipeline)}. Explain: ${
- tojson(coll.explain().aggregate(pipeline))}`;
- });
-}
-
-// Test with measurement group key -- a rewrite in this situation would be wrong.
-(function testNonMetaGroupKey() {
- const t = new Date();
- const docs = [
- {time: t, meta: 1, key: 2, val: 1}, // global min
- {time: t, meta: 1, key: 1, val: 3}, // min for key = 1
- {time: t, meta: 1, key: 1, val: 5}, // max for key = 1
- {time: t, meta: 1, key: 2, val: 7}, // global max
- ];
- runGroupRewriteTest(docs,
- [{$group: {_id: "$key", min: {$min: "$val"}}}, {$match: {_id: 1}}],
- [{"_id": 1, "min": 3}]);
- runGroupRewriteTest(docs,
- [{$group: {_id: "$key", max: {$max: "$val"}}}, {$match: {_id: 1}}],
- [{"_id": 1, "max": 5}]);
-})();
-
-// While a group with const group key can be re-written in terms of a group on the buckets, we don't
-// currently do it. However, when/if we start doing it, it should work.
-(function testConstGroupKey_NoFilter() {
- const t = new Date();
- const docs = [
- {time: t, meta: 1, val: 0},
- {time: t, meta: 1, val: 1},
- {time: t, meta: 1, val: 5},
- ];
- runGroupRewriteTest(
- docs, [{$group: {_id: null, min: {$min: "$val"}}}], [{"_id": null, "min": 0}]);
- runGroupRewriteTest(
- docs, [{$group: {_id: null, max: {$max: "$val"}}}], [{"_id": null, "max": 5}]);
-})();
-
-// With a filter on meta the group re-write would still apply if the group key is const.
-(function testConstGroupKey_WithFilterOnMeta() {
- const t = new Date();
- const docs = [
- {time: t, meta: 1, val: 0},
- {time: t, meta: 2, val: 1},
- {time: t, meta: 2, val: 3},
- {time: t, meta: 1, val: 5},
- ];
- runGroupRewriteTest(docs,
- [{$match: {meta: 2}}, {$group: {_id: null, min: {$min: "$val"}}}],
- [{"_id": null, "min": 1}]);
- runGroupRewriteTest(docs,
- [{$match: {meta: 2}}, {$group: {_id: null, max: {$max: "$val"}}}],
- [{"_id": null, "max": 3}]);
-})();
-
-// In presense of a non-meta filter the group re-write doesn't apply even if the group key is const.
-(function testConstGroupKey_WithFilterOnMeasurement() {
- const t = new Date();
- const docs = [
- {time: t, meta: 1, val: 0, include: false},
- {time: t, meta: 1, val: 1, include: true},
- {time: t, meta: 1, val: 5, include: false},
- ];
- runGroupRewriteTest(docs,
- [{$match: {include: true}}, {$group: {_id: null, min: {$min: "$val"}}}],
- [{"_id": null, "min": 1}]);
- runGroupRewriteTest(docs,
- [{$match: {include: true}}, {$group: {_id: null, max: {$max: "$val"}}}],
- [{"_id": null, "max": 1}]);
-})();
-
-// Test with meta group key. The group re-write applies.
-(function testMetaGroupKey_NoFilter() {
- const t = new Date();
- const docs = [
- {time: t, meta: 1, val: 5},
- {time: t, meta: 2, val: 4},
- {time: t, meta: 2, val: 3},
- {time: t, meta: 1, val: 1},
- ];
- runGroupRewriteTest(docs,
- [{$group: {_id: "$meta", min: {$min: "$val"}}}, {$match: {_id: 2}}],
- [{"_id": 2, "min": 3}]);
- runGroupRewriteTest(docs,
- [{$group: {_id: "$meta", max: {$max: "$val"}}}, {$match: {_id: 2}}],
- [{"_id": 2, "max": 4}]);
-})();
-
-// Test with meta group key preceeded by a filter on the meta key. The re-write still applies.
-(function testMetaGroupKey_WithFilterOnMeta() {
- const t = new Date();
- const docs = [
- {time: t, meta: 1, val: 5},
- {time: t, meta: 2, val: 4},
- {time: t, meta: 2, val: 3},
- {time: t, meta: 1, val: 1},
- ];
- runGroupRewriteTest(docs,
- [{$match: {meta: 2}}, {$group: {_id: "$meta", min: {$min: "$val"}}}],
- [{"_id": 2, "min": 3}]);
- runGroupRewriteTest(docs,
- [{$match: {meta: 2}}, {$group: {_id: "$meta", max: {$max: "$val"}}}],
- [{"_id": 2, "max": 4}]);
-})();
-
-// Test with meta group key preceeded by a filter on a measurement key. The re-write doesn't apply.
-(function testMetaGroupKey_WithFilterOnMeasurement() {
- const t = new Date();
- const docs = [
- {time: t, meta: 1, val: 3, include: false},
- {time: t, meta: 1, val: 4, include: true},
- {time: t, meta: 1, val: 5, include: false},
- ];
- runGroupRewriteTest(docs,
- [{$match: {include: true}}, {$group: {_id: "$meta", min: {$min: "$val"}}}],
- [{"_id": 1, "min": 4}]);
- runGroupRewriteTest(docs,
- [{$match: {include: true}}, {$group: {_id: "$meta", max: {$max: "$val"}}}],
- [{"_id": 1, "max": 4}]);
-})();
-
-// Test SERVER-73822 fix: complex $min and $max (i.e. not just straight field refs) work correctly.
-(function testMetaGroupKey_WithNonPathExpressionUnderMinMax() {
- const t = new Date();
- // min(a+b) != min(a) + min(b) and max(a+b) != max(a) + max(b)
- const docs = [
- {time: t, meta: 1, a: 1, b: 20}, // max(a + b)
- {time: t, meta: 1, a: 2, b: 10},
- {time: t, meta: 1, a: 3, b: 1}, // min(a + b)
- ];
- runGroupRewriteTest(docs,
- [{$group: {_id: "$meta", min: {$min: {$add: ["$a", "$b"]}}}}],
- [{"_id": 1, "min": 4}]);
- runGroupRewriteTest(docs,
- [{$group: {_id: "$meta", max: {$max: {$add: ["$a", "$b"]}}}}],
- [{"_id": 1, "max": 21}]);
-})();
-
-// Test with meta group key and a non-min/max accumulator that doesn't use any fields. The buckets
-// still have to be unpacked because we don't know the number of events in uncompressed buckets.
-(function testMetaGroupKey_WithAccumulatorNotUsingAnyFields() {
- const t = new Date();
- const docs = [
- {time: t, meta: 1, val: 3},
- {time: t, meta: 3, val: 4},
- {time: t, meta: 1, val: 5},
- ];
- runGroupRewriteTest(
- docs, [{$group: {_id: "$meta", x: {$sum: 1}}}, {$match: {_id: 1}}], [{"_id": 1, "x": 2}]);
-})();
-
-// Test with meta group key and a non-min/max accumulator that uses only the meta field. This query
-// is _not_ eligible for the re-write w/o bucket unpacking because while it doesn't depend on any
-// fields of the individual events it still depends on the number of events in a bucket.
-(function testMetaGroupKey_WithNonMinMaxAccumulatorOnMeta() {
- const t = new Date();
- const docs = [
- {time: t, meta: 1, val: 3},
- {time: t, meta: 3, val: 4},
- {time: t, meta: 1, val: 5},
- ];
- runGroupRewriteTest(docs,
- [{$group: {_id: "$meta", x: {$sum: "$meta"}}}, {$match: {_id: 1}}],
- [{"_id": 1, "x": 2}]);
-})();
-
-// In presence of a filter $min and $max on the meta field cannot be re-written because a filter
-// might end up selecting nothing in buckets with a particular meta.
-(function testMetaGroupKey_WithAccumulatorOnMeta_WithFilterOnMeasurement() {
- const t = new Date();
- const docs = [
- {time: t, meta: 1, include: false},
- ];
- runGroupRewriteTest(
- docs, [{$match: {include: true}}, {$group: {_id: "$meta", x: {$min: "$meta"}}}], []);
-})();
-
-// Test min/max on the time field (cannot rewrite $min because the control.time.min is rounded
-// down).
-(function testMetaGroupKey_WithMinMaxOnTime() {
- const docs = [
- {time: ISODate("2023-07-20T23:16:47.683Z"), meta: 1},
- ];
- runGroupRewriteTest(docs,
- [{$group: {_id: "$meta", min: {$min: "$time"}}}],
- [{_id: 1, min: ISODate("2023-07-20T23:16:47.683Z")}]);
-})();
-
-// Test a group key which is an object with single field path. Ensure that the output perseveres the
-// user requested object structure for _id field.
-(function testMetaGroupKey_ObjectWithSingleFieldPathElement() {
- const t = new Date();
- const docs = [
- {time: t, meta: {a: 1}, val: 5},
- {time: t, meta: {a: 2}, val: 4},
- {time: t, meta: {a: 2}, val: 3},
- {time: t, meta: {a: 1}, val: 1},
- ];
- runGroupRewriteTest(
- docs,
- [{$group: {_id: {d: "$meta.a"}, min: {$min: "$val"}}}, {$match: {_id: {d: 2}}}],
- [{"_id": {d: 2}, "min": 3}]);
-})();
-})();
diff --git a/jstests/core/timeseries/timeseries_index.js b/jstests/core/timeseries/timeseries_index.js
index 9547362d105..bef86453999 100644
--- a/jstests/core/timeseries/timeseries_index.js
+++ b/jstests/core/timeseries/timeseries_index.js
@@ -11,9 +11,8 @@
(function() {
"use strict";
-load("jstests/core/timeseries/libs/timeseries.js");
-load("jstests/libs/feature_flag_util.js");
load("jstests/libs/fixture_helpers.js");
+load("jstests/core/timeseries/libs/timeseries.js");
TimeseriesTest.run((insert) => {
const collNamePrefix = 'timeseries_index_';
@@ -226,7 +225,7 @@ TimeseriesTest.run((insert) => {
runTest({[metaFieldName + '.location']: "2d", [metaFieldName + '.tag1']: -1},
{'meta.location': "2d", 'meta.tag1': -1});
- if (FeatureFlagUtil.isEnabled(db, "TimeseriesMetricIndexes")) {
+ if (TimeseriesTest.timeseriesMetricIndexesEnabled(db.getMongo())) {
// Measurement 2dsphere index
runTest({'loc': '2dsphere'}, {'data.loc': '2dsphere_bucket'});
}
@@ -243,7 +242,7 @@ TimeseriesTest.run((insert) => {
coll.getName(), {timeseries: {timeField: timeFieldName, metaField: metaFieldName}}));
assert.commandWorked(insert(coll, doc), 'failed to insert doc: ' + tojson(doc));
- if (!FeatureFlagUtil.isEnabled(db, "TimeseriesMetricIndexes")) {
+ if (!TimeseriesTest.timeseriesMetricIndexesEnabled(db.getMongo())) {
// Reject index keys that do not include the metadata field.
assert.commandFailedWithCode(coll.createIndex({not_metadata: 1}),
ErrorCodes.CannotCreateIndex);
@@ -266,7 +265,7 @@ TimeseriesTest.run((insert) => {
[ErrorCodes.CannotCreateIndex, ErrorCodes.InvalidOptions]);
};
- if (!FeatureFlagUtil.isEnabled(db, "TimeseriesMetricIndexes")) {
+ if (!TimeseriesTest.timeseriesMetricIndexesEnabled(db.getMongo())) {
// Partial indexes are not supported on time-series collections if the time-series metric
// feature flag is disabled.
testCreateIndexFailed({[metaFieldName]: 1}, {partialFilterExpression: {meta: {$gt: 5}}});
diff --git a/jstests/core/timeseries/timeseries_index_partial.js b/jstests/core/timeseries/timeseries_index_partial.js
index 014158c3542..eb2512b1bd7 100644
--- a/jstests/core/timeseries/timeseries_index_partial.js
+++ b/jstests/core/timeseries/timeseries_index_partial.js
@@ -2,8 +2,6 @@
* Test creating and using partial indexes, on a time-series collection.
*
* @tags: [
- * # TODO (SERVER-73316): remove
- * assumes_against_mongod_not_mongos,
* does_not_support_stepdowns,
* does_not_support_transactions,
* requires_fcv_52,
@@ -14,10 +12,10 @@
(function() {
"use strict";
+load("jstests/core/timeseries/libs/timeseries.js");
load("jstests/libs/analyze_plan.js");
-load("jstests/libs/feature_flag_util.js");
-if (!FeatureFlagUtil.isEnabled(db, "TimeseriesMetricIndexes")) {
+if (!TimeseriesTest.timeseriesMetricIndexesEnabled(db.getMongo())) {
jsTestLog(
"Skipped test as the featureFlagTimeseriesMetricIndexes feature flag is not enabled.");
return;
@@ -80,24 +78,10 @@ assert.commandFailedWithCode(coll.createIndex({a: 1}, {partialFilterExpression:
// Test creating and using a partial index.
{
- let ixscanInWinningPlan = 0;
-
- // Make sure the {a: 1} index was considered for this query.
+ // Make sure the query uses the {a: 1} index.
function checkPlan(predicate) {
const explain = coll.find(predicate).explain();
- let scan = getAggPlanStage(explain, 'IXSCAN');
- // If scan is not present, check rejected plans
- if (scan === null) {
- const rejectedPlans = getRejectedPlans(getAggPlanStage(explain, "$cursor")["$cursor"]);
- if (rejectedPlans.length === 1) {
- const scans = getPlanStages(getRejectedPlan(rejectedPlans[0]), "IXSCAN");
- if (scans.length === 1) {
- scan = scans[0];
- }
- }
- } else {
- ixscanInWinningPlan++;
- }
+ const scan = getAggPlanStage(explain, 'IXSCAN');
const indexes = buckets.getIndexes();
assert(scan,
"Expected an index scan for predicate: " + tojson(predicate) +
@@ -106,10 +90,10 @@ assert.commandFailedWithCode(coll.createIndex({a: 1}, {partialFilterExpression:
}
// Make sure the query results match a collection-scan plan.
function checkResults(predicate) {
- const result = coll.aggregate([{$match: predicate}], {hint: {a: 1}}).toArray();
+ const result = coll.aggregate({$match: predicate}).toArray();
const unindexed =
coll.aggregate([{$_internalInhibitOptimization: {}}, {$match: predicate}]).toArray();
- assert.sameMembers(result, unindexed);
+ assert.docEq(result, unindexed);
}
function checkPlanAndResults(predicate) {
checkPlan(predicate);
@@ -149,6 +133,10 @@ assert.commandFailedWithCode(coll.createIndex({a: 1}, {partialFilterExpression:
const t1 = ISODate('2000-01-01T00:00:01Z');
const t2 = ISODate('2000-01-01T00:00:02Z');
+ // When the collection is sharded, there is an index on time that can win, instead of the
+ // partial index. So only check the results in that case, not the plan.
+ const check = FixtureHelpers.isSharded(buckets) ? checkResults : checkPlanAndResults;
+
assert.commandWorked(coll.dropIndex({a: 1}));
assert.commandWorked(
coll.createIndex({a: 1}, {partialFilterExpression: {[timeField]: {$lt: t1}}}));
@@ -177,7 +165,6 @@ assert.commandFailedWithCode(coll.createIndex({a: 1}, {partialFilterExpression:
assert.commandWorked(coll.dropIndex({a: 1}));
assert.sameMembers(coll.getIndexes(), extraIndexes);
assert.sameMembers(buckets.getIndexes(), extraBucketIndexes);
- assert.gt(ixscanInWinningPlan, 0);
}
// Check that partialFilterExpression can use a mixture of metadata, time, and measurement fields,
diff --git a/jstests/core/timeseries/timeseries_index_spec.js b/jstests/core/timeseries/timeseries_index_spec.js
index d3822dc642b..f13c64dd80b 100644
--- a/jstests/core/timeseries/timeseries_index_spec.js
+++ b/jstests/core/timeseries/timeseries_index_spec.js
@@ -16,7 +16,6 @@
"use strict";
load("jstests/core/timeseries/libs/timeseries.js");
-load("jstests/libs/feature_flag_util.js");
TimeseriesTest.run(() => {
const collName = "timeseries_index_spec";
@@ -86,7 +85,7 @@ TimeseriesTest.run(() => {
{name: "time_meta_field_downgradable"}));
verifyAndDropIndex(/*isDowngradeCompatible=*/true, "time_meta_field_downgradable");
- if (FeatureFlagUtil.isEnabled(db, "TimeseriesMetricIndexes")) {
+ if (TimeseriesTest.timeseriesMetricIndexesEnabled(db.getMongo())) {
assert.commandWorked(coll.createIndex({x: 1}, {name: "x_1"}));
verifyAndDropIndex(/*isDowngradeCompatible=*/false, "x_1");
@@ -111,7 +110,7 @@ TimeseriesTest.run(() => {
// Creating an index directly on the buckets collection is permitted. However, these types of
// index creations will not have an "originalSpec" field and rely on the reverse mapping
// mechanism.
- if (FeatureFlagUtil.isEnabled(db, "TimeseriesMetricIndexes")) {
+ if (TimeseriesTest.timeseriesMetricIndexesEnabled(db.getMongo())) {
assert.commandWorked(
bucketsColl.createIndex({"control.min.y": 1, "control.max.y": 1}, {name: "y"}));
diff --git a/jstests/core/timeseries/timeseries_index_use.js b/jstests/core/timeseries/timeseries_index_use.js
index 6ce128ecbbd..8770047f4d2 100644
--- a/jstests/core/timeseries/timeseries_index_use.js
+++ b/jstests/core/timeseries/timeseries_index_use.js
@@ -50,9 +50,8 @@ const generateTest = (useHint) => {
/**
* Creates the index specified by the spec and options, then explains the query to ensure
- * that the created index is used or was considered by multi-planner.
- * Runs the query and verifies that the expected number of documents are matched.
- * Finally, deletes the created index.
+ * 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 = {}) {
@@ -68,23 +67,9 @@ const generateTest = (useHint) => {
assert.eq(numMatches, query.itcount());
const explain = query.explain();
- if (useHint) {
- const ixscan = getAggPlanStage(explain, "IXSCAN");
- assert.neq(null, ixscan, tojson(explain));
- assert.eq("testIndexName", ixscan.indexName, tojson(ixscan));
- } else {
- let ixscan = getAggPlanStage(explain, "IXSCAN");
- // If ixscan is not present, check rejected plans
- if (ixscan === null) {
- const rejectedPlans =
- getRejectedPlans(getAggPlanStage(explain, "$cursor")["$cursor"]);
- assert.eq(1, rejectedPlans.length);
- const ixscans = getPlanStages(getRejectedPlan(rejectedPlans[0]), "IXSCAN");
- assert.eq(1, ixscans.length);
- ixscan = ixscans[0];
- }
- assert.eq("testIndexName", ixscan.indexName, tojson(ixscan));
- }
+ const ixscan = getAggPlanStage(explain, "IXSCAN");
+ assert.neq(null, ixscan, tojson(explain));
+ assert.eq("testIndexName", ixscan.indexName, tojson(ixscan));
assert.commandWorked(coll.dropIndex("testIndexName"));
};
diff --git a/jstests/core/timeseries/timeseries_insert_mixed_schema_bucket.js b/jstests/core/timeseries/timeseries_insert_mixed_schema_bucket.js
deleted file mode 100644
index cbce31e9795..00000000000
--- a/jstests/core/timeseries/timeseries_insert_mixed_schema_bucket.js
+++ /dev/null
@@ -1,75 +0,0 @@
-/**
- * Tests directly inserting a time-series bucket with mixed schema.
- *
- * @tags: [
- * # $listCatalog does not include the tenant prefix in its results.
- * command_not_supported_in_serverless,
- * requires_timeseries,
- * # $listCatalog not supported inside of a multi-document transaction
- * does_not_support_transactions,
- * # $listCatalog only exists since v6
- * multiversion_incompatible,
- * ]
- */
-(function() {
-"use strict";
-
-load("jstests/core/timeseries/libs/timeseries.js"); // For 'TimeseriesTest'.
-
-TestData.skipEnforceTimeseriesBucketsAreAlwaysCompressedOnValidate = true;
-
-const testDB = db.getSiblingDB(jsTestName());
-const collName = "ts";
-
-assert.commandWorkedOrFailedWithCode(testDB.runCommand({drop: collName}),
- ErrorCodes.NamespaceNotFound);
-assert.commandWorked(
- testDB.createCollection(collName, {timeseries: {timeField: "t", metaField: "m"}}));
-const coll = testDB[collName];
-const bucketsColl = testDB["system.buckets." + collName];
-
-const bucket = {
- _id: ObjectId("65a6eb806ffc9fa4280ecac4"),
- control: {
- version: NumberInt(1),
- min: {
- _id: ObjectId("65a6eba7e6d2e848e08c3750"),
- t: ISODate("2024-01-16T20:48:00Z"),
- a: 1,
- },
- max: {
- _id: ObjectId("65a6eba7e6d2e848e08c3751"),
- t: ISODate("2024-01-16T20:48:39.448Z"),
- a: "a",
- },
- },
- meta: 0,
- data: {
- _id: {
- 0: ObjectId("65a6eba7e6d2e848e08c3750"),
- 1: ObjectId("65a6eba7e6d2e848e08c3751"),
- },
- t: {
- 0: ISODate("2024-01-16T20:48:39.448Z"),
- 1: ISODate("2024-01-16T20:48:39.448Z"),
- },
- a: {
- 0: "a",
- 1: 1,
- },
- }
-};
-
-assert.commandFailedWithCode(bucketsColl.insert(bucket),
- ErrorCodes.CannotInsertTimeseriesBucketsWithMixedSchema);
-assert.eq(TimeseriesTest.bucketsMayHaveMixedSchemaData(bucketsColl), false);
-assert.commandWorked(
- testDB.runCommand({collMod: collName, timeseriesBucketsMayHaveMixedSchemaData: true}));
-assert.eq(TimeseriesTest.bucketsMayHaveMixedSchemaData(bucketsColl), true);
-assert.commandWorked(bucketsColl.insert(bucket));
-assert.commandWorked(bucketsColl.deleteOne({_id: bucket._id}));
-assert.eq(TimeseriesTest.bucketsMayHaveMixedSchemaData(bucketsColl), true);
-assert.commandWorked(
- testDB.runCommand({collMod: collName, timeseriesBucketsMayHaveMixedSchemaData: false}));
-assert.eq(TimeseriesTest.bucketsMayHaveMixedSchemaData(bucketsColl), false);
-})();
diff --git a/jstests/core/timeseries/timeseries_internal_bucket_geo_within.js b/jstests/core/timeseries/timeseries_internal_bucket_geo_within.js
index 2e350ebdbb0..9c237d8dd77 100644
--- a/jstests/core/timeseries/timeseries_internal_bucket_geo_within.js
+++ b/jstests/core/timeseries/timeseries_internal_bucket_geo_within.js
@@ -4,7 +4,7 @@
* collection.
*
* @tags: [
- * requires_fcv_60,
+ * requires_fcv_51,
* requires_pipeline_optimization,
* requires_timeseries,
* does_not_support_stepdowns,
@@ -47,6 +47,10 @@ for (let collScanStage of collScanStages) {
"field": "loc"
}
};
+ // TODO SERVER-60373 Fix duplicate predicates for sharded time-series collection
+ if (FixtureHelpers.isSharded(bucketsColl)) {
+ expectedPredicate = {$and: [expectedPredicate, expectedPredicate]};
+ }
assert.docEq(expectedPredicate, collScanStage.filter, collScanStages);
}
diff --git a/jstests/core/timeseries/timeseries_lastpoint_top.js b/jstests/core/timeseries/timeseries_lastpoint_top.js
index c85f48fb076..2efd352b574 100644
--- a/jstests/core/timeseries/timeseries_lastpoint_top.js
+++ b/jstests/core/timeseries/timeseries_lastpoint_top.js
@@ -27,7 +27,6 @@ assert.commandWorked(testDB.dropDatabase());
// Do not run the rest of the tests if the lastpoint optimization is disabled.
if (!FeatureFlagUtil.isEnabled(db, "LastPointQuery")) {
- jsTestLog("Skipping the test.");
return;
}
diff --git a/jstests/core/timeseries/timeseries_lookup.js b/jstests/core/timeseries/timeseries_lookup.js
index 6d559e9356d..c173764a591 100644
--- a/jstests/core/timeseries/timeseries_lookup.js
+++ b/jstests/core/timeseries/timeseries_lookup.js
@@ -5,13 +5,11 @@
* does_not_support_transactions,
* requires_timeseries,
* requires_fcv_51,
- * references_foreign_collection,
* ]
*/
(function() {
"use strict";
-load("jstests/aggregation/extras/utils.js");
load("jstests/core/timeseries/libs/timeseries.js");
TimeseriesTest.run((insert) => {
@@ -26,44 +24,43 @@ TimeseriesTest.run((insert) => {
Random.setRandomSeed();
const hosts = TimeseriesTest.generateHosts(numHosts);
- let
- testFunc = function(collAOption, collBOption) {
- // Prepare two time-series collections.
- const collA = testDB.getCollection("a");
- const collB = testDB.getCollection("b");
- collA.drop();
- collB.drop();
- assert.commandWorked(testDB.createCollection(collA.getName(), collAOption));
- assert.commandWorked(testDB.createCollection(collB.getName(), collBOption));
-
- let entryCountPerHost = new Array(numHosts).fill(0);
-
- // Insert into collA, one entry per host.
- for (let i = 0; i < numHosts; i++) {
- let host = hosts[i];
- assert.commandWorked(insert(collA, {
- measurement: "cpu",
- time: ISODate(),
- tags: host.tags,
- }));
- }
-
- // Insert some random documents to collB.
- for (let i = 0; i < numDocs; i++) {
- let host = TimeseriesTest.getRandomElem(hosts);
- assert.commandWorked(insert(collB, {
- measurement: "cpu",
- time: ISODate(),
- tags: host.tags,
- }));
- // Here we extract the hostId from "host.tags.hostname". It is expected that the
- // "host.tags.hostname" is in the form of 'host_<hostNum>'.
- entryCountPerHost[parseInt(
- host.tags.hostname.substring(5, host.tags.hostname.length))]++;
- }
-
- // Equality Match
- let results = collA.aggregate([
+ let testFunc = function(collAOption, collBOption) {
+ // Prepare two time-series collections.
+ const collA = testDB.getCollection("a");
+ const collB = testDB.getCollection("b");
+ collA.drop();
+ collB.drop();
+ assert.commandWorked(testDB.createCollection(collA.getName(), collAOption));
+ assert.commandWorked(testDB.createCollection(collB.getName(), collBOption));
+
+ let entryCountPerHost = new Array(numHosts).fill(0);
+
+ // Insert into collA, one entry per host.
+ for (let i = 0; i < numHosts; i++) {
+ let host = hosts[i];
+ assert.commandWorked(insert(collA, {
+ measurement: "cpu",
+ time: ISODate(),
+ tags: host.tags,
+ }));
+ }
+
+ // Insert some random documents to collB.
+ for (let i = 0; i < numDocs; i++) {
+ let host = TimeseriesTest.getRandomElem(hosts);
+ assert.commandWorked(insert(collB, {
+ measurement: "cpu",
+ time: ISODate(),
+ tags: host.tags,
+ }));
+ // Here we extract the hostId from "host.tags.hostname". It is expected that the
+ // "host.tags.hostname" is in the form of 'host_<hostNum>'.
+ entryCountPerHost[parseInt(
+ host.tags.hostname.substring(5, host.tags.hostname.length))]++;
+ }
+
+ // Equality Match
+ let results = collA.aggregate([
{
$lookup: {
from: collB.getName(),
@@ -82,106 +79,13 @@ TimeseriesTest.run((insert) => {
},
{$sort: {host: 1}}
]).toArray();
- assert.eq(numHosts, results.length, results);
- for (let i = 0; i < numHosts; i++) {
- assert.eq({host: "host_" + i, matchedB: entryCountPerHost[i]}, results[i], results);
- }
+ assert.eq(numHosts, results.length, results);
+ for (let i = 0; i < numHosts; i++) {
+ assert.eq({host: "host_" + i, matchedB: entryCountPerHost[i]}, results[i], results);
+ }
- // Equality Match With Let (uncorrelated)
- // Make sure injected $sequentialDocumentCache (right after unpack bucket stage)
- // in the inner pipeline is removed.
- results = collA.aggregate([
- {
- $lookup: {
- from: collB.getName(),
- let: {"outer_hostname": "$tags.hostname"},
- pipeline: [
- // $match will be pushed before unpack bucket stage
- {$match: {$expr: {$eq: ["$$outer_hostname", hosts[0].tags.hostname]}}},
- ],
- as: "matchedB"
- }
- }, {
- $project: {
- _id: 0,
- host: "$tags.hostname",
- matchedB: {
- $size: "$matchedB"
- }
- }
- },
- {$sort: {host: 1}}
- ]).toArray();
- assert.eq(numHosts, results.length, results);
- for (let i = 0; i < numHosts; i++) {
- const matched = i === 0 ? numDocs : 0;
- assert.eq({host: "host_" + i, matchedB: matched}, results[i], results);
- }
-
- // Equality Match With Let (uncorrelated)
- // Make sure injected $sequentialDocumentCache in the inner pipeline is removed.
- // $sequentialDocumentCache is not located right after unpack bucket stage.
- results = collA.aggregate([
- {
- $lookup: {
- from: collB.getName(),
- let: {"outer_hostname": "$tags.hostname"},
- pipeline: [
- {$match: {$expr: {$eq: ["$$outer_hostname", hosts[0].tags.hostname]}}},
- {$set: {foo: {$const: 123}}}, // uncorrelated
- ],
- as: "matchedB"
- }
- }, {
- $project: {
- _id: 0,
- host: "$tags.hostname",
- matchedB: {
- $size: "$matchedB"
- }
- }
- },
- {$sort: {host: 1}}
- ]).toArray();
- assert.eq(numHosts, results.length, results);
- for (let i = 0; i < numHosts; i++) {
- const matched = i === 0 ? numDocs : 0;
- assert.eq({host: "host_" + i, matchedB: matched}, results[i], results);
- }
-
- // Equality Match With Let (correlated, no $match re-order)
- // Make sure injected $sequentialDocumentCache in the inner pipeline is removed.
- // $sequentialDocumentCache is located at the very end of pipeline.
- results = collA.aggregate([
- {
- $lookup: {
- from: collB.getName(),
- let: {"outer_hostname": "$tags.hostname"},
- pipeline: [
- {$match: {$expr: {$eq: ["$$outer_hostname", hosts[0].tags.hostname]}}},
- {$set: {foo: "$$outer_hostname"}}, // correlated
- ],
- as: "matchedB"
- }
- }, {
- $project: {
- _id: 0,
- host: "$tags.hostname",
- matchedB: {
- $size: "$matchedB"
- }
- }
- },
- {$sort: {host: 1}}
- ]).toArray();
- assert.eq(numHosts, results.length, results);
- for (let i = 0; i < numHosts; i++) {
- const matched = i === 0 ? numDocs : 0;
- assert.eq({host: "host_" + i, matchedB: matched}, results[i], results);
- }
-
- // Unequal joins
- results = collA.aggregate([
+ // Unequal joins
+ results = collA.aggregate([
{
$lookup: {
from: collB.getName(),
@@ -210,28 +114,13 @@ TimeseriesTest.run((insert) => {
},
{$sort: {host: 1}}
]).toArray();
- assert.eq(numHosts, results.length, results);
- let expectedCount = 0;
- for (let i = 0; i < numHosts; i++) {
- expectedCount += entryCountPerHost[i];
- assert.eq(
- {host: "host_" + i, matchedB: expectedCount}, results[i], entryCountPerHost);
- }
-
- // $sequenceDocumentsCache might optimize out $internalUnpackBucket and cause a
- // crash on such query.
- results = collA.aggregate([
- {$lookup: {
- from: collB.getName(),
- as: 'docs',
- let: {},
- pipeline: [
- {$sort: {_id: 1}}
- ]
- }}
- ]).toArray();
- assert.eq(numHosts, results.length, results);
- };
+ assert.eq(numHosts, results.length, results);
+ let expectedCount = 0;
+ for (let i = 0; i < numHosts; i++) {
+ expectedCount += entryCountPerHost[i];
+ assert.eq({host: "host_" + i, matchedB: expectedCount}, results[i], entryCountPerHost);
+ }
+ };
// Exhaust the combinations of non-time-series and time-series collections for inner and outer
// $lookup collections.
@@ -247,91 +136,3 @@ TimeseriesTest.run((insert) => {
});
});
})();
-
-{
- // Test that we get the right results for $lookup on a timeseries collection with an internal
- // pipeline containing both correlated and uncorrelated $match stages. Ensures we do not cache
- // the results of this internal pipeline incorrectly
- // (src/mongo/db/pipeline/document_source_sequential_document_cache.h) regardless of the order
- // of the $match stages.
- const testDB = db.getSiblingDB(jsTestName());
- testDB.local.insertMany([{_id: 0, key: 1}, {_id: 1, key: 2}]);
- testDB.createCollection("foreign", {timeseries: {timeField: "time", metaField: "meta"}});
- testDB.foreign.insertMany([
- {time: new Date(), _id: 0, meta: 1, val1: 42, val2: 100},
- {time: new Date(), _id: 2, meta: 2, val1: 17, val2: 100}
- ]);
-
- // The $sequentialCache (document_source_sequential_document_cache.h) decides whether or not it
- // can cache the results of the internal lookup pipeline based on if the pipeline is correlated
- // or uncorrelated. With timeseries, many rewrites occur which can merge $match stages together,
- // have them pushed into the $_internalUnpackBucket stage as an eventFilter, or push them in
- // front of the
- // $_internalUnpackBucket stage. We need to make sure that the ability of the cache to recognize
- // when there is a correlated $match in the pipeline to remain regardless of these rewrites.
- (function testUncorrelatedFollowedByCorrelatedMatch() {
- const lookupStage = {$lookup: {
- from: "foreign",
- let: {lkey: "$key"},
- pipeline: [
- {$match: {$expr: {$lt: ["$val1","$val2"]}}},
- {$match: {$expr: {$eq: ["$meta","$$lkey"]}}},
- {$project: {lkey: "$$lkey",fkey: "$meta",val: "$val1",_id: 0,}},
- ],
- as: "joined"
- }};
- const result = testDB.local.aggregate(lookupStage);
- assertArrayEq({
- actual: result.toArray(),
- expected: [
- {"_id": 1, "key": 2, "joined": [{"lkey": 2, "fkey": 2, "val": 17}]},
- {"_id": 0, "key": 1, "joined": [{"lkey": 1, "fkey": 1, "val": 42}]}
- ],
- extraErrorMsg: `Unexpected result of running pipeline ${tojson(lookupStage)}`
- });
- })();
-
- (function testCorrelatedFollowedByUncorrelatedMatch() {
- const lookupStage = {$lookup: {
- from: "foreign",
- let: {lkey: "$key"},
- pipeline: [
- {$match: {$expr: {$eq: ["$meta","$$lkey"]}}},
- {$match: {$expr: {$lt: ["$val1","$val2"]}}},
- {$project: {lkey: "$$lkey",fkey: "$meta",val: "$val1",_id: 0,}},
- ],
- as: "joined"
- }};
- const result = testDB.local.aggregate(lookupStage);
- assertArrayEq({
- actual: result.toArray(),
- expected: [
- {"_id": 1, "key": 2, "joined": [{"lkey": 2, "fkey": 2, "val": 17}]},
- {"_id": 0, "key": 1, "joined": [{"lkey": 1, "fkey": 1, "val": 42}]}
- ],
- extraErrorMsg: `Unexpected result of running pipeline ${tojson(lookupStage)}`
- });
- })();
-
- (function testUncorrelatedFollowedByUncorrelatedMatch() {
- const lookupStage = {$lookup: {
- from: "foreign",
- let: {lkey: "$key"},
- pipeline: [
- {$match: {$expr: {$lt: ["$meta","$val1"]}}},
- {$match: {$expr: {$lt: ["$val1","$val2"]}}},
- {$project: {meta: "$meta",val: "$val1",_id: 0,}},
- ],
- as: "joined"
- }};
- const result = testDB.local.aggregate(lookupStage);
- assertArrayEq({
- actual: result.toArray(),
- expected: [
- {"_id": 0, "key": 1, "joined": [{"meta": 1, "val": 42}, {"meta": 2, "val": 17}]},
- {"_id": 1, "key": 2, "joined": [{"meta": 1, "val": 42}, {"meta": 2, "val": 17}]}
- ],
- extraErrorMsg: `Unexpected result of running pipeline ${tojson(lookupStage)}`
- });
- })();
-}
diff --git a/jstests/core/timeseries/timeseries_match_pushdown.js b/jstests/core/timeseries/timeseries_match_pushdown.js
deleted file mode 100644
index 93c841da9ec..00000000000
--- a/jstests/core/timeseries/timeseries_match_pushdown.js
+++ /dev/null
@@ -1,634 +0,0 @@
-/**
- * Tests that the $match stage followed by unpacking stage has been pushed down with correct
- * predicates.
- *
- * @tags: [
- * requires_timeseries,
- * requires_fcv_60,
- * does_not_support_stepdowns,
- * does_not_support_transactions,
- * directly_against_shardsvrs_incompatible,
- * ]
- */
-(function() {
-"use strict";
-
-load("jstests/libs/analyze_plan.js"); // For getAggPlanStages
-
-const coll = db.timeseries_match_pushdown;
-const timeField = 'time';
-const metaField = 'meta';
-const measureField = 'a';
-
-// The docs and queries are designed so that some buckets match the query entirely, some buckets
-// match the query partially, and some with no matches.
-const defaultDocs = [
- {[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'), [metaField]: 2},
- {[timeField]: ISODate('2022-01-01T00:00:07'), [measureField]: null, [metaField]: 2},
- {[timeField]: ISODate('2022-01-01T00:00:08'), [measureField]: [1, 2, 3], [metaField]: 2}
-];
-
-/**
- * Setup the collection and run 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({docsToInsert, pipeline, eventFilter, wholeBucketFilter, expectedDocs}) {
- // Set up the collection. Each test will have it's own collection setup.
- coll.drop();
- assert.commandWorked(db.createCollection(coll.getName(), {timeseries: {timeField, metaField}}));
- // Insert documents into the collection.
- if (!docsToInsert) {
- docsToInsert = defaultDocs;
- }
- assert.commandWorked(coll.insert(docsToInsert));
-
- 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));
- docs.forEach((doc, i) => {
- // Do not need to check document _id, since checking time is already unique.
- delete doc._id;
- assert.docEq(doc, expectedDocs[i], "Incorrect docs: " + tojson(docs));
- });
-};
-
-const aTime = ISODate('2022-01-01T00:00:03');
-const bTime = ISODate('2022-01-01T00:00:07');
-const bMeta = 3;
-const aMeasure = 3;
-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'), [metaField]: 2},
- {[timeField]: ISODate('2022-01-01T00:00:07'), [measureField]: null, [metaField]: 2},
- {[timeField]: ISODate('2022-01-01T00:00:08'), [measureField]: [1, 2, 3], [metaField]: 2},
- ],
-});
-
-// $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},
- ],
-});
-
-// $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'), [metaField]: 2},
- {[timeField]: ISODate('2022-01-01T00:00:07'), [measureField]: null, [metaField]: 2},
- {[timeField]: ISODate('2022-01-01T00:00:08'), [measureField]: [1, 2, 3], [metaField]: 2},
- ],
-});
-
-// $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'), [metaField]: 2},
- {[timeField]: ISODate('2022-01-01T00:00:07'), [measureField]: null, [metaField]: 2},
- {[timeField]: ISODate('2022-01-01T00:00:08'), [measureField]: [1, 2, 3], [metaField]: 2}
- ],
-});
-
-// $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:08'), [measureField]: [1, 2, 3], [metaField]: 2}
- ],
-});
-
-// $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'), [metaField]: 2},
- {[timeField]: ISODate('2022-01-01T00:00:07'), [measureField]: null, [metaField]: 2},
- {[timeField]: ISODate('2022-01-01T00:00:08'), [measureField]: [1, 2, 3], [metaField]: 2}
- ],
-});
-
-// $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},
- {[timeField]: ISODate('2022-01-01T00:00:08'), [measureField]: [1, 2, 3], [metaField]: 2}
- ],
-});
-
-// $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},
- {[timeField]: ISODate('2022-01-01T00:00:08'), [measureField]: [1, 2, 3], [metaField]: 2}
- ],
-});
-
-// $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({
- docsToInsert: [
- ...defaultDocs,
- {[timeField]: ISODate('2022-01-01T00:00:06'), [metaField]: 1},
- {[timeField]: ISODate('2022-01-01T00:00:06'), [measureField]: null, [metaField]: 1}
- ],
- eventFilter: {[measureField]: {$eq: aMeasure}},
- expectedDocs: [
- {[timeField]: ISODate('2022-01-01T00:00:03'), [measureField]: 3, [metaField]: 1},
- {[timeField]: ISODate('2022-01-01T00:00:08'), [measureField]: [1, 2, 3], [metaField]: 2}
- ],
-});
-
-// $and on time
-runTest({
- docsToInsert: [
- {[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},
- ],
- 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({
- docsToInsert: [
- {[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},
- ],
- 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},
- ],
-});
-
-// $match on time and meta
-runTest({
- docsToInsert: [
- {[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},
- ],
- 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 and meta inside $expr. There should not be a wholeBucketFilter, since the entire
-// $and expression cannot be rewritten as a MatchExpression, and for $expr predicates we only
-// generate a wholeBucketFilter for single predicates on the timeField.
-runTest({
- docsToInsert: [
- {[timeField]: ISODate('2022-01-01T00:00:03'), [metaField]: 1, [measureField]: 3},
- {[timeField]: ISODate('2022-01-01T00:00:04'), [metaField]: 1, [measureField]: 1},
- {[timeField]: ISODate('2022-01-01T00:00:05'), [metaField]: 2, [measureField]: 3},
- ],
- pipeline: [{
- $match: {
- $expr: {
- $and:
- [{$eq: [`$${metaField}`, `$${measureField}`]}, {$gt: [`$${timeField}`, aTime]}]
- }
- }
- }],
- eventFilter: {
- $and: [
- {[timeField]: {$_internalExprGt: aTime}},
- {
- $expr: {
- $and: [
- {$eq: [`$${metaField}`, `$${measureField}`]},
- {$gt: [`$${timeField}`, {$const: aTime}]}
- ]
- }
- },
- ]
- },
- expectedDocs: [{[timeField]: ISODate('2022-01-01T00:00:04'), [metaField]: 1, [measureField]: 1}]
-});
-
-// $match on time and meta inside $expr. The entire $and expression can be rewritten into a
-// MatchExpression. However, for $expr predicates we only generate a wholeBucketFilter for single
-// predicates on the timeField.
-runTest({
- docsToInsert: [
- {[timeField]: ISODate('2022-01-01T00:00:03'), [metaField]: 1},
- {[timeField]: ISODate('2022-01-01T00:00:04'), [metaField]: 1},
- {[timeField]: ISODate('2022-01-01T00:00:05'), [metaField]: 2},
- ],
- pipeline:
- [{$match: {$expr: {$and: [{$eq: [`$${metaField}`, 1]}, {$gt: [`$${timeField}`, aTime]}]}}}],
- eventFilter: {
- $and: [
- {[timeField]: {$_internalExprGt: aTime}},
- {
- $expr: {
- $and: [
- {$eq: [`$${metaField}`, {$const: 1}]},
- {$gt: [`$${timeField}`, {$const: aTime}]}
- ]
- }
- },
- {[metaField]: {$_internalExprEq: 1}},
- {[timeField]: {$_internalExprGt: aTime}},
- ]
- },
- expectedDocs: [{[timeField]: ISODate('2022-01-01T00:00:04'), [metaField]: 1}]
-});
-
-// $match on time or meta
-runTest({
- docsToInsert: [
- {[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]: 3},
- {[timeField]: ISODate('2022-01-01T00:00:06'), [measureField]: 6, [metaField]: 4},
- ],
- 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:06'), [measureField]: 6, [metaField]: 4},
- ],
-});
-
-// double $match
-runTest({
- docsToInsert: [
- {[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},
- ],
- 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({
- docsToInsert: [
- {[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},
- ],
- 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: [],
-});
-
-// $and inside $expr with comparison on meta and measurement. There should not be a
-// wholeBucketFilter, since the entire $and expression cannot be rewritten as a MatchExpression, and
-// for $expr predicates we only generate a wholeBucketFilter for single predicates on the timeField.
-runTest({
- docsToInsert: [
- {[timeField]: ISODate('2022-01-01T00:00:00'), [measureField]: 0, [metaField]: 1},
- {[timeField]: ISODate('2022-01-01T00:00:01'), [measureField]: 1, [metaField]: 2},
- {[timeField]: ISODate('2022-01-01T00:00:02'), [measureField]: 2, [metaField]: 2},
- {[timeField]: ISODate('2022-01-01T00:00:03'), [measureField]: 3, [metaField]: 2},
- {[timeField]: ISODate('2022-01-01T00:00:04'), [metaField]: 2},
- {[timeField]: ISODate('2022-01-01T00:00:05'), [measureField]: 5, [metaField]: 3},
- ],
- pipeline: [{
- $match: {
- $expr:
- {$and: [{$lt: [`$${measureField}`, `$${metaField}`]}, {$gt: [`$${metaField}`, 1]}]}
- }
- }],
- eventFilter: {
- $and: [
- {"meta": {$_internalExprGt: 1}},
- {
- $expr: {
- $and: [
- {$lt: [`$${measureField}`, `$${metaField}`]},
- {$gt: [`$${metaField}`, {$const: 1}]}
- ]
- }
- }
- ]
- },
- expectedDocs: [
- {[timeField]: ISODate('2022-01-01T00:00:01'), [measureField]: 1, [metaField]: 2},
- {[timeField]: ISODate('2022-01-01T00:00:04'), [metaField]: 2},
- ]
-});
-
-// Same test as above, but the entire $and expression can be rewritten as a MatchExpression.
-// However, for $expr predicates we only generate a wholeBucketFilter for single predicates on the
-// timeField.
-runTest({
- docsToInsert: [
- {[timeField]: ISODate('2022-01-01T00:00:00'), [measureField]: 0, [metaField]: 1},
- {[timeField]: ISODate('2022-01-01T00:00:01'), [measureField]: 1, [metaField]: 2},
- {[timeField]: ISODate('2022-01-01T00:00:02'), [measureField]: 2, [metaField]: 2},
- {[timeField]: ISODate('2022-01-01T00:00:03'), [measureField]: 3, [metaField]: 2},
- {[timeField]: ISODate('2022-01-01T00:00:04'), [metaField]: 2},
- {[timeField]: ISODate('2022-01-01T00:00:05'), [measureField]: null, [metaField]: 2},
- {[timeField]: ISODate('2022-01-01T00:00:06'), [measureField]: 6, [metaField]: 3},
- ],
- pipeline:
- [{$match: {$expr: {$and: [{$gte: [`$${measureField}`, 2]}, {$lt: [`$${metaField}`, 3]}]}}}],
- eventFilter: {
- $and: [
- {[measureField]: {$_internalExprGte: 2}},
- {
- $expr: {
- $and: [
- {$gte: [`$${measureField}`, {$const: 2}]},
- {$lt: [`$${metaField}`, {$const: 3}]}
- ]
- }
- },
- {[measureField]: {$_internalExprGte: 2}},
- {[metaField]: {$_internalExprLt: 3}},
- ]
- },
- expectedDocs: [
- {[timeField]: ISODate('2022-01-01T00:00:02'), [measureField]: 2, [metaField]: 2},
- {[timeField]: ISODate('2022-01-01T00:00:03'), [measureField]: 3, [metaField]: 2},
- ]
-});
-
-// Same test as above but with $or and different comparison operators.
-runTest({
- docsToInsert: [
- {[timeField]: ISODate('2022-01-01T00:00:00'), [measureField]: 0, [metaField]: 1},
- {[timeField]: ISODate('2022-01-01T00:00:01'), [measureField]: 1, [metaField]: 2},
- {[timeField]: ISODate('2022-01-01T00:00:02'), [measureField]: 2, [metaField]: 2},
- {[timeField]: ISODate('2022-01-01T00:00:03'), [measureField]: 3, [metaField]: 2},
- {[timeField]: ISODate('2022-01-01T00:00:04'), [metaField]: 2},
- {[timeField]: ISODate('2022-01-01T00:00:05'), [measureField]: 5, [metaField]: 3},
- ],
- pipeline: [{
- $match: {
- $expr:
- {$or: [{$lt: [`$${measureField}`, `$${metaField}`]}, {$lte: [`$${metaField}`, 2]}]}
- }
- }],
- eventFilter: {
- $expr: {
- $or: [
- {$lt: [`$${measureField}`, `$${metaField}`]},
- {$lte: [`$${metaField}`, {$const: 2}]}
- ]
- }
- },
- expectedDocs: [
- {[timeField]: ISODate('2022-01-01T00:00:00'), [measureField]: 0, [metaField]: 1},
- {[timeField]: ISODate('2022-01-01T00:00:01'), [measureField]: 1, [metaField]: 2},
- {[timeField]: ISODate('2022-01-01T00:00:02'), [measureField]: 2, [metaField]: 2},
- {[timeField]: ISODate('2022-01-01T00:00:03'), [measureField]: 3, [metaField]: 2},
- {[timeField]: ISODate('2022-01-01T00:00:04'), [metaField]: 2},
- ]
-});
-})();
diff --git a/jstests/core/timeseries/timeseries_match_pushdown_with_project.js b/jstests/core/timeseries/timeseries_match_pushdown_with_project.js
deleted file mode 100644
index 2d8e872c33a..00000000000
--- a/jstests/core/timeseries/timeseries_match_pushdown_with_project.js
+++ /dev/null
@@ -1,131 +0,0 @@
-/**
- * Tests that the unpacking stage has correct unpacking behaviour when $match is pushed into it.
- *
- * @tags: [
- * requires_timeseries,
- * requires_fcv_60,
- * does_not_support_stepdowns,
- * does_not_support_transactions,
- * directly_against_shardsvrs_incompatible,
- * ]
- */
-(function() {
-"use strict";
-
-load("jstests/libs/analyze_plan.js"); // For getAggPlanStages
-
-const coll = db.timeseries_match_pushdown_with_project;
-coll.drop();
-
-const timeField = 'time';
-const metaField = 'meta';
-assert.commandWorked(db.createCollection(coll.getName(), {timeseries: {timeField, metaField}}));
-
-const aTime = ISODate('2022-01-01T00:00:00');
-assert.commandWorked(coll.insert([
- {[timeField]: aTime, a: 1, b: 1, _id: 1},
- {[timeField]: aTime, a: 2, b: 2, _id: 2},
- {[timeField]: aTime, a: 3, b: 3, _id: 3},
- {[timeField]: aTime, a: 4, b: 4, _id: 4},
- {[timeField]: aTime, a: 5, b: 5, _id: 5},
- {[timeField]: aTime, a: 6, b: 6, _id: 6},
- {[timeField]: aTime, a: 7, b: 7, _id: 7},
- {[timeField]: aTime, a: 8, b: 8, _id: 8},
- {[timeField]: aTime, a: 9, b: 9, _id: 9},
-]));
-
-/**
- * Runs a 'pipeline', asserts the bucket unpacking 'behaviour' (either include or exclude) is
- * expected.
- */
-const runTest = function({pipeline, behaviour, expectedDocs}) {
- 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;
- if (behaviour.include) {
- assert(unpackStage.include,
- "Unpacking stage must have 'include' behaviour: " + tojson(explain));
- assert.sameMembers(behaviour.include, unpackStage.include);
- }
- if (behaviour.exclude) {
- assert(unpackStage.exclude,
- "Unpacking stage must have 'exclude' behaviour: " + tojson(explain));
- assert.sameMembers(behaviour.exclude, unpackStage.exclude);
- }
-
- const docs = coll.aggregate([...pipeline, {$sort: {a: 1, b: 1, _id: 1}}]).toArray();
- assert.eq(docs.length, expectedDocs.length, "Incorrect docs: " + tojson(docs));
- docs.forEach((doc, i) => {
- assert.docEq(doc, expectedDocs[i], "Incorrect docs: " + tojson(docs));
- });
-};
-
-runTest({
- pipeline: [{$match: {a: {$gt: 5}}}, {$project: {b: 1}}],
- behaviour: {include: ['_id', 'a', 'b']},
- expectedDocs: [
- {b: 6, _id: 6},
- {b: 7, _id: 7},
- {b: 8, _id: 8},
- {b: 9, _id: 9},
- ],
-});
-
-runTest({
- pipeline: [{$match: {a: {$gt: 5}}}, {$project: {_id: 0, b: 1}}],
- behaviour: {include: ['a', 'b']},
- expectedDocs: [
- {b: 6},
- {b: 7},
- {b: 8},
- {b: 9},
- ],
-});
-
-runTest({
- pipeline: [{$match: {a: {$gt: 5}}}, {$project: {a: 1}}],
- behaviour: {include: ['_id', 'a']},
- expectedDocs: [
- {a: 6, _id: 6},
- {a: 7, _id: 7},
- {a: 8, _id: 8},
- {a: 9, _id: 9},
- ],
-});
-
-runTest({
- pipeline: [{$match: {a: {$gt: 5}}}, {$project: {_id: 0, a: 1}}],
- behaviour: {include: ['a']},
- expectedDocs: [
- {a: 6},
- {a: 7},
- {a: 8},
- {a: 9},
- ],
-});
-
-runTest({
- pipeline: [{$match: {a: {$gt: 5}}}, {$project: {a: 0}}],
- behaviour: {exclude: []},
- expectedDocs: [
- {[timeField]: aTime, b: 6, _id: 6},
- {[timeField]: aTime, b: 7, _id: 7},
- {[timeField]: aTime, b: 8, _id: 8},
- {[timeField]: aTime, b: 9, _id: 9},
- ],
-});
-
-runTest({
- pipeline: [{$match: {a: {$gt: 5}}}, {$project: {b: 0}}],
- behaviour: {exclude: []},
- expectedDocs: [
- {[timeField]: aTime, a: 6, _id: 6},
- {[timeField]: aTime, a: 7, _id: 7},
- {[timeField]: aTime, a: 8, _id: 8},
- {[timeField]: aTime, a: 9, _id: 9},
- ],
-});
-})();
diff --git a/jstests/core/timeseries/timeseries_merge.js b/jstests/core/timeseries/timeseries_merge.js
index fb6a23df803..59d4dbee889 100644
--- a/jstests/core/timeseries/timeseries_merge.js
+++ b/jstests/core/timeseries/timeseries_merge.js
@@ -6,7 +6,6 @@
* does_not_support_stepdowns,
* does_not_support_transactions,
* requires_timeseries,
- * references_foreign_collection,
* ]
*/
(function() {
diff --git a/jstests/core/timeseries/timeseries_metric_index_2dsphere.js b/jstests/core/timeseries/timeseries_metric_index_2dsphere.js
index 7096ace9513..e47119b5bbd 100644
--- a/jstests/core/timeseries/timeseries_metric_index_2dsphere.js
+++ b/jstests/core/timeseries/timeseries_metric_index_2dsphere.js
@@ -18,9 +18,8 @@
load("jstests/core/timeseries/libs/timeseries.js");
load("jstests/libs/analyze_plan.js");
-load("jstests/libs/feature_flag_util.js");
-if (!FeatureFlagUtil.isEnabled(db, "TimeseriesMetricIndexes")) {
+if (!TimeseriesTest.timeseriesMetricIndexesEnabled(db.getMongo())) {
return;
}
diff --git a/jstests/core/timeseries/timeseries_metric_index_ascending_descending.js b/jstests/core/timeseries/timeseries_metric_index_ascending_descending.js
index 8a827b9d07b..bf1dc10e625 100644
--- a/jstests/core/timeseries/timeseries_metric_index_ascending_descending.js
+++ b/jstests/core/timeseries/timeseries_metric_index_ascending_descending.js
@@ -14,10 +14,9 @@
"use strict";
load("jstests/core/timeseries/libs/timeseries.js");
-load("jstests/libs/feature_flag_util.js");
load("jstests/libs/fixture_helpers.js");
-if (!FeatureFlagUtil.isEnabled(db, "TimeseriesMetricIndexes")) {
+if (!TimeseriesTest.timeseriesMetricIndexesEnabled(db.getMongo())) {
jsTestLog(
"Skipped test as the featureFlagTimeseriesMetricIndexes feature flag is not enabled.");
return;
diff --git a/jstests/core/timeseries/timeseries_metric_index_compound.js b/jstests/core/timeseries/timeseries_metric_index_compound.js
index c81f9a4c53c..2ecc4732b70 100644
--- a/jstests/core/timeseries/timeseries_metric_index_compound.js
+++ b/jstests/core/timeseries/timeseries_metric_index_compound.js
@@ -13,9 +13,8 @@
"use strict";
load("jstests/core/timeseries/libs/timeseries.js");
-load("jstests/libs/feature_flag_util.js");
-if (!FeatureFlagUtil.isEnabled(db, "TimeseriesMetricIndexes")) {
+if (!TimeseriesTest.timeseriesMetricIndexesEnabled(db.getMongo())) {
jsTestLog(
"Skipped test as the featureFlagTimeseriesMetricIndexes feature flag is not enabled.");
return;
diff --git a/jstests/core/timeseries/timeseries_predicates.js b/jstests/core/timeseries/timeseries_predicates.js
index ca78464cb31..a273cc8b128 100644
--- a/jstests/core/timeseries/timeseries_predicates.js
+++ b/jstests/core/timeseries/timeseries_predicates.js
@@ -17,7 +17,7 @@ const tsColl = db.timeseries_predicates_timeseries;
coll.drop();
tsColl.drop();
assert.commandWorked(
- db.createCollection(tsColl.getName(), {timeseries: {timeField: 'time', metaField: 'mt'}}));
+ db.createCollection(tsColl.getName(), {timeseries: {timeField: 'time', metaField: 'meta'}}));
const bucketsColl = db.getCollection('system.buckets.' + tsColl.getName());
// Test that 'predicate' behaves correctly on the example documents,
@@ -86,18 +86,18 @@ checkAllBucketings({x: {$exists: true}}, [
// Test $or...
{
- // ... on metric + mt.
+ // ... on metric + meta.
checkAllBucketings({
$or: [
{x: {$lt: 0}},
- {'mt.y': {$gt: 0}},
+ {'meta.y': {$gt: 0}},
]
},
[
- {x: +1, mt: {y: -1}},
- {x: +1, mt: {y: +1}},
- {x: -1, mt: {y: -1}},
- {x: -1, mt: {y: +1}},
+ {x: +1, meta: {y: -1}},
+ {x: +1, meta: {y: +1}},
+ {x: -1, meta: {y: -1}},
+ {x: -1, meta: {y: +1}},
]);
// ... when one argument can't be pushed down.
@@ -131,18 +131,18 @@ checkAllBucketings({x: {$exists: true}}, [
// Test $and...
{
- // ... on metric + mt.
+ // ... on metric + meta.
checkAllBucketings({
$and: [
{x: {$lt: 0}},
- {'mt.y': {$gt: 0}},
+ {'meta.y': {$gt: 0}},
]
},
[
- {x: +1, mt: {y: -1}},
- {x: +1, mt: {y: +1}},
- {x: -1, mt: {y: -1}},
- {x: -1, mt: {y: +1}},
+ {x: +1, meta: {y: -1}},
+ {x: +1, meta: {y: +1}},
+ {x: -1, meta: {y: -1}},
+ {x: -1, meta: {y: +1}},
]);
// ... when one argument can't be pushed down.
@@ -180,28 +180,28 @@ checkAllBucketings({
$or: [
{
$and: [
- {'mt.a': {$gt: 0}},
+ {'meta.a': {$gt: 0}},
{'x': {$lt: 0}},
]
},
{
$and: [
- {'mt.b': {$gte: 0}},
+ {'meta.b': {$gte: 0}},
{time: {$gt: ISODate('2020-01-01')}},
]
},
]
},
[
- {mt: {a: -1, b: -1}, x: -1, time: ISODate('2020-02-01')},
- {mt: {a: -1, b: -1}, x: -1, time: ISODate('2019-12-31')},
- {mt: {a: -1, b: -1}, x: +1, time: ISODate('2020-02-01')},
- {mt: {a: -1, b: -1}, x: +1, time: ISODate('2019-12-31')},
+ {meta: {a: -1, b: -1}, x: -1, time: ISODate('2020-02-01')},
+ {meta: {a: -1, b: -1}, x: -1, time: ISODate('2019-12-31')},
+ {meta: {a: -1, b: -1}, x: +1, time: ISODate('2020-02-01')},
+ {meta: {a: -1, b: -1}, x: +1, time: ISODate('2019-12-31')},
- {mt: {a: +1, b: -1}, x: -1, time: ISODate('2020-02-01')},
- {mt: {a: +1, b: -1}, x: -1, time: ISODate('2019-12-31')},
- {mt: {a: +1, b: -1}, x: +1, time: ISODate('2020-02-01')},
- {mt: {a: +1, b: -1}, x: +1, time: ISODate('2019-12-31')},
+ {meta: {a: +1, b: -1}, x: -1, time: ISODate('2020-02-01')},
+ {meta: {a: +1, b: -1}, x: -1, time: ISODate('2019-12-31')},
+ {meta: {a: +1, b: -1}, x: +1, time: ISODate('2020-02-01')},
+ {meta: {a: +1, b: -1}, x: +1, time: ISODate('2019-12-31')},
]);
// Test nested $and / $or where some leaf predicates cannot be pushed down.
@@ -209,72 +209,72 @@ checkAllBucketings({
$or: [
{
$and: [
- {'mt.a': {$gt: 0}},
+ {'meta.a': {$gt: 0}},
{'x': {$exists: false}},
]
},
{
$and: [
- {'mt.b': {$gte: 0}},
+ {'meta.b': {$gte: 0}},
{time: {$gt: ISODate('2020-01-01')}},
]
},
]
},
[
- {mt: {a: -1, b: -1}, time: ISODate('2020-02-01')},
- {mt: {a: -1, b: -1}, time: ISODate('2019-12-31')},
- {mt: {a: -1, b: -1}, x: 'asdf', time: ISODate('2020-02-01')},
- {mt: {a: -1, b: -1}, x: 'asdf', time: ISODate('2019-12-31')},
+ {meta: {a: -1, b: -1}, time: ISODate('2020-02-01')},
+ {meta: {a: -1, b: -1}, time: ISODate('2019-12-31')},
+ {meta: {a: -1, b: -1}, x: 'asdf', time: ISODate('2020-02-01')},
+ {meta: {a: -1, b: -1}, x: 'asdf', time: ISODate('2019-12-31')},
- {mt: {a: +1, b: -1}, time: ISODate('2020-02-01')},
- {mt: {a: +1, b: -1}, time: ISODate('2019-12-31')},
- {mt: {a: +1, b: -1}, x: 'asdf', time: ISODate('2020-02-01')},
- {mt: {a: +1, b: -1}, x: 'asdf', time: ISODate('2019-12-31')},
+ {meta: {a: +1, b: -1}, time: ISODate('2020-02-01')},
+ {meta: {a: +1, b: -1}, time: ISODate('2019-12-31')},
+ {meta: {a: +1, b: -1}, x: 'asdf', time: ISODate('2020-02-01')},
+ {meta: {a: +1, b: -1}, x: 'asdf', time: ISODate('2019-12-31')},
]);
-// Test $exists on mt, inside $or.
+// Test $exists on meta, inside $or.
checkAllBucketings({
$or: [
- {"mt.a": {$exists: true}},
+ {"meta.a": {$exists: true}},
{"x": {$gt: 2}},
]
},
[
- {mt: {a: 1}, x: 1},
- {mt: {a: 2}, x: 2},
- {mt: {a: 3}, x: 3},
- {mt: {a: 4}, x: 4},
- {mt: {}, x: 1},
- {mt: {}, x: 2},
- {mt: {}, x: 3},
- {mt: {}, x: 4},
+ {meta: {a: 1}, x: 1},
+ {meta: {a: 2}, x: 2},
+ {meta: {a: 3}, x: 3},
+ {meta: {a: 4}, x: 4},
+ {meta: {}, x: 1},
+ {meta: {}, x: 2},
+ {meta: {}, x: 3},
+ {meta: {}, x: 4},
]);
-// Test $in on mt, inside $or.
+// Test $in on meta, inside $or.
checkAllBucketings({
$or: [
- {"mt.a": {$in: [1, 3]}},
+ {"meta.a": {$in: [1, 3]}},
{"x": {$gt: 2}},
]
},
[
- {mt: {a: 1}, x: 1},
- {mt: {a: 2}, x: 2},
- {mt: {a: 3}, x: 3},
- {mt: {a: 4}, x: 4},
- {mt: {}, x: 1},
- {mt: {}, x: 2},
- {mt: {}, x: 3},
- {mt: {}, x: 4},
+ {meta: {a: 1}, x: 1},
+ {meta: {a: 2}, x: 2},
+ {meta: {a: 3}, x: 3},
+ {meta: {a: 4}, x: 4},
+ {meta: {}, x: 1},
+ {meta: {}, x: 2},
+ {meta: {}, x: 3},
+ {meta: {}, x: 4},
]);
-// Test geo predicates on mt, inside $or.
+// Test geo predicates on meta, inside $or.
for (const pred of ['$geoWithin', '$geoIntersects']) {
checkAllBucketings({
$or: [
{
- "mt.location": {
+ "meta.location": {
[pred]: {
$geometry: {
type: "Polygon",
@@ -293,90 +293,66 @@ for (const pred of ['$geoWithin', '$geoIntersects']) {
]
},
[
- {mt: {location: [1, 1]}, x: 1},
- {mt: {location: [1, 1]}, x: 2},
- {mt: {location: [1, 1]}, x: 3},
- {mt: {location: [1, 1]}, x: 4},
- {mt: {location: [5, 5]}, x: 1},
- {mt: {location: [5, 5]}, x: 2},
- {mt: {location: [5, 5]}, x: 3},
- {mt: {location: [5, 5]}, x: 4},
+ {meta: {location: [1, 1]}, x: 1},
+ {meta: {location: [1, 1]}, x: 2},
+ {meta: {location: [1, 1]}, x: 3},
+ {meta: {location: [1, 1]}, x: 4},
+ {meta: {location: [5, 5]}, x: 1},
+ {meta: {location: [5, 5]}, x: 2},
+ {meta: {location: [5, 5]}, x: 3},
+ {meta: {location: [5, 5]}, x: 4},
]);
}
-// Test $mod on mt, inside $or.
+// Test $mod on meta, inside $or.
// $mod is an example of a predicate that we don't handle specially in time-series optimizations:
// it can be pushed down if and only if it's on a metadata field.
checkAllBucketings({
$or: [
- {"mt.a": {$mod: [2, 0]}},
+ {"meta.a": {$mod: [2, 0]}},
{"x": {$gt: 4}},
]
},
[
- {mt: {a: 1}, x: 1},
- {mt: {a: 2}, x: 2},
- {mt: {a: 3}, x: 3},
- {mt: {a: 4}, x: 4},
- {mt: {a: 5}, x: 5},
- {mt: {a: 6}, x: 6},
- {mt: {a: 7}, x: 7},
- {mt: {a: 8}, x: 8},
+ {meta: {a: 1}, x: 1},
+ {meta: {a: 2}, x: 2},
+ {meta: {a: 3}, x: 3},
+ {meta: {a: 4}, x: 4},
+ {meta: {a: 5}, x: 5},
+ {meta: {a: 6}, x: 6},
+ {meta: {a: 7}, x: 7},
+ {meta: {a: 8}, x: 8},
]);
-// Test $elemMatch on mt, inside $or.
+// Test $elemMatch on meta, inside $or.
checkAllBucketings({
$or: [
- {"mt.a": {$elemMatch: {b: 3}}},
+ {"meta.a": {$elemMatch: {b: 3}}},
{"x": {$gt: 4}},
]
},
[
- {x: 1, mt: {a: []}},
- {x: 2, mt: {a: [{b: 2}]}},
- {x: 3, mt: {a: [{b: 3}]}},
- {x: 4, mt: {a: [{b: 2}, {b: 3}]}},
- {x: 5, mt: {a: []}},
- {x: 6, mt: {a: [{b: 2}]}},
- {x: 7, mt: {a: [{b: 3}]}},
- {x: 8, mt: {a: [{b: 2}, {b: 3}]}},
+ {x: 1, meta: {a: []}},
+ {x: 2, meta: {a: [{b: 2}]}},
+ {x: 3, meta: {a: [{b: 3}]}},
+ {x: 4, meta: {a: [{b: 2}, {b: 3}]}},
+ {x: 5, meta: {a: []}},
+ {x: 6, meta: {a: [{b: 2}]}},
+ {x: 7, meta: {a: [{b: 3}]}},
+ {x: 8, meta: {a: [{b: 2}, {b: 3}]}},
]);
checkAllBucketings({
$or: [
- {"mt.a": {$elemMatch: {b: 2, c: 3}}},
+ {"meta.a": {$elemMatch: {b: 2, c: 3}}},
{"x": {$gt: 3}},
]
},
[
- {x: 1, mt: {a: []}},
- {x: 2, mt: {a: [{b: 2, c: 3}]}},
- {x: 3, mt: {a: [{b: 2}, {c: 3}]}},
- {x: 4, mt: {a: []}},
- {x: 5, mt: {a: [{b: 2, c: 3}]}},
- {x: 6, mt: {a: [{b: 2}, {c: 3}]}},
+ {x: 1, meta: {a: []}},
+ {x: 2, meta: {a: [{b: 2, c: 3}]}},
+ {x: 3, meta: {a: [{b: 2}, {c: 3}]}},
+ {x: 4, meta: {a: []}},
+ {x: 5, meta: {a: [{b: 2, c: 3}]}},
+ {x: 6, meta: {a: [{b: 2}, {c: 3}]}},
]);
-
-// Test a standalone $elemMatch on mt.
-checkAllBucketings({"mt.a": {$elemMatch: {b: 3}}}, [
- {mt: {a: []}},
- {mt: {a: [{b: 2}]}},
- {mt: {a: [{b: 3}]}},
- {mt: {a: [{b: 2}, {b: 3}]}},
- {mt: {a: []}},
- {mt: {a: [{b: 2}]}},
- {mt: {a: [{b: 3}]}},
- {mt: {a: [{b: 2}, {b: 3}]}},
-]);
-
-// Test a standalone $size on mt.
-checkAllBucketings({"mt.a": {$size: 1}}, [
- {mt: {a: []}},
- {mt: {a: [{b: 2}]}},
- {mt: {a: [{b: 3}]}},
- {mt: {a: [{b: 2}, {b: 3}]}},
- {mt: {a: []}},
- {mt: {a: [{b: 2}]}},
- {mt: {a: [{b: 3}]}},
- {mt: {a: [{b: 2}, {b: 3}]}},
-]);
})();
diff --git a/jstests/core/timeseries/timeseries_project.js b/jstests/core/timeseries/timeseries_project.js
index b735a8a3b66..9375f06240d 100644
--- a/jstests/core/timeseries/timeseries_project.js
+++ b/jstests/core/timeseries/timeseries_project.js
@@ -4,7 +4,7 @@
* @tags: [
* does_not_support_stepdowns,
* does_not_support_transactions,
- * requires_fcv_60,
+ * requires_fcv_53,
* ]
*/
(function() {
@@ -97,7 +97,6 @@ const doc = {
time: new Date("2019-10-11T14:39:18.670Z"),
x: 5,
a: 3,
- obj: {a: 3},
};
assert.commandWorked(tsColl.insert(doc));
assert.commandWorked(regColl.insert(doc));
@@ -108,192 +107,10 @@ let tsDoc = tsColl.aggregate(pipeline).toArray();
let regDoc = regColl.aggregate(pipeline).toArray();
assert.docEq(tsDoc, regDoc);
-pipeline = [{$project: {_id: 0, obj: "$x", b: {$add: ["$obj.a", 1]}}}];
-tsDoc = tsColl.aggregate(pipeline).toArray();
-regDoc = regColl.aggregate(pipeline).toArray();
-assert.docEq(tsDoc, regDoc);
-
// Test $addFields.
pipeline = [{$addFields: {a: "$x", b: "$a"}}, {$project: {_id: 0}}];
tsDoc = tsColl.aggregate(pipeline).toArray();
regDoc = regColl.aggregate(pipeline).toArray();
assert.docEq(tsDoc, regDoc);
-
-pipeline = [{$addFields: {obj: "$x", b: {$add: ["$obj.a", 1]}}}, {$project: {_id: 0}}];
-tsDoc = tsColl.aggregate(pipeline).toArray();
-regDoc = regColl.aggregate(pipeline).toArray();
-assert.docEq(tsDoc, regDoc);
-
-pipeline = [{$project: {a: 1, _id: 0}}, {$project: {newMeta: "$x"}}];
-tsDoc = tsColl.aggregate(pipeline).toArray();
-regDoc = regColl.aggregate(pipeline).toArray();
-assert.docEq(tsDoc, regDoc);
-})();
-
-// Test that an includes projection that doesn't include previously included computed meta fields
-// outputs correctly.
-(function testIncludeComputedMetaFieldThenProjectWithoutComputedMetaField() {
- coll.drop();
- assert.commandWorked(
- db.createCollection(coll.getName(), {timeseries: {timeField: 'time', metaField: 'tag'}}));
-
- const doc = {_id: 0, time: ISODate("2024-01-01T00:00:00.000Z"), tag: {field: "A"}};
- assert.commandWorked(coll.insert(doc));
- let result = {};
-
- // Added field which uses meta should not be in the result.
- result =
- coll.aggregate(
- [{$addFields: {time: {$dateFromParts: {year: "$tag.none"}}}}, {$project: {tag: 1}}])
- .toArray();
- assert.docEq([{"tag": {"field": "A"}, "_id": 0}], result);
-
- // Test with non-null value.
- result = coll.aggregate([{$addFields: {time: {$toLower: "$tag.field"}}}, {$project: {tag: 1}}])
- .toArray();
- assert.docEq([{"tag": {"field": "A"}, "_id": 0}], result);
-
- // Testing with $match between $addFields and $project.
- result = coll.aggregate([
- {$addFields: {time: {$dateFromParts: {year: "$tag.none"}}}},
- {$match: {tag: {field: "A"}}},
- {$project: {tag: 1}}
- ])
- .toArray();
- assert.docEq([{"tag": {field: "A"}, "_id": 0}], result);
-
- // Test with non-null value.
- result =
- coll.aggregate(
- [{$addFields: {time: "$tag.field"}}, {$match: {time: "A"}}, {$project: {tag: 1}}])
- .toArray();
- assert.docEq([{"tag": {field: "A"}, "_id": 0}], result);
-
- // Testing with $project after $project.
- result = coll.aggregate([{$project: {tag: 1}}, {$project: {time: 1}}]).toArray();
- assert.docEq([{"_id": 0}], result);
-
- // Testing with $set.
- result = coll.aggregate([{$set: {tag: "$tag.field"}}, {$project: {time: 1}}]).toArray();
- assert.docEq([{"_id": 0, "time": ISODate("2024-01-01T00:00:00Z")}], result);
-
- // Test that computedMetaFields that are included by the $project are still included.
- result = coll.aggregate([
- {$addFields: {hi: {$dateFromParts: {year: "$tag.none"}}}},
- {$addFields: {bye: {$dateFromParts: {year: "$tag.none"}}}},
- {$project: {hi: 1}}
- ])
- .toArray();
- assert.docEq([{"_id": 0, "hi": null}], result);
-
- // Test with non-null value.
- result = coll.aggregate([
- {$addFields: {hi: "$tag.field"}},
- {$addFields: {bye: "$tag.field"}},
- {$project: {hi: 1}}
- ])
- .toArray();
- assert.docEq([{"_id": 0, "hi": "A"}], result);
-
- // Test that $project that replaces field that is used works.
- result = coll.aggregate([
- {$addFields: {hello: "$tag.field"}},
- {$project: {newTime: "$time", time: "$tag.field"}}
- ])
- .toArray();
-
- assert.docEq([{"_id": 0, newTime: ISODate("2024-01-01T00:00:00.000Z"), time: "A"}], result);
-
- // Test adding fields and including one of them.
- result = coll.aggregate([
- {$addFields: {a: {$toLower: "$tag.field"}}},
- {$addFields: {b: {$toUpper: "$tag.field"}}},
- {$project: {b: 1}}
- ])
- .toArray();
- assert.docEq([{b: "A", "_id": 0}], result);
-})();
-
-// Test that an excludes projection that excludes previously included computed meta fields
-// outputs correctly.
-(function testIncludeComputedMetaFieldThenExcludeComputedMetaField() {
- coll.drop();
- assert.commandWorked(
- db.createCollection(coll.getName(), {timeseries: {timeField: 'time', metaField: 'tag'}}));
-
- const doc = {_id: 0, time: ISODate("2024-01-01T00:00:00.000Z"), tag: {field: "A"}};
- assert.commandWorked(coll.insert(doc));
- let result = {};
-
- // Excluded '_computedMetaProjField' should not be included.
- result = coll.aggregate([
- {$addFields: {hello: {$dateFromParts: {year: "$tag.none"}}}},
- {$project: {hello: 0}}
- ])
- .toArray();
- assert.docEq([{"time": ISODate("2024-01-01T00:00:00Z"), "tag": {"field": "A"}, "_id": 0}],
- result);
-
- // Test with non-null value.
- result = coll.aggregate([{$addFields: {time: {$toLower: "$tag.field"}}}, {$project: {time: 0}}])
- .toArray();
- assert.docEq([{"tag": {"field": "A"}, "_id": 0}], result);
-
- // Testing with $match between $addFields and $project.
- result = coll.aggregate([
- {$addFields: {time: {$dateFromParts: {year: "$tag.none"}}}},
- {$match: {tag: {field: "A"}}},
- {$project: {time: 0}}
- ])
- .toArray();
- assert.docEq([{"tag": {field: "A"}, "_id": 0}], result);
-
- // Test with non-null value.
- result =
- coll.aggregate(
- [{$addFields: {time: "$tag.field"}}, {$match: {time: "A"}}, {$project: {time: 0}}])
- .toArray();
- assert.docEq([{"tag": {field: "A"}, "_id": 0}], result);
-
- // Testing with exclude $project after include $project.
- result = coll.aggregate([{$project: {tag: 1}}, {$project: {tag: 0}}]).toArray();
- assert.docEq([{"_id": 0}], result);
-
- // Testing with $set.
- result = coll.aggregate([{$set: {tag: "$tag.field"}}, {$project: {tag: 0}}]).toArray();
- assert.docEq([{"time": ISODate("2024-01-01T00:00:00Z"), "_id": 0}], result);
-
- // Exclude one added field.
- result = coll.aggregate([
- {$addFields: {hi: {$dateFromParts: {year: "$tag.none"}}}},
- {$addFields: {bye: {$dateFromParts: {year: "$tag.none"}}}},
- {$project: {hi: 0}}
- ])
- .toArray();
- assert.docEq(
- [{"time": ISODate("2024-01-01T00:00:00Z"), "tag": {"field": "A"}, "_id": 0, "bye": null}],
- result);
-
- // Test with non-null value.
- result = coll.aggregate([
- {$addFields: {hi: "$tag.field"}},
- {$addFields: {bye: "$tag.field"}},
- {$project: {hi: 0}}
- ])
- .toArray();
- assert.docEq(
- [{"time": ISODate("2024-01-01T00:00:00Z"), "tag": {"field": "A"}, "_id": 0, "bye": "A"}],
- result);
-
- // Test adding fields and excluding one of them.
- result = coll.aggregate([
- {$addFields: {a: {$toLower: "$tag.field"}}},
- {$addFields: {b: {$toUpper: "$tag.field"}}},
- {$project: {b: 0}}
- ])
- .toArray();
- assert.docEq(
- [{"time": ISODate("2024-01-01T00:00:00Z"), "tag": {"field": "A"}, "_id": 0, "a": "a"}],
- result);
})();
})();
diff --git a/jstests/core/timeseries/timeseries_project_pushdown.js b/jstests/core/timeseries/timeseries_project_pushdown.js
deleted file mode 100644
index 91b009aa00d..00000000000
--- a/jstests/core/timeseries/timeseries_project_pushdown.js
+++ /dev/null
@@ -1,196 +0,0 @@
-/**
- * Test the behavior of $project with pipelines that require the whole document. Specifically, we
- * are targeting that rewrites for $project with $getField, or '$$ROOT'/'$$CURRENT' should only
- * occur in certain situations.
- *
- * @tags: [
- * requires_timeseries,
- * does_not_support_stepdowns,
- * does_not_support_transactions,
- * ]
- */
-(function() {
-"use strict";
-
-const timeField = "t";
-const metaField = "meta1";
-const coll = db.timeseries_project_pushdown;
-
-function runTest({docs, pipeline, expectedResults}) {
- coll.drop();
- assert.commandWorked(db.createCollection(
- coll.getName(), {timeseries: {timeField: timeField, metaField: metaField}}));
- assert.commandWorked(coll.insertMany(docs));
- const results = coll.aggregate(pipeline).toArray();
- assert.sameMembers(expectedResults, results, () => {
- return `Pipeline: ${tojson(pipeline)}. Explain: ${
- tojson(coll.explain().aggregate(pipeline))}`;
- });
-}
-
-//
-// The following tests confirm the behavior of queries in the form: {$getField: "fieldName"}.
-//
-(function testMeta_OnlyStringField() {
- runTest({
- docs: [{_id: 1, [timeField]: new Date(), [metaField]: 2}],
- pipeline: [{$project: {new: {$getField: metaField}, _id: 0}}],
- expectedResults: [{new: 2}]
- });
-})();
-
-// $getField does not traverse objects, and should not be rewritten when it relies on a mix of
-// metaField and measurement fields. Let's validate that behavior.
-(function testDottedPath_OnlyStringField() {
- runTest({
- docs: [
- {_id: 1, [timeField]: new Date(), [metaField]: 2, "a.b": 3, a: {b: 2}},
- {_id: 2, [timeField]: new Date(), [metaField]: 2, a: {b: 2}} // missing field.
- ],
- pipeline: [{$project: {new: {$add: [`$${metaField}`, {$getField: "a.b"}]}}}],
- expectedResults: [{_id: 1, new: 5}, {_id: 2, new: null}]
- });
-})();
-
-//
-// The following tests confirm the behavior of queries in the form: {$getField: {$literal:
-// "fieldName"}}.
-//
-(function testDottedPath_LiteralExpr() {
- runTest({
- docs: [
- {_id: 1, [timeField]: new Date(), [metaField]: 2, "a.$b": 3, a: {b: 4}},
- {_id: 2, [timeField]: new Date(), [metaField]: 2, a: {b: 2}} // missing field.
- ],
- pipeline: [{$project: {new: {$add: [`$${metaField}`, {$getField: {$literal: "a.$b"}}]}}}],
- expectedResults: [{_id: 1, new: 5}, {_id: 2, new: null}]
- });
-}());
-
-// There is a difference between the metaField "meta1", and "$meta1". Field paths are allowed to
-// have a '$' and are accessed by $getField. We need to validate we return the correct results when
-// we have both the metaField and "$meta1".
-(function testDollarMeta_LiteralExpr() {
- runTest({
- docs: [
- {_id: 1, [timeField]: new Date(), [metaField]: 2},
- {_id: 2, [timeField]: new Date(), [metaField]: 2, "$meta1": 3} // missing field.
- ],
- pipeline: [{$project: {new: {$add: [`$${metaField}`, {$getField: {$literal: "$meta1"}}]}}}],
- expectedResults: [{_id: 1, new: null}, {_id: 2, new: 5}]
- });
-})();
-
-// The following tests confirm the behavior of queries in the form: {$getField: {input: "string",
-// field: "string"}}.
-//
-(function testMetaField_FieldAndInputString() {
- runTest({
- docs: [
- {_id: 1, [timeField]: new Date(), [metaField]: {b: 4}},
- {_id: 2, [timeField]: new Date(), [metaField]: {c: 5}} // missing subfield.
- ],
- pipeline: [{$project: {new: {$getField: {input: `$${metaField}`, field: "b"}}}}],
- expectedResults: [{_id: 1, new: 4}, {_id: 2}]
- });
-})();
-
-// Validate the correct results are returned when there is a field with '$' inside the metaField.
-// The rewrite should be valid and return the correct field.
-(function testMetaFieldObj_FieldAndInputString() {
- runTest({
- docs: [
- {_id: 1, [timeField]: new Date(), [metaField]: {"a.$b": 4}},
- {_id: 2, [timeField]: new Date(), [metaField]: {c: 5}} // missing subfield.
- ],
- pipeline: [{$project: {new: {$getField: {input: `$${metaField}`, field: "a.$b"}}}}],
- expectedResults: [{_id: 1, new: 4}, {_id: 2}]
- });
-})();
-
-// When we rely on both the metaField and a measurementField we should not perform the rewrite and
-// return the correct result.
-(function testDollarMeta_FieldAndInputString() {
- runTest({
- docs: [
- {_id: 1, [timeField]: new Date(), [metaField]: 2, a: {"$meta1": 4}},
- {_id: 2, [timeField]: new Date(), [metaField]: 2, a: {c: 5}} // missing subfield.
- ],
-
- pipeline: [{
- $project: {
- new: {
- $add: [`$${metaField}`, {$getField: {input: "$a", field: {$literal: "$meta1"}}}]
- }
- }
- }],
- expectedResults: [{_id: 1, new: 6}, {_id: 2, new: null}]
- });
-})();
-
-// same test as above but with $addFields and not $project.
-(function testDollarMeta_FieldAndInputString_AddFields() {
- const time = new Date();
- runTest({
- docs: [
- {_id: 1, [timeField]: time, [metaField]: 2, a: {"$meta1": 4}},
- {_id: 2, [timeField]: time, [metaField]: 2, a: {c: 5}} // missing subfield.
- ],
- pipeline: [{
- $addFields: {
- new: {
- $add: [`$${metaField}`, {$getField: {input: "$a", field: {$literal: "$meta1"}}}]
- }
- }
- }],
- expectedResults: [
- {[timeField]: time, [metaField]: 2, a: {"$meta1": 4}, _id: 1, new: 6},
- {[timeField]: time, [metaField]: 2, a: {c: 5}, _id: 2, new: null}
- ]
- });
-})();
-
-// This test validates that $project with '$$ROOT' which requires the whole document returns the
-// correct results.
-(function testProject_WithROOT() {
- const time = new Date();
- runTest({
- docs: [
- {_id: 1, [timeField]: time, [metaField]: 2, a: 2},
- {_id: 2, [timeField]: time, [metaField]: 2, b: 3}
- ],
- pipeline: [{$project: {new: "$$ROOT", _id: 1}}],
- expectedResults: [
- {_id: 1, new: {_id: 1, [timeField]: time, [metaField]: 2, a: 2}},
- {_id: 2, new: {_id: 2, [timeField]: time, [metaField]: 2, b: 3}}
- ]
- });
-})();
-
-(function testAddFields_WithROOT() {
- const time = new Date();
- runTest({
- docs: [
- {_id: 1, [timeField]: time, [metaField]: 2, a: 2},
- {_id: 2, [timeField]: time, [metaField]: 2, b: 3}
- ],
- pipeline: [{$addFields: {new: "$$ROOT"}}],
- expectedResults: [
- {
- _id: 1,
- [timeField]: time,
- [metaField]: 2,
- a: 2,
- new: {_id: 1, [timeField]: time, [metaField]: 2, a: 2}
- },
- {
- _id: 2,
- [timeField]: time,
- [metaField]: 2,
- b: 3,
- new: {_id: 2, [timeField]: time, [metaField]: 2, b: 3}
- }
- ]
- });
-})();
-})();
diff --git a/jstests/core/timeseries/timeseries_resume_after.js b/jstests/core/timeseries/timeseries_resume_after.js
index 66d020d43b4..a2b31972c85 100644
--- a/jstests/core/timeseries/timeseries_resume_after.js
+++ b/jstests/core/timeseries/timeseries_resume_after.js
@@ -8,7 +8,6 @@
* does_not_support_stepdowns,
* does_not_support_transactions,
* requires_getmore,
- * requires_fcv_60
* ]
*/
(function() {
@@ -92,25 +91,5 @@ TimeseriesTest.run((insert) => {
resumeToken = res.cursor.postBatchResumeToken;
jsTestLog("Got resume token " + tojson(resumeToken));
-
- // Test that '$_resumeAfter' fails if the recordId is Long.
- assert.commandFailedWithCode(db.runCommand({
- find: bucketsColl.getName(),
- filter: {},
- $_requestResumeToken: true,
- $_resumeAfter: {'$recordId': NumberLong(10)},
- hint: {$natural: 1}
- }),
- 7738600);
-
- // Test that '$_resumeAfter' fails if querying the time-series view.
- assert.commandFailedWithCode(db.runCommand({
- find: coll.getName(),
- filter: {},
- $_requestResumeToken: true,
- $_resumeAfter: {'$recordId': BinData(5, '1234')},
- hint: {$natural: 1}
- }),
- ErrorCodes.InvalidPipelineOperator);
});
})();
diff --git a/jstests/core/timeseries/timeseries_sparse_index.js b/jstests/core/timeseries/timeseries_sparse_index.js
index 7c9f7556e38..9ffd008ff62 100644
--- a/jstests/core/timeseries/timeseries_sparse_index.js
+++ b/jstests/core/timeseries/timeseries_sparse_index.js
@@ -13,9 +13,8 @@
"use strict";
load("jstests/core/timeseries/libs/timeseries.js");
-load("jstests/libs/feature_flag_util.js");
-if (!FeatureFlagUtil.isEnabled(db, "TimeseriesMetricIndexes")) {
+if (!TimeseriesTest.timeseriesMetricIndexesEnabled(db.getMongo())) {
jsTestLog(
"Skipped test as the featureFlagTimeseriesMetricIndexes feature flag is not enabled.");
return;
diff --git a/jstests/core/timeseries/timeseries_streaming_group.js b/jstests/core/timeseries/timeseries_streaming_group.js
deleted file mode 100644
index 6d97eb51621..00000000000
--- a/jstests/core/timeseries/timeseries_streaming_group.js
+++ /dev/null
@@ -1,159 +0,0 @@
-/**
- * Tests that the $group stage will be replaced with $_internalStreamingGroup when group id is
- * monotonic on time and documents are sorted on time.
- *
- * @tags: [
- * # Explain of a resolved view must be executed by mongos.
- * directly_against_shardsvrs_incompatible,
- * does_not_support_stepdowns,
- * does_not_support_transactions,
- * requires_fcv_60,
- * requires_timeseries,
- * ]
- */
-(function() {
-"use strict";
-
-load("jstests/libs/analyze_plan.js"); // For getAggPlanStages
-load("jstests/libs/fail_point_util.js"); // For configureFailPoint
-
-const ts = db.timeseries_streaming_group;
-ts.drop();
-
-const coll = db.timeseires_streaming_group_regular_collection;
-coll.drop();
-
-assert.commandWorked(
- db.createCollection(ts.getName(), {timeseries: {timeField: "time", metaField: "meta"}}));
-
-const numTimes = 100;
-const numSymbols = 10;
-const minPrice = 100;
-const maxPrice = 200;
-const minAmount = 1;
-const maxAmount = 20;
-
-Random.setRandomSeed(1);
-
-const randRange = function(min, max) {
- return min + Random.randInt(max - min);
-};
-
-const symbols = [];
-for (let i = 0; i < numSymbols; i++) {
- let randomName = "";
- const randomStrLen = 5;
- for (let j = 0; j < randomStrLen; j++) {
- randomName += String.fromCharCode("A".charCodeAt(0) + Random.randInt(26));
- }
- symbols.push(randomName);
-}
-
-const documents = [];
-const startTime = 1641027600000;
-for (let i = 0; i < numTimes; i++) {
- for (const symbol of symbols) {
- documents.push({
- time: new Date(startTime + i * 1000),
- price: randRange(minPrice, maxPrice),
- amount: randRange(minAmount, maxAmount),
- meta: {"symbol": symbol}
- });
- }
-}
-
-assert.commandWorked(ts.insert(documents));
-assert.commandWorked(coll.insert(documents));
-
-// Incorrect use of $_internalStreamingGroup should return error
-assert.commandFailedWithCode(db.runCommand({
- aggregate: "timeseires_streaming_group_regular_collection",
- pipeline: [{
- $_internalStreamingGroup: {
- _id: {symbol: "$symbol", time: "$time"},
- count: {$sum: 1},
- $monotonicIdFields: ["price"]
- }
- }],
- cursor: {},
-}),
- 7026705);
-assert.commandFailedWithCode(db.runCommand({
- aggregate: "timeseires_streaming_group_regular_collection",
- pipeline: [{$_internalStreamingGroup: {_id: null, count: {$sum: 1}}}],
- cursor: {},
-}),
- 7026702);
-assert.commandFailedWithCode(db.runCommand({
- aggregate: "timeseires_streaming_group_regular_collection",
- pipeline:
- [{$_internalStreamingGroup: {_id: null, count: {$sum: 1}, $monotonicIdFields: ["_id"]}}],
- cursor: {},
-}),
- 7026708);
-
-const runTest = function(pipeline, expectedMonotonicIdFields) {
- const explain = assert.commandWorked(ts.explain().aggregate(pipeline));
- const streamingGroupStage = getAggPlanStage(explain, "$_internalStreamingGroup");
- assert.neq(streamingGroupStage, null);
- assert.eq(streamingGroupStage.$_internalStreamingGroup.$monotonicIdFields,
- expectedMonotonicIdFields);
-
- const found = ts.aggregate(pipeline).toArray();
- const expected = coll.aggregate(pipeline).toArray();
- assert.eq(expected, found);
-};
-
-runTest(
- [
- {$sort: {time: 1}},
- {
- $group: {
- _id: {
- symbol: "$meta.symbol",
- minute: {
- $subtract: [
- {$dateTrunc: {date: "$time", unit: "minute"}},
- {$dateTrunc: {date: new Date(startTime), unit: "minute"}},
- ]
- }
- },
- "average_price": {$avg: {$multiply: ["$price", "$amount"]}},
- "average_amount": {$avg: "$amount"}
- }
- },
- {$addFields: {"average_price": {$divide: ["$average_price", "$average_amount"]}}},
- {$sort: {_id: 1}}
- ],
- ["minute"]);
-
-runTest(
- [
- {$sort: {time: 1}},
- {
- $group: {
- _id: {$dateTrunc: {date: "$time", unit: "minute"}},
- "average_price": {$avg: {$multiply: ["$price", "$amount"]}},
- "average_amount": {$avg: "$amount"}
- }
- },
- {$addFields: {"average_price": {$divide: ["$average_price", "$average_amount"]}}},
- {$sort: {_id: 1}}
- ],
- ["_id"]);
-
-runTest(
- [
- {$sort: {time: 1}},
- {
- $group: {
- _id: "$time",
- "average_price": {$avg: {$multiply: ["$price", "$amount"]}},
- "average_amount": {$avg: "$amount"}
- }
- },
- {$addFields: {"average_price": {$divide: ["$average_price", "$average_amount"]}}},
- {$sort: {_id: 1}}
- ],
- ["_id"]);
-})();
diff --git a/jstests/core/timeseries/timeseries_union_with.js b/jstests/core/timeseries/timeseries_union_with.js
index e92e4c4ca37..b78bbd6937c 100644
--- a/jstests/core/timeseries/timeseries_union_with.js
+++ b/jstests/core/timeseries/timeseries_union_with.js
@@ -4,11 +4,6 @@
* @tags: [
* does_not_support_transactions,
* requires_timeseries,
- * # This test depends on certain writes ending up in the same bucket. Stepdowns and tenant
- * # migrations may result in writes splitting between two primaries, and thus different buckets.
- * does_not_support_stepdowns,
- * tenant_migration_incompatible,
- * references_foreign_collection,
* ]
*/
(function() {
diff --git a/jstests/core/timeseries/timeseries_update_mixed_schema_bucket.js b/jstests/core/timeseries/timeseries_update_mixed_schema_bucket.js
deleted file mode 100644
index 0211daa29d8..00000000000
--- a/jstests/core/timeseries/timeseries_update_mixed_schema_bucket.js
+++ /dev/null
@@ -1,79 +0,0 @@
-/**
- * Tests directly updating a time-series bucket to contain mixed schema.
- *
- * @tags: [
- * # $listCatalog does not include the tenant prefix in its results.
- * command_not_supported_in_serverless,
- * requires_timeseries,
- * # $listCatalog not supported inside of a multi-document transaction
- * does_not_support_transactions,
- * # $listCatalog only exists since v6
- * multiversion_incompatible,
- * ]
- */
-(function() {
-"use strict";
-load("jstests/core/timeseries/libs/timeseries.js"); // For 'TimeseriesTest'.
-
-TestData.skipEnforceTimeseriesBucketsAreAlwaysCompressedOnValidate = true;
-
-const testDB = db.getSiblingDB(jsTestName());
-const collName = "ts";
-
-assert.commandWorkedOrFailedWithCode(testDB.runCommand({drop: collName}),
- ErrorCodes.NamespaceNotFound);
-assert.commandWorked(
- testDB.createCollection(collName, {timeseries: {timeField: "t", metaField: "m"}}));
-const coll = testDB[collName];
-const bucketsColl = testDB["system.buckets." + collName];
-
-const bucket = {
- _id: ObjectId("65a6eb806ffc9fa4280ecac4"),
- control: {
- version: NumberInt(1),
- min: {
- _id: ObjectId("65a6eba7e6d2e848e08c3750"),
- t: ISODate("2024-01-16T20:48:00Z"),
- a: 0,
- },
- max: {
- _id: ObjectId("65a6eba7e6d2e848e08c3751"),
- t: ISODate("2024-01-16T20:48:39.448Z"),
- a: 1,
- },
- },
- meta: 0,
- data: {
- _id: {
- 0: ObjectId("65a6eba7e6d2e848e08c3750"),
- 1: ObjectId("65a6eba7e6d2e848e08c3751"),
- },
- t: {
- 0: ISODate("2024-01-16T20:48:39.448Z"),
- 1: ISODate("2024-01-16T20:48:39.448Z"),
- },
- a: {
- 0: 0,
- 1: 1,
- },
- }
-};
-
-const update = function() {
- return bucketsColl.update({_id: bucket._id},
- {$set: {"control.min.a": 1, "control.max.a": "a", "data.a.0": "a"}});
-};
-
-assert.commandWorked(bucketsColl.insert(bucket));
-assert.commandFailedWithCode(update(), ErrorCodes.CannotInsertTimeseriesBucketsWithMixedSchema);
-assert.eq(TimeseriesTest.bucketsMayHaveMixedSchemaData(bucketsColl), false);
-assert.commandWorked(
- testDB.runCommand({collMod: collName, timeseriesBucketsMayHaveMixedSchemaData: true}));
-assert.eq(TimeseriesTest.bucketsMayHaveMixedSchemaData(bucketsColl), true);
-assert.commandWorked(update());
-assert.commandWorked(bucketsColl.deleteOne({_id: bucket._id}));
-assert.eq(TimeseriesTest.bucketsMayHaveMixedSchemaData(bucketsColl), true);
-assert.commandWorked(
- testDB.runCommand({collMod: collName, timeseriesBucketsMayHaveMixedSchemaData: false}));
-assert.eq(TimeseriesTest.bucketsMayHaveMixedSchemaData(bucketsColl), false);
-})();