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
|
// Tests $merge over documents with $-field in it.
//
// Sharded collections have special requirements on the join field.
// @tags: [assumes_unsharded_collection]
(function() {
"use strict";
load("jstests/libs/collection_drop_recreate.js"); // For assertDropCollection.
const sourceName = 'merge_with_dollar_fields_source';
const source = db[sourceName];
const targetName = 'merge_with_dollar_fields_target';
const target = db[targetName];
const joinField = 'joinField';
const sourceDoc = {
$dollar: 1,
joinField
};
const targetDoc = {
a: 1,
joinField
};
assertDropCollection(db, sourceName);
assert.commandWorked(source.insert(sourceDoc));
function runTest({whenMatched, whenNotMatched}, targetDocs) {
assertDropCollection(db, targetName);
assert.commandWorked(target.createIndex({joinField: 1}, {unique: true}));
assert.commandWorked(target.insert(targetDocs));
source.aggregate([
{$project: {_id: 0}},
{
$merge: {
into: targetName,
on: joinField,
whenMatched,
whenNotMatched,
}
}
]);
return target.findOne({}, {_id: 0});
}
function runTestMatched(mode) {
return runTest(mode, [targetDoc]);
}
function runTestNotMatched(mode) {
return runTest(mode, []);
}
// TODO: SERVER-76999: Currently $merge may throw 'FailedToParse' error due to non-local updates.
// We should return consistent results for dollar field documents.
// whenMatched: 'replace', whenNotMatched: 'insert'
assert.throwsWithCode(() => runTestMatched({whenMatched: 'replace', whenNotMatched: 'insert'}),
[ErrorCodes.DollarPrefixedFieldName, ErrorCodes.FailedToParse]);
try {
assert.docEq(sourceDoc, runTestNotMatched({whenMatched: 'replace', whenNotMatched: 'insert'}));
} catch (error) {
assert.commandFailedWithCode(error, ErrorCodes.FailedToParse);
}
// whenMatched: 'replace', whenNotMatched: 'fail'
assert.throwsWithCode(() => runTestMatched({whenMatched: 'replace', whenNotMatched: 'fail'}),
[ErrorCodes.DollarPrefixedFieldName, ErrorCodes.FailedToParse]);
assert.throwsWithCode(() => runTestNotMatched({whenMatched: 'replace', whenNotMatched: 'fail'}),
[ErrorCodes.MergeStageNoMatchingDocument, ErrorCodes.FailedToParse]);
// whenMatched: 'replace', whenNotMatched: 'discard'
assert.throwsWithCode(() => runTestMatched({whenMatched: 'replace', whenNotMatched: 'discard'}),
[ErrorCodes.DollarPrefixedFieldName, ErrorCodes.FailedToParse]);
try {
assert.eq(null, runTestNotMatched({whenMatched: 'replace', whenNotMatched: 'discard'}));
} catch (error) {
assert.commandFailedWithCode(error, ErrorCodes.FailedToParse);
}
// whenMatched: 'merge', whenNotMatched: 'insert'
assert.throwsWithCode(() => runTestMatched({whenMatched: 'merge', whenNotMatched: 'insert'}),
ErrorCodes.DollarPrefixedFieldName);
assert.docEq(sourceDoc, runTestNotMatched({whenMatched: 'merge', whenNotMatched: 'insert'}));
// whenMatched: 'merge', whenNotMatched: 'fail'
assert.throwsWithCode(() => runTestMatched({whenMatched: 'merge', whenNotMatched: 'fail'}),
ErrorCodes.DollarPrefixedFieldName);
assert.throwsWithCode(() => runTestNotMatched({whenMatched: 'merge', whenNotMatched: 'fail'}),
ErrorCodes.MergeStageNoMatchingDocument);
// whenMatched: 'merge', whenNotMatched: 'discard'
assert.throwsWithCode(() => runTestMatched({whenMatched: 'merge', whenNotMatched: 'discard'}),
ErrorCodes.DollarPrefixedFieldName);
assert.eq(null, runTestNotMatched({whenMatched: 'merge', whenNotMatched: 'discard'}));
// whenMatched: 'keepExisting', whenNotMatched: 'insert'
assert.docEq(targetDoc, runTestMatched({whenMatched: 'keepExisting', whenNotMatched: 'insert'}));
assert.docEq(sourceDoc, runTestNotMatched({whenMatched: 'keepExisting', whenNotMatched: 'insert'}));
// whenMatched: 'fail', whenNotMatched: 'insert'
assert.throwsWithCode(() => runTestMatched({whenMatched: 'fail', whenNotMatched: 'insert'}),
ErrorCodes.DuplicateKey);
assert.docEq(sourceDoc, runTestNotMatched({whenMatched: 'fail', whenNotMatched: 'insert'}));
// whenMatched: 'pipeline', whenNotMatched: 'insert'
const pipeline = [{$addFields: {b: 1}}];
const targetDocAddFields = {
...targetDoc,
b: 1
};
assert.docEq(targetDocAddFields, runTestMatched({whenMatched: pipeline, whenNotMatched: 'insert'}));
assert.docEq(sourceDoc, runTestNotMatched({whenMatched: pipeline, whenNotMatched: 'insert'}));
// whenMatched: 'pipeline', whenNotMatched: 'fail'
assert.docEq(targetDocAddFields, runTestMatched({whenMatched: pipeline, whenNotMatched: 'fail'}));
assert.throwsWithCode(() => runTestNotMatched({whenMatched: pipeline, whenNotMatched: 'fail'}),
ErrorCodes.MergeStageNoMatchingDocument);
// whenMatched: 'pipeline', whenNotMatched: 'discard'
assert.docEq(targetDocAddFields,
runTestMatched({whenMatched: pipeline, whenNotMatched: 'discard'}));
assert.eq(null, runTestNotMatched({whenMatched: pipeline, whenNotMatched: 'discard'}));
}());
|