summaryrefslogtreecommitdiff
path: root/jstests/core/update_with_pipeline.js
blob: d9dae0918357e829e844a33fd06896bbbad9a21b (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
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
/**
 * Tests execution of pipeline-style update.
 *
 * 'requires_find_command' needed to prevent this test from running with 'compatibility' write mode
 * as pipeline-style update is not supported by OP_UPDATE.
 *
 * @tags: [requires_find_command, requires_non_retryable_writes]
 */
(function() {
    "use strict";

    load("jstests/libs/fixture_helpers.js");  // For FixtureHelpers.

    const collName = "update_with_pipeline";
    const coll = db[collName];

    assert.commandWorked(coll.createIndex({x: 1}));
    assert.commandWorked(coll.createIndex({"y.$**": 1}));

    /**
     * Confirms that an update returns the expected set of documents. 'nModified' documents from
     * 'resultDocList' must match. 'nModified' may be smaller then the number of elements in
     * 'resultDocList'. This allows for the case where there are multiple documents that could be
     * updated, but only one is actually updated due to a 'multi: false' argument.
     */
    function testUpdate(
        {query, initialDocumentList, update, resultDocList, nModified, options = {}}) {
        assert.eq(initialDocumentList.length, resultDocList.length);
        assert.commandWorked(coll.remove({}));
        assert.commandWorked(coll.insert(initialDocumentList));
        const res = assert.commandWorked(coll.update(query, update, options));
        assert.eq(nModified, res.nModified);

        let nMatched = 0;
        for (let i = 0; i < resultDocList.length; ++i) {
            if (0 === bsonWoCompare(coll.findOne(resultDocList[i]), resultDocList[i])) {
                ++nMatched;
            }
        }
        assert.eq(nModified, nMatched);
    }

    function testUpsertDoesInsert(query, update, resultDoc) {
        assert.commandWorked(coll.remove({}));
        assert.commandWorked(coll.update(query, update, {upsert: true}));
        assert.eq(coll.findOne({}), resultDoc, coll.find({}).toArray());
    }

    // Update with existing document.
    testUpdate({
        query: {_id: 1},
        initialDocumentList: [{_id: 1, x: 1}],
        update: [{$addFields: {foo: 4}}],
        resultDocList: [{_id: 1, x: 1, foo: 4}],
        nModified: 1
    });
    testUpdate({
        query: {_id: 1},
        initialDocumentList: [{_id: 1, x: 1, y: 1}],
        update: [{$project: {x: 1}}],
        resultDocList: [{_id: 1, x: 1}],
        nModified: 1
    });
    testUpdate({
        query: {_id: 1},
        initialDocumentList: [{_id: 1, x: 1, t: {u: {v: 1}}}],
        update: [{$replaceRoot: {newRoot: "$t"}}],
        resultDocList: [{_id: 1, u: {v: 1}}],
        nModified: 1
    });

    // Multi-update.
    testUpdate({
        query: {x: 1},
        initialDocumentList: [{_id: 1, x: 1}, {_id: 2, x: 1}],
        update: [{$addFields: {bar: 4}}],
        resultDocList: [{_id: 1, x: 1, bar: 4}, {_id: 2, x: 1, bar: 4}],
        nModified: 2,
        options: {multi: true}
    });

    // This test will fail in a sharded cluster when the 2 initial documents live on different
    // shards.
    if (!FixtureHelpers.isMongos(db)) {
        testUpdate({
            query: {_id: {$in: [1, 2]}},
            initialDocumentList: [{_id: 1, x: 1}, {_id: 2, x: 2}],
            update: [{$addFields: {bar: 4}}],
            resultDocList: [{_id: 1, x: 1, bar: 4}, {_id: 2, x: 2, bar: 4}],
            nModified: 1,
            options: {multi: false}
        });
    }

    // Upsert performs insert.
    testUpsertDoesInsert({_id: 1, x: 1}, [{$addFields: {foo: 4}}], {_id: 1, x: 1, foo: 4});
    testUpsertDoesInsert({_id: 1, x: 1}, [{$project: {x: 1}}], {_id: 1, x: 1});
    testUpsertDoesInsert({_id: 1, x: 1}, [{$project: {x: "foo"}}], {_id: 1, x: "foo"});

    // Update fails when invalid stage is specified. This is a sanity check rather than an
    // exhaustive test of all stages.
    assert.commandFailedWithCode(coll.update({x: 1}, [{$match: {x: 1}}]),
                                 ErrorCodes.InvalidOptions);
    assert.commandFailedWithCode(coll.update({x: 1}, [{$sort: {x: 1}}]), ErrorCodes.InvalidOptions);
    assert.commandFailedWithCode(coll.update({x: 1}, [{$facet: {a: [{$match: {x: 1}}]}}]),
                                 ErrorCodes.InvalidOptions);
    assert.commandFailedWithCode(coll.update({x: 1}, [{$indexStats: {}}]),
                                 ErrorCodes.InvalidOptions);
    assert.commandFailedWithCode(coll.update({x: 1}, [{
                                                 $bucket: {
                                                     groupBy: "$a",
                                                     boundaries: [0, 1],
                                                     default: "foo",
                                                     output: {count: {$sum: 1}}
                                                 }
                                             }]),
                                 ErrorCodes.InvalidOptions);
    assert.commandFailedWithCode(
        coll.update({x: 1},
                    [{$lookup: {from: "foo", as: "as", localField: "a", foreignField: "b"}}]),
        ErrorCodes.InvalidOptions);
    assert.commandFailedWithCode(coll.update({x: 1}, [{
                                                 $graphLookup: {
                                                     from: "foo",
                                                     startWith: "$a",
                                                     connectFromField: "a",
                                                     connectToField: "b",
                                                     as: "as"
                                                 }
                                             }]),
                                 ErrorCodes.InvalidOptions);

    // Update fails when supported agg stage is specified outside of pipeline.
    assert.commandFailedWithCode(coll.update({_id: 1}, {$addFields: {x: 1}}),
                                 ErrorCodes.FailedToParse);

    // The 'arrayFilters' option is not valid for pipeline updates.
    assert.commandFailedWithCode(
        coll.update({_id: 1}, [{$addFields: {x: 1}}], {arrayFilters: [{x: {$eq: 1}}]}),
        ErrorCodes.FailedToParse);
})();