diff options
author | Alya Berciu <alya.berciu@mongodb.com> | 2023-04-13 13:22:36 +0000 |
---|---|---|
committer | Evergreen Agent <no-reply@evergreen.mongodb.com> | 2023-04-13 14:32:17 +0000 |
commit | 34cde74dae0c59ff7ae215fbbbf0050ecb91f201 (patch) | |
tree | 0aada62c49d3a698a48b3c7eab10e6b53c75702f /jstests | |
parent | 61f6d836338319a4b17fcbf87584aa560e58df23 (diff) | |
download | mongo-34cde74dae0c59ff7ae215fbbbf0050ecb91f201.tar.gz |
SERVER-73482 Fix $natural hint with clustered collection sorts
Diffstat (limited to 'jstests')
-rw-r--r-- | jstests/libs/clustered_collections/clustered_collection_hint_common.js | 123 |
1 files changed, 111 insertions, 12 deletions
diff --git a/jstests/libs/clustered_collections/clustered_collection_hint_common.js b/jstests/libs/clustered_collections/clustered_collection_hint_common.js index a65939698e2..c96fa821bd3 100644 --- a/jstests/libs/clustered_collections/clustered_collection_hint_common.js +++ b/jstests/libs/clustered_collections/clustered_collection_hint_common.js @@ -196,6 +196,91 @@ function testClusteredCollectionHint(coll, clusterKey, clusterKeyName) { } }); + // Find with $natural hints and sorts: we should scan the collection in the hinted + // direction regardless of sort direction, and provide a blocking sort if needed. + validateClusteredCollectionHint(coll, { + expectedNReturned: batchSize, + cmd: {find: collName, hint: {$natural: 1}, sort: {[clusterKeyFieldName]: 1}}, + expectedWinningPlanStats: { + stage: "COLLSCAN", + direction: "forward", + }, + unexpectedWinningPlanStats: ["SORT"] // We shouldn't need a blocking sort here. + }); + validateClusteredCollectionHint(coll, { + expectedNReturned: batchSize, + cmd: {find: collName, hint: {$natural: -1}, sort: {[clusterKeyFieldName]: 1}}, + expectedWinningPlanStats: [ + {stage: "SORT", sortPattern: {[clusterKeyFieldName]: 1}}, + { + stage: "COLLSCAN", + direction: "backward", + } + ] + }); + validateClusteredCollectionHint(coll, { + expectedNReturned: batchSize, + cmd: {find: collName, hint: {$natural: 1}, sort: {[clusterKeyFieldName]: -1}}, + expectedWinningPlanStats: [ + {stage: "SORT", sortPattern: {[clusterKeyFieldName]: -1}}, + { + stage: "COLLSCAN", + direction: "forward", + } + ] + }); + validateClusteredCollectionHint(coll, { + expectedNReturned: batchSize, + cmd: {find: collName, hint: {$natural: -1}, sort: {[clusterKeyFieldName]: -1}}, + expectedWinningPlanStats: { + stage: "COLLSCAN", + direction: "backward", + }, + unexpectedWinningPlanStats: ["SORT"] // We shouldn't need a blocking sort here. + }); + + // We always need a blocking sort when the sort pattern does not match the provided sort for + // the clustered collection. + validateClusteredCollectionHint(coll, { + expectedNReturned: batchSize, + cmd: {find: collName, hint: {$natural: 1}, sort: {a: 1}}, + expectedWinningPlanStats: [ + {stage: "SORT", sortPattern: {a: 1}}, + { + stage: "COLLSCAN", + direction: "forward", + } + ] + }); + validateClusteredCollectionHint(coll, { + expectedNReturned: batchSize, + cmd: {find: collName, hint: {$natural: -1}, sort: {a: 1}}, + expectedWinningPlanStats: [ + {stage: "SORT", sortPattern: {a: 1}}, + { + stage: "COLLSCAN", + direction: "backward", + } + ] + }); + validateClusteredCollectionHint(coll, { + expectedNReturned: batchSize, + cmd: {find: collName, hint: {$natural: 1}, sort: {a: -1}}, + expectedWinningPlanStats: [ + {stage: "SORT", sortPattern: {a: -1}}, + { + stage: "COLLSCAN", + direction: "forward", + } + ] + }); + validateClusteredCollectionHint(coll, { + expectedNReturned: batchSize, + cmd: {find: collName, hint: {$natural: -1}, sort: {a: -1}}, + expectedWinningPlanStats: + [{stage: "SORT", sortPattern: {a: -1}}, {stage: "COLLSCAN", direction: "backward"}], + }); + // Find on a standard index. validateClusteredCollectionHint(coll, { expectedNReturned: batchSize, @@ -280,25 +365,39 @@ function testClusteredCollectionHint(coll, clusterKey, clusterKeyName) { return testHint(coll, clusterKey, clusterKeyName); } -function validateClusteredCollectionHint(coll, - {expectedNReturned, cmd, expectedWinningPlanStats = {}}) { +function validateClusteredCollectionHint( + coll, + {expectedNReturned, cmd, expectedWinningPlanStats = {}, unexpectedWinningPlanStats = []}) { const explain = assert.commandWorked(coll.runCommand({explain: cmd})); assert.eq(explain.executionStats.nReturned, expectedNReturned, tojson(explain)); const actualWinningPlan = getWinningPlan(explain.queryPlanner); - const stageOfInterest = getPlanStage(actualWinningPlan, expectedWinningPlanStats.stage); - assert.neq(null, stageOfInterest); - for (const [key, value] of Object.entries(expectedWinningPlanStats)) { - assert(stageOfInterest[key] !== undefined, tojson(explain)); - assert.eq(stageOfInterest[key], value, tojson(explain)); + if (!Array.isArray(expectedWinningPlanStats)) { + expectedWinningPlanStats = [expectedWinningPlanStats]; } - // Explicitly check that the plan is not bounded by default. - if (!expectedWinningPlanStats.hasOwnProperty("minRecord")) { - assert(!actualWinningPlan.hasOwnProperty("minRecord"), tojson(explain)); + for (const excludedStage of unexpectedWinningPlanStats) { + const stageOfInterest = getPlanStage(actualWinningPlan, excludedStage); + assert.eq(null, stageOfInterest); } - if (!expectedWinningPlanStats.hasOwnProperty("maxRecord")) { - assert(!actualWinningPlan.hasOwnProperty("maxRecord"), tojson(explain)); + + for (const expectedWinningPlanStageStats of expectedWinningPlanStats) { + const stageOfInterest = + getPlanStage(actualWinningPlan, expectedWinningPlanStageStats.stage); + assert.neq(null, stageOfInterest); + + for (const [key, value] of Object.entries(expectedWinningPlanStageStats)) { + assert(stageOfInterest[key] !== undefined, tojson(explain)); + assert.eq(stageOfInterest[key], value, tojson(explain)); + } + + // Explicitly check that the plan is not bounded by default. + if (!expectedWinningPlanStageStats.hasOwnProperty("minRecord")) { + assert(!actualWinningPlan.hasOwnProperty("minRecord"), tojson(explain)); + } + if (!expectedWinningPlanStageStats.hasOwnProperty("maxRecord")) { + assert(!actualWinningPlan.hasOwnProperty("maxRecord"), tojson(explain)); + } } } |