summaryrefslogtreecommitdiff
path: root/jstests/noPassthrough/lookup_pushdown.js
diff options
context:
space:
mode:
Diffstat (limited to 'jstests/noPassthrough/lookup_pushdown.js')
-rw-r--r--jstests/noPassthrough/lookup_pushdown.js200
1 files changed, 200 insertions, 0 deletions
diff --git a/jstests/noPassthrough/lookup_pushdown.js b/jstests/noPassthrough/lookup_pushdown.js
new file mode 100644
index 00000000000..fe10110b407
--- /dev/null
+++ b/jstests/noPassthrough/lookup_pushdown.js
@@ -0,0 +1,200 @@
+/**
+ * Tests basic functionality of pushing $lookup into the find layer.
+ */
+(function() {
+"use strict";
+
+load("jstests/libs/sbe_util.js"); // For checkSBEEnabled.
+
+function runTest(coll, pipeline, expectedToPushdown) {
+ const cmd = () => coll.aggregate(pipeline).toArray();
+ if (expectedToPushdown) {
+ assert.throwsWithCode(cmd, 5843700);
+ } else {
+ assert.doesNotThrow(cmd);
+ }
+}
+
+// Standalone cases.
+const conn = MongoRunner.runMongod({setParameter: "internalEnableMultipleAutoGetCollections=true"});
+assert.neq(null, conn, "mongod was unable to start up");
+const name = "lookup_pushdown";
+let db = conn.getDB(name);
+
+if (!checkSBEEnabled(db, ["featureFlagSBELookupPushdown"])) {
+ jsTestLog("Skipping test because the sbe lookup pushdown feature flag is disabled");
+ MongoRunner.stopMongod(conn);
+ return;
+}
+
+const foreignCollName = "foreign_lookup_pushdown";
+const viewName = "view_lookup_pushdown";
+let coll = db[name];
+assert.commandWorked(coll.insert({_id: 1, a: 2}));
+let foreignColl = db[foreignCollName];
+assert.commandWorked(foreignColl.insert({_id: 0, b: 2}));
+assert.commandWorked(db.createView(viewName, foreignCollName, [{$match: {b: {$gte: 0}}}]));
+let view = db[viewName];
+
+// Basic $lookup.
+runTest(coll,
+ [{$lookup: {from: foreignCollName, localField: "a", foreignField: "b", as: "out"}}],
+ true /* expectedToPushdown */);
+
+// Self join $lookup, no views.
+runTest(coll,
+ [{$lookup: {from: name, localField: "a", foreignField: "a", as: "out"}}],
+ true /* expectedToPushdown */);
+
+// Self join $lookup; left hand is a view. This is expected to be pushed down because the view
+// pipeline itself is a $match, which is eligible for pushdown.
+runTest(view,
+ [{$lookup: {from: name, localField: "a", foreignField: "a", as: "out"}}],
+ true /* expectedToPushdown */);
+
+// Self join $lookup; right hand is a view.
+runTest(coll,
+ [{$lookup: {from: viewName, localField: "a", foreignField: "a", as: "out"}}],
+ false /* expectedToPushdown */);
+
+// Self join $lookup; both namespaces are views.
+runTest(view,
+ [{$lookup: {from: viewName, localField: "a", foreignField: "a", as: "out"}}],
+ false /* expectedToPushdown */);
+
+// $lookup preceded by $match.
+runTest(coll,
+ [
+ {$match: {a: {$gte: 0}}},
+ {$lookup: {from: foreignCollName, localField: "a", foreignField: "b", as: "out"}}
+ ],
+ true /* expectedToPushdown */);
+
+// $lookup preceded by $project.
+runTest(coll,
+ [
+ {$project: {a: 1}},
+ {$lookup: {from: foreignCollName, localField: "a", foreignField: "b", as: "out"}}
+ ],
+ true /* expectedToPushdown */);
+
+// $lookup preceded by $project which features an SBE-incompatible expression.
+// TODO SERVER-51542: Update or remove this test case once $pow is implemented in SBE.
+runTest(coll,
+ [
+ {$project: {exp: {$pow: ["$a", 3]}}},
+ {$lookup: {from: foreignCollName, localField: "a", foreignField: "b", as: "out"}}
+ ],
+ false /* expectedToPushdown */);
+
+// $lookup preceded by $group.
+runTest(coll,
+ [
+ {$group: {_id: "$a", sum: {$sum: 1}}},
+ {$lookup: {from: foreignCollName, localField: "a", foreignField: "b", as: "out"}}
+ ],
+ true /* expectedToPushdown */);
+
+// Consecutive $lookups (first is against view).
+runTest(coll,
+ [
+ {$lookup: {from: viewName, localField: "a", foreignField: "b", as: "out"}},
+ {$lookup: {from: foreignCollName, localField: "a", foreignField: "b", as: "out"}}
+ ],
+ false /* expectedToPushdown */);
+
+// Consecutive $lookups (first is against regular collection).
+runTest(coll,
+ [
+ {$lookup: {from: foreignCollName, localField: "a", foreignField: "b", as: "out"}},
+ {$lookup: {from: viewName, localField: "a", foreignField: "b", as: "out"}}
+ ],
+ true /* expectedToPushdown */);
+
+// $lookup with pipeline.
+runTest(coll,
+ [{$lookup: {from: foreignCollName, let: {foo: "$b"}, pipeline: [{$match: {$expr: {$eq: ["$$foo",
+2]}}}], as: "out"}}], false /* expectedToPushdown */);
+
+// $lookup that absorbs $unwind.
+runTest(coll,
+ [
+ {$lookup: {from: foreignCollName, localField: "a", foreignField: "b", as: "out"}},
+ {$unwind: "$out"}
+ ],
+ false /* expectedToPushdown */);
+
+// $lookup that absorbs $match.
+runTest(coll,
+ [
+ {$lookup: {from: foreignCollName, localField: "a", foreignField: "b", as: "out"}},
+ {$unwind: "$out"},
+ {$match: {out: {$gte: 0}}}
+ ],
+ false /* expectedToPushdown */);
+
+// $lookup that does not absorb $match.
+runTest(coll,
+ [
+ {$lookup: {from: foreignCollName, localField: "a", foreignField: "b", as: "out"}},
+ {$match: {out: {$gte: 0}}}
+ ],
+ true /* expectedToPushdown */);
+
+MongoRunner.stopMongod(conn);
+
+// Sharded cases.
+const st = new ShardingTest({
+ shards: 2,
+ mongos: 1,
+ other: {shardOptions: {setParameter: "internalEnableMultipleAutoGetCollections=true"}}
+});
+db = st.s.getDB(name);
+
+// Setup. Here, 'coll' is sharded, 'foreignColl' is unsharded, 'viewName' is an unsharded view,
+// and 'shardedViewName' is a sharded view.
+const shardedViewName = "sharded_foreign_view";
+coll = db[name];
+assert.commandWorked(coll.insert({a: 1, shardKey: 1}));
+assert.commandWorked(coll.insert({a: 2, shardKey: 10}));
+assert.commandWorked(coll.createIndex({shardKey: 1}));
+st.shardColl(coll.getName(), {shardKey: 1}, {shardKey: 5}, {shardKey: 5}, name);
+
+foreignColl = db[foreignCollName];
+assert.commandWorked(foreignColl.insert({b: 5}));
+
+assert.commandWorked(db.createView(viewName, foreignCollName, [{$match: {b: {$gte: 0}}}]));
+assert.commandWorked(db.createView(shardedViewName, name, [{$match: {b: {$gte: 0}}}]));
+
+// Both collections are unsharded.
+runTest(foreignColl,
+ [{$lookup: {from: foreignCollName, localField: "a", foreignField: "b", as: "out"}}],
+ true /* expectedToPushdown */);
+
+// Sharded main collection, unsharded right side. This is not expected to be eligible for pushdown
+// because the $lookup will be preceded by a $mergeCursors stage on the merging shard.
+runTest(coll,
+ [{$lookup: {from: foreignCollName, localField: "a", foreignField: "b", as: "out"}}],
+ false /* expectedToPushdown */);
+
+// Both collections are sharded.
+runTest(coll,
+ [{$lookup: {from: name, localField: "a", foreignField: "b", as: "out"}}],
+ false /* expectedToPushdown */);
+
+// Unsharded main collection, sharded right side.
+runTest(foreignColl,
+ [{$lookup: {from: name, localField: "a", foreignField: "b", as: "out"}}],
+ false /* expectedToPushdown */);
+
+// Unsharded main collection, unsharded view right side.
+runTest(foreignColl,
+ [{$lookup: {from: viewName, localField: "a", foreignField: "b", as: "out"}}],
+ false /* expectedToPushdown */);
+
+// Unsharded main collection, sharded view on the right side.
+runTest(foreignColl,
+ [{$lookup: {from: shardedViewName, localField: "a", foreignField: "b", as: "out"}}],
+ false /* expectedToPushdown */);
+st.stop();
+}());