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
|
// This test verifies that a nested $lookup pipeline can have a total combined depth of 20 but no
// greater.
// @tags: [requires_sharding]
(function() {
"use strict";
load("jstests/aggregation/extras/utils.js"); // For assertErrorCode.
load("jstests/libs/collection_drop_recreate.js"); // For assertDropCollection.
load("jstests/libs/discover_topology.js"); // For findNonConfigNodes.
function generateNestedPipeline(foreignCollName, numLevels) {
let pipeline = [{"$lookup": {pipeline: [], from: foreignCollName, as: "same"}}];
for (let level = 1; level < numLevels; level++) {
pipeline = [{"$lookup": {pipeline: pipeline, from: foreignCollName, as: "same"}}];
}
return pipeline;
}
function runTest(lookup) {
const db = null; // Using the db variable is banned in this function.
const lookupName = lookup.getName();
// Deeply nested $lookup pipeline. Confirm that we can execute an aggregation with nested
// $lookup sub-pipelines up to the maximum depth, but not beyond.
let nestedPipeline = generateNestedPipeline("lookup", 20);
assert.commandWorked(
lookup.getDB().runCommand({aggregate: lookupName, pipeline: nestedPipeline, cursor: {}}));
nestedPipeline = generateNestedPipeline("lookup", 21);
assertErrorCode(lookup, nestedPipeline, ErrorCodes.MaxSubPipelineDepthExceeded);
// Confirm that maximum $lookup sub-pipeline depth is respected when aggregating views whose
// combined nesting depth exceeds the limit.
nestedPipeline = generateNestedPipeline(lookupName, 10);
assertDropCollection(lookup.getDB(), "view1");
assert.commandWorked(
lookup.getDB().runCommand({create: "view1", viewOn: lookupName, pipeline: nestedPipeline}));
nestedPipeline = generateNestedPipeline("view1", 10);
assertDropCollection(lookup.getDB(), "view2");
assert.commandWorked(
lookup.getDB().runCommand({create: "view2", viewOn: "view1", pipeline: nestedPipeline}));
// Confirm that a composite sub-pipeline depth of 20 is allowed.
assert.commandWorked(lookup.getDB().runCommand({aggregate: "view2", pipeline: [], cursor: {}}));
const pipelineWhichExceedsNestingLimit = generateNestedPipeline("view2", 1);
assertDropCollection(lookup.getDB(), "view3");
assert.commandWorked(lookup.getDB().runCommand(
{create: "view3", viewOn: "view2", pipeline: pipelineWhichExceedsNestingLimit}));
// Confirm that a composite sub-pipeline depth greater than 20 fails.
assertErrorCode(lookup.getDB().view3, [], ErrorCodes.MaxSubPipelineDepthExceeded);
}
// Run tests on single node.
const standalone = MongoRunner.runMongod();
runTest(standalone.getDB("test").lookup);
MongoRunner.stopMongod(standalone);
// Run tests in a sharded environment. We must set up sharding explicitly, because
// implicitly_shard_accessed_collections.js will attempt to shard views when they are accessed.
const sharded = new ShardingTest({mongos: 1, shards: 2});
assert(sharded.adminCommand({enableSharding: "test"}));
assert(sharded.adminCommand({shardCollection: "test.lookup", key: {_id: 'hashed'}}));
runTest(sharded.getDB('test').lookup);
sharded.stop();
}());
|