summaryrefslogtreecommitdiff
path: root/jstests/aggregation
diff options
context:
space:
mode:
authorDavid Storch <david.storch@mongodb.com>2021-06-01 18:14:41 -0400
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2021-06-14 15:25:21 +0000
commitd75f3a8bb66ef1ef41bfd16eecd42c31f91c818b (patch)
tree1dbfaf604282e43d5fcf2d6a3e67f9eeaa254cdb /jstests/aggregation
parenta2d174f0aa4c9cbac4b39863d0f6c37f4774470b (diff)
downloadmongo-d75f3a8bb66ef1ef41bfd16eecd42c31f91c818b.tar.gz
SERVER-21929 Make $in with null always match undefined
Diffstat (limited to 'jstests/aggregation')
-rw-r--r--jstests/aggregation/sources/lookup/lookup_null_semantics.js111
1 files changed, 111 insertions, 0 deletions
diff --git a/jstests/aggregation/sources/lookup/lookup_null_semantics.js b/jstests/aggregation/sources/lookup/lookup_null_semantics.js
new file mode 100644
index 00000000000..5ea50382558
--- /dev/null
+++ b/jstests/aggregation/sources/lookup/lookup_null_semantics.js
@@ -0,0 +1,111 @@
+/**
+ * Test that $lookup behaves correctly for null values, as well as "missing" and undefined.
+ *
+ * @tags: [
+ * # We don't yet support lookup into a sharded collection.
+ * assumes_unsharded_collection,
+ * ]
+ */
+(function() {
+"use strict";
+
+load("jstests/aggregation/extras/utils.js");
+
+const localColl = db.lookup_null_semantics_local;
+const foreignColl = db.lookup_null_semantics_foreign;
+
+localColl.drop();
+foreignColl.drop();
+
+assert.commandWorked(localColl.insert([
+ {_id: 0},
+ {_id: 1, a: null},
+ {_id: 2, a: 9},
+ {_id: 3, a: [null, 9]},
+]));
+assert.commandWorked(foreignColl.insert([
+ {_id: 0},
+ {_id: 1, b: null},
+ {_id: 2, b: undefined},
+ {_id: 3, b: 9},
+ {_id: 4, b: [null, 9]},
+ {_id: 5, b: 42},
+ {_id: 6, b: [undefined, 9]},
+ {_id: 7, b: [9, 10]},
+]));
+
+const actualResults = localColl.aggregate([{
+ $lookup: {from: foreignColl.getName(), localField: "a", foreignField: "b", as: "lookupResults"}
+}]).toArray();
+
+const expectedResults = [
+ // Missing on the left-hand side results in a lookup with $eq:null semantics on the left-hand
+ // side. Namely, we expect this document to join with null, missing, and undefined, or arrays
+ // thereof.
+ {
+ _id: 0,
+ lookupResults: [
+ {_id: 0},
+ {_id: 1, b: null},
+ {_id: 2, b: undefined},
+ {_id: 4, b: [null, 9]},
+ {_id: 6, b: [undefined, 9]}
+ ]
+ },
+ // Null on the left-hand side is the same as the missing case above.
+ {
+ _id: 1,
+ a: null,
+ lookupResults: [
+ {_id: 0},
+ {_id: 1, b: null},
+ {_id: 2, b: undefined},
+ {_id: 4, b: [null, 9]},
+ {_id: 6, b: [undefined, 9]}
+ ]
+ },
+ // A "negative" test-case where the value being looked up is not nullish.
+ {
+ _id: 2,
+ a: 9,
+ lookupResults: [
+ {_id: 3, b: 9},
+ {_id: 4, b: [null, 9]},
+ {_id: 6, b: [undefined, 9]},
+ {_id: 7, b: [9, 10]},
+ ]
+ },
+ // Here we are looking up both null and a scalar. We expected missing, null, and undefined to
+ // match in addition to the matches due to the scalar.
+ {
+ _id: 3,
+ a: [null, 9],
+ lookupResults: [
+ {_id: 0},
+ {_id: 1, b: null},
+ {_id: 2, b: undefined},
+ {_id: 3, b: 9},
+ {_id: 4, b: [null, 9]},
+ {_id: 6, b: [undefined, 9]},
+ {_id: 7, b: [9, 10]},
+ ]
+ },
+];
+
+assertArrayEq({actual: actualResults, expected: expectedResults});
+
+// The results should not change there is an index available on the right-hand side.
+assert.commandWorked(foreignColl.createIndex({b: 1}));
+assertArrayEq({actual: actualResults, expected: expectedResults});
+
+// If the left-hand side collection has a value of undefined for "localField", then the query will
+// fail. This is a consequence of the fact that queries which explicitly compare to undefined, such
+// as {$eq:undefined}, are banned. Arguably this behavior could be improved, but we are unlikely to
+// change it given that the undefined BSON type has been deprecated for many years.
+assert.commandWorked(localColl.insert({a: undefined}));
+assert.throws(() => {
+ localColl.aggregate([{
+ $lookup: {from: foreignColl.getName(), localField: "a", foreignField: "b", as: "lookupResults"}
+}]).toArray();
+});
+}());