summaryrefslogtreecommitdiff
path: root/jstests/noPassthrough/lookup_max_nested_pipeline.js
blob: 030021515ea7b4507255522bbda0081279c53dbd (plain)
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();
}());