summaryrefslogtreecommitdiff
path: root/jstests/core/timeseries/timeseries_lastpoint.js
diff options
context:
space:
mode:
Diffstat (limited to 'jstests/core/timeseries/timeseries_lastpoint.js')
-rw-r--r--jstests/core/timeseries/timeseries_lastpoint.js214
1 files changed, 58 insertions, 156 deletions
diff --git a/jstests/core/timeseries/timeseries_lastpoint.js b/jstests/core/timeseries/timeseries_lastpoint.js
index 3be28645c7b..05344c194da 100644
--- a/jstests/core/timeseries/timeseries_lastpoint.js
+++ b/jstests/core/timeseries/timeseries_lastpoint.js
@@ -29,173 +29,75 @@ if (!isLastpointEnabled) {
return;
}
-// Timeseries test parameters.
-const numHosts = 10;
-const numIterations = 20;
-
-function verifyTsResults({pipeline, precedingFilter, expectStage, prepareTest}) {
- // Prepare collections. Note: we test without idle measurements (all meta subfields are
- // non-null). If we allow the insertion of idle measurements, we will obtain multiple lastpoints
- // per bucket, and may have different results on the observer and timeseries collections.
- const [tsColl, observerColl] = TimeseriesAggTests.prepareInputCollections(
- numHosts, numIterations, false /* includeIdleMeasurements */);
-
- // Additional preparation before running the test.
- if (prepareTest) {
- prepareTest(tsColl, observerColl);
+function verifyTsResults(pipeline, index, precedingFilter) {
+ // Prepare collections.
+ const numHosts = 10;
+ const numIterations = 20;
+ const [tsColl, observerColl] =
+ TimeseriesAggTests.prepareInputCollections(numHosts, numIterations);
+ if (index) {
+ tsColl.createIndex(index);
}
// Verify lastpoint optmization.
const explain = tsColl.explain().aggregate(pipeline);
- expectStage({explain, precedingFilter});
-
- // Assert that the time-series aggregation results match that of the observer collection.
- const expected = observerColl.aggregate(pipeline).toArray();
- const actual = tsColl.aggregate(pipeline).toArray();
- assertArrayEq({actual, expected});
-
- // Drop collections.
- tsColl.drop();
- observerColl.drop();
-}
-
-function verifyTsResultsWithAndWithoutIndex(
- {pipeline, index, bucketsIndex, precedingFilter, expectStage, prePrepareTest}) {
- verifyTsResults(
- {pipeline, precedingFilter, expectStage: expectCollScan, prepareTest: prePrepareTest});
- verifyTsResults({
- pipeline,
- precedingFilter,
- expectStage,
- prepareTest: (testColl, observerColl) => {
- // Optionally do extra test preparation.
- if (prePrepareTest) {
- prePrepareTest(testColl, observerColl);
- }
-
- // Create index on the timeseries collection.
- testColl.createIndex(index);
-
- // Create an additional secondary index directly on the buckets collection so that we
- // can test the DISTINCT_SCAN optimization when time is sorted in ascending order.
- if (bucketsIndex) {
- const bucketsColl = testDB["system.buckets.in"];
- bucketsColl.createIndex(bucketsIndex);
- }
+ if (index) {
+ // The query can utilize DISTINCT_SCAN.
+ assert.neq(getAggPlanStage(explain, "DISTINCT_SCAN"), null, explain);
+
+ // Pipelines that use the DISTINCT_SCAN optimization should not also have a blocking sort.
+ assert.eq(getAggPlanStage(explain, "SORT"), null, explain);
+ } else {
+ // $sort can be pushed into the cursor layer.
+ assert.neq(getAggPlanStage(explain, "SORT"), null, explain);
+
+ // At the bottom, there should be a COLLSCAN.
+ const collScanStage = getAggPlanStage(explain, "COLLSCAN");
+ assert.neq(collScanStage, null, explain);
+ if (precedingFilter) {
+ assert.eq(precedingFilter, collScanStage.filter, collScanStage);
}
- });
-}
-
-function expectDistinctScan({explain}) {
- // The query can utilize DISTINCT_SCAN.
- assert.neq(getAggPlanStage(explain, "DISTINCT_SCAN"), null, explain);
-
- // Pipelines that use the DISTINCT_SCAN optimization should not also have a blocking sort.
- assert.eq(getAggPlanStage(explain, "SORT"), null, explain);
-}
-
-function expectCollScan({explain, precedingFilter}) {
- // $sort can be pushed into the cursor layer.
- assert.neq(getAggPlanStage(explain, "SORT"), null, explain);
-
- // At the bottom, there should be a COLLSCAN.
- const collScanStage = getAggPlanStage(explain, "COLLSCAN");
- assert.neq(collScanStage, null, explain);
- if (precedingFilter) {
- assert.eq(precedingFilter, collScanStage.filter, collScanStage);
}
-}
-
-function expectIxscan({explain}) {
- // $sort can be pushed into the cursor layer.
- assert.neq(getAggPlanStage(explain, "SORT"), null, explain);
- // At the bottom, there should be a IXSCAN.
- assert.neq(getAggPlanStage(explain, "IXSCAN"), null, explain);
+ // Assert that the time-series aggregation results match that of the observer collection.
+ const expectedResults = observerColl.aggregate(pipeline).toArray();
+ const actualResults = tsColl.aggregate(pipeline).toArray();
+ assert(resultsEq(actualResults, expectedResults),
+ `Expected ${tojson(expectedResults)} but got ${tojson(actualResults)}`);
}
-function getGroupStage(accumulator) {
- return {
- $group: {
- _id: "$tags.hostid",
- usage_user: {[accumulator]: "$usage_user"},
- usage_guest: {[accumulator]: "$usage_guest"},
- usage_idle: {[accumulator]: "$usage_idle"}
- }
- };
+function verifyTsResultsWithAndWithoutIndex(pipeline, index, precedingFilter) {
+ verifyTsResults(pipeline, undefined, precedingFilter);
+ verifyTsResults(pipeline, index, precedingFilter);
}
-/**
- Test cases:
- 1. Lastpoint queries on indexes with descending time and $first (DISTINCT_SCAN).
- 2. Lastpoint queries on indexes with ascending time and $last (no DISTINCT_SCAN).
- 3. Lastpoint queries on indexes with ascending time and $last and an additional secondary
- index so that we can use the DISTINCT_SCAN optimization.
-*/
-const testCases = [
- {time: -1},
- {time: 1},
- {time: -1, bucketsIndex: {"meta.hostid": -1, "control.max.time": 1, "control.min.time": 1}}
-];
-
-for (const {time, bucketsIndex} of testCases) {
- const isTimeDescending = time < 0;
- const canUseDistinct = isTimeDescending || bucketsIndex;
- const groupStage = isTimeDescending ? getGroupStage("$first") : getGroupStage("$last");
-
- // Test both directions of the metaField sort for each direction of time.
- for (const index of [{"tags.hostid": 1, time}, {"tags.hostid": -1, time}]) {
- // Test pipeline without a preceding $match stage.
- verifyTsResultsWithAndWithoutIndex({
- pipeline: [{$sort: index}, groupStage],
- index,
- bucketsIndex,
- expectStage: (canUseDistinct ? expectDistinctScan : expectCollScan)
- });
-
- // Test pipeline without a preceding $match stage which has an extra idle measurement. This
- // verifies that the query rewrite correctly returns missing fields.
- verifyTsResultsWithAndWithoutIndex({
- pipeline: [{$sort: index}, groupStage],
- index,
- bucketsIndex,
- expectStage: (canUseDistinct ? expectDistinctScan : expectCollScan),
- prePrepareTest: (testColl, observerColl) => {
- const currTime = new Date();
- for (const host of TimeseriesTest.generateHosts(numHosts)) {
- const idleMeasurement = {
- tags: host.tags,
- time: new Date(currTime + numIterations), // Ensure this is the lastpoint.
- idle_user: 100 - TimeseriesTest.getRandomUsage()
- };
- assert.commandWorked(testColl.insert(idleMeasurement));
- assert.commandWorked(observerColl.insert(idleMeasurement));
- }
+verifyTsResultsWithAndWithoutIndex(
+ [
+ {$sort: {"tags.hostid": 1, time: -1}},
+ {
+ $group: {
+ _id: "$tags.hostid",
+ usage_user: {$first: "$usage_user"},
+ usage_guest: {$first: "$usage_guest"},
+ usage_idle: {$first: "$usage_idle"}
}
- });
-
- // Test pipeline with a preceding $match stage.
- function testWithMatch(matchStage, precedingFilter) {
- verifyTsResultsWithAndWithoutIndex({
- pipeline: [matchStage, {$sort: index}, groupStage],
- index,
- bucketsIndex,
- expectStage: canUseDistinct ? expectDistinctScan : expectIxscan,
- precedingFilter
- });
}
-
- // Test pipeline with an equality $match stage.
- testWithMatch({$match: {"tags.hostid": 0}}, {"meta.hostid": {$eq: 0}});
-
- // Test pipeline with an inequality $match stage.
- testWithMatch({$match: {"tags.hostid": {$ne: 0}}}, {"meta.hostid": {$not: {$eq: 0}}});
-
- // Test pipeline with a $match stage that uses a $gt query.
- testWithMatch({$match: {"tags.hostid": {$gt: 5}}}, {"meta.hostid": {$gt: 5}});
-
- // Test pipeline with a $match stage that uses a $lt query.
- testWithMatch({$match: {"tags.hostid": {$lt: 5}}}, {"meta.hostid": {$lt: 5}});
- }
-}
+ ],
+ {"tags.hostid": 1, time: -1});
+
+verifyTsResultsWithAndWithoutIndex(
+ [
+ {$match: {"tags.hostid": "host_0"}},
+ {$sort: {"tags.hostid": 1, time: -1}},
+ {
+ $group: {
+ _id: "$tags.hostid",
+ usage_user: {$first: "$usage_user"},
+ usage_guest: {$first: "$usage_guest"},
+ usage_idle: {$first: "$usage_idle"}
+ }
+ }
+ ],
+ {"tags.hostid": 1, time: -1},
+ {"meta.hostid": {$eq: "host_0"}});
})();