summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--jstests/core/arrayfind10.js59
-rw-r--r--src/mongo/db/query/sbe_stage_builder_filter.cpp27
2 files changed, 62 insertions, 24 deletions
diff --git a/jstests/core/arrayfind10.js b/jstests/core/arrayfind10.js
index 0040dc18099..80652ebca6a 100644
--- a/jstests/core/arrayfind10.js
+++ b/jstests/core/arrayfind10.js
@@ -1,6 +1,5 @@
/**
- * Tests some of the find command's quirky semantics regarding how arrays are handled in various
- * situations. Includes tests for bugs described in SERVER-49720.
+ * Tests some of the find command's semantics with respect to how arrays are handled.
*/
(function() {
"use strict";
@@ -10,18 +9,54 @@ load("jstests/aggregation/extras/utils.js"); // arrayEq
const t = db.jstests_arrayfind10;
t.drop();
-t.save({a: "foo"});
-t.save({a: ["foo"]});
-t.save({a: [["foo"]]});
-t.save({a: [[["foo"]]]});
+function runWithAndWithoutIndex(keyPattern, testFunc) {
+ testFunc();
+ assert.commandWorked(t.createIndex(keyPattern));
+ testFunc();
+}
+
+assert.commandWorked(t.insert([{a: "foo"}, {a: ["foo"]}, {a: [["foo"]]}, {a: [[["foo"]]]}]));
-function doTest1(t) {
+runWithAndWithoutIndex({a: 1}, () => {
assert(arrayEq(t.find({a: "foo"}, {_id: 0}).toArray(), [{a: "foo"}, {a: ["foo"]}]));
-}
+});
-doTest1(t);
-t.ensureIndex({'a': 1});
-doTest1(t);
+assert(t.drop());
-t.drop();
+assert.commandWorked(t.insert([
+ {a: [123, "foo"]},
+ {a: ["foo", 123]},
+ {a: ["bar", "foo"]},
+ {a: ["bar", "baz", "foo"]},
+ {a: ["bar", "baz", 123]}
+]));
+
+runWithAndWithoutIndex({a: 1}, () => {
+ assert(arrayEq(
+ t.find({a: "foo"}, {_id: 0}).toArray(),
+ [{a: [123, "foo"]}, {a: ["foo", 123]}, {a: ["bar", "foo"]}, {a: ["bar", "baz", "foo"]}]));
+});
+
+assert(t.drop());
+
+assert.commandWorked(t.insert([
+ {a: [{}, {b: "foo"}]},
+ {a: [{b: "foo"}, {}]},
+ {a: [{b: 123}, {b: "foo"}]},
+ {a: [{b: "foo"}, {b: 123}]},
+ {a: [{b: "bar"}, {b: "foo"}]},
+ {a: [{b: "bar"}, {b: "baz"}, {b: "foo"}]},
+ {a: [{b: "bar"}, {b: "baz"}, {b: 123}]}
+]));
+
+runWithAndWithoutIndex({"a.b": 1}, () => {
+ assert(arrayEq(t.find({"a.b": "foo"}, {_id: 0}).toArray(), [
+ {a: [{}, {b: "foo"}]},
+ {a: [{b: "foo"}, {}]},
+ {a: [{b: 123}, {b: "foo"}]},
+ {a: [{b: "foo"}, {b: 123}]},
+ {a: [{b: "bar"}, {b: "foo"}]},
+ {a: [{b: "bar"}, {b: "baz"}, {b: "foo"}]}
+ ]));
+});
})();
diff --git a/src/mongo/db/query/sbe_stage_builder_filter.cpp b/src/mongo/db/query/sbe_stage_builder_filter.cpp
index aff3137f7da..a9382bd116a 100644
--- a/src/mongo/db/query/sbe_stage_builder_filter.cpp
+++ b/src/mongo/db/query/sbe_stage_builder_filter.cpp
@@ -112,6 +112,13 @@ struct MatchExpressionVisitorContext {
sbe::value::SlotId inputVar;
};
+std::unique_ptr<sbe::EExpression> makeFillEmptyFalse(std::unique_ptr<sbe::EExpression> e) {
+ using namespace std::literals;
+ return sbe::makeE<sbe::EFunction>(
+ "fillEmpty"sv,
+ sbe::makeEs(std::move(e), sbe::makeE<sbe::EConstant>(sbe::value::TypeTags::Boolean, 0)));
+}
+
/**
* A helper function to generate a path traversal plan stage at the given nested 'level' of the
* traversal path. For example, for a dotted path expression {'a.b': 2}, the traversal sub-tree will
@@ -201,7 +208,7 @@ std::unique_ptr<sbe::PlanStage> generateTraverseHelper(MatchExpressionVisitorCon
sbe::makeE<sbe::EVariable>(traversePredicateVar));
}
- // The final traverse stage for the current nested level.
+ // The traverse stage for the current nested level.
return sbe::makeS<sbe::TraverseStage>(
std::move(inputStage),
std::move(innerBranch),
@@ -262,8 +269,8 @@ void generateTraverseForComparisonPredicate(MatchExpressionVisitorContext* conte
// SBE EConstant assumes ownership of the value so we have to make a copy here.
auto [tag, val] = sbe::value::copyValue(tagView, valView);
- return sbe::makeE<sbe::EPrimBinary>(
- binaryOp, sbe::makeE<sbe::EVariable>(inputSlot), sbe::makeE<sbe::EConstant>(tag, val));
+ return makeFillEmptyFalse(sbe::makeE<sbe::EPrimBinary>(
+ binaryOp, sbe::makeE<sbe::EVariable>(inputSlot), sbe::makeE<sbe::EConstant>(tag, val)));
};
generateTraverse(context, expr, std::move(makeEExprFn));
}
@@ -580,15 +587,11 @@ public:
// TODO: In the future, this needs to account for the fact that the regex match
// expression matches strings, but also matches stored regexes. For example,
// {$match: {a: /foo/}} matches the document {a: /foo/} in addition to {a: "foobar"}.
- return sbe::makeE<sbe::EPrimBinary>(
- sbe::EPrimBinary::logicAnd,
- sbe::makeE<sbe::EFunction>("isString",
- sbe::makeEs(sbe::makeE<sbe::EVariable>(inputSlot))),
- sbe::makeE<sbe::EFunction>(
- "regexMatch",
- sbe::makeEs(
- sbe::makeE<sbe::EConstant>(sbe::value::TypeTags::pcreRegex, ownedRegexVal),
- sbe::makeE<sbe::EVariable>(inputSlot))));
+ return makeFillEmptyFalse(sbe::makeE<sbe::EFunction>(
+ "regexMatch",
+ sbe::makeEs(
+ sbe::makeE<sbe::EConstant>(sbe::value::TypeTags::pcreRegex, ownedRegexVal),
+ sbe::makeE<sbe::EVariable>(inputSlot))));
};
generateTraverse(_context, expr, std::move(makeEExprFn));