mongo/jstests/change_streams/expanded_update_description.js

228 lines
7.9 KiB
JavaScript

/**
* Test change stream 'updateDescription' with 'showExpandedEvents'.
*/
import {assertDropAndRecreateCollection} from "jstests/libs/collection_drop_recreate.js";
import {ChangeStreamTest} from "jstests/libs/query/change_stream_util.js";
// Drop and recreate the collections to be used in this set of tests.
assertDropAndRecreateCollection(db, "coll");
const cst = new ChangeStreamTest(db);
const kLargeStr = "*".repeat(128);
assert.commandWorked(
db.coll.insert({
_id: 100,
"topLevelArray": [{subArray: [0, [0, [{bottomArray: [1, 2, kLargeStr]}]], 2, 3, kLargeStr]}],
"arrayForReplacement": [0, 1, 2, 3],
"arrayForResize": [kLargeStr, 1],
obj: {
"sub.obj": {"d.o.t.t.e.d.a.r.r.a.y..": [[{a: {"b.c": 1, field: kLargeStr}}, "truncated"]]},
},
"d.o.t.t.e.d.o.b.j.": {"sub.obj": {"b.c": 2}},
"objectWithNumericField": {"0": {"1": "numeric", field: kLargeStr}},
"arrayWithNumericField": [[{"0": "numeric", a: {"b.c": 1}, field: kLargeStr}]],
"arrayWithDotted.AndNumericFields": [[{"0": [{"1.2": {"a.b": null, c: kLargeStr}}]}]],
}),
);
const changeStreamCursor = cst.startWatchingChanges({
pipeline: [{$changeStream: {showExpandedEvents: true}}],
collection: db.coll,
});
// Test that a path which only contains non-dotted fields and array indices is not reported under
// 'disambiguatedPaths'.
assert.commandWorked(
db.coll.update(
{_id: 100},
{
$set: {"a": 2, "topLevelArray.0.subArray.1.1.0.bottomArray.2": 3, "arrayForReplacement": [0]},
},
),
);
let expected = {
documentKey: {_id: 100},
ns: {db: "test", coll: "coll"},
operationType: "update",
updateDescription: {
updatedFields: {"arrayForReplacement": [0], "a": 2, "topLevelArray.0.subArray.1.1.0.bottomArray.2": 3},
removedFields: [],
truncatedArrays: [],
disambiguatedPaths: {},
},
};
cst.assertNextChangesEqual({cursor: changeStreamCursor, expectedChanges: [expected]});
// Tests that an update modifying a non-array numeric field name is reported as a string rather than
// as an integer under 'disambiguatedPaths'. Array indexes are reported as integers.
assert.commandWorked(db.coll.update({_id: 100}, {$set: {"arrayWithNumericField.0.0.1": {"b.c": 1}}}));
expected = {
documentKey: {_id: 100},
ns: {db: "test", coll: "coll"},
operationType: "update",
updateDescription: {
updatedFields: {"arrayWithNumericField.0.0.1": {"b.c": 1}},
removedFields: [],
truncatedArrays: [],
disambiguatedPaths: {"arrayWithNumericField.0.0.1": ["arrayWithNumericField", 0, 0, "1"]},
},
};
cst.assertNextChangesEqual({cursor: changeStreamCursor, expectedChanges: [expected]});
// Tests that an update modifying a non-array numeric field name is reported when no array indices
// or dotted fields are present.
assert.commandWorked(db.coll.update({_id: 100}, {$set: {"objectWithNumericField.0.1": "updated"}}));
expected = {
documentKey: {_id: 100},
ns: {db: "test", coll: "coll"},
operationType: "update",
updateDescription: {
updatedFields: {"objectWithNumericField.0.1": "updated"},
removedFields: [],
truncatedArrays: [],
disambiguatedPaths: {"objectWithNumericField.0.1": ["objectWithNumericField", "0", "1"]},
},
};
cst.assertNextChangesEqual({cursor: changeStreamCursor, expectedChanges: [expected]});
// Tests that an update with $unset array does not report the array under 'disambiguatedPaths'.
assert.commandWorked(db.coll.update({_id: 100}, [{$unset: ["arrayForReplacement"]}]));
expected = {
documentKey: {_id: 100},
ns: {db: "test", coll: "coll"},
operationType: "update",
updateDescription: {
updatedFields: {},
removedFields: ["arrayForReplacement"],
truncatedArrays: [],
disambiguatedPaths: {},
},
};
cst.assertNextChangesEqual({cursor: changeStreamCursor, expectedChanges: [expected]});
// Tests that an update with 'truncatedArrays' does not report the array under 'disambiguatedPaths'.
assert.commandWorked(
db.coll.update({_id: 100}, [
{$replaceWith: {$setField: {field: "arrayForResize", input: "$$ROOT", value: [kLargeStr]}}},
]),
);
expected = {
documentKey: {_id: 100},
ns: {db: "test", coll: "coll"},
operationType: "update",
updateDescription: {
updatedFields: {},
removedFields: [],
truncatedArrays: [{field: "arrayForResize", newSize: 1}],
disambiguatedPaths: {},
},
};
cst.assertNextChangesEqual({cursor: changeStreamCursor, expectedChanges: [expected]});
// Verify that top-level dotted fields are reported under 'disambiguatedPaths'.
assert.commandWorked(
db.coll.update({_id: 100}, [
{
$replaceWith: {$setField: {field: "d.o.t.t.e.d.o.b.j.", input: "$$ROOT", value: {"subObj": 1}}},
},
{$replaceWith: {$setField: {field: "new.Field.", input: "$$ROOT", value: 1}}},
]),
);
expected = {
documentKey: {_id: 100},
ns: {db: "test", coll: "coll"},
operationType: "update",
updateDescription: {
updatedFields: {"d.o.t.t.e.d.o.b.j.": {subObj: 1}, "new.Field.": 1},
removedFields: [],
truncatedArrays: [],
disambiguatedPaths: {"d.o.t.t.e.d.o.b.j.": ["d.o.t.t.e.d.o.b.j."], "new.Field.": ["new.Field."]},
},
};
cst.assertNextChangesEqual({cursor: changeStreamCursor, expectedChanges: [expected]});
// Test that a combination of dotted fields and array indices are reported in 'disambiguatedPaths'.
assert.commandWorked(
db.coll.update({_id: 100}, [
{
$set: {
obj: {
$setField: {
field: "sub.obj",
input: "$obj",
value: {
$literal: {"d.o.t.t.e.d.a.r.r.a.y..": [[{a: {"b.c": 2, field: kLargeStr}}]]},
},
},
},
},
},
]),
);
expected = {
documentKey: {_id: 100},
ns: {db: "test", coll: "coll"},
operationType: "update",
updateDescription: {
updatedFields: {"obj.sub.obj.d.o.t.t.e.d.a.r.r.a.y...0.0.a.b.c": 2},
removedFields: [],
truncatedArrays: [{field: "obj.sub.obj.d.o.t.t.e.d.a.r.r.a.y...0", newSize: 1}],
disambiguatedPaths: {
"obj.sub.obj.d.o.t.t.e.d.a.r.r.a.y...0": ["obj", "sub.obj", "d.o.t.t.e.d.a.r.r.a.y..", 0],
"obj.sub.obj.d.o.t.t.e.d.a.r.r.a.y...0.0.a.b.c": [
"obj",
"sub.obj",
"d.o.t.t.e.d.a.r.r.a.y..",
0,
0,
"a",
"b.c",
],
},
},
};
cst.assertNextChangesEqual({cursor: changeStreamCursor, expectedChanges: [expected]});
// Test that an update which modifies a path containing dotted, numeric and array index fields
// distinguishes all three in 'disambiguatedPaths'.
assert.commandWorked(
db.coll.update({_id: 100}, [
{
$replaceWith: {
$setField: {
field: "arrayWithDotted.AndNumericFields",
input: "$$ROOT",
value: {$literal: [[{"0": [{"1.2": {"a.b": true, c: kLargeStr}}]}]]},
},
},
},
]),
);
expected = {
documentKey: {_id: 100},
ns: {db: "test", coll: "coll"},
operationType: "update",
updateDescription: {
updatedFields: {"arrayWithDotted.AndNumericFields.0.0.0.0.1.2.a.b": true},
removedFields: [],
truncatedArrays: [],
disambiguatedPaths: {
"arrayWithDotted.AndNumericFields.0.0.0.0.1.2.a.b": [
"arrayWithDotted.AndNumericFields",
0,
0,
"0",
0,
"1.2",
"a.b",
],
},
},
};
cst.assertNextChangesEqual({cursor: changeStreamCursor, expectedChanges: [expected]});
cst.cleanUp();