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
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
|
/**
* Tests that the $match stage is pushed before $lookup stage.
*/
(function() {
"use strict";
load("jstests/aggregation/extras/utils.js"); // For assertArrayEq.
load('jstests/libs/analyze_plan.js'); // For getWinningPlan().
const coll = db.lookup_match_pushdown;
coll.drop();
const other = db.lookup_match_pushdown_other;
other.drop();
assert.commandWorked(
db.adminCommand({"configureFailPoint": 'disablePipelineOptimization', "mode": 'off'}));
assert.commandWorked(coll.insertMany([{_id: 1, x: 5}, {_id: 2, x: 6}]));
assert.commandWorked(
other.insertMany([{_id: 2, y: 5, z: 10}, {_id: 3, y: 5, z: 12}, {_id: 4, y: 6, z: 10}]));
// Checks that the order of the pipeline stages matches the expected ordering.
function checkPipelineAndResults(pipeline, expectedPipeline, expectedResults) {
// Check pipeline is as expected.
const explain = assert.commandWorked(coll.explain().aggregate(pipeline));
if (expectedPipeline.length > 0) {
assert.eq(getWinningPlan(explain.stages[0].$cursor.queryPlanner).stage,
expectedPipeline[0],
explain);
}
assert.eq(explain.stages.length, expectedPipeline.length, explain);
for (let i = 1; i < expectedPipeline.length; i++) {
assert.eq(Object.keys(explain.stages[i]), expectedPipeline[i], explain);
}
// Check results are as expected.
const res = coll.aggregate(pipeline).toArray();
assertArrayEq({actual: res, expected: expectedResults});
}
const expectedPipeline = ["COLLSCAN", "$lookup"];
// For $eq and $expr:$eq, we should see the same results (in this particular case).
const expectedResultsEq = [{_id: 1, x: 5, a: {_id: 2, y: 5, z: 10}}];
// Ensure $match gets pushed down into $lookup when $eq is used.
const pipelineEq = [
{$lookup: {as: "a", from: other.getName(), localField: "x", foreignField: "y"}},
{$unwind: "$a"},
{$match: {"a.z": 10, x: {$eq: 5}}}
];
checkPipelineAndResults(pipelineEq, expectedPipeline, expectedResultsEq);
// Ensure $match gets pushed down into $lookup when $expr:$eq is used.
const pipelineExprEq = [
{$lookup: {as: "a", from: other.getName(), localField: "x", foreignField: "y"}},
{$unwind: "$a"},
{$match: {"a.z": 10, $expr: {$eq: ["$x", 5]}}}
];
checkPipelineAndResults(pipelineExprEq, expectedPipeline, expectedResultsEq);
// For $eq and $expr:$gt, we should see the same results (in this particular case).
const expectedResultsGt = [{_id: 2, x: 6, a: {_id: 4, y: 6, z: 10}}];
// Ensure $match gets pushed down into $lookup when $eq is used.
const pipelineGt = [
{$lookup: {as: "a", from: other.getName(), localField: "x", foreignField: "y"}},
{$unwind: "$a"},
{$match: {"a.z": 10, x: {$gt: 5}}}
];
checkPipelineAndResults(pipelineGt, expectedPipeline, expectedResultsGt);
// Ensure $match gets pushed down into $lookup when $expr:$eq is used.
const pipelineExprGt = [
{$lookup: {as: "a", from: other.getName(), localField: "x", foreignField: "y"}},
{$unwind: "$a"},
{$match: {"a.z": 10, $expr: {$gt: ["$x", 5]}}}
];
checkPipelineAndResults(pipelineExprGt, expectedPipeline, expectedResultsGt);
}());
|