summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJustin Seyster <justin.seyster@mongodb.com>2020-09-29 20:16:14 -0400
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2020-09-30 03:55:52 +0000
commit2c81f4579bbe0465acbd3ba1fe34adc5ec2033a4 (patch)
treec2742e77bbbb6639718d265006e61bbf15566011
parent25beba702528e9dfec59f9bf2d1d66e8e7cda657 (diff)
downloadmongo-2c81f4579bbe0465acbd3ba1fe34adc5ec2033a4.tar.gz
SERVER-51203 More find subsystem tests for better SBE coverage
This reverts commit c27100db0125e1792766497a428b28bef2a6135d.
-rwxr-xr-xbuildscripts/resmokeconfig/suites/cst_jscore_passthrough.yml2
-rw-r--r--jstests/core/computed_projections.js1105
-rw-r--r--jstests/core/find_project_sort.js808
-rw-r--r--jstests/libs/sbe_assert_error_override.js2
4 files changed, 1917 insertions, 0 deletions
diff --git a/buildscripts/resmokeconfig/suites/cst_jscore_passthrough.yml b/buildscripts/resmokeconfig/suites/cst_jscore_passthrough.yml
index 3816970423f..6aa50d8dc8e 100755
--- a/buildscripts/resmokeconfig/suites/cst_jscore_passthrough.yml
+++ b/buildscripts/resmokeconfig/suites/cst_jscore_passthrough.yml
@@ -83,6 +83,7 @@ selector:
- jstests/core/collection_truncate.js
- jstests/core/command_json_schema_field.js
- jstests/core/command_let_variables.js
+ - jstests/core/computed_projections.js
- jstests/core/connection_string_validation.js
- jstests/core/count11.js
- jstests/core/count4.js
@@ -157,6 +158,7 @@ selector:
- jstests/core/find_and_modify_server6226.js
- jstests/core/find_and_modify_server6865.js
- jstests/core/find_dedup.js
+ - jstests/core/find_project_sort.js
- jstests/core/finda.js
- jstests/core/fts_mix.js
- jstests/core/geo2.js
diff --git a/jstests/core/computed_projections.js b/jstests/core/computed_projections.js
new file mode 100644
index 00000000000..3da51cfcdc9
--- /dev/null
+++ b/jstests/core/computed_projections.js
@@ -0,0 +1,1105 @@
+// @tags: [
+// # Can't set the 'failOnPoisonedFieldLookup' failpoint on mongos.
+// assumes_against_mongod_not_mongos,
+// ]
+
+(function() {
+"use strict";
+
+load("jstests/aggregation/extras/utils.js"); // For arrayEq and orderedArrayEq.
+
+if (assert
+ .commandWorked(
+ db.adminCommand({getParameter: 1, internalQueryEnableSlotBasedExecutionEngine: 1}))
+ .internalQueryEnableSlotBasedExecutionEngine) {
+ // Override error-code-checking APIs. We only load this when SBE is explicitly enabled, because
+ // it causes failures in the parallel suites.
+ load("jstests/libs/sbe_assert_error_override.js");
+}
+
+// It is safe for other tests to run while this failpoint is active, so long as those tests do not
+// use documents containing a field with "POISON" as their name.
+assert.commandWorked(
+ db.adminCommand({configureFailPoint: "failOnPoisonedFieldLookup", mode: "alwaysOn"}));
+
+const coll = db.computed_projection;
+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, i: {j: 5}, k: {l: 10}},
+ {_id: 20, x: [[{y: 1}, {y: 2}], {y: 3}, {y: 4}, [[[{y: 5}]]], {y: 6}]},
+ {_id: 21, x: [[{y: {z: 1}}, {y: 2}], {y: 3}, {y: {z: 2}}, [[[{y: 5}, {y: {z: 3}}]]], {y: 6}]},
+ {_id: 22, tf: [true, false], ff: [false, false], t: true, f: false, n: null, a: 1, b: 0},
+ {_id: 23, i1: NumberInt(1), i2: NumberInt(-1), i3: NumberInt(-2147483648)},
+ {_id: 24, l1: NumberLong("12345678900"), l2: NumberLong("-12345678900")},
+ {_id: 25, s: "string", l: NumberLong("-9223372036854775808"), n: null},
+ {_id: 26, d1: 4.6, d2: -4.6, dec1: NumberDecimal("4.6"), dec2: NumberDecimal("-4.6")}
+];
+assert.commandWorked(coll.insert(documents));
+
+// A concise way to express an "expected" result that takes the form
+// [{_id: 0, foo: <BOOL>}, {_id, 1, foo: <BOOL>}, ..., {_id: 26, foo: <BOOL>}] by passing an object
+// of the form {foo: [INTEGER_LIST]}, where INTEGER_LIST is the list of '_id' values for documents
+// where "foo" should be true.
+function computedProjectionWithBoolValues(boolProj) {
+ return documents.map(doc => {
+ const projectedDoc = {_id: doc._id};
+ Object.keys(boolProj).forEach(key => {
+ projectedDoc[key] = boolProj[key].includes(doc._id);
+ });
+ return projectedDoc;
+ });
+}
+
+const testCases = [
+ {
+ desc: "Single-level path 1",
+ expected: [
+ {_id: 0, foo: 1}, {_id: 1, foo: 2}, {_id: 2, foo: 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, foo: 10}, {_id: 16, foo: 10}, {_id: 17}, {_id: 18}, {_id: 19},
+ {_id: 20}, {_id: 21}, {_id: 22, foo: 1}, {_id: 23}, {_id: 24},
+ {_id: 25}, {_id: 26}
+ ],
+ query: {},
+ proj: {_id: 1, foo: "$a"}
+ },
+ {
+ desc: "Single-level path 2",
+ expected: [
+ {_id: 0, foo: 1}, {_id: 1, foo: 2}, {_id: 2, foo: 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, foo: 10}, {_id: 16, foo: 10}, {_id: 17}, {_id: 18}, {_id: 19},
+ {_id: 20}, {_id: 21}, {_id: 22, foo: 1}, {_id: 23}, {_id: 24},
+ {_id: 25}, {_id: 26}
+ ],
+ query: {},
+ proj: {foo: "$a"}
+ },
+ {
+ desc: "Single-level path 3",
+ expected: [
+ {foo: 1}, {foo: 2}, {foo: 3}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {}, {},
+ {}, {foo: 10}, {foo: 10}, {}, {}, {}, {}, {}, {foo: 1}, {}, {}, {}, {}
+ ],
+ query: {},
+ proj: {_id: 0, foo: "$a"}
+ },
+ {
+ desc: "Two single-level paths",
+ expected: [
+ {_id: 0, foo: 1, bar: "x"},
+ {_id: 1, foo: 2, bar: "y"},
+ {_id: 2, foo: 3, bar: "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, foo: 10},
+ {_id: 16, foo: 10},
+ {_id: 17},
+ {_id: 18},
+ {_id: 19},
+ {_id: 20},
+ {_id: 21},
+ {_id: 22, foo: 1, bar: 0},
+ {_id: 23},
+ {_id: 24},
+ {_id: 25},
+ {_id: 26}
+ ],
+ query: {},
+ proj: {_id: 1, foo: "$a", bar: "$b"}
+ },
+ {
+ desc: "Simple addition",
+ expected: [{_id: 0, foo: 11}, {_id: 1, foo: 13}, {_id: 2, foo: 15}],
+ query: {c: {$gt: 0}},
+ proj: {foo: {$add: ["$a", "$c"]}}
+ },
+ {
+ desc: "Single-level path 4",
+ expected: [
+ {_id: 0, a: 1, foo: "x"},
+ {_id: 1, a: 2, foo: "y"},
+ {_id: 2, a: 3, foo: "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},
+ {_id: 21},
+ {_id: 22, a: 1, foo: 0},
+ {_id: 23},
+ {_id: 24},
+ {_id: 25},
+ {_id: 26}
+ ],
+ query: {},
+ proj: {a: 1, foo: "$b"}
+ },
+ {
+ desc: "Two-level path 1",
+ expected: [
+ {_id: 0},
+ {_id: 1},
+ {_id: 2},
+ {_id: 3, foo: 1},
+ {_id: 4, foo: 2},
+ {_id: 5, foo: [1, 2, 3]},
+ {_id: 6, foo: 4},
+ {_id: 7, foo: [1]},
+ {_id: 8, foo: [1, 2]},
+ {_id: 9, foo: [1, [1, 2, 3]]},
+ {_id: 10},
+ {_id: 11},
+ {_id: 12},
+ {_id: 13},
+ {_id: 14},
+ {_id: 15},
+ {_id: 16},
+ {_id: 17, foo: [{z: 1}, {z: 2}]},
+ {_id: 18, foo: [3, 4, 6]},
+ {_id: 19},
+ {_id: 20, foo: [3, 4, 6]},
+ {_id: 21, foo: [3, {z: 2}, 6]},
+ {_id: 22},
+ {_id: 23},
+ {_id: 24},
+ {_id: 25},
+ {_id: 26}
+ ],
+ query: {},
+ proj: {_id: 1, foo: "$x.y"}
+ },
+ {
+ desc: "Two-level path 2",
+ expected: [
+ {_id: 0},
+ {_id: 1},
+ {_id: 2},
+ {_id: 3, foo: 1},
+ {_id: 4, foo: 2},
+ {_id: 5, foo: [1, 2, 3]},
+ {_id: 6, foo: 4},
+ {_id: 7, foo: [1]},
+ {_id: 8, foo: [1, 2]},
+ {_id: 9, foo: [1, [1, 2, 3]]},
+ {_id: 10},
+ {_id: 11},
+ {_id: 12},
+ {_id: 13},
+ {_id: 14},
+ {_id: 15},
+ {_id: 16},
+ {_id: 17, foo: [{z: 1}, {z: 2}]},
+ {_id: 18, foo: [3, 4, 6]},
+ {_id: 19},
+ {_id: 20, foo: [3, 4, 6]},
+ {_id: 21, foo: [3, {z: 2}, 6]},
+ {_id: 22},
+ {_id: 23},
+ {_id: 24},
+ {_id: 25},
+ {_id: 26}
+ ],
+ query: {},
+ proj: {foo: "$x.y"}
+ },
+ {
+ desc: "Two-level path 3",
+ expected: [
+ {},
+ {},
+ {},
+ {foo: 1},
+ {foo: 2},
+ {foo: [1, 2, 3]},
+ {foo: 4},
+ {foo: [1]},
+ {foo: [1, 2]},
+ {foo: [1, [1, 2, 3]]},
+ {},
+ {},
+ {},
+ {},
+ {},
+ {},
+ {},
+ {foo: [{z: 1}, {z: 2}]},
+ {foo: [3, 4, 6]},
+ {},
+ {foo: [3, 4, 6]},
+ {foo: [3, {z: 2}, 6]},
+ {},
+ {},
+ {},
+ {},
+ {}
+ ],
+ query: {},
+ proj: {_id: 0, foo: "$x.y"}
+ },
+ {
+ desc: "Two two-level paths",
+ expected: [
+ {_id: 0},
+ {_id: 1},
+ {_id: 2},
+ {_id: 3, foo: 1},
+ {_id: 4, foo: 2},
+ {_id: 5, foo: [1, 2, 3], bar: [4, 5, 6]},
+ {_id: 6, foo: 4, bar: 4},
+ {_id: 7, foo: [1], bar: [1]},
+ {_id: 8, foo: [1, 2], bar: [5, 6]},
+ {_id: 9, foo: [1, [1, 2, 3]], bar: [4, [4, 5, 6]]},
+ {_id: 10},
+ {_id: 11},
+ {_id: 12},
+ {_id: 13},
+ {_id: 14},
+ {_id: 15},
+ {_id: 16},
+ {_id: 17, foo: [{z: 1}, {z: 2}]},
+ {_id: 18, foo: [3, 4, 6]},
+ {_id: 19},
+ {_id: 20, foo: [3, 4, 6]},
+ {_id: 21, foo: [3, {z: 2}, 6]},
+ {_id: 22},
+ {_id: 23},
+ {_id: 24},
+ {_id: 25},
+ {_id: 26}
+ ],
+ query: {},
+ proj: {foo: "$x.y", bar: "$v.w"}
+ },
+ {
+ desc: "Addition of two-level paths",
+ expected: [{_id: 19, foo: 15}],
+ query: {"i.j": {$gt: 0}},
+ proj: {foo: {$add: ["$i.j", "$k.l"]}}
+ },
+ {
+ desc: "Dotted-path projection and two-level path",
+ expected: [
+ {_id: 0},
+ {_id: 1},
+ {_id: 2},
+ {_id: 3, x: {y: 1}},
+ {_id: 4, x: {y: 2}},
+ {_id: 5, x: {y: [1, 2, 3]}, foo: [4, 5, 6]},
+ {_id: 6, x: {y: 4}, foo: 4},
+ {_id: 7, x: [{y: 1}], foo: [1]},
+ {_id: 8, x: [{y: 1}, {y: 2}], foo: [5, 6]},
+ {_id: 9, x: [{y: 1}, {y: [1, 2, 3]}], foo: [4, [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},
+ {_id: 20, x: [[{y: 1}, {y: 2}], {y: 3}, {y: 4}, [[[{y: 5}]]], {y: 6}]},
+ {
+ _id: 21,
+ x: [[{y: {z: 1}}, {y: 2}], {y: 3}, {y: {z: 2}}, [[[{y: 5}, {y: {z: 3}}]]], {y: 6}]
+ },
+ {_id: 22},
+ {_id: 23},
+ {_id: 24},
+ {_id: 25},
+ {_id: 26}
+ ],
+ query: {},
+ proj: {"x.y": 1, foo: "$v.w"}
+ },
+ //
+ // Test simple expressions with the $abs operator.
+ //
+ {
+ desc: "$abs operator",
+ expected: [{_id: 23, abs_i1: 1, abs_i2: 1, abs_i3: NumberLong("2147483648")}],
+ query: {i1: 1},
+ proj: {abs_i1: {$abs: "$i1"}, abs_i2: {$abs: "$i2"}, abs_i3: {$abs: "$i3"}}
+ },
+ {
+ desc: "$abs with NumberLong input",
+ expected: [{_id: 24, abs_l1: NumberLong("12345678900"), abs_l2: NumberLong("12345678900")}],
+ query: {l1: NumberLong("12345678900")},
+ proj: {abs_l1: {$abs: "$l1"}, abs_l2: {$abs: "$l2"}}
+ },
+ {
+ desc: "$abs with NumberDecimal input",
+ expected: [{
+ _id: 26,
+ abs_d1: 4.6,
+ abs_d2: 4.6,
+ abs_dec1: NumberDecimal("4.6"),
+ abs_dec2: NumberDecimal("4.6")
+ }],
+ query: {d1: 4.6},
+ proj: {
+ abs_d1: {$abs: "$d1"},
+ abs_d2: {$abs: "$d2"},
+ abs_dec1: {$abs: "$dec1"},
+ abs_dec2: {$abs: "$dec2"}
+ }
+ },
+ {
+ desc: "$abs with string input",
+ expectedErrorCode: 28765,
+ query: {s: "string"},
+ proj: {abs_s: {$abs: "$s"}}
+ },
+ {
+ desc: "$abs with MIN_LONG_LONG input",
+ expectedErrorCode: 28680,
+ query: {s: "string"},
+ proj: {abs_l: {$abs: "$l"}}
+ },
+ {
+ desc: "$abs with missing input",
+ expected: [{_id: 23, abs_n: null, abs_ne: null}],
+ query: {s: "string"},
+ proj: {abs_n: {$abs: "$n"}, abs_ne: {$abs: "$non_existent"}}
+ },
+ //
+ // Test $and/$or.
+ //
+ {
+ desc: "Single-branch $and",
+ expected: [
+ {_id: 0, foo: true}, {_id: 1, foo: true}, {_id: 2, foo: true},
+ {_id: 3, foo: false}, {_id: 4, foo: false}, {_id: 5, foo: false},
+ {_id: 6, foo: false}, {_id: 7, foo: false}, {_id: 8, foo: false},
+ {_id: 9, foo: false}, {_id: 10, foo: false}, {_id: 11, foo: false},
+ {_id: 12, foo: false}, {_id: 13, foo: false}, {_id: 14, foo: false},
+ {_id: 15, foo: true}, {_id: 16, foo: true}, {_id: 17, foo: false},
+ {_id: 18, foo: false}, {_id: 19, foo: false}, {_id: 20, foo: false},
+ {_id: 21, foo: false}, {_id: 22, foo: true}, {_id: 23, foo: false},
+ {_id: 24, foo: false}, {_id: 25, foo: false}, {_id: 26, foo: false}
+ ],
+ query: {},
+ proj: {foo: {$and: ["$a"]}}
+ },
+ {
+ desc: "Single-branch $or",
+ expected: [
+ {_id: 0, foo: true}, {_id: 1, foo: true}, {_id: 2, foo: true},
+ {_id: 3, foo: false}, {_id: 4, foo: false}, {_id: 5, foo: false},
+ {_id: 6, foo: false}, {_id: 7, foo: false}, {_id: 8, foo: false},
+ {_id: 9, foo: false}, {_id: 10, foo: false}, {_id: 11, foo: false},
+ {_id: 12, foo: false}, {_id: 13, foo: false}, {_id: 14, foo: false},
+ {_id: 15, foo: true}, {_id: 16, foo: true}, {_id: 17, foo: false},
+ {_id: 18, foo: false}, {_id: 19, foo: false}, {_id: 20, foo: false},
+ {_id: 21, foo: false}, {_id: 22, foo: true}, {_id: 23, foo: false},
+ {_id: 24, foo: false}, {_id: 25, foo: false}, {_id: 26, foo: false}
+ ],
+ query: {},
+ proj: {foo: {$or: ["$a"]}}
+ },
+ {
+ desc: "$and with BSONNull branch",
+ expected: [{_id: 22, foo: false}],
+ query: {_id: 22, a: 1},
+ proj: {foo: {$and: ["$n"]}}
+ },
+ {
+ desc: "$or with BSONNull branch",
+ expected: [{_id: 22, foo: false}],
+ query: {_id: 22, a: 1},
+ proj: {foo: {$or: ["$n"]}}
+ },
+ {
+ desc: "$and with missing path in branch",
+ expected: [{_id: 22, foo: false}],
+ query: {_id: 22, a: 1},
+ proj: {foo: {$and: ["$nonexistent"]}}
+ },
+ {
+ desc: "$or with missing path in branch",
+ expected: [{_id: 22, foo: false}],
+ query: {_id: 22, a: 1},
+ proj: {foo: {$or: ["$nonexistent"]}}
+ },
+ {
+ desc: "$and with array branch",
+ expected: [{_id: 22, foo: true, bar: true}],
+ query: {_id: 22, a: 1},
+ proj: {foo: {$and: []}, bar: {$and: ["$tf", "$t", "$a"]}}
+ },
+ {
+ desc: "Three-branch $or",
+ expected: [{_id: 22, foo: false, bar: false}],
+ query: {_id: 22, a: 1},
+ proj: {foo: {$or: []}, bar: {$or: ["$f", "$n", "$nonexistent"]}}
+ },
+ {
+ desc: "Multiple computed $and projections 1",
+ expected: [{_id: 22, foo: false, bar: false, baz: false}],
+ query: {_id: 22, a: 1},
+ proj: {foo: {$and: ["$a", "$b"]}, bar: {$and: ["$a", "$f"]}, baz: {$and: ["$a", "$n"]}}
+ },
+ {
+ desc: "Multiple computed $or projections",
+ expected: [{_id: 22, foo: true, bar: true, baz: true}],
+ query: {_id: 22, a: 1},
+ proj: {foo: {$or: ["$a", "$b"]}, bar: {$or: ["$a", "$f"]}, baz: {$or: ["$a", "$n"]}}
+ },
+ {
+ desc: "Multiple computed $and projections 2",
+ expected: [{_id: 22, foo: true, bar: false}],
+ query: {_id: 22, a: 1},
+ proj: {foo: {$and: ["$ff", "$t"]}, bar: {$and: ["$nonexistent", "$t"]}}
+ },
+ //
+ // $switch and $cond tests.
+ //
+ {
+ desc: "Single-case $switch with default",
+ expected: [
+ {_id: 0, foo: "x"}, {_id: 1, foo: 11}, {_id: 2, foo: 12}, {_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}, {_id: 16}, {_id: 17}, {_id: 18}, {_id: 19},
+ {_id: 20}, {_id: 21}, {_id: 22, foo: 0}, {_id: 23}, {_id: 24},
+ {_id: 25}, {_id: 26}
+ ],
+ query: {},
+ proj: {foo: {$switch: {branches: [{case: {$eq: ["$a", 1]}, then: "$b"}], default: "$c"}}}
+ },
+ {
+ desc: "$cond",
+ expected: [
+ {_id: 0, foo: "x"}, {_id: 1, foo: 11}, {_id: 2, foo: 12}, {_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}, {_id: 16}, {_id: 17}, {_id: 18}, {_id: 19},
+ {_id: 20}, {_id: 21}, {_id: 22, foo: 0}, {_id: 23}, {_id: 24},
+ {_id: 25}, {_id: 26}
+ ],
+ query: {},
+ proj: {foo: {$cond: {if: {$eq: ["$a", 1]}, then: "$b", else: "$c"}}}
+ },
+ {
+ desc: "Two-case $switch with default",
+ expected: [
+ {_id: 0, foo: "x"}, {_id: 1, foo: 2}, {_id: 2, foo: 12}, {_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}, {_id: 16}, {_id: 17}, {_id: 18}, {_id: 19},
+ {_id: 20}, {_id: 21}, {_id: 22, foo: 0}, {_id: 23}, {_id: 24},
+ {_id: 25}, {_id: 26}
+ ],
+ query: {},
+ proj: {
+ foo: {
+ $switch: {
+ branches: [
+ {case: {$eq: ["$a", 1]}, then: "$b"},
+ {case: {$eq: ["$b", "y"]}, then: "$a"}
+ ],
+ default: "$c"
+ }
+ }
+ }
+ },
+ //
+ // Failing expressions
+ //
+ {
+ desc: "$abs with string input as $and branch",
+ expectedErrorCode: 28765,
+ query: {s: "string"},
+ proj: {foo: {$and: [{$abs: ["$s"]}, "$n"]}}
+ },
+ {
+ desc: "$abs with string input as $or branch",
+ expectedErrorCode: 28765,
+ query: {s: "string"},
+ proj: {foo: {$or: [{$abs: ["$s"]}, "$s"]}}
+ },
+ {
+ desc: "Switch fall-through with no default",
+ expectedErrorCode: 40066,
+ query: {},
+ proj: {
+ foo: {
+ $switch: {
+ branches: [
+ {case: {$eq: ["$a", 1]}, then: "$b"},
+ {case: {$eq: ["$b", "y"]}, then: "$a"}
+ ]
+ // No default case.
+ }
+ }
+ }
+ },
+ {
+ desc: "$abs with string input as case condition",
+ expectedErrorCode: 28765,
+ query: {s: "string"},
+ proj: {foo: {$switch: {branches: [{case: {$gt: [{$abs: ["$s"]}, 0]}, then: "$n"}]}}}
+ },
+ {
+ desc: "$abs with string input as case result",
+ expectedErrorCode: 28765,
+ query: {s: "string"},
+ proj: {foo: {$switch: {branches: [{case: {$eq: ["$s", "string"]}, then: {$abs: ["$s"]}}]}}}
+ },
+ {
+ desc: "$abs with string input as $switch default",
+ expectedErrorCode: 28765,
+ query: {s: "string"},
+ proj: {
+ foo:
+ {$switch: {branches: [{case: {$eq: ["$s", 0]}, then: "$n"}], default: {$abs: "$s"}}}
+ }
+ },
+ {
+ desc: "$abs with string input as $cond condition",
+ expectedErrorCode: 28765,
+ query: {s: "string"},
+ proj: {foo: {$cond: {if: {$eq: [{$abs: "$s"}, "$n"]}, then: "$b", else: "$c"}}}
+ },
+ {
+ desc: "$abs with string input as $cond result (then)",
+ expectedErrorCode: 28765,
+ query: {s: "string"},
+ proj: {foo: {$cond: {if: {$eq: ["$s", "string"]}, then: {$abs: ["$s"]}, else: "$c"}}}
+ },
+ {
+ desc: "$abs with string input as $cond result (else)",
+ expectedErrorCode: 28765,
+ query: {s: "string"},
+ proj: {foo: {$cond: {if: {$eq: ["$s", "gnirts"]}, then: "$b", else: {$abs: ["$s"]}}}}
+ },
+ //
+ // Test short circuiting: these queries have expressions that would fail (because |$x| is
+ // invalid) but won't because they do not execute.
+ //
+ {
+ desc: "$abs with string input as short-circuited $and branch",
+ expected: [{_id: 25, foo: false}],
+ query: {s: "string"},
+ proj: {foo: {$and: ["$n", {$abs: ["$s"]}]}}
+ },
+ {
+ desc: "$abs with string input as short-circuited $or branch",
+ expected: [{_id: 25, foo: true}],
+ query: {s: "string"},
+ proj: {foo: {$or: ["$s", {$abs: ["$s"]}]}}
+ },
+ //
+ // Test that short-circuited branches do not do any work, such as traversing an array. The
+ // 'failOnPoisonedFieldLookup' failpoint ensures that none of the "$POISON" lookups, which are
+ // in short-circuited branches, ever execute. If they were to execute, it would result in an
+ // error from the query that would cause the test to fail.
+ //
+ {
+ desc: "$POISON in short-circuited $and/$or branches",
+ expected: [{_id: 22, foo: false, bar: true}],
+ query: {_id: 22, a: 1},
+ proj: {foo: {$and: ["$f", "$POISON"]}, bar: {$or: ["$t", "$POISON"]}}
+ },
+ {
+ desc: "$POISON in nested short-circuited $or branches",
+ expected: [{_id: 22, foo: false, bar: true}],
+ query: {_id: 22, a: 1},
+ proj: {
+ foo: {$and: ["$f", {$or: ["$f", "$POISON"]}, {$eq: ["$a", 1]}]},
+ bar: {$and: ["$t", {$or: ["$t", "$POISON"]}, {$eq: ["$a", 1]}]}
+ }
+ },
+ {
+ desc: "$POISON in untaken $switch cases",
+ expected: [{_id: 0, foo: "x"}, {_id: 22, foo: 0}],
+ query: {a: 1},
+ proj: {
+ foo: {
+ $switch: {
+ branches: [
+ {case: {$eq: ["$a", 2]}, then: "$POISON"},
+ {case: {$eq: ["$a", 3]}, then: "$POISON"}
+ ],
+ default: "$b"
+ }
+ }
+ }
+ },
+ {
+ desc: "$POISON in unevaluated case condition and untaken $switch default branch",
+ expected: [{_id: 0, foo: "x"}, {_id: 22, foo: 0}],
+ query: {a: 1},
+ proj: {
+ foo: {
+ $switch: {
+ branches:
+ [{case: {$eq: ["$a", 1]}, then: "$b"}, {case: "$POISON", then: "$POISON"}],
+ default: "$POISON"
+ }
+ }
+ }
+ },
+ {
+ desc: "$POISON in untaken $cond branch (else)",
+ expected: [{_id: 0, foo: "x"}, {_id: 22, foo: 0}],
+ query: {a: 1},
+ proj: {foo: {$cond: {if: {$eq: ["$a", 1]}, then: "$b", else: "$POISON"}}}
+ },
+ {
+ desc: "$POISON in untaken $cond branch (then)",
+ expected: [{_id: 0, foo: "x"}, {_id: 22, foo: 0}],
+ query: {a: 1},
+ proj: {foo: {$cond: {if: {$eq: ["$a", 2]}, then: "$POISON", else: "$b"}}}
+ },
+ //
+ // $let tests.
+ //
+ {
+ desc: "$let with single-path variable definitions 1",
+ expected: computedProjectionWithBoolValues({foo: [0, 1, 2, 15, 16, 22]}),
+ query: {},
+ proj: {foo: {$let: {vars: {va: "$a", vb: "$b"}, "in": {$and: "$$va"}}}}
+ },
+ {
+ desc: "$let with single-path variable definitions 2",
+ expected: computedProjectionWithBoolValues({foo: [0, 1, 2]}),
+ query: {},
+ proj: {foo: {$let: {vars: {va: "$a", vb: "$b"}, "in": {$and: "$$vb"}}}}
+ },
+ {
+ desc: "$let with single-path variable definitions 3",
+ expected: computedProjectionWithBoolValues({foo: [0, 1, 2]}),
+ query: {},
+ proj: {foo: {$let: {vars: {va: "$a", vb: "$b"}, "in": {$and: ["$$va", "$$vb"]}}}}
+ },
+ {
+ desc: "$let with $and variable definition",
+ expected: computedProjectionWithBoolValues({foo: [0, 1, 2]}),
+ query: {},
+ proj: {foo: {$let: {vars: {va: {$and: ["$a", "$b"]}}, "in": "$$va"}}}
+ },
+ {
+ desc: "Two-level path including $let variable",
+ expected: [
+ {_id: 0},
+ {_id: 1},
+ {_id: 2},
+ {_id: 3, foo: 1},
+ {_id: 4, foo: 2},
+ {_id: 5, foo: [1, 2, 3]},
+ {_id: 6, foo: 4},
+ {_id: 7, foo: [1]},
+ {_id: 8, foo: [1, 2]},
+ {_id: 9, foo: [1, [1, 2, 3]]},
+ {_id: 10},
+ {_id: 11},
+ {_id: 12},
+ {_id: 13},
+ {_id: 14},
+ {_id: 15},
+ {_id: 16},
+ {_id: 17, foo: [{z: 1}, {z: 2}]},
+ {_id: 18, foo: [3, 4, 6]},
+ {_id: 19},
+ {_id: 20, foo: [3, 4, 6]},
+ {_id: 21, foo: [3, {z: 2}, 6]},
+ {_id: 22},
+ {_id: 23},
+ {_id: 24},
+ {_id: 25},
+ {_id: 26}
+ ],
+ query: {},
+ proj: {foo: {$let: {vars: {va: "$x"}, "in": "$$va.y"}}}
+ },
+ {
+ desc: "Nested $let expressions",
+ expected: computedProjectionWithBoolValues({foo: [22]}),
+ query: {},
+ proj: {
+ foo: {
+ $let: {
+ vars: {vt: "$t", vf: "$f"},
+ "in": {
+ $let: {vars: {vf: "$$vt", va: "$a"},
+ "in": {$and: ["$$vt", "$$vf", "$$va"]}}
+ }
+ }
+ }
+ }
+ },
+ {
+ desc: "$let with variable definition including nested $let",
+ expected: [{_id: 22, foo: false}],
+ query: {_id: 22, a: 1},
+ proj: {
+ foo: {
+ $let: {
+ vars:
+ {va: {$let: {vars: {vt: "$t", va: "$va"}, "in": {$and: ["$$vt", "$$va"]}}}},
+ "in": "$$va"
+ }
+ }
+ }
+ },
+ {
+ desc: "$let renaming $$CURRENT",
+ expected: [
+ {_id: 0, foo: 1}, {_id: 1, foo: 2}, {_id: 2, foo: 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, foo: 10}, {_id: 16, foo: 10}, {_id: 17}, {_id: 18}, {_id: 19},
+ {_id: 20}, {_id: 21}, {_id: 22, foo: 1}, {_id: 23}, {_id: 24},
+ {_id: 25}, {_id: 26}
+ ],
+ query: {},
+ proj: {foo: {$let: {vars: {doc: "$$CURRENT"}, "in": "$$doc.a"}}}
+ },
+ {
+ desc: "$let shadowing $$CURRENT",
+ expected: [
+ {_id: 0, foo: 1}, {_id: 1, foo: 2}, {_id: 2, foo: 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, foo: 10}, {_id: 16, foo: 10}, {_id: 17}, {_id: 18}, {_id: 19},
+ {_id: 20}, {_id: 21}, {_id: 22, foo: 1}, {_id: 23}, {_id: 24},
+ {_id: 25}, {_id: 26}
+ ],
+ query: {},
+ proj: {foo: {$let: {vars: {CURRENT: "$$CURRENT.a"}, "in": "$$CURRENT"}}}
+ },
+ {
+ desc: "$$REMOVE",
+ expected: documents.map(doc => ({_id: doc._id})),
+ query: {},
+ proj: {a: "$$REMOVE"}
+ },
+ {
+ desc: "$$REMOVE with additional path components",
+ expected: documents.map(doc => ({_id: doc._id})),
+ query: {},
+ proj: {a: "$$REMOVE.x.y"}
+ },
+ {
+ desc: "$lt",
+ expected: computedProjectionWithBoolValues(
+ {foo: [3, 4, 5, 6, 7, 8, 9, 17, 18, 20, 21], bar: [0, 1, 2], baz: [5, 8, 9], qux: []}),
+ query: {},
+ proj: {
+ foo: {$lt: ["$a", "$x"]},
+ bar: {$lt: ["$a", "$b"]},
+ baz: {$lt: ["$x.y", "$v.w"]},
+ qux: {$lt: ["$a", "$nonexistent"]}
+ }
+ },
+ {
+ desc: "$lte",
+ expected: computedProjectionWithBoolValues({
+ foo: [3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 16, 17, 18, 19, 20, 21, 23, 24, 25, 26],
+ bar: [
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
+ 12, 13, 14, 17, 18, 19, 20, 21, 23, 24, 25, 26
+ ],
+ baz: [0, 1, 2, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 19, 22, 23, 24, 25, 26],
+ qux: [3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 17, 18, 19, 20, 21, 23, 24, 25, 26]
+ }),
+ query: {},
+ proj: {
+ foo: {$lte: ["$a", "$x"]},
+ bar: {$lte: ["$a", "$b"]},
+ baz: {$lte: ["$x.y", "$v.w"]},
+ qux: {$lte: ["$a", "$nonexistent"]}
+ }
+ },
+ {
+ desc: "$gt",
+ expected: computedProjectionWithBoolValues({
+ foo: [0, 1, 2, 15, 22],
+ bar: [15, 16, 22],
+ baz: [3, 4, 17, 18, 20, 21],
+ qux: [0, 1, 2, 15, 16, 22]
+ }),
+ query: {},
+ proj: {
+ foo: {$gt: ["$a", "$x"]},
+ bar: {$gt: ["$a", "$b"]},
+ baz: {$gt: ["$x.y", "$v.w"]},
+ qux: {$gt: ["$a", "$nonexistent"]}
+ }
+ },
+ {
+ desc: "$gte",
+ expected: computedProjectionWithBoolValues({
+ foo: [0, 1, 2, 10, 11, 12, 13, 14, 15, 16, 19, 22, 23, 24, 25, 26],
+ bar: [
+ 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26
+ ],
+ baz: [
+ 0, 1, 2, 3, 4, 6, 7, 10, 11, 12, 13, 14,
+ 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26
+ ],
+ qux: [
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13,
+ 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26
+ ]
+ }),
+ query: {},
+ proj: {
+ foo: {$gte: ["$a", "$x"]},
+ bar: {$gte: ["$a", "$b"]},
+ baz: {$gte: ["$x.y", "$v.w"]},
+ qux: {$gte: ["$a", "$nonexistent"]}
+ }
+ },
+ {
+ desc: "$eq",
+ expected: computedProjectionWithBoolValues({
+ foo: [10, 11, 12, 13, 14, 16, 19, 23, 24, 25, 26],
+ bar: [3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 17, 18, 19, 20, 21, 23, 24, 25, 26],
+ baz: [0, 1, 2, 6, 7, 10, 11, 12, 13, 14, 15, 16, 19, 22, 23, 24, 25, 26],
+ qux: [3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 17, 18, 19, 20, 21, 23, 24, 25, 26]
+ }),
+ query: {},
+ proj: {
+ foo: {$eq: ["$a", "$x"]},
+ bar: {$eq: ["$a", "$b"]},
+ baz: {$eq: ["$x.y", "$v.w"]},
+ qux: {$eq: ["$a", "$nonexistent"]}
+ }
+ },
+ {
+ desc: "$ne",
+ expected: computedProjectionWithBoolValues({
+ foo: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 15, 17, 18, 20, 21, 22],
+ bar: [0, 1, 2, 15, 16, 22],
+ baz: [3, 4, 5, 8, 9, 17, 18, 20, 21],
+ qux: [0, 1, 2, 15, 16, 22]
+ }),
+ query: {},
+ proj: {
+ foo: {$ne: ["$a", "$x"]},
+ bar: {$ne: ["$a", "$b"]},
+ baz: {$ne: ["$x.y", "$v.w"]},
+ qux: {$ne: ["$a", "$nonexistent"]}
+ }
+ },
+ {
+ desc: "$cmp",
+ expected: [
+ {_id: 0, foo: 1, bar: -1, baz: 0, qux: 1}, {_id: 1, foo: 1, bar: -1, baz: 0, qux: 1},
+ {_id: 2, foo: 1, bar: -1, baz: 0, qux: 1}, {_id: 3, foo: -1, bar: 0, baz: 1, qux: 0},
+ {_id: 4, foo: -1, bar: 0, baz: 1, qux: 0}, {_id: 5, foo: -1, bar: 0, baz: -1, qux: 0},
+ {_id: 6, foo: -1, bar: 0, baz: 0, qux: 0}, {_id: 7, foo: -1, bar: 0, baz: 0, qux: 0},
+ {_id: 8, foo: -1, bar: 0, baz: -1, qux: 0}, {_id: 9, foo: -1, bar: 0, baz: -1, qux: 0},
+ {_id: 10, foo: 0, bar: 0, baz: 0, qux: 0}, {_id: 11, foo: 0, bar: 0, baz: 0, qux: 0},
+ {_id: 12, foo: 0, bar: 0, baz: 0, qux: 0}, {_id: 13, foo: 0, bar: 0, baz: 0, qux: 0},
+ {_id: 14, foo: 0, bar: 0, baz: 0, qux: 0}, {_id: 15, foo: 1, bar: 1, baz: 0, qux: 1},
+ {_id: 16, foo: 0, bar: 1, baz: 0, qux: 1}, {_id: 17, foo: -1, bar: 0, baz: 1, qux: 0},
+ {_id: 18, foo: -1, bar: 0, baz: 1, qux: 0}, {_id: 19, foo: 0, bar: 0, baz: 0, qux: 0},
+ {_id: 20, foo: -1, bar: 0, baz: 1, qux: 0}, {_id: 21, foo: -1, bar: 0, baz: 1, qux: 0},
+ {_id: 22, foo: 1, bar: 1, baz: 0, qux: 1}, {_id: 23, foo: 0, bar: 0, baz: 0, qux: 0},
+ {_id: 24, foo: 0, bar: 0, baz: 0, qux: 0}, {_id: 25, foo: 0, bar: 0, baz: 0, qux: 0},
+ {_id: 26, foo: 0, bar: 0, baz: 0, qux: 0}
+ ],
+ query: {},
+ proj: {
+ foo: {$cmp: ["$a", "$x"]},
+ bar: {$cmp: ["$a", "$b"]},
+ baz: {$cmp: ["$x.y", "$v.w"]},
+ qux: {$cmp: ["$a", "$nonexistent"]}
+ }
+ },
+ //
+ // Nesting torture tests.
+ //
+ {
+ desc: "Nesting torture test 1",
+ expected: computedProjectionWithBoolValues({foo: [5, 6, 7, 8, 9]}),
+ query: {},
+ proj: {
+ foo: {
+ $let: {
+ vars: {v1: {$or: ["$x.y", "$v.w"]}, vx: "$x"},
+ "in": {$and: ["$$vx.y", "$v.w"]}
+ }
+ }
+ }
+ },
+ {
+ desc: "Nesting torture test 2",
+ expected: computedProjectionWithBoolValues({foo: [5, 6, 7, 8, 9]}),
+ query: {},
+ proj: {
+ foo: {
+ $let: {
+ vars: {v1: "$x.y"},
+ "in": {
+ $let: {
+ vars:
+ {v2: {$let: {vars: {v3: "$v.w"}, "in": {$and: ["$$v1", "$$v3"]}}}},
+ "in": "$$v2"
+ }
+ }
+ }
+ }
+ }
+ },
+ {
+ desc: "Nesting torture test 3",
+ expected: computedProjectionWithBoolValues(
+ {foo: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 15, 16, 17, 18, 20, 21, 22]}),
+ query: {},
+ proj: {foo: {$or: ["$a", {$lt: [{$and: ["$a", "$c"]}, {$or: ["$c", "$x"]}]}, "$x"]}}
+ },
+ {
+ desc: "Ludicrous nesting torture test with multiple computed fields",
+ expected: [
+ {_id: 0, foo: false, baz: false},
+ {_id: 1, foo: false, baz: false},
+ {_id: 2, foo: false, baz: false},
+ {_id: 3, foo: 2, baz: false},
+ {_id: 4, bar: 2, baz: false},
+ {_id: 5, foo: true, bar: [1, 2, 3], baz: true},
+ {_id: 6, foo: true, bar: 4, baz: false},
+ {_id: 7, foo: true, bar: [1], baz: false},
+ {_id: 8, foo: true, bar: [1, 2], baz: true},
+ {_id: 9, foo: true, bar: [1, [1, 2, 3]], baz: true},
+ {_id: 10, foo: false, baz: false},
+ {_id: 11, foo: false, baz: false},
+ {_id: 12, foo: false, baz: false},
+ {_id: 13, foo: false, baz: false},
+ {_id: 14, foo: false, baz: false},
+ {_id: 15, foo: false, baz: false},
+ {_id: 16, foo: false, baz: false},
+ {_id: 17, foo: true, bar: [{z: 1}, {z: 2}], baz: false},
+ {_id: 18, foo: true, bar: [3, 4, 6], baz: false},
+ {_id: 19, foo: false, baz: false},
+ {_id: 20, foo: true, bar: [3, 4, 6], baz: false},
+ {_id: 21, foo: true, bar: [3, {z: 2}, 6], baz: false},
+ {_id: 22, foo: true, baz: false},
+ {_id: 23, foo: false, baz: false},
+ {_id: 24, foo: false, baz: false},
+ {_id: 25, foo: false, baz: false},
+ {_id: 26, foo: false, baz: false}
+ ],
+ query: {},
+ proj: {
+ foo: {
+ $let: {
+ vars: {
+ v1: {$or: ["$x.y", "$v.w"]},
+ v2: {$switch: {branches: [{case: "$v.w", then: 1}], default: 2}},
+ v3: "$b"
+ },
+ "in": {
+ $switch: {
+ branches: [
+ {case: {$eq: ["$x.y", 1]}, then: "$$v2"},
+ {case: {$eq: ["$x.y", 2]}, then: "$v.w"},
+ {case: {$eq: ["$$v3", 2]}, then: "$c"}
+ ],
+ default: {$or: ["$$v1", "$tf"]}
+ }
+ }
+ }
+ },
+ bar: {
+ $switch: {
+ branches: [
+ {case: {$gt: ["$x.y", 1]}, then: "$x.y"},
+ {case: {$let: {vars: {v4: "$v.w1"}, "in": "$$v4"}}, then: "$v.w2"}
+ ],
+ default: "$x.y.z"
+ }
+ },
+ baz: {
+ $let: {
+ vars: {v5: "$x.y", v6: "$v.w"},
+ "in": {
+ $cond: {
+ if: {$lt: ["$$v5", "$$v6"]},
+ then: {
+ $switch: {
+ branches: [
+ {case: {$eq: ["$v.w", 5]}, then: "$v.w"},
+ {case: {$eq: ["$v.w", 4]}, then: "$x.y"}
+ ],
+ default: {$or: ["$$v5", "$$v6"]}
+ }
+ },
+ else: false
+ }
+ }
+ }
+ }
+ }
+ }
+];
+
+testCases.forEach(function(test) {
+ if (test.expected) {
+ let actual;
+ assert.doesNotThrow(() => {
+ actual = coll.find(test.query, test.proj).toArray();
+ }, [], test);
+ assert(arrayEq(actual, test.expected), Object.assign({actual: actual}, test));
+ } else {
+ assert(test.expectedErrorCode, test);
+ const result =
+ db.runCommand({find: coll.getName(), filter: test.query, projection: test.proj});
+ assert.commandFailedWithCode(
+ result, test.expectedErrorCode, Object.assign({result: result}, test));
+ }
+});
+
+assert.commandWorked(
+ db.adminCommand({configureFailPoint: "failOnPoisonedFieldLookup", mode: "off"}));
+}());
diff --git a/jstests/core/find_project_sort.js b/jstests/core/find_project_sort.js
new file mode 100644
index 00000000000..3d359fd9e41
--- /dev/null
+++ b/jstests/core/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
+}());
diff --git a/jstests/libs/sbe_assert_error_override.js b/jstests/libs/sbe_assert_error_override.js
index 8e31b51cc97..8cda6fd7113 100644
--- a/jstests/libs/sbe_assert_error_override.js
+++ b/jstests/libs/sbe_assert_error_override.js
@@ -26,6 +26,7 @@ const equivalentErrorCodesList = [
[16608, 4848401],
[16609, 5073101],
[16555, 5073102],
+ [28680, 4903701],
[28765, 4822870],
[28714, 4903710],
[28761, 4903708],
@@ -33,6 +34,7 @@ const equivalentErrorCodesList = [
[28766, 4903706],
[31034, 4848972],
[31095, 4848972],
+ [40066, 4934200],
[40515, 4848979],
[40517, 4848980],
[40523, 4848972],