99 * Test covering behavior for queries over a multikey index.
1010 */
1111// For making assertions about explain output.
12- import { getPlanStage , getWinningPlan , isIxscan , planHasStage } from "jstests/libs/analyze_plan.js" ;
12+ import {
13+ getOptimizer ,
14+ getPlanStage ,
15+ getWinningPlan ,
16+ isCollscan ,
17+ isIxscan ,
18+ isIxscanMultikey ,
19+ planHasStage
20+ } from "jstests/libs/analyze_plan.js" ;
1321
1422let coll = db . covered_multikey ;
1523coll . drop ( ) ;
@@ -21,7 +29,18 @@ assert.eq(1, coll.find({a: 1, b: 2}, {_id: 0, a: 1}).itcount());
2129assert . eq ( { a : 1 } , coll . findOne ( { a : 1 , b : 2 } , { _id : 0 , a : 1 } ) ) ;
2230let explainRes = coll . explain ( "queryPlanner" ) . find ( { a : 1 , b : 2 } , { _id : 0 , a : 1 } ) . finish ( ) ;
2331let winningPlan = getWinningPlan ( explainRes . queryPlanner ) ;
24- assert ( isIxscan ( db , winningPlan ) ) ;
32+ switch ( getOptimizer ( explainRes ) ) {
33+ case "classic" : {
34+ assert ( isIxscan ( db , winningPlan ) ) ;
35+ break ;
36+ }
37+ case "CQF" : {
38+ // TODO SERVER-77719: Ensure that the decision for using the scan lines up with CQF
39+ // optimizer. M2: allow only collscans, M4: check bonsai behavior for index scan.
40+ assert ( isCollscan ( db , winningPlan ) ) ;
41+ break
42+ }
43+ }
2544assert ( ! planHasStage ( db , winningPlan , "FETCH" ) ) ;
2645
2746coll . drop ( ) ;
@@ -47,10 +66,20 @@ assert.commandWorked(coll.createIndex({a: 1}));
4766assert . eq ( { a : [ ] } , coll . findOne ( { a : [ ] } , { _id : 0 , a : 1 } ) ) ;
4867explainRes = coll . explain ( "queryPlanner" ) . find ( { a : [ ] } , { _id : 0 , a : 1 } ) . finish ( ) ;
4968winningPlan = getWinningPlan ( explainRes . queryPlanner ) ;
50- assert ( planHasStage ( db , winningPlan , "IXSCAN" ) ) ;
51- assert ( planHasStage ( db , winningPlan , "FETCH" ) ) ;
52- let ixscanStage = getPlanStage ( winningPlan , "IXSCAN" ) ;
53- assert . eq ( true , ixscanStage . isMultiKey ) ;
69+ switch ( getOptimizer ( explainRes ) ) {
70+ case "classic" : {
71+ assert ( isIxscan ( db , winningPlan ) ) ;
72+ assert ( planHasStage ( db , winningPlan , "FETCH" ) ) ;
73+ assert ( isIxscanMultikey ( winningPlan ) ) ;
74+ break ;
75+ }
76+ case "CQF" : {
77+ // TODO SERVER-77719: Ensure that the decision for using the scan lines up with CQF
78+ // optimizer. M2: allow only collscans, M4: check bonsai behavior for index scan.
79+ assert ( isCollscan ( db , winningPlan ) ) ;
80+ break
81+ }
82+ }
5483
5584// Verify that a query cannot be covered over a path which is multikey due to a single-element
5685// array.
@@ -60,10 +89,20 @@ assert.commandWorked(coll.createIndex({a: 1}));
6089assert . eq ( { a : [ 2 ] } , coll . findOne ( { a : 2 } , { _id : 0 , a : 1 } ) ) ;
6190explainRes = coll . explain ( "queryPlanner" ) . find ( { a : 2 } , { _id : 0 , a : 1 } ) . finish ( ) ;
6291winningPlan = getWinningPlan ( explainRes . queryPlanner ) ;
63- assert ( planHasStage ( db , winningPlan , "IXSCAN" ) ) ;
64- assert ( planHasStage ( db , winningPlan , "FETCH" ) ) ;
65- ixscanStage = getPlanStage ( winningPlan , "IXSCAN" ) ;
66- assert . eq ( true , ixscanStage . isMultiKey ) ;
92+ switch ( getOptimizer ( explainRes ) ) {
93+ case "classic" : {
94+ assert ( isIxscan ( db , winningPlan ) ) ;
95+ assert ( planHasStage ( db , winningPlan , "FETCH" ) ) ;
96+ assert ( isIxscanMultikey ( winningPlan ) ) ;
97+ break ;
98+ }
99+ case "CQF" : {
100+ // TODO SERVER-77719: Ensure that the decision for using the scan lines up with CQF
101+ // optimizer. M2: allow only collscans, M4: check bonsai behavior for index scan.
102+ assert ( isCollscan ( db , winningPlan ) ) ;
103+ break ;
104+ }
105+ }
67106
68107// Verify that a query cannot be covered over a path which is multikey due to a single-element
69108// array, where the path is made multikey by an update rather than an insert.
@@ -74,43 +113,82 @@ assert.commandWorked(coll.update({}, {$set: {a: [2]}}));
74113assert . eq ( { a : [ 2 ] } , coll . findOne ( { a : 2 } , { _id : 0 , a : 1 } ) ) ;
75114explainRes = coll . explain ( "queryPlanner" ) . find ( { a : 2 } , { _id : 0 , a : 1 } ) . finish ( ) ;
76115winningPlan = getWinningPlan ( explainRes . queryPlanner ) ;
77- assert ( planHasStage ( db , winningPlan , "IXSCAN" ) ) ;
78- assert ( planHasStage ( db , winningPlan , "FETCH" ) ) ;
79- ixscanStage = getPlanStage ( winningPlan , "IXSCAN" ) ;
80- assert . eq ( true , ixscanStage . isMultiKey ) ;
116+ switch ( getOptimizer ( explainRes ) ) {
117+ case "classic" : {
118+ assert ( isIxscan ( db , winningPlan ) ) ;
119+ assert ( planHasStage ( db , winningPlan , "FETCH" ) ) ;
120+ assert ( isIxscanMultikey ( winningPlan ) ) ;
121+ break ;
122+ }
123+ case "CQF" : {
124+ // TODO SERVER-77719: Ensure that the decision for using the scan lines up with CQF
125+ // optimizer. M2: allow only collscans, M4: check bonsai behavior for index scan.
126+ assert ( isCollscan ( db , winningPlan ) ) ;
127+ break ;
128+ }
129+ }
81130
82131// Verify that a trailing empty array makes a 2dsphere index multikey.
83132assert ( coll . drop ( ) ) ;
84133assert . commandWorked ( coll . createIndex ( { "a.b" : 1 , c : "2dsphere" } ) ) ;
85134assert . commandWorked ( coll . insert ( { a : { b : 1 } , c : { type : "Point" , coordinates : [ 0 , 0 ] } } ) ) ;
86135explainRes = coll . explain ( ) . find ( ) . hint ( { "a.b" : 1 , c : "2dsphere" } ) . finish ( ) ;
87136winningPlan = getWinningPlan ( explainRes . queryPlanner ) ;
88- ixscanStage = getPlanStage ( winningPlan , "IXSCAN" ) ;
137+ let optimizer = getOptimizer ( explainRes ) ;
138+ let stages = { "classic" : "IXSCAN" , "CQF" : "IndexScan" } ;
139+ let ixscanStage = getPlanStage ( winningPlan , stages [ optimizer ] ) ;
89140assert . neq ( null , ixscanStage ) ;
90141assert . eq ( false , ixscanStage . isMultiKey ) ;
91142assert . commandWorked ( coll . insert ( { a : { b : [ ] } , c : { type : "Point" , coordinates : [ 0 , 0 ] } } ) ) ;
92143explainRes = coll . explain ( ) . find ( ) . hint ( { "a.b" : 1 , c : "2dsphere" } ) . finish ( ) ;
93144winningPlan = getWinningPlan ( explainRes . queryPlanner ) ;
94- ixscanStage = getPlanStage ( winningPlan , "IXSCAN" ) ;
95- assert . neq ( null , ixscanStage ) ;
96- assert . eq ( true , ixscanStage . isMultiKey ) ;
145+ switch ( getOptimizer ( explainRes ) ) {
146+ case "classic" : {
147+ assert ( isIxscan ( db , winningPlan ) ) ;
148+ assert ( isIxscanMultikey ( winningPlan ) ) ;
149+ break ;
150+ }
151+ case "CQF" : {
152+ // TODO SERVER-77719: Ensure that the decision for using the scan lines up with CQF
153+ // optimizer. M2: allow only collscans, M4: check bonsai behavior for index scan.
154+ break ;
155+ }
156+ }
97157
98158// Verify that a mid-path empty array makes a 2dsphere index multikey.
99159assert ( coll . drop ( ) ) ;
100160assert . commandWorked ( coll . createIndex ( { "a.b" : 1 , c : "2dsphere" } ) ) ;
101161assert . commandWorked ( coll . insert ( { a : [ ] , c : { type : "Point" , coordinates : [ 0 , 0 ] } } ) ) ;
102162explainRes = coll . explain ( ) . find ( ) . hint ( { "a.b" : 1 , c : "2dsphere" } ) . finish ( ) ;
103163winningPlan = getWinningPlan ( explainRes . queryPlanner ) ;
104- ixscanStage = getPlanStage ( winningPlan , "IXSCAN" ) ;
105- assert . neq ( null , ixscanStage ) ;
106- assert . eq ( true , ixscanStage . isMultiKey ) ;
164+ switch ( getOptimizer ( explainRes ) ) {
165+ case "classic" : {
166+ assert ( isIxscan ( db , winningPlan ) ) ;
167+ assert ( isIxscanMultikey ( winningPlan ) ) ;
168+ break ;
169+ }
170+ case "CQF" : {
171+ // TODO SERVER-77719: Ensure that the decision for using the scan lines up with CQF
172+ // optimizer. M2: allow only collscans, M4: check bonsai behavior for index scan.
173+ break ;
174+ }
175+ }
107176
108177// Verify that a single-element array makes a 2dsphere index multikey.
109178assert ( coll . drop ( ) ) ;
110179assert . commandWorked ( coll . createIndex ( { "a.b" : 1 , c : "2dsphere" } ) ) ;
111180assert . commandWorked ( coll . insert ( { a : { b : [ 3 ] } , c : { type : "Point" , coordinates : [ 0 , 0 ] } } ) ) ;
112181explainRes = coll . explain ( ) . find ( ) . hint ( { "a.b" : 1 , c : "2dsphere" } ) . finish ( ) ;
113182winningPlan = getWinningPlan ( explainRes . queryPlanner ) ;
114- ixscanStage = getPlanStage ( winningPlan , "IXSCAN" ) ;
115- assert . neq ( null , ixscanStage ) ;
116- assert . eq ( true , ixscanStage . isMultiKey ) ;
183+ switch ( getOptimizer ( explainRes ) ) {
184+ case "classic" : {
185+ assert ( isIxscan ( db , winningPlan ) ) ;
186+ assert ( isIxscanMultikey ( winningPlan ) ) ;
187+ break ;
188+ }
189+ case "CQF" : {
190+ // TODO SERVER-77719: Ensure that the decision for using the scan lines up with CQF
191+ // optimizer. M2: allow only collscans, M4: check bonsai behavior for index scan.
192+ break ;
193+ }
194+ }
0 commit comments