diff options
Diffstat (limited to 'jstests/core/query/find/find_project_sort.js')
-rw-r--r-- | jstests/core/query/find/find_project_sort.js | 808 |
1 files changed, 808 insertions, 0 deletions
diff --git a/jstests/core/query/find/find_project_sort.js b/jstests/core/query/find/find_project_sort.js new file mode 100644 index 00000000000..3d359fd9e41 --- /dev/null +++ b/jstests/core/query/find/find_project_sort.js @@ -0,0 +1,808 @@ +/** + * Test a variety of predicates for the find command's filter, projection and sort expressions. + */ +(function() { +"use strict"; + +load("jstests/aggregation/extras/utils.js"); // For arrayEq and orderedArrayEq. + +const coll = db.find_project_sort; +coll.drop(); +const documents = [ + {_id: 0, a: 1, b: "x", c: 10}, + {_id: 1, a: 2, b: "y", c: 11}, + {_id: 2, a: 3, b: "z", c: 12}, + {_id: 3, x: {y: 1}}, + {_id: 4, x: {y: 2}}, + {_id: 5, x: {y: [1, 2, 3]}, v: {w: [4, 5, 6]}}, + {_id: 6, x: {y: 4}, v: {w: 4}}, + {_id: 7, x: [{y: 1}], v: [{w: 1}]}, + {_id: 8, x: [{y: 1}, {y: 2}], v: [{w: 5}, {w: 6}]}, + {_id: 9, x: [{y: 1}, {y: [1, 2, 3]}], v: [{w: 4}, {w: [4, 5, 6]}]}, + {_id: 10, z: 1}, + {_id: 11, z: 2}, + {_id: 12, z: [1, 2, 3]}, + {_id: 13, z: 3}, + {_id: 14, z: 4}, + {_id: 15, a: 10, x: 1}, + {_id: 16, a: 10, x: 10}, + {_id: 17, x: {y: [{z: 1}, {z: 2}]}}, + {_id: 18, x: [[{y: 1}, {y: 2}], {y: 3}, {y: 4}, [[[{y: 5}]]], {y: 6}]}, + {_id: 19, x: [[{y: 1}, {y: 2}], {y: 3}, {y: 4}, [[[{y: 5}]]], {y: 6}]}, + {_id: 20, x: [[{y: {z: 1}}, {y: 2}], {y: 3}, {y: {z: 2}}, [[[{y: 5}, {y: {z: 3}}]]], {y: 6}]}, +]; +assert.commandWorked(coll.insert(documents)); + +assert.commandWorked(coll.createIndex({a: 1})); +assert.commandWorked(coll.createIndex({z: 1})); + +function checkQuery( + {expected = [], query = {}, proj = {}, sort = null, limit = null, skip = null, desc = null}, + hint) { + let findCommand = coll.find(query, proj); + if (sort) { + findCommand = findCommand.sort(sort); + } + if (limit) { + findCommand = findCommand.limit(limit); + } + if (skip) { + findCommand = findCommand.skip(skip); + } + if (hint) { + findCommand = findCommand.hint(hint); + } + + let results = findCommand.toArray(); + + function operationDescription() { + const result = {query: query}; + if (sort) { + result.sort = sort; + } + if (proj) { + result.proj = proj; + } + if (limit) { + result.limit = limit; + } + if (skip) { + result.skip = skip; + } + if (hint) { + result.hint = hint; + } + if (desc) { + result.desc = desc; + } + return tojson(result); + } + if (sort) { + assert(orderedArrayEq(results, expected), + `operation=${operationDescription()}, actual=${tojson(results)}, expected=${ + tojson(expected)}`); + } else { + assert(arrayEq(results, expected), + `operation=${operationDescription()}, actual=${tojson(results)}, expected=${ + tojson(expected)}`); + } +} + +function documentsWithExcludedField(...fieldNames) { + return documents.map(doc => { + const copy = Object.assign({}, doc); + fieldNames.forEach(name => { + delete copy[name]; + }); + return copy; + }); +} + +// Test the IDHack plan. There's no way to hint IDHack, but we trust that the planner will choose it +// for this query. +function runIDHackTest() { + checkQuery( + {desc: "_id point query", expected: [{_id: 1, a: 2, b: "y", c: 11}], query: {_id: 1}}); +} + +// These tests are intended to validate covered projections, so we prevent covered plans by +// requesting a collection scans. +function runCollScanTests() { + const testCases = [ + // + // Simple projections + // + { + desc: "_id-only projection", + expected: documents.map(doc => ({_id: doc._id})), + proj: {_id: 1} + }, + { + desc: "Single-field projection 1", + expected: [ + {_id: 0, a: 1}, {_id: 1, a: 2}, {_id: 2, a: 3}, {_id: 3}, {_id: 4}, + {_id: 5}, {_id: 6}, {_id: 7}, {_id: 8}, {_id: 9}, + {_id: 10}, {_id: 11}, {_id: 12}, {_id: 13}, {_id: 14}, + {_id: 15, a: 10}, {_id: 16, a: 10}, {_id: 17}, {_id: 18}, {_id: 19}, + {_id: 20} + ], + proj: {a: 1} + }, + { + desc: "Single-field projection 2", + expected: [ + {_id: 0}, + {_id: 1}, + {_id: 2}, + {_id: 3}, + {_id: 4}, + {_id: 5}, + {_id: 6}, + {_id: 7}, + {_id: 8}, + {_id: 9}, + {_id: 10, z: 1}, + {_id: 11, z: 2}, + {_id: 12, z: [1, 2, 3]}, + {_id: 13, z: 3}, + {_id: 14, z: 4}, + {_id: 15}, + {_id: 16}, + {_id: 17}, + {_id: 18}, + {_id: 19}, + {_id: 20} + ], + proj: {z: 1} + }, + { + desc: "Two-field projection", + expected: [ + {_id: 0, a: 1, b: "x"}, + {_id: 1, a: 2, b: "y"}, + {_id: 2, a: 3, b: "z"}, + {_id: 3}, + {_id: 4}, + {_id: 5}, + {_id: 6}, + {_id: 7}, + {_id: 8}, + {_id: 9}, + {_id: 10}, + {_id: 11}, + {_id: 12}, + {_id: 13}, + {_id: 14}, + {_id: 15, a: 10}, + {_id: 16, a: 10}, + {_id: 17}, + {_id: 18}, + {_id: 19}, + {_id: 20} + ], + proj: {b: 1, a: 1} + }, + { + desc: "Projection excluding _id", + expected: [ + {a: 1}, {a: 2}, {a: 3}, {}, {}, {}, {}, {}, {}, {}, {}, + {}, {}, {}, {}, {a: 10}, {a: 10}, {}, {}, {}, {} + ], + proj: {a: 1, _id: 0} + }, + { + desc: "$gt query with single-field projection", + expected: [{_id: 1, a: 2}, {_id: 2, a: 3}, {_id: 15, a: 10}, {_id: 16, a: 10}], + query: {a: {$gt: 1}}, + proj: {a: 1} + }, + { + desc: "Projection of missing field", + expected: [ + {_id: 0, a: 1}, {_id: 1, a: 2}, {_id: 2, a: 3}, {_id: 3}, {_id: 4}, + {_id: 5}, {_id: 6}, {_id: 7}, {_id: 8}, {_id: 9}, + {_id: 10}, {_id: 11}, {_id: 12}, {_id: 13}, {_id: 14}, + {_id: 15, a: 10}, {_id: 16, a: 10}, {_id: 17}, {_id: 18}, {_id: 19}, + {_id: 20} + ], + proj: {a: 1, nonexistent: 1} + }, + // + // Dotted-path projections. + // + { + desc: "Dotted-path projection explicitly including _id", + expected: [ + {_id: 0}, + {_id: 1}, + {_id: 2}, + {_id: 3, x: {y: 1}}, + {_id: 4, x: {y: 2}}, + {_id: 5, x: {y: [1, 2, 3]}}, + {_id: 6, x: {y: 4}}, + {_id: 7, x: [{y: 1}]}, + {_id: 8, x: [{y: 1}, {y: 2}]}, + {_id: 9, x: [{y: 1}, {y: [1, 2, 3]}]}, + {_id: 10}, + {_id: 11}, + {_id: 12}, + {_id: 13}, + {_id: 14}, + {_id: 15}, + {_id: 16}, + {_id: 17, x: {y: [{z: 1}, {z: 2}]}}, + {_id: 18, x: [[{y: 1}, {y: 2}], {y: 3}, {y: 4}, [[[{y: 5}]]], {y: 6}]}, + {_id: 19, x: [[{y: 1}, {y: 2}], {y: 3}, {y: 4}, [[[{y: 5}]]], {y: 6}]}, + { + _id: 20, + x: [ + [{y: {z: 1}}, {y: 2}], + {y: 3}, + {y: {z: 2}}, + [[[{y: 5}, {y: {z: 3}}]]], + {y: 6} + ] + } + ], + proj: {_id: 1, "x.y": 1} + }, + { + desc: "Dotted-path projection implicitly including _id", + expected: [ + {_id: 0}, + {_id: 1}, + {_id: 2}, + {_id: 3, x: {y: 1}}, + {_id: 4, x: {y: 2}}, + {_id: 5, x: {y: [1, 2, 3]}}, + {_id: 6, x: {y: 4}}, + {_id: 7, x: [{y: 1}]}, + {_id: 8, x: [{y: 1}, {y: 2}]}, + {_id: 9, x: [{y: 1}, {y: [1, 2, 3]}]}, + {_id: 10}, + {_id: 11}, + {_id: 12}, + {_id: 13}, + {_id: 14}, + {_id: 15}, + {_id: 16}, + {_id: 17, x: {y: [{z: 1}, {z: 2}]}}, + {_id: 18, x: [[{y: 1}, {y: 2}], {y: 3}, {y: 4}, [[[{y: 5}]]], {y: 6}]}, + {_id: 19, x: [[{y: 1}, {y: 2}], {y: 3}, {y: 4}, [[[{y: 5}]]], {y: 6}]}, + { + _id: 20, + x: [ + [{y: {z: 1}}, {y: 2}], + {y: 3}, + {y: {z: 2}}, + [[[{y: 5}, {y: {z: 3}}]]], + {y: 6} + ] + } + ], + proj: {"x.y": 1} + }, + { + desc: "Dotted-path projection excluding _id", + expected: [ + {}, + {}, + {}, + {x: {y: 1}}, + {x: {y: 2}}, + {x: {y: [1, 2, 3]}}, + {x: {y: 4}}, + {x: [{y: 1}]}, + {x: [{y: 1}, {y: 2}]}, + {x: [{y: 1}, {y: [1, 2, 3]}]}, + {}, + {}, + {}, + {}, + {}, + {}, + {}, + {x: {y: [{z: 1}, {z: 2}]}}, + {x: [[{y: 1}, {y: 2}], {y: 3}, {y: 4}, [[[{y: 5}]]], {y: 6}]}, + {x: [[{y: 1}, {y: 2}], {y: 3}, {y: 4}, [[[{y: 5}]]], {y: 6}]}, + {x: [[{y: {z: 1}}, {y: 2}], {y: 3}, {y: {z: 2}}, [[[{y: 5}, {y: {z: 3}}]]], {y: 6}]} + + ], + proj: {_id: 0, "x.y": 1} + }, + { + desc: "Three-level dotted-path projection", + expected: [ + {_id: 0}, + {_id: 1}, + {_id: 2}, + {_id: 3, x: {}}, + {_id: 4, x: {}}, + {_id: 5, x: {y: []}}, + {_id: 6, x: {}}, + {_id: 7, x: [{}]}, + {_id: 8, x: [{}, {}]}, + {_id: 9, x: [{}, {y: []}]}, + {_id: 10}, + {_id: 11}, + {_id: 12}, + {_id: 13}, + {_id: 14}, + {_id: 15}, + {_id: 16}, + {_id: 17, x: {y: [{z: 1}, {z: 2}]}}, + {_id: 18, x: [[{}, {}], {}, {}, [[[{}]]], {}]}, + {_id: 19, x: [[{}, {}], {}, {}, [[[{}]]], {}]}, + { + _id: 20, + x: [ + [ + {y: {z: 1}}, + { + + } + ], + { + + }, + {y: {z: 2}}, + [[[ + { + + }, + {y: {z: 3}} + ]]], + { + + } + ] + } + ], + proj: {"x.y.z": 1} + }, + { + desc: "Two-level dotted-path projection", + expected: [ + {_id: 0}, {_id: 1}, {_id: 2}, {_id: 3}, {_id: 4}, {_id: 5}, {_id: 6}, + {_id: 7}, {_id: 8}, {_id: 9}, {_id: 10}, {_id: 11}, {_id: 12, z: []}, {_id: 13}, + {_id: 14}, {_id: 15}, {_id: 16}, {_id: 17}, {_id: 18}, {_id: 19}, {_id: 20} + ], + proj: {"z.a": 1} + }, + { + desc: "Two two-level dotted-path projections", + expected: [ + {_id: 0}, + {_id: 1}, + {_id: 2}, + {_id: 3, x: {y: 1}}, + {_id: 4, x: {y: 2}}, + {_id: 5, x: {y: [1, 2, 3]}, v: {w: [4, 5, 6]}}, + {_id: 6, x: {y: 4}, v: {w: 4}}, + {_id: 7, x: [{y: 1}], v: [{w: 1}]}, + {_id: 8, x: [{y: 1}, {y: 2}], v: [{w: 5}, {w: 6}]}, + {_id: 9, x: [{y: 1}, {y: [1, 2, 3]}], v: [{w: 4}, {w: [4, 5, 6]}]}, + {_id: 10}, + {_id: 11}, + {_id: 12}, + {_id: 13}, + {_id: 14}, + {_id: 15}, + {_id: 16}, + {_id: 17, x: {y: [{z: 1}, {z: 2}]}}, + {_id: 18, x: [[{y: 1}, {y: 2}], {y: 3}, {y: 4}, [[[{y: 5}]]], {y: 6}]}, + {_id: 19, x: [[{y: 1}, {y: 2}], {y: 3}, {y: 4}, [[[{y: 5}]]], {y: 6}]}, + { + _id: 20, + x: [ + [{y: {z: 1}}, {y: 2}], + {y: 3}, + {y: {z: 2}}, + [[[{y: 5}, {y: {z: 3}}]]], + {y: 6} + ] + } + ], + proj: {"x.y": 1, "v.w": 1} + }, + { + desc: "$gt query with two-level dotted-path projection", + expected: [ + {_id: 4}, + {_id: 5, v: {w: [4, 5, 6]}}, + {_id: 6, v: {w: 4}}, + {_id: 8, v: [{w: 5}, {w: 6}]}, + {_id: 9, v: [{w: 4}, {w: [4, 5, 6]}]}, + {_id: 18}, + {_id: 19}, + {_id: 20}, + ], + query: {'x.y': {$gt: 1}}, + proj: {"v.w": 1} + }, + { + desc: "Three-level dotted-path component with missing field", + expected: [ + {_id: 0}, + {_id: 1}, + {_id: 2}, + {_id: 3, x: {}}, + {_id: 4, x: {}}, + {_id: 5, x: {y: []}}, + {_id: 6, x: {}}, + {_id: 7, x: [{}]}, + {_id: 8, x: [{}, {}]}, + {_id: 9, x: [{}, {y: []}]}, + {_id: 10}, + {_id: 11}, + {_id: 12}, + {_id: 13}, + {_id: 14}, + {_id: 15}, + {_id: 16}, + {_id: 17, x: {y: [{}, {}]}}, + {_id: 18, x: [[{}, {}], {}, {}, [[[{}]]], {}]}, + {_id: 19, x: [[{}, {}], {}, {}, [[[{}]]], {}]}, + {_id: 20, x: [[{y: {}}, {}], {}, {y: {}}, [[[{}, {y: {}}]]], {}]} + ], + proj: {"x.y.nonexistent": 1} + }, + { + desc: "Dotted-path exclusion projection explicitly including _id", + expected: [ + {_id: 0, a: 1, b: "x", c: 10}, + {_id: 1, a: 2, b: "y", c: 11}, + {_id: 2, a: 3, b: "z", c: 12}, + {_id: 3, x: {}}, + {_id: 4, x: {}}, + {_id: 5, x: {}, v: {w: [4, 5, 6]}}, + {_id: 6, x: {}, v: {w: 4}}, + {_id: 7, x: [{}], v: [{w: 1}]}, + {_id: 8, x: [{}, {}], v: [{w: 5}, {w: 6}]}, + {_id: 9, x: [{}, {}], v: [{w: 4}, {w: [4, 5, 6]}]}, + {_id: 10, z: 1}, + {_id: 11, z: 2}, + {_id: 12, z: [1, 2, 3]}, + {_id: 13, z: 3}, + {_id: 14, z: 4}, + {_id: 15, a: 10, x: 1}, + {_id: 16, a: 10, x: 10}, + {_id: 17, x: {}}, + {_id: 18, x: [[{}, {}], {}, {}, [[[{}]]], {}]}, + {_id: 19, x: [[{}, {}], {}, {}, [[[{}]]], {}]}, + {_id: 20, x: [[{}, {}], {}, {}, [[[{}, {}]]], {}]} + ], + proj: {_id: 1, "x.y": 0} + }, + { + desc: "Dotted-path exclusion projection implicitly including _id", + expected: [ + {_id: 0, a: 1, b: "x", c: 10}, + {_id: 1, a: 2, b: "y", c: 11}, + {_id: 2, a: 3, b: "z", c: 12}, + {_id: 3, x: {}}, + {_id: 4, x: {}}, + {_id: 5, x: {}, v: {w: [4, 5, 6]}}, + {_id: 6, x: {}, v: {w: 4}}, + {_id: 7, x: [{}], v: [{w: 1}]}, + {_id: 8, x: [{}, {}], v: [{w: 5}, {w: 6}]}, + {_id: 9, x: [{}, {}], v: [{w: 4}, {w: [4, 5, 6]}]}, + {_id: 10, z: 1}, + {_id: 11, z: 2}, + {_id: 12, z: [1, 2, 3]}, + {_id: 13, z: 3}, + {_id: 14, z: 4}, + {_id: 15, a: 10, x: 1}, + {_id: 16, a: 10, x: 10}, + {_id: 17, x: {}}, + {_id: 18, x: [[{}, {}], {}, {}, [[[{}]]], {}]}, + {_id: 19, x: [[{}, {}], {}, {}, [[[{}]]], {}]}, + {_id: 20, x: [[{}, {}], {}, {}, [[[{}, {}]]], {}]} + ], + proj: {"x.y": 0} + }, + { + desc: "Dotted-path exclusion projection excluding _id", + expected: [ + {a: 1, b: "x", c: 10}, + {a: 2, b: "y", c: 11}, + {a: 3, b: "z", c: 12}, + {x: {}}, + {x: {}}, + {x: {}, v: {w: [4, 5, 6]}}, + {x: {}, v: {w: 4}}, + {x: [{}], v: [{w: 1}]}, + {x: [{}, {}], v: [{w: 5}, {w: 6}]}, + {x: [{}, {}], v: [{w: 4}, {w: [4, 5, 6]}]}, + {z: 1}, + {z: 2}, + {z: [1, 2, 3]}, + {z: 3}, + {z: 4}, + {a: 10, x: 1}, + {a: 10, x: 10}, + {x: {}}, + {x: [[{}, {}], {}, {}, [[[{}]]], {}]}, + {x: [[{}, {}], {}, {}, [[[{}]]], {}]}, + {x: [[{}, {}], {}, {}, [[[{}, {}]]], {}]} + ], + proj: {_id: 0, "x.y": 0} + }, + { + desc: "Three-level dotted-path exclusion projection", + expected: [ + {_id: 0, a: 1, b: "x", c: 10}, + {_id: 1, a: 2, b: "y", c: 11}, + {_id: 2, a: 3, b: "z", c: 12}, + {_id: 3, x: {y: 1}}, + {_id: 4, x: {y: 2}}, + {_id: 5, x: {y: [1, 2, 3]}, v: {w: [4, 5, 6]}}, + {_id: 6, x: {y: 4}, v: {w: 4}}, + {_id: 7, x: [{y: 1}], v: [{w: 1}]}, + {_id: 8, x: [{y: 1}, {y: 2}], v: [{w: 5}, {w: 6}]}, + {_id: 9, x: [{y: 1}, {y: [1, 2, 3]}], v: [{w: 4}, {w: [4, 5, 6]}]}, + {_id: 10, z: 1}, + {_id: 11, z: 2}, + {_id: 12, z: [1, 2, 3]}, + {_id: 13, z: 3}, + {_id: 14, z: 4}, + {_id: 15, a: 10, x: 1}, + {_id: 16, a: 10, x: 10}, + {_id: 17, x: {y: [{}, {}]}}, + {_id: 18, x: [[{y: 1}, {y: 2}], {y: 3}, {y: 4}, [[[{y: 5}]]], {y: 6}]}, + {_id: 19, x: [[{y: 1}, {y: 2}], {y: 3}, {y: 4}, [[[{y: 5}]]], {y: 6}]}, + {_id: 20, x: [[{y: {}}, {y: 2}], {y: 3}, {y: {}}, [[[{y: 5}, {y: {}}]]], {y: 6}]} + ], + proj: {"x.y.z": 0} + }, + { + desc: "Two-level dotted-path exclusion projection", + expected: [ + {_id: 0, a: 1, b: "x", c: 10}, + {_id: 1, a: 2, b: "y", c: 11}, + {_id: 2, a: 3, b: "z", c: 12}, + {_id: 3, x: {y: 1}}, + {_id: 4, x: {y: 2}}, + {_id: 5, x: {y: [1, 2, 3]}, v: {w: [4, 5, 6]}}, + {_id: 6, x: {y: 4}, v: {w: 4}}, + {_id: 7, x: [{y: 1}], v: [{w: 1}]}, + {_id: 8, x: [{y: 1}, {y: 2}], v: [{w: 5}, {w: 6}]}, + {_id: 9, x: [{y: 1}, {y: [1, 2, 3]}], v: [{w: 4}, {w: [4, 5, 6]}]}, + {_id: 10, z: 1}, + {_id: 11, z: 2}, + {_id: 12, z: [1, 2, 3]}, + {_id: 13, z: 3}, + {_id: 14, z: 4}, + {_id: 15, a: 10, x: 1}, + {_id: 16, a: 10, x: 10}, + {_id: 17, x: {y: [{z: 1}, {z: 2}]}}, + {_id: 18, x: [[{y: 1}, {y: 2}], {y: 3}, {y: 4}, [[[{y: 5}]]], {y: 6}]}, + {_id: 19, x: [[{y: 1}, {y: 2}], {y: 3}, {y: 4}, [[[{y: 5}]]], {y: 6}]}, + { + _id: 20, + x: [ + [{y: {z: 1}}, {y: 2}], + {y: 3}, + {y: {z: 2}}, + [[[{y: 5}, {y: {z: 3}}]]], + {y: 6} + ] + } + ], + proj: {"z.a": 0} + }, + { + desc: "Exclusion projection with two dotted paths", + expected: [ + {_id: 0, a: 1, b: "x", c: 10}, + {_id: 1, a: 2, b: "y", c: 11}, + {_id: 2, a: 3, b: "z", c: 12}, + {_id: 3, x: {}}, + {_id: 4, x: {}}, + {_id: 5, x: {}, v: {}}, + {_id: 6, x: {}, v: {}}, + {_id: 7, x: [{}], v: [{}]}, + {_id: 8, x: [{}, {}], v: [{}, {}]}, + {_id: 9, x: [{}, {}], v: [{}, {}]}, + {_id: 10, z: 1}, + {_id: 11, z: 2}, + {_id: 12, z: [1, 2, 3]}, + {_id: 13, z: 3}, + {_id: 14, z: 4}, + {_id: 15, a: 10, x: 1}, + {_id: 16, a: 10, x: 10}, + {_id: 17, x: {}}, + {_id: 18, x: [[{}, {}], {}, {}, [[[{}]]], {}]}, + {_id: 19, x: [[{}, {}], {}, {}, [[[{}]]], {}]}, + {_id: 20, x: [[{}, {}], {}, {}, [[[{}, {}]]], {}]} + ], + proj: {"x.y": 0, "v.w": 0} + }, + { + desc: "$gt query with two-level dotted-path exclusion projection", + expected: [ + {_id: 4, x: {y: 2}}, + {_id: 5, x: {y: [1, 2, 3]}, v: {}}, + {_id: 6, x: {y: 4}, v: {}}, + {_id: 8, x: [{y: 1}, {y: 2}], v: [{}, {}]}, + {_id: 9, x: [{y: 1}, {y: [1, 2, 3]}], v: [{}, {}]}, + {_id: 18, x: [[{y: 1}, {y: 2}], {y: 3}, {y: 4}, [[[{y: 5}]]], {y: 6}]}, + {_id: 19, x: [[{y: 1}, {y: 2}], {y: 3}, {y: 4}, [[[{y: 5}]]], {y: 6}]}, + { + _id: 20, + x: [ + [{y: {z: 1}}, {y: 2}], + {y: 3}, + {y: {z: 2}}, + [[[{y: 5}, {y: {z: 3}}]]], + {y: 6} + ] + } + ], + query: {"x.y": {$gt: 1}}, + proj: {"v.w": 0} + }, + { + desc: "Three-level dotted-path exclusion projection with missing field", + expected: [ + {_id: 0, a: 1, b: "x", c: 10}, + {_id: 1, a: 2, b: "y", c: 11}, + {_id: 2, a: 3, b: "z", c: 12}, + {_id: 3, x: {y: 1}}, + {_id: 4, x: {y: 2}}, + {_id: 5, x: {y: [1, 2, 3]}, v: {w: [4, 5, 6]}}, + {_id: 6, x: {y: 4}, v: {w: 4}}, + {_id: 7, x: [{y: 1}], v: [{w: 1}]}, + {_id: 8, x: [{y: 1}, {y: 2}], v: [{w: 5}, {w: 6}]}, + {_id: 9, x: [{y: 1}, {y: [1, 2, 3]}], v: [{w: 4}, {w: [4, 5, 6]}]}, + {_id: 10, z: 1}, + {_id: 11, z: 2}, + {_id: 12, z: [1, 2, 3]}, + {_id: 13, z: 3}, + {_id: 14, z: 4}, + {_id: 15, a: 10, x: 1}, + {_id: 16, a: 10, x: 10}, + {_id: 17, x: {y: [{z: 1}, {z: 2}]}}, + {_id: 18, x: [[{y: 1}, {y: 2}], {y: 3}, {y: 4}, [[[{y: 5}]]], {y: 6}]}, + {_id: 19, x: [[{y: 1}, {y: 2}], {y: 3}, {y: 4}, [[[{y: 5}]]], {y: 6}]}, + { + _id: 20, + x: [ + [{y: {z: 1}}, {y: 2}], + {y: 3}, + {y: {z: 2}}, + [[[{y: 5}, {y: {z: 3}}]]], + {y: 6} + ] + } + ], + proj: {"x.y.nonexistent": 0} + }, + // + // Simple exclusion projections. + // + { + desc: "_id-exclusion projection", + expected: documentsWithExcludedField("_id"), + proj: {_id: 0} + }, + { + desc: "Single-field exclusion projection 1", + expected: documentsWithExcludedField("a"), + proj: {a: 0} + }, + { + desc: "Single-field exclusion projection 2", + expected: documentsWithExcludedField("z"), + proj: {z: 0} + }, + { + desc: "Exclusion projection with two fields", + expected: documentsWithExcludedField("b", "a"), + proj: {b: 0, a: 0} + }, + { + desc: "Exclusion projection explicitly including _id", + expected: documentsWithExcludedField("a"), + proj: {a: 0, _id: 1} + }, + { + desc: "Exclusion projection with missing field", + expected: documentsWithExcludedField("a", "nonexistent"), + proj: {a: 0, nonexistent: 0} + } + ]; + + testCases.forEach(test => checkQuery(test, {$natural: 1})); +} + +function runFindTestsWithHint(hint) { + // Note that sorts are chosen so that results are deterministic. Either there are no ties, or + // any tied documents are filted out by a "limit" parameter. + const testCases = [ + {desc: "All-document query", expected: documents, query: {}}, + {desc: "Point query 1", expected: [documents[1]], query: {a: 2}}, + { + desc: "$gt query", + expected: [documents[1], documents[2], documents[15], documents[16]], + query: {a: {$gt: 1}} + }, + { + desc: "$gte query", + expected: [documents[0], documents[1], documents[2], documents[15], documents[16]], + query: {a: {$gte: 1}} + }, + {desc: "$lt query", expected: [], query: {a: {$lt: 1}}}, + {desc: "$lte query", expected: [documents[0]], query: {a: {$lte: 1}}}, + {desc: "(Range) query", expected: [documents[1]], query: {a: {$gt: 1, $lt: 3}}}, + { + desc: "(Range] query", + expected: [documents[1], documents[2]], + query: {a: {$gt: 1, $lte: 3}} + }, + { + desc: "[Range) query", + expected: [documents[0], documents[1]], + query: {a: {$gte: 1, $lt: 3}} + }, + { + desc: "[Range] query", + expected: [documents[0], documents[1], documents[2]], + query: {a: {$gte: 1, $lte: 3}} + }, + {desc: "Query with implicit conjunction", expected: [documents[15]], query: {a: 10, x: 1}}, + { + desc: "$lt query with sort", + expected: [documents[0], documents[1]], + query: {a: {$lt: 3}}, + sort: {a: 1} + }, + { + desc: "$lte query with compound sort 1", + expected: [documents[0], documents[1], documents[2]], + query: {a: {$lte: 3}}, + sort: {a: 1, b: 1} + }, + { + desc: "$lte query with compound sort 2", + expected: [documents[0], documents[1], documents[2]], + query: {a: {$lte: 3}}, + sort: {b: 1, a: 1} + }, + {expected: [documents[0]], query: {a: {$lt: 3}}, sort: {a: 1}, limit: 1}, + { + desc: "$lte query with compound sort and limit", + expected: [documents[0]], + query: {a: {$lte: 3}}, + sort: {a: 1, b: 1}, + limit: 1, + }, + { + desc: "Point query with limit", + expected: [documents[0]], + query: {a: 1}, + limit: 2, + hint: hint + }, + {desc: "Point query siwth skip", expected: [], query: {a: 1}, skip: 2}, + {desc: "Point query siwth skip and limit", expected: [], query: {a: 1}, skip: 1, limit: 1}, + {desc: "Point query 2", expected: [documents[11], documents[12]], query: {z: 2}}, + { + desc: "Query on dotted path", + expected: [documents[4], documents[5], documents[8], documents[9]], + query: {"x.y": 2} + }, + {desc: "Query on dotted path returning no documents", expected: [], query: {"x.y": 5}} + ]; + + testCases.forEach(test => checkQuery(test, hint)); +} + +runIDHackTest(); + +runCollScanTests(); + +runFindTestsWithHint({$natural: 1}); +runFindTestsWithHint({a: 1}); +runFindTestsWithHint({z: 1}); // Multi-key +}()); |