diff options
21 files changed, 524 insertions, 87 deletions
diff --git a/src/mongo/db/field_ref.h b/src/mongo/db/field_ref.h index 51591b63d96..4ae903d35d2 100644 --- a/src/mongo/db/field_ref.h +++ b/src/mongo/db/field_ref.h @@ -36,7 +36,6 @@ #include <string> #include <vector> -#include "mongo/base/disallow_copying.h" #include "mongo/base/string_data.h" namespace mongo { @@ -275,6 +274,14 @@ inline bool operator>=(const FieldRef& lhs, const FieldRef& rhs) { return lhs.compare(rhs) >= 0; } +inline FieldRef operator+(const FieldRef& lhs, const FieldRef& rhs) { + FieldRef result = lhs; + for (size_t i = 0; i < rhs.numParts(); ++i) { + result.appendPart(rhs.getPart(i)); + } + return result; +} + std::ostream& operator<<(std::ostream& stream, const FieldRef& value); } // namespace mongo diff --git a/src/mongo/db/field_ref_set.cpp b/src/mongo/db/field_ref_set.cpp index 5016ad4f33a..b8db7e52b07 100644 --- a/src/mongo/db/field_ref_set.cpp +++ b/src/mongo/db/field_ref_set.cpp @@ -145,16 +145,16 @@ bool FieldRefSet::insert(const FieldRef* toInsert, const FieldRef** conflict) { } const std::string FieldRefSet::toString() const { - str::stream res; - res << "Fields:[ "; - FieldRefSet::const_iterator where = _fieldSet.begin(); - const FieldRefSet::const_iterator end = _fieldSet.end(); - for (; where != end; ++where) { - const FieldRef& current = **where; - res << current.dottedField() << ","; + str::stream ss; + ss << "{"; + const auto last = _fieldSet.rbegin(); + for (auto path : _fieldSet) { + ss << path->dottedField(); + if (path != *last) + ss << ", "; } - res << "]"; - return res; + ss << "}"; + return ss; } } // namespace mongo diff --git a/src/mongo/db/field_ref_set.h b/src/mongo/db/field_ref_set.h index 76af3025932..735683252d2 100644 --- a/src/mongo/db/field_ref_set.h +++ b/src/mongo/db/field_ref_set.h @@ -60,8 +60,8 @@ class FieldRefSet { typedef std::set<const FieldRef*, FieldRefPtrLessThan> FieldSet; public: - typedef FieldSet::iterator iterator; - typedef FieldSet::const_iterator const_iterator; + using iterator = FieldSet::iterator; + using const_iterator = FieldSet::const_iterator; FieldRefSet(); @@ -108,7 +108,7 @@ public: void fillFrom(const std::vector<FieldRef*>& fields); /** - * Replace any existing conflicting FieldRef with the shortest (closest to root) one + * Replace any existing conflicting FieldRef with the shortest (closest to root) one. */ void keepShortest(const FieldRef* toInsert); @@ -124,6 +124,10 @@ public: _fieldSet.clear(); } + void erase(const FieldRef* item) { + _fieldSet.erase(item); + } + /** * A debug/log-able string */ @@ -134,4 +138,40 @@ private: FieldSet _fieldSet; }; +/** + * A wrapper class for FieldRefSet which owns the storage of the underlying FieldRef objects. + */ +class FieldRefSetWithStorage { +public: + /** + * Inserts the given FieldRef into the set. In the case of a conflict with an existing element, + * only the shortest path is kept in the set. + */ + void keepShortest(const FieldRef& toInsert) { + const FieldRef* inserted = &(*_ownedFieldRefs.insert(toInsert).first); + _fieldRefSet.keepShortest(inserted); + } + + bool empty() const { + return _fieldRefSet.empty(); + } + + void clear() { + _ownedFieldRefs.clear(); + _fieldRefSet.clear(); + } + + std::string toString() const { + return _fieldRefSet.toString(); + } + +private: + // Holds the storage for FieldRef's inserted into the set. This may become out of sync with + // '_fieldRefSet' since we don't attempt to remove conflicts from the backing set, which can + // leave '_ownedFieldRefs' holding storage for a superset of the field refs that are actually + // contained in '_fieldRefSet'. + std::set<FieldRef> _ownedFieldRefs; + FieldRefSet _fieldRefSet; +}; + } // namespace mongo diff --git a/src/mongo/db/field_ref_set_test.cpp b/src/mongo/db/field_ref_set_test.cpp index d3b4c574cbb..db7879bd6a0 100644 --- a/src/mongo/db/field_ref_set_test.cpp +++ b/src/mongo/db/field_ref_set_test.cpp @@ -28,15 +28,16 @@ * it in the license file. */ +#include "mongo/platform/basic.h" + #include "mongo/db/field_ref_set.h" #include "mongo/db/field_ref.h" #include "mongo/unittest/unittest.h" -namespace { +namespace mongo { -using mongo::FieldRef; -using mongo::FieldRefSet; +namespace { TEST(EmptySet, Normal) { // insert "b" @@ -143,4 +144,47 @@ TEST(NotEmpty, Conflict) { ASSERT_EQUALS(bDotE, *conflict); } -} // unnamed namespace +TEST(FieldRefSetWithStorageTest, ManagesFieldRefLifetime) { + FieldRefSetWithStorage fieldRefSet; + FieldRef ref("a.b"); + + fieldRefSet.keepShortest(ref); + ASSERT_EQUALS("{a.b}", fieldRefSet.toString()); + + // Re-use 'ref', and verify that the set still contains the previous FieldRef. + ref.parse("b.c"_sd); + fieldRefSet.keepShortest(ref); + ASSERT_EQUALS("{a.b, b.c}", fieldRefSet.toString()); +} + +TEST(FieldRefSetWithStorageTest, InsertRemovesConflictsByKeepingShortest) { + FieldRefSetWithStorage fieldRefSet; + FieldRef ref("a.b"); + + fieldRefSet.keepShortest(ref); + ASSERT_EQUALS("{a.b}", fieldRefSet.toString()); + + ref.parse("a"_sd); + fieldRefSet.keepShortest(ref); + ASSERT_EQUALS("{a}", fieldRefSet.toString()); +} + +TEST(FieldRefSetWithStorageTest, InsertRemovesMultipleConflicts) { + FieldRefSetWithStorage fieldRefSet; + FieldRef ref("a.b"); + + fieldRefSet.keepShortest(ref); + ASSERT_EQUALS("{a.b}", fieldRefSet.toString()); + + ref.parse("a.c"_sd); + fieldRefSet.keepShortest(ref); + ASSERT_EQUALS("{a.b, a.c}", fieldRefSet.toString()); + + // Inserting 'a' should remove both conflicts with longer paths. + ref.parse("a"_sd); + fieldRefSet.keepShortest(ref); + ASSERT_EQUALS("{a}", fieldRefSet.toString()); +} + +} // namespace +} // namespace mongo diff --git a/src/mongo/db/update/addtoset_node_test.cpp b/src/mongo/db/update/addtoset_node_test.cpp index 37350cef4d1..004dbd5657c 100644 --- a/src/mongo/db/update/addtoset_node_test.cpp +++ b/src/mongo/db/update/addtoset_node_test.cpp @@ -135,6 +135,7 @@ TEST_F(AddToSetNodeTest, ApplyNonEach) { ASSERT_EQUALS(fromjson("{a: [0, 1]}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(fromjson("{$set: {a: [0, 1]}}"), getLogDoc()); + ASSERT_EQUALS(getModifiedPaths(), "{a}"); } TEST_F(AddToSetNodeTest, ApplyNonEachArray) { @@ -152,6 +153,7 @@ TEST_F(AddToSetNodeTest, ApplyNonEachArray) { ASSERT_EQUALS(fromjson("{a: [0, [1]]}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(fromjson("{$set: {a: [0, [1]]}}"), getLogDoc()); + ASSERT_EQUALS(getModifiedPaths(), "{a}"); } TEST_F(AddToSetNodeTest, ApplyEach) { @@ -169,6 +171,7 @@ TEST_F(AddToSetNodeTest, ApplyEach) { ASSERT_EQUALS(fromjson("{a: [0, 1, 2]}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(fromjson("{$set: {a: [0, 1, 2]}}"), getLogDoc()); + ASSERT_EQUALS(getModifiedPaths(), "{a}"); } TEST_F(AddToSetNodeTest, ApplyToEmptyArray) { @@ -186,6 +189,7 @@ TEST_F(AddToSetNodeTest, ApplyToEmptyArray) { ASSERT_EQUALS(fromjson("{a: [1, 2]}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(fromjson("{$set: {a: [1, 2]}}"), getLogDoc()); + ASSERT_EQUALS(getModifiedPaths(), "{a}"); } TEST_F(AddToSetNodeTest, ApplyDeduplicateElementsToAdd) { @@ -203,6 +207,7 @@ TEST_F(AddToSetNodeTest, ApplyDeduplicateElementsToAdd) { ASSERT_EQUALS(fromjson("{a: [0, 1]}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(fromjson("{$set: {a: [0, 1]}}"), getLogDoc()); + ASSERT_EQUALS(getModifiedPaths(), "{a}"); } TEST_F(AddToSetNodeTest, ApplyDoNotAddExistingElements) { @@ -220,6 +225,7 @@ TEST_F(AddToSetNodeTest, ApplyDoNotAddExistingElements) { ASSERT_EQUALS(fromjson("{a: [0, 1]}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(fromjson("{$set: {a: [0, 1]}}"), getLogDoc()); + ASSERT_EQUALS(getModifiedPaths(), "{a}"); } TEST_F(AddToSetNodeTest, ApplyDoNotDeduplicateExistingElements) { @@ -237,6 +243,7 @@ TEST_F(AddToSetNodeTest, ApplyDoNotDeduplicateExistingElements) { ASSERT_EQUALS(fromjson("{a: [0, 0, 1]}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(fromjson("{$set: {a: [0, 0, 1]}}"), getLogDoc()); + ASSERT_EQUALS(getModifiedPaths(), "{a}"); } TEST_F(AddToSetNodeTest, ApplyNoElementsToAdd) { @@ -254,6 +261,7 @@ TEST_F(AddToSetNodeTest, ApplyNoElementsToAdd) { ASSERT_EQUALS(fromjson("{a: [0]}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(fromjson("{}"), getLogDoc()); + ASSERT_EQUALS(getModifiedPaths(), "{a}"); } TEST_F(AddToSetNodeTest, ApplyNoNonDuplicateElementsToAdd) { @@ -271,6 +279,7 @@ TEST_F(AddToSetNodeTest, ApplyNoNonDuplicateElementsToAdd) { ASSERT_EQUALS(fromjson("{a: [0]}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(fromjson("{}"), getLogDoc()); + ASSERT_EQUALS(getModifiedPaths(), "{a}"); } TEST_F(AddToSetNodeTest, ApplyCreateArray) { @@ -288,6 +297,7 @@ TEST_F(AddToSetNodeTest, ApplyCreateArray) { ASSERT_EQUALS(fromjson("{a: [0, 1]}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(fromjson("{$set: {a: [0, 1]}}"), getLogDoc()); + ASSERT_EQUALS(getModifiedPaths(), "{a}"); } TEST_F(AddToSetNodeTest, ApplyCreateEmptyArrayIsNotNoop) { @@ -305,6 +315,7 @@ TEST_F(AddToSetNodeTest, ApplyCreateEmptyArrayIsNotNoop) { ASSERT_EQUALS(fromjson("{a: []}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(fromjson("{$set: {a: []}}"), getLogDoc()); + ASSERT_EQUALS(getModifiedPaths(), "{a}"); } TEST_F(AddToSetNodeTest, ApplyDeduplicationOfElementsToAddRespectsCollation) { @@ -324,6 +335,7 @@ TEST_F(AddToSetNodeTest, ApplyDeduplicationOfElementsToAddRespectsCollation) { ASSERT_EQUALS(fromjson("{a: ['abc', 'def']}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(fromjson("{$set: {a: ['abc', 'def']}}"), getLogDoc()); + ASSERT_EQUALS(getModifiedPaths(), "{a}"); } TEST_F(AddToSetNodeTest, ApplyComparisonToExistingElementsRespectsCollation) { @@ -343,6 +355,7 @@ TEST_F(AddToSetNodeTest, ApplyComparisonToExistingElementsRespectsCollation) { ASSERT_EQUALS(fromjson("{a: ['ABC', 'def']}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(fromjson("{$set: {a: ['ABC', 'def']}}"), getLogDoc()); + ASSERT_EQUALS(getModifiedPaths(), "{a}"); } TEST_F(AddToSetNodeTest, ApplyRespectsCollationFromSetCollator) { @@ -364,6 +377,7 @@ TEST_F(AddToSetNodeTest, ApplyRespectsCollationFromSetCollator) { ASSERT_EQUALS(fromjson("{a: ['abc', 'def']}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(fromjson("{$set: {a: ['abc', 'def']}}"), getLogDoc()); + ASSERT_EQUALS(getModifiedPaths(), "{a}"); } DEATH_TEST(AddToSetNodeTest, CannotSetCollatorIfCollatorIsNonNull, "Invariant failure !_collator") { @@ -403,6 +417,7 @@ TEST_F(AddToSetNodeTest, ApplyNestedArray) { ASSERT_EQUALS(fromjson("{ _id : 1, a : [ 1, [ 1 ] ] }"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(fromjson("{ $set : { 'a.1' : [ 1 ] } }"), getLogDoc()); + ASSERT_EQUALS(getModifiedPaths(), "{a.1}"); } TEST_F(AddToSetNodeTest, ApplyIndexesNotAffected) { @@ -419,6 +434,7 @@ TEST_F(AddToSetNodeTest, ApplyIndexesNotAffected) { ASSERT_FALSE(result.indexesAffected); ASSERT_FALSE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(fromjson("{$set: {a: [0, 1]}}"), getLogDoc()); + ASSERT_EQUALS(getModifiedPaths(), "{a}"); } TEST_F(AddToSetNodeTest, ApplyNoIndexDataOrLogBuilder) { @@ -435,6 +451,7 @@ TEST_F(AddToSetNodeTest, ApplyNoIndexDataOrLogBuilder) { ASSERT_FALSE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: [0, 1]}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); + ASSERT_EQUALS(getModifiedPaths(), "{a}"); } } // namespace diff --git a/src/mongo/db/update/arithmetic_node_test.cpp b/src/mongo/db/update/arithmetic_node_test.cpp index 3aa6aeeeced..0bfe1a8f71a 100644 --- a/src/mongo/db/update/arithmetic_node_test.cpp +++ b/src/mongo/db/update/arithmetic_node_test.cpp @@ -127,6 +127,7 @@ TEST_F(ArithmeticNodeTest, ApplyIncNoOp) { ASSERT_EQUALS(fromjson("{a: 5}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(fromjson("{}"), getLogDoc()); + ASSERT_EQUALS(getModifiedPaths(), "{a}"); } TEST_F(ArithmeticNodeTest, ApplyMulNoOp) { @@ -144,6 +145,7 @@ TEST_F(ArithmeticNodeTest, ApplyMulNoOp) { ASSERT_EQUALS(fromjson("{a: 5}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(fromjson("{}"), getLogDoc()); + ASSERT_EQUALS(getModifiedPaths(), "{a}"); } TEST_F(ArithmeticNodeTest, ApplyRoundingNoOp) { @@ -161,6 +163,7 @@ TEST_F(ArithmeticNodeTest, ApplyRoundingNoOp) { ASSERT_EQUALS(fromjson("{a: 6.022e23}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(fromjson("{}"), getLogDoc()); + ASSERT_EQUALS(getModifiedPaths(), "{a}"); } TEST_F(ArithmeticNodeTest, ApplyEmptyPathToCreate) { @@ -178,6 +181,7 @@ TEST_F(ArithmeticNodeTest, ApplyEmptyPathToCreate) { ASSERT_EQUALS(fromjson("{a: 11}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(fromjson("{$set: {a: 11}}"), getLogDoc()); + ASSERT_EQUALS(getModifiedPaths(), "{a}"); } TEST_F(ArithmeticNodeTest, ApplyCreatePath) { @@ -196,6 +200,7 @@ TEST_F(ArithmeticNodeTest, ApplyCreatePath) { ASSERT_EQUALS(fromjson("{a: {d: 5, b: {c: 6}}}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(fromjson("{$set: {'a.b.c': 6}}"), getLogDoc()); + ASSERT_EQUALS(getModifiedPaths(), "{a.b.c}"); } TEST_F(ArithmeticNodeTest, ApplyExtendPath) { @@ -213,6 +218,7 @@ TEST_F(ArithmeticNodeTest, ApplyExtendPath) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: {c: 1, b: 2}}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); + ASSERT_EQUALS(getModifiedPaths(), "{a.b}"); } TEST_F(ArithmeticNodeTest, ApplyCreatePathFromRoot) { @@ -230,6 +236,7 @@ TEST_F(ArithmeticNodeTest, ApplyCreatePathFromRoot) { ASSERT_EQUALS(fromjson("{c: 5, a: {b: 6}}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(fromjson("{$set: {'a.b': 6}}"), getLogDoc()); + ASSERT_EQUALS(getModifiedPaths(), "{a.b}"); } TEST_F(ArithmeticNodeTest, ApplyPositional) { @@ -248,6 +255,7 @@ TEST_F(ArithmeticNodeTest, ApplyPositional) { ASSERT_EQUALS(fromjson("{a: [0, 7, 2]}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(fromjson("{$set: {'a.1': 7}}"), getLogDoc()); + ASSERT_EQUALS(getModifiedPaths(), "{a.1}"); } TEST_F(ArithmeticNodeTest, ApplyNonViablePathToInc) { @@ -283,6 +291,7 @@ TEST_F(ArithmeticNodeTest, ApplyNonViablePathToCreateFromReplicationIsNoOp) { ASSERT_EQUALS(fromjson("{a: 5}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(fromjson("{}"), getLogDoc()); + ASSERT_EQUALS(getModifiedPaths(), "{a.b}"); } TEST_F(ArithmeticNodeTest, ApplyNoIndexDataNoLogBuilder) { @@ -299,6 +308,7 @@ TEST_F(ArithmeticNodeTest, ApplyNoIndexDataNoLogBuilder) { ASSERT_FALSE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: 11}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); + ASSERT_EQUALS(getModifiedPaths(), "{a}"); } TEST_F(ArithmeticNodeTest, ApplyDoesNotAffectIndexes) { @@ -315,6 +325,7 @@ TEST_F(ArithmeticNodeTest, ApplyDoesNotAffectIndexes) { ASSERT_FALSE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: 11}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); + ASSERT_EQUALS(getModifiedPaths(), "{a}"); } TEST_F(ArithmeticNodeTest, IncTypePromotionIsNotANoOp) { @@ -331,6 +342,7 @@ TEST_F(ArithmeticNodeTest, IncTypePromotionIsNotANoOp) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: NumberLong(2)}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); + ASSERT_EQUALS(getModifiedPaths(), "{a}"); } TEST_F(ArithmeticNodeTest, MulTypePromotionIsNotANoOp) { @@ -347,6 +359,7 @@ TEST_F(ArithmeticNodeTest, MulTypePromotionIsNotANoOp) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: NumberLong(2)}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); + ASSERT_EQUALS(getModifiedPaths(), "{a}"); } TEST_F(ArithmeticNodeTest, TypePromotionFromIntToDecimalIsNotANoOp) { @@ -364,6 +377,7 @@ TEST_F(ArithmeticNodeTest, TypePromotionFromIntToDecimalIsNotANoOp) { ASSERT_EQUALS(fromjson("{a: NumberDecimal(\"5.0\")}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(fromjson("{$set: {a: NumberDecimal(\"5.0\")}}"), getLogDoc()); + ASSERT_EQUALS(getModifiedPaths(), "{a}"); } TEST_F(ArithmeticNodeTest, TypePromotionFromLongToDecimalIsNotANoOp) { @@ -381,6 +395,7 @@ TEST_F(ArithmeticNodeTest, TypePromotionFromLongToDecimalIsNotANoOp) { ASSERT_EQUALS(fromjson("{a: NumberDecimal(\"5.0\")}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(fromjson("{$set: {a: NumberDecimal(\"5.0\")}}"), getLogDoc()); + ASSERT_EQUALS(getModifiedPaths(), "{a}"); } TEST_F(ArithmeticNodeTest, TypePromotionFromDoubleToDecimalIsNotANoOp) { @@ -398,6 +413,7 @@ TEST_F(ArithmeticNodeTest, TypePromotionFromDoubleToDecimalIsNotANoOp) { ASSERT_EQUALS(fromjson("{a: NumberDecimal(\"5.25\")}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(fromjson("{$set: {a: NumberDecimal(\"5.25\")}}"), getLogDoc()); + ASSERT_EQUALS(getModifiedPaths(), "{a}"); } TEST_F(ArithmeticNodeTest, ApplyPromoteToFloatingPoint) { @@ -414,6 +430,7 @@ TEST_F(ArithmeticNodeTest, ApplyPromoteToFloatingPoint) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: 1.2}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); + ASSERT_EQUALS(getModifiedPaths(), "{a}"); } TEST_F(ArithmeticNodeTest, IncrementedDecimalStaysDecimal) { @@ -431,6 +448,7 @@ TEST_F(ArithmeticNodeTest, IncrementedDecimalStaysDecimal) { ASSERT_EQUALS(fromjson("{a: NumberDecimal(\"11.5\")}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(fromjson("{$set: {a: NumberDecimal(\"11.5\")}}"), getLogDoc()); + ASSERT_EQUALS(getModifiedPaths(), "{a}"); } TEST_F(ArithmeticNodeTest, OverflowIntToLong) { @@ -450,6 +468,7 @@ TEST_F(ArithmeticNodeTest, OverflowIntToLong) { ASSERT_EQUALS(mongo::NumberLong, doc.root()["a"].getType()); ASSERT_EQUALS(BSON("a" << static_cast<long long>(initialValue) + 1), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); + ASSERT_EQUALS(getModifiedPaths(), "{a}"); } TEST_F(ArithmeticNodeTest, UnderflowIntToLong) { @@ -469,6 +488,7 @@ TEST_F(ArithmeticNodeTest, UnderflowIntToLong) { ASSERT_EQUALS(mongo::NumberLong, doc.root()["a"].getType()); ASSERT_EQUALS(BSON("a" << static_cast<long long>(initialValue) - 1), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); + ASSERT_EQUALS(getModifiedPaths(), "{a}"); } TEST_F(ArithmeticNodeTest, IncModeCanBeReused) { @@ -486,6 +506,7 @@ TEST_F(ArithmeticNodeTest, IncModeCanBeReused) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: 2}"), doc1); ASSERT_TRUE(doc1.isInPlaceModeEnabled()); + ASSERT_EQUALS(getModifiedPaths(), "{a}"); resetApplyParams(); setPathTaken("a"); @@ -495,6 +516,7 @@ TEST_F(ArithmeticNodeTest, IncModeCanBeReused) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: 3}"), doc2); ASSERT_TRUE(doc1.isInPlaceModeEnabled()); + ASSERT_EQUALS(getModifiedPaths(), "{a}"); } TEST_F(ArithmeticNodeTest, CreatedNumberHasSameTypeAsInc) { @@ -511,6 +533,7 @@ TEST_F(ArithmeticNodeTest, CreatedNumberHasSameTypeAsInc) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{b: 6, a: NumberLong(5)}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); + ASSERT_EQUALS(getModifiedPaths(), "{a}"); } TEST_F(ArithmeticNodeTest, CreatedNumberHasSameTypeAsMul) { @@ -527,6 +550,7 @@ TEST_F(ArithmeticNodeTest, CreatedNumberHasSameTypeAsMul) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{b: 6, a: NumberLong(0)}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); + ASSERT_EQUALS(getModifiedPaths(), "{a}"); } TEST_F(ArithmeticNodeTest, ApplyEmptyDocument) { @@ -543,6 +567,7 @@ TEST_F(ArithmeticNodeTest, ApplyEmptyDocument) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: 2}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); + ASSERT_EQUALS(getModifiedPaths(), "{a}"); } TEST_F(ArithmeticNodeTest, ApplyIncToObjectFails) { @@ -624,6 +649,7 @@ TEST_F(ArithmeticNodeTest, ApplyNewPath) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{b: 1, a: 2}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); + ASSERT_EQUALS(getModifiedPaths(), "{a}"); } TEST_F(ArithmeticNodeTest, ApplyEmptyIndexData) { @@ -639,6 +665,7 @@ TEST_F(ArithmeticNodeTest, ApplyEmptyIndexData) { ASSERT_TRUE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(countChildren(getLogDoc().root()), 1u); ASSERT_EQUALS(fromjson("{$set: {a: 3}}"), getLogDoc()); + ASSERT_EQUALS(getModifiedPaths(), "{a}"); } TEST_F(ArithmeticNodeTest, ApplyNoOpDottedPath) { @@ -655,6 +682,7 @@ TEST_F(ArithmeticNodeTest, ApplyNoOpDottedPath) { ASSERT_FALSE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: {b : 2}}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); + ASSERT_EQUALS(getModifiedPaths(), "{a.b}"); } TEST_F(ArithmeticNodeTest, TypePromotionOnDottedPathIsNotANoOp) { @@ -671,6 +699,7 @@ TEST_F(ArithmeticNodeTest, TypePromotionOnDottedPathIsNotANoOp) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: {b : NumberLong(2)}}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); + ASSERT_EQUALS(getModifiedPaths(), "{a.b}"); } TEST_F(ArithmeticNodeTest, ApplyPathNotViableArray) { @@ -702,6 +731,7 @@ TEST_F(ArithmeticNodeTest, ApplyInPlaceDottedPath) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: {b: 3}}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); + ASSERT_EQUALS(getModifiedPaths(), "{a.b}"); } TEST_F(ArithmeticNodeTest, ApplyPromotionDottedPath) { @@ -718,6 +748,7 @@ TEST_F(ArithmeticNodeTest, ApplyPromotionDottedPath) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: {b: NumberLong(5)}}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); + ASSERT_EQUALS(getModifiedPaths(), "{a.b}"); } TEST_F(ArithmeticNodeTest, ApplyDottedPathEmptyDoc) { @@ -734,6 +765,7 @@ TEST_F(ArithmeticNodeTest, ApplyDottedPathEmptyDoc) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: {b: 2}}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); + ASSERT_EQUALS(getModifiedPaths(), "{a.b}"); } TEST_F(ArithmeticNodeTest, ApplyFieldWithDot) { @@ -750,6 +782,7 @@ TEST_F(ArithmeticNodeTest, ApplyFieldWithDot) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{'a.b':4, a: {b: 2}}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); + ASSERT_EQUALS(getModifiedPaths(), "{a.b}"); } TEST_F(ArithmeticNodeTest, ApplyNoOpArrayIndex) { @@ -766,6 +799,7 @@ TEST_F(ArithmeticNodeTest, ApplyNoOpArrayIndex) { ASSERT_FALSE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: [{b: 0},{b: 1},{b: 2}]}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); + ASSERT_EQUALS(getModifiedPaths(), "{a.2.b}"); } TEST_F(ArithmeticNodeTest, TypePromotionInArrayIsNotANoOp) { @@ -783,6 +817,7 @@ TEST_F(ArithmeticNodeTest, TypePromotionInArrayIsNotANoOp) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: [{b: 0},{b: 1},{b: NumberLong(2)}]}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); + ASSERT_EQUALS(getModifiedPaths(), "{a.2.b}"); } TEST_F(ArithmeticNodeTest, ApplyNonViablePathThroughArray) { @@ -814,6 +849,7 @@ TEST_F(ArithmeticNodeTest, ApplyInPlaceArrayIndex) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: [{b: 0},{b: 1},{b: 3}]}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); + ASSERT_EQUALS(getModifiedPaths(), "{a.2.b}"); } TEST_F(ArithmeticNodeTest, ApplyAppendArray) { @@ -831,6 +867,7 @@ TEST_F(ArithmeticNodeTest, ApplyAppendArray) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: [{b: 0},{b: 1},{b: 2}]}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); + ASSERT_EQUALS(getModifiedPaths(), "{a}"); } TEST_F(ArithmeticNodeTest, ApplyPaddingArray) { @@ -848,6 +885,7 @@ TEST_F(ArithmeticNodeTest, ApplyPaddingArray) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: [{b: 0},null,{b: 2}]}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); + ASSERT_EQUALS(getModifiedPaths(), "{a}"); } TEST_F(ArithmeticNodeTest, ApplyNumericObject) { @@ -865,6 +903,7 @@ TEST_F(ArithmeticNodeTest, ApplyNumericObject) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: {b: 0, '2': {b: 2}}}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); + ASSERT_EQUALS(getModifiedPaths(), "{a.2.b}"); } TEST_F(ArithmeticNodeTest, ApplyNumericField) { @@ -881,6 +920,7 @@ TEST_F(ArithmeticNodeTest, ApplyNumericField) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: {'2': {b: 3}}}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); + ASSERT_EQUALS(getModifiedPaths(), "{a.2.b}"); } TEST_F(ArithmeticNodeTest, ApplyExtendNumericField) { @@ -898,6 +938,7 @@ TEST_F(ArithmeticNodeTest, ApplyExtendNumericField) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: {'2': {c: 1, b: 2}}}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); + ASSERT_EQUALS(getModifiedPaths(), "{a.2.b}"); } TEST_F(ArithmeticNodeTest, ApplyNumericFieldToEmptyObject) { @@ -915,6 +956,7 @@ TEST_F(ArithmeticNodeTest, ApplyNumericFieldToEmptyObject) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: {'2': {b: 2}}}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); + ASSERT_EQUALS(getModifiedPaths(), "{a.2.b}"); } TEST_F(ArithmeticNodeTest, ApplyEmptyArray) { @@ -932,6 +974,7 @@ TEST_F(ArithmeticNodeTest, ApplyEmptyArray) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: [null, null, {b: 2}]}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); + ASSERT_EQUALS(getModifiedPaths(), "{a}"); } TEST_F(ArithmeticNodeTest, ApplyLogDottedPath) { @@ -948,6 +991,7 @@ TEST_F(ArithmeticNodeTest, ApplyLogDottedPath) { ASSERT_FALSE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(countChildren(getLogDoc().root()), 1u); ASSERT_EQUALS(fromjson("{$set: {'a.2.b': 2}}"), getLogDoc()); + ASSERT_EQUALS(getModifiedPaths(), "{a}"); } TEST_F(ArithmeticNodeTest, LogEmptyArray) { @@ -964,6 +1008,7 @@ TEST_F(ArithmeticNodeTest, LogEmptyArray) { ASSERT_FALSE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(countChildren(getLogDoc().root()), 1u); ASSERT_EQUALS(fromjson("{$set: {'a.2.b': 2}}"), getLogDoc()); + ASSERT_EQUALS(getModifiedPaths(), "{a}"); } TEST_F(ArithmeticNodeTest, LogEmptyObject) { @@ -980,6 +1025,7 @@ TEST_F(ArithmeticNodeTest, LogEmptyObject) { ASSERT_FALSE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(countChildren(getLogDoc().root()), 1u); ASSERT_EQUALS(fromjson("{$set: {'a.2.b': 2}}"), getLogDoc()); + ASSERT_EQUALS(getModifiedPaths(), "{a.2.b}"); } TEST_F(ArithmeticNodeTest, ApplyDeserializedDocNotNoOp) { @@ -999,6 +1045,7 @@ TEST_F(ArithmeticNodeTest, ApplyDeserializedDocNotNoOp) { ASSERT_EQUALS(fromjson("{a: 1, b: NumberInt(0)}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(fromjson("{$set: {b: NumberInt(0)}}"), getLogDoc()); + ASSERT_EQUALS(getModifiedPaths(), "{b}"); } TEST_F(ArithmeticNodeTest, ApplyToDeserializedDocNoOp) { @@ -1018,6 +1065,7 @@ TEST_F(ArithmeticNodeTest, ApplyToDeserializedDocNoOp) { ASSERT_EQUALS(fromjson("{a: NumberInt(2)}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(fromjson("{}"), getLogDoc()); + ASSERT_EQUALS(getModifiedPaths(), "{a}"); } TEST_F(ArithmeticNodeTest, ApplyToDeserializedDocNestedNoop) { @@ -1037,6 +1085,7 @@ TEST_F(ArithmeticNodeTest, ApplyToDeserializedDocNestedNoop) { ASSERT_EQUALS(fromjson("{a: {b: NumberInt(1)}}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(fromjson("{}"), getLogDoc()); + ASSERT_EQUALS(getModifiedPaths(), "{a.b}"); } TEST_F(ArithmeticNodeTest, ApplyToDeserializedDocNestedNotNoop) { @@ -1056,6 +1105,7 @@ TEST_F(ArithmeticNodeTest, ApplyToDeserializedDocNestedNotNoop) { ASSERT_EQUALS(fromjson("{a: {b: 3}}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(fromjson("{$set: {'a.b': 3}}"), getLogDoc()); + ASSERT_EQUALS(getModifiedPaths(), "{a.b}"); } } // namespace diff --git a/src/mongo/db/update/modifier_node.cpp b/src/mongo/db/update/modifier_node.cpp index a5c54691334..3ab764afd08 100644 --- a/src/mongo/db/update/modifier_node.cpp +++ b/src/mongo/db/update/modifier_node.cpp @@ -277,6 +277,11 @@ UpdateNode::ApplyResult ModifierNode::applyToNonexistentElement(ApplyParams appl } else { fullPath = FieldRef(str::stream() << applyParams.pathTaken->dottedField() << "." << applyParams.pathToCreate->dottedField()); + + // If adding an element to an array, only mark the path to the array itself as modified. + if (applyParams.modifiedPaths && applyParams.element.getType() == BSONType::Array) { + applyParams.modifiedPaths->keepShortest(*applyParams.pathTaken); + } } ApplyResult applyResult; @@ -315,13 +320,20 @@ UpdateNode::ApplyResult ModifierNode::applyToNonexistentElement(ApplyParams appl } UpdateNode::ApplyResult ModifierNode::apply(ApplyParams applyParams) const { + ApplyResult result; if (context == Context::kInsertOnly && !applyParams.insert) { - return ApplyResult::noopResult(); + result = ApplyResult::noopResult(); } else if (!applyParams.pathToCreate->empty()) { - return applyToNonexistentElement(applyParams); + result = applyToNonexistentElement(applyParams); } else { - return applyToExistingElement(applyParams); + result = applyToExistingElement(applyParams); } + + if (applyParams.modifiedPaths) { + applyParams.modifiedPaths->keepShortest(*applyParams.pathTaken + *applyParams.pathToCreate); + } + + return result; } void ModifierNode::validateUpdate(mutablebson::ConstElement updatedElement, diff --git a/src/mongo/db/update/pop_node_test.cpp b/src/mongo/db/update/pop_node_test.cpp index 72fdbb08310..0d1757ed893 100644 --- a/src/mongo/db/update/pop_node_test.cpp +++ b/src/mongo/db/update/pop_node_test.cpp @@ -117,6 +117,7 @@ TEST_F(PopNodeTest, NoopWhenFirstPathComponentDoesNotExist) { ASSERT_FALSE(result.indexesAffected); ASSERT_EQUALS(fromjson("{b: [1, 2, 3]}"), doc); ASSERT_EQUALS(fromjson("{}"), getLogDoc()); + ASSERT_EQUALS("{a.b}", getModifiedPaths()); } TEST_F(PopNodeTest, NoopWhenPathPartiallyExists) { @@ -134,6 +135,7 @@ TEST_F(PopNodeTest, NoopWhenPathPartiallyExists) { ASSERT_FALSE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: {}}"), doc); ASSERT_EQUALS(fromjson("{}"), getLogDoc()); + ASSERT_EQUALS("{a.b.c}", getModifiedPaths()); } TEST_F(PopNodeTest, NoopWhenNumericalPathComponentExceedsArrayLength) { @@ -151,6 +153,7 @@ TEST_F(PopNodeTest, NoopWhenNumericalPathComponentExceedsArrayLength) { ASSERT_FALSE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: []}"), doc); ASSERT_EQUALS(fromjson("{}"), getLogDoc()); + ASSERT_EQUALS("{a.0}", getModifiedPaths()); } TEST_F(PopNodeTest, ThrowsWhenPathIsBlockedByAScalar) { @@ -213,6 +216,7 @@ TEST_F(PopNodeTest, NoopWhenPathContainsAnEmptyArray) { ASSERT_FALSE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: {b: []}}"), doc); ASSERT_EQUALS(fromjson("{}"), getLogDoc()); + ASSERT_EQUALS("{a.b}", getModifiedPaths()); } TEST_F(PopNodeTest, PopsSingleElementFromTheBack) { @@ -230,6 +234,7 @@ TEST_F(PopNodeTest, PopsSingleElementFromTheBack) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: {b: []}}"), doc); ASSERT_EQUALS(fromjson("{$set: {'a.b': []}}"), getLogDoc()); + ASSERT_EQUALS("{a.b}", getModifiedPaths()); } TEST_F(PopNodeTest, PopsSingleElementFromTheFront) { @@ -247,6 +252,7 @@ TEST_F(PopNodeTest, PopsSingleElementFromTheFront) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: {b: []}}"), doc); ASSERT_EQUALS(fromjson("{$set: {'a.b': []}}"), getLogDoc()); + ASSERT_EQUALS("{a.b}", getModifiedPaths()); } TEST_F(PopNodeTest, PopsFromTheBackOfMultiElementArray) { @@ -264,6 +270,7 @@ TEST_F(PopNodeTest, PopsFromTheBackOfMultiElementArray) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: {b: [1, 2]}}"), doc); ASSERT_EQUALS(fromjson("{$set: {'a.b': [1, 2]}}"), getLogDoc()); + ASSERT_EQUALS("{a.b}", getModifiedPaths()); } TEST_F(PopNodeTest, PopsFromTheFrontOfMultiElementArray) { @@ -281,6 +288,7 @@ TEST_F(PopNodeTest, PopsFromTheFrontOfMultiElementArray) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: {b: [2, 3]}}"), doc); ASSERT_EQUALS(fromjson("{$set: {'a.b': [2, 3]}}"), getLogDoc()); + ASSERT_EQUALS("{a.b}", getModifiedPaths()); } TEST_F(PopNodeTest, PopsFromTheFrontOfMultiElementArrayWithoutAffectingIndexes) { @@ -298,6 +306,7 @@ TEST_F(PopNodeTest, PopsFromTheFrontOfMultiElementArrayWithoutAffectingIndexes) ASSERT_FALSE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: {b: [2, 3]}}"), doc); ASSERT_EQUALS(fromjson("{$set: {'a.b': [2, 3]}}"), getLogDoc()); + ASSERT_EQUALS("{a.b}", getModifiedPaths()); } TEST_F(PopNodeTest, SucceedsWithNullUpdateIndexData) { @@ -314,6 +323,7 @@ TEST_F(PopNodeTest, SucceedsWithNullUpdateIndexData) { ASSERT_FALSE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: {b: [1, 2]}}"), doc); ASSERT_EQUALS(fromjson("{$set: {'a.b': [1, 2]}}"), getLogDoc()); + ASSERT_EQUALS("{a.b}", getModifiedPaths()); } TEST_F(PopNodeTest, SucceedsWithNullLogBuilder) { @@ -331,6 +341,7 @@ TEST_F(PopNodeTest, SucceedsWithNullLogBuilder) { ASSERT_FALSE(result.noop); ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: {b: [1, 2]}}"), doc); + ASSERT_EQUALS("{a.b}", getModifiedPaths()); } TEST_F(PopNodeTest, ThrowsWhenPathIsImmutable) { @@ -404,6 +415,7 @@ TEST_F(PopNodeTest, NoopOnImmutablePathSucceeds) { ASSERT_FALSE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: {b: []}}"), doc); ASSERT_EQUALS(fromjson("{}"), getLogDoc()); + ASSERT_EQUALS("{a.b}", getModifiedPaths()); } } // namespace diff --git a/src/mongo/db/update/push_node_test.cpp b/src/mongo/db/update/push_node_test.cpp index 5262aea886b..92349bb70ba 100644 --- a/src/mongo/db/update/push_node_test.cpp +++ b/src/mongo/db/update/push_node_test.cpp @@ -289,6 +289,7 @@ TEST_F(PushNodeTest, ApplyToEmptyArray) { ASSERT_EQUALS(fromjson("{a: [1]}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(fromjson("{$set: {a: [1]}}"), getLogDoc()); + ASSERT_EQUALS("{a}", getModifiedPaths()); } TEST_F(PushNodeTest, ApplyToEmptyDocument) { @@ -306,6 +307,7 @@ TEST_F(PushNodeTest, ApplyToEmptyDocument) { ASSERT_EQUALS(fromjson("{a: [1]}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(fromjson("{$set: {a: [1]}}"), getLogDoc()); + ASSERT_EQUALS("{a}", getModifiedPaths()); } TEST_F(PushNodeTest, ApplyToArrayWithOneElement) { @@ -323,6 +325,7 @@ TEST_F(PushNodeTest, ApplyToArrayWithOneElement) { ASSERT_EQUALS(fromjson("{a: [0, 1]}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(fromjson("{$set: {'a.1': 1}}"), getLogDoc()); + ASSERT_EQUALS("{a}", getModifiedPaths()); } TEST_F(PushNodeTest, ApplyToDottedPathElement) { @@ -351,6 +354,7 @@ TEST_F(PushNodeTest, ApplyToDottedPathElement) { doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(fromjson("{$set: {'choices.first.votes': [1]}}"), getLogDoc()); + ASSERT_EQUALS("{choices.first.votes}", getModifiedPaths()); } TEST_F(PushNodeTest, ApplySimpleEachToEmptyArray) { @@ -368,6 +372,7 @@ TEST_F(PushNodeTest, ApplySimpleEachToEmptyArray) { ASSERT_EQUALS(fromjson("{a: [1]}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(fromjson("{$set: {a: [1]}}"), getLogDoc()); + ASSERT_EQUALS("{a}", getModifiedPaths()); } TEST_F(PushNodeTest, ApplySimpleEachToEmptyDocument) { @@ -385,6 +390,7 @@ TEST_F(PushNodeTest, ApplySimpleEachToEmptyDocument) { ASSERT_EQUALS(fromjson("{a: [1]}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(fromjson("{$set: {a: [1]}}"), getLogDoc()); + ASSERT_EQUALS("{a}", getModifiedPaths()); } TEST_F(PushNodeTest, ApplyMultipleEachToEmptyDocument) { @@ -402,6 +408,7 @@ TEST_F(PushNodeTest, ApplyMultipleEachToEmptyDocument) { ASSERT_EQUALS(fromjson("{a: [1, 2]}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(fromjson("{$set: {a: [1, 2]}}"), getLogDoc()); + ASSERT_EQUALS("{a}", getModifiedPaths()); } TEST_F(PushNodeTest, ApplySimpleEachToArrayWithOneElement) { @@ -419,6 +426,7 @@ TEST_F(PushNodeTest, ApplySimpleEachToArrayWithOneElement) { ASSERT_EQUALS(fromjson("{a: [0, 1]}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(fromjson("{$set: {'a.1': 1}}"), getLogDoc()); + ASSERT_EQUALS("{a}", getModifiedPaths()); } TEST_F(PushNodeTest, ApplyMultipleEachToArrayWithOneElement) { @@ -436,6 +444,7 @@ TEST_F(PushNodeTest, ApplyMultipleEachToArrayWithOneElement) { ASSERT_EQUALS(fromjson("{a: [0, 1, 2]}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(fromjson("{$set: {'a.1': 1, 'a.2': 2}}"), getLogDoc()); + ASSERT_EQUALS("{a}", getModifiedPaths()); } TEST_F(PushNodeTest, ApplyEmptyEachToEmptyArray) { @@ -453,6 +462,7 @@ TEST_F(PushNodeTest, ApplyEmptyEachToEmptyArray) { ASSERT_EQUALS(fromjson("{a: []}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(fromjson("{}"), getLogDoc()); + ASSERT_EQUALS("{a}", getModifiedPaths()); } TEST_F(PushNodeTest, ApplyEmptyEachToEmptyDocument) { @@ -470,6 +480,7 @@ TEST_F(PushNodeTest, ApplyEmptyEachToEmptyDocument) { ASSERT_EQUALS(fromjson("{a: []}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(fromjson("{$set: {a: []}}"), getLogDoc()); + ASSERT_EQUALS("{a}", getModifiedPaths()); } TEST_F(PushNodeTest, ApplyEmptyEachToArrayWithOneElement) { @@ -487,6 +498,7 @@ TEST_F(PushNodeTest, ApplyEmptyEachToArrayWithOneElement) { ASSERT_EQUALS(fromjson("{a: [0]}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(fromjson("{}"), getLogDoc()); + ASSERT_EQUALS("{a}", getModifiedPaths()); } TEST_F(PushNodeTest, ApplyToArrayWithSlice) { @@ -504,6 +516,7 @@ TEST_F(PushNodeTest, ApplyToArrayWithSlice) { ASSERT_EQUALS(fromjson("{a: [3]}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(fromjson("{$set: {a: [3]}}"), getLogDoc()); + ASSERT_EQUALS("{a}", getModifiedPaths()); } TEST_F(PushNodeTest, ApplyWithNumericSort) { @@ -521,6 +534,7 @@ TEST_F(PushNodeTest, ApplyWithNumericSort) { ASSERT_EQUALS(fromjson("{a: [-1, 2, 3]}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(fromjson("{$set: {a: [-1, 2, 3]}}"), getLogDoc()); + ASSERT_EQUALS("{a}", getModifiedPaths()); } TEST_F(PushNodeTest, ApplyWithReverseNumericSort) { @@ -538,6 +552,7 @@ TEST_F(PushNodeTest, ApplyWithReverseNumericSort) { ASSERT_EQUALS(fromjson("{a: [4, 3, -1]}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(fromjson("{$set: {a: [4, 3, -1]}}"), getLogDoc()); + ASSERT_EQUALS("{a}", getModifiedPaths()); } TEST_F(PushNodeTest, ApplyWithMixedSort) { @@ -555,6 +570,7 @@ TEST_F(PushNodeTest, ApplyWithMixedSort) { ASSERT_EQUALS(fromjson("{a: [-1, 3, 4, 't', {a: 1}, {b: 1}]}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(fromjson("{$set: {a: [-1, 3, 4, 't', {a: 1}, {b: 1}]}}"), getLogDoc()); + ASSERT_EQUALS("{a}", getModifiedPaths()); } TEST_F(PushNodeTest, ApplyWithReverseMixedSort) { @@ -572,6 +588,7 @@ TEST_F(PushNodeTest, ApplyWithReverseMixedSort) { ASSERT_EQUALS(fromjson("{a: [{b: 1}, {a: 1}, 't', 4, 3, -1]}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(fromjson("{$set: {a: [{b: 1}, {a: 1}, 't', 4, 3, -1]}}"), getLogDoc()); + ASSERT_EQUALS("{a}", getModifiedPaths()); } TEST_F(PushNodeTest, ApplyWithEmbeddedFieldSort) { @@ -589,6 +606,7 @@ TEST_F(PushNodeTest, ApplyWithEmbeddedFieldSort) { ASSERT_EQUALS(fromjson("{a: [3, 't', {b: 1}, 4, -1, {a: 1}]}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(fromjson("{$set: {a: [3, 't', {b: 1}, 4, -1, {a: 1}]}}"), getLogDoc()); + ASSERT_EQUALS("{a}", getModifiedPaths()); } TEST_F(PushNodeTest, ApplySortWithCollator) { @@ -608,6 +626,7 @@ TEST_F(PushNodeTest, ApplySortWithCollator) { ASSERT_EQUALS(fromjson("{a: ['ha', 'gb', 'fc', 'dd']}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(fromjson("{$set: {a: ['ha', 'gb', 'fc', 'dd']}}"), getLogDoc()); + ASSERT_EQUALS("{a}", getModifiedPaths()); } TEST_F(PushNodeTest, ApplySortAfterSetCollator) { @@ -623,8 +642,12 @@ TEST_F(PushNodeTest, ApplySortAfterSetCollator) { ASSERT_FALSE(result.noop); ASSERT_EQUALS(fromjson("{a: ['dd', 'fc', 'gb', 'ha']}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); + ASSERT_EQUALS("{a}", getModifiedPaths()); // Now with a collator. + resetApplyParams(); + setPathTaken("a"); + setLogBuilderToNull(); CollatorInterfaceMock mockCollator(CollatorInterfaceMock::MockType::kReverseString); node.setCollator(&mockCollator); mutablebson::Document doc2(fromjson("{a: ['dd', 'fc', 'gb']}")); @@ -632,6 +655,7 @@ TEST_F(PushNodeTest, ApplySortAfterSetCollator) { ASSERT_FALSE(result.noop); ASSERT_EQUALS(fromjson("{a: ['ha', 'gb', 'fc', 'dd']}"), doc2); ASSERT_FALSE(doc2.isInPlaceModeEnabled()); + ASSERT_EQUALS("{a}", getModifiedPaths()); } // Some of the below tests apply multiple different update modifiers. This special check function @@ -678,6 +702,7 @@ TEST_F(PushNodeTest, ApplyToEmptyArrayWithSliceValues) { ASSERT_OK(node.init(update["$push"]["a"], expCtx)); mutablebson::Document doc(fromjson("{a: []}")); + resetApplyParams(); setPathTaken("a"); setLogBuilderToNull(); auto result = node.apply(getApplyParams(doc.root()["a"])); @@ -710,6 +735,7 @@ TEST_F(PushNodeTest, ApplyToPopulatedArrayWithSliceValues) { ASSERT_OK(node.init(update["$push"]["a"], expCtx)); mutablebson::Document doc(fromjson("{a: [2, 3]}")); + resetApplyParams(); setPathTaken("a"); setLogBuilderToNull(); auto result = node.apply(getApplyParams(doc.root()["a"])); @@ -811,6 +837,7 @@ TEST_F(PushNodeTest, ApplyToPopulatedArrayWithSortAndSliceValues) { ASSERT_OK(node.init(update["$push"]["a"], expCtx)); mutablebson::Document doc(fromjson("{a: [{a: 2, b: 3}, {a: 3, b: 1}]}")); + resetApplyParams(); setPathTaken("a"); setLogBuilderToNull(); auto result = node.apply(getApplyParams(doc.root()["a"])); @@ -833,6 +860,7 @@ TEST_F(PushNodeTest, ApplyToEmptyArrayWithPositionZero) { ASSERT_EQUALS(fromjson("{a: [1]}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(fromjson("{$set: {a: [1]}}"), getLogDoc()); + ASSERT_EQUALS("{a}", getModifiedPaths()); } TEST_F(PushNodeTest, ApplyToEmptyArrayWithPositionOne) { @@ -850,6 +878,7 @@ TEST_F(PushNodeTest, ApplyToEmptyArrayWithPositionOne) { ASSERT_EQUALS(fromjson("{a: [1]}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(fromjson("{$set: {a: [1]}}"), getLogDoc()); + ASSERT_EQUALS("{a}", getModifiedPaths()); } TEST_F(PushNodeTest, ApplyToEmptyArrayWithLargePosition) { @@ -867,6 +896,7 @@ TEST_F(PushNodeTest, ApplyToEmptyArrayWithLargePosition) { ASSERT_EQUALS(fromjson("{a: [1]}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(fromjson("{$set: {a: [1]}}"), getLogDoc()); + ASSERT_EQUALS("{a}", getModifiedPaths()); } TEST_F(PushNodeTest, ApplyToSingletonArrayWithPositionZero) { @@ -884,6 +914,7 @@ TEST_F(PushNodeTest, ApplyToSingletonArrayWithPositionZero) { ASSERT_EQUALS(fromjson("{a: [1, 0]}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(fromjson("{$set: {a: [1, 0]}}"), getLogDoc()); + ASSERT_EQUALS("{a}", getModifiedPaths()); } TEST_F(PushNodeTest, ApplyToSingletonArrayWithLargePosition) { @@ -901,6 +932,7 @@ TEST_F(PushNodeTest, ApplyToSingletonArrayWithLargePosition) { ASSERT_EQUALS(fromjson("{a: [0, 1]}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(fromjson("{$set: {'a.1': 1}}"), getLogDoc()); + ASSERT_EQUALS("{a}", getModifiedPaths()); } TEST_F(PushNodeTest, ApplyToEmptyArrayWithNegativePosition) { @@ -918,6 +950,7 @@ TEST_F(PushNodeTest, ApplyToEmptyArrayWithNegativePosition) { ASSERT_EQUALS(fromjson("{a: [1]}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(fromjson("{$set: {a: [1]}}"), getLogDoc()); + ASSERT_EQUALS("{a}", getModifiedPaths()); } TEST_F(PushNodeTest, ApplyToSingletonArrayWithNegativePosition) { @@ -935,6 +968,7 @@ TEST_F(PushNodeTest, ApplyToSingletonArrayWithNegativePosition) { ASSERT_EQUALS(fromjson("{a: [1, 0]}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(fromjson("{$set: {a: [1, 0]}}"), getLogDoc()); + ASSERT_EQUALS("{a}", getModifiedPaths()); } TEST_F(PushNodeTest, ApplyToPopulatedArrayWithNegativePosition) { @@ -952,6 +986,7 @@ TEST_F(PushNodeTest, ApplyToPopulatedArrayWithNegativePosition) { ASSERT_EQUALS(fromjson("{a: [0, 1, 2, 5, 3, 4]}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(fromjson("{$set: {a: [0, 1, 2, 5, 3, 4]}}"), getLogDoc()); + ASSERT_EQUALS("{a}", getModifiedPaths()); } TEST_F(PushNodeTest, ApplyToPopulatedArrayWithOutOfBoundsNegativePosition) { @@ -969,6 +1004,7 @@ TEST_F(PushNodeTest, ApplyToPopulatedArrayWithOutOfBoundsNegativePosition) { ASSERT_EQUALS(fromjson("{a: [5, 0, 1, 2, 3, 4]}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(fromjson("{$set: {a: [5, 0, 1, 2, 3, 4]}}"), getLogDoc()); + ASSERT_EQUALS("{a}", getModifiedPaths()); } TEST_F(PushNodeTest, ApplyMultipleElementsPushWithNegativePosition) { @@ -986,6 +1022,7 @@ TEST_F(PushNodeTest, ApplyMultipleElementsPushWithNegativePosition) { ASSERT_EQUALS(fromjson("{a: [0, 1, 2, 5, 6, 7, 3, 4]}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(fromjson("{$set: {a: [0, 1, 2, 5, 6, 7, 3, 4]}}"), getLogDoc()); + ASSERT_EQUALS("{a}", getModifiedPaths()); } } // namespace diff --git a/src/mongo/db/update/rename_node.cpp b/src/mongo/db/update/rename_node.cpp index b91370cedd4..53cca49df75 100644 --- a/src/mongo/db/update/rename_node.cpp +++ b/src/mongo/db/update/rename_node.cpp @@ -169,7 +169,11 @@ UpdateNode::ApplyResult RenameNode::apply(ApplyParams applyParams) const { } // The element we want to rename does not exist. When that happens, we treat the operation - // as a no-op. + // as a no-op. The attempted from/to paths are still considered modified. + if (applyParams.modifiedPaths) { + applyParams.modifiedPaths->keepShortest(*fromFieldRef); + applyParams.modifiedPaths->keepShortest(toFieldRef); + } return ApplyResult::noopResult(); } diff --git a/src/mongo/db/update/rename_node_test.cpp b/src/mongo/db/update/rename_node_test.cpp index 8dfaeee7405..f57d7ee3334 100644 --- a/src/mongo/db/update/rename_node_test.cpp +++ b/src/mongo/db/update/rename_node_test.cpp @@ -125,6 +125,7 @@ TEST_F(RenameNodeTest, SimpleNumberAtRoot) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{b: 2}"), doc); ASSERT_EQUALS(fromjson("{$set: {b: 2}, $unset: {a: true}}"), getLogDoc()); + ASSERT_EQUALS(getModifiedPaths(), "{a, b}"); } TEST_F(RenameNodeTest, ToExistsAtSameLevel) { @@ -141,6 +142,7 @@ TEST_F(RenameNodeTest, ToExistsAtSameLevel) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{b: 2}"), doc); ASSERT_EQUALS(fromjson("{$set: {b: 2}, $unset: {a: true}}"), getLogDoc()); + ASSERT_EQUALS(getModifiedPaths(), "{a, b}"); } TEST_F(RenameNodeTest, ToAndFromHaveSameValue) { @@ -157,6 +159,7 @@ TEST_F(RenameNodeTest, ToAndFromHaveSameValue) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{b: 2}"), doc); ASSERT_EQUALS(fromjson("{$set: {b: 2}, $unset: {a: true}}"), getLogDoc()); + ASSERT_EQUALS(getModifiedPaths(), "{a, b}"); } TEST_F(RenameNodeTest, RenameToFieldWithSameValueButDifferentType) { @@ -173,6 +176,7 @@ TEST_F(RenameNodeTest, RenameToFieldWithSameValueButDifferentType) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{b: 1}"), doc); ASSERT_EQUALS(fromjson("{$set: {b: 1}, $unset: {a: true}}"), getLogDoc()); + ASSERT_EQUALS(getModifiedPaths(), "{a, b}"); } TEST_F(RenameNodeTest, FromDottedElement) { @@ -189,6 +193,7 @@ TEST_F(RenameNodeTest, FromDottedElement) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: {}, b: {d: 6}}"), doc); ASSERT_EQUALS(fromjson("{$set: {b: {d: 6}}, $unset: {'a.c': true}}"), getLogDoc()); + ASSERT_EQUALS(getModifiedPaths(), "{a.c, b}"); } TEST_F(RenameNodeTest, RenameToExistingNestedFieldDoesNotReorderFields) { @@ -205,6 +210,7 @@ TEST_F(RenameNodeTest, RenameToExistingNestedFieldDoesNotReorderFields) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: {b: {c: 4, d: 2}}, b: 3, c: {}}"), doc); ASSERT_EQUALS(fromjson("{$set: {'a.b.c': 4}, $unset: {'c.d': true}}"), getLogDoc()); + ASSERT_EQUALS(getModifiedPaths(), "{a.b.c, c.d}"); } TEST_F(RenameNodeTest, MissingCompleteTo) { @@ -222,6 +228,7 @@ TEST_F(RenameNodeTest, MissingCompleteTo) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{b: 1, c: {r: {d: 2}}}"), doc); ASSERT_EQUALS(fromjson("{$set: {'c.r.d': 2}, $unset: {'a': true}}"), getLogDoc()); + ASSERT_EQUALS(getModifiedPaths(), "{a, c.r.d}"); } TEST_F(RenameNodeTest, ToIsCompletelyMissing) { @@ -238,6 +245,7 @@ TEST_F(RenameNodeTest, ToIsCompletelyMissing) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{b: {c: {d: 2}}}"), doc); ASSERT_EQUALS(fromjson("{$set: {'b.c.d': 2}, $unset: {'a': true}}"), getLogDoc()); + ASSERT_EQUALS(getModifiedPaths(), "{a, b.c.d}"); } TEST_F(RenameNodeTest, ToMissingDottedField) { @@ -254,6 +262,7 @@ TEST_F(RenameNodeTest, ToMissingDottedField) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{b: {c: {d: [{a:2, b:1}]}}}"), doc); ASSERT_EQUALS(fromjson("{$set: {'b.c.d': [{a:2, b:1}]}, $unset: {'a': true}}"), getLogDoc()); + ASSERT_EQUALS(getModifiedPaths(), "{a, b.c.d}"); } TEST_F(RenameNodeTest, MoveIntoArray) { @@ -368,6 +377,7 @@ TEST_F(RenameNodeTest, ReplaceArrayField) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{b: 2}"), doc); ASSERT_EQUALS(fromjson("{$set: {b: 2}, $unset: {a: true}}"), getLogDoc()); + ASSERT_EQUALS(getModifiedPaths(), "{a, b}"); } TEST_F(RenameNodeTest, ReplaceWithArrayField) { @@ -384,6 +394,7 @@ TEST_F(RenameNodeTest, ReplaceWithArrayField) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{b: []}"), doc); ASSERT_EQUALS(fromjson("{$set: {b: []}, $unset: {a: true}}"), getLogDoc()); + ASSERT_EQUALS(getModifiedPaths(), "{a, b}"); } TEST_F(RenameNodeTest, CanRenameFromInvalidFieldName) { @@ -400,6 +411,7 @@ TEST_F(RenameNodeTest, CanRenameFromInvalidFieldName) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: 2}"), doc); ASSERT_EQUALS(fromjson("{$set: {a: 2}, $unset: {'$a': true}}"), getLogDoc()); + ASSERT_EQUALS(getModifiedPaths(), "{$a, a}"); } TEST_F(RenameNodeTest, RenameWithoutLogBuilderOrIndexData) { @@ -430,6 +442,7 @@ TEST_F(RenameNodeTest, RenameFromNonExistentPathIsNoOp) { ASSERT_FALSE(result.indexesAffected); ASSERT_EQUALS(fromjson("{b: 2}"), doc); ASSERT_EQUALS(fromjson("{}"), getLogDoc()); + ASSERT_EQUALS(getModifiedPaths(), "{a, b}"); } TEST_F(RenameNodeTest, ApplyCannotRemoveRequiredPartOfDBRef) { @@ -466,6 +479,7 @@ TEST_F(RenameNodeTest, ApplyCanRemoveRequiredPartOfDBRefIfValidateForStorageIsFa ASSERT_EQUALS(updated, doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(fromjson("{$set: {'b': 0}, $unset: {'a.$id': true}}"), getLogDoc()); + ASSERT_EQUALS(getModifiedPaths(), "{a.$id, b}"); } TEST_F(RenameNodeTest, ApplyCannotRemoveImmutablePath) { @@ -532,6 +546,7 @@ TEST_F(RenameNodeTest, ApplyCanRemoveImmutablePathIfNoop) { ASSERT_EQUALS(fromjson("{a: {b: {}}}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(fromjson("{}"), getLogDoc()); + ASSERT_EQUALS(getModifiedPaths(), "{a.b.c, d}"); } TEST_F(RenameNodeTest, ApplyCannotCreateDollarPrefixedField) { diff --git a/src/mongo/db/update/set_node_test.cpp b/src/mongo/db/update/set_node_test.cpp index 81e6896cee8..950557f6706 100644 --- a/src/mongo/db/update/set_node_test.cpp +++ b/src/mongo/db/update/set_node_test.cpp @@ -76,6 +76,7 @@ TEST_F(SetNodeTest, ApplyNoOp) { ASSERT_EQUALS(fromjson("{a: 5}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(fromjson("{}"), getLogDoc()); + ASSERT_EQUALS("{a}", getModifiedPaths()); } TEST_F(SetNodeTest, ApplyEmptyPathToCreate) { @@ -93,6 +94,7 @@ TEST_F(SetNodeTest, ApplyEmptyPathToCreate) { ASSERT_EQUALS(fromjson("{a: 6}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(fromjson("{$set: {a: 6}}"), getLogDoc()); + ASSERT_EQUALS("{a}", getModifiedPaths()); } TEST_F(SetNodeTest, ApplyCreatePath) { @@ -111,6 +113,7 @@ TEST_F(SetNodeTest, ApplyCreatePath) { ASSERT_EQUALS(fromjson("{a: {d: 5, b: {c: 6}}}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(fromjson("{$set: {'a.b.c': 6}}"), getLogDoc()); + ASSERT_EQUALS("{a.b.c}", getModifiedPaths()); } TEST_F(SetNodeTest, ApplyCreatePathFromRoot) { @@ -128,6 +131,7 @@ TEST_F(SetNodeTest, ApplyCreatePathFromRoot) { ASSERT_EQUALS(fromjson("{c: 5, a: {b: 6}}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(fromjson("{$set: {'a.b': 6}}"), getLogDoc()); + ASSERT_EQUALS("{a.b}", getModifiedPaths()); } TEST_F(SetNodeTest, ApplyPositional) { @@ -146,6 +150,7 @@ TEST_F(SetNodeTest, ApplyPositional) { ASSERT_EQUALS(fromjson("{a: [0, 6, 2]}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(fromjson("{$set: {'a.1': 6}}"), getLogDoc()); + ASSERT_EQUALS("{a.1}", getModifiedPaths()); } TEST_F(SetNodeTest, ApplyNonViablePathToCreate) { @@ -181,6 +186,7 @@ TEST_F(SetNodeTest, ApplyNonViablePathToCreateFromReplicationIsNoOp) { ASSERT_EQUALS(fromjson("{a: 5}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(fromjson("{}"), getLogDoc()); + ASSERT_EQUALS("{a.b}", getModifiedPaths()); } TEST_F(SetNodeTest, ApplyNoIndexDataNoLogBuilder) { @@ -197,6 +203,7 @@ TEST_F(SetNodeTest, ApplyNoIndexDataNoLogBuilder) { ASSERT_FALSE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: 6}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); + ASSERT_EQUALS("{a}", getModifiedPaths()); } TEST_F(SetNodeTest, ApplyDoesNotAffectIndexes) { @@ -213,6 +220,7 @@ TEST_F(SetNodeTest, ApplyDoesNotAffectIndexes) { ASSERT_FALSE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: 6}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); + ASSERT_EQUALS("{a}", getModifiedPaths()); } TEST_F(SetNodeTest, TypeChangeIsNotANoop) { @@ -229,6 +237,7 @@ TEST_F(SetNodeTest, TypeChangeIsNotANoop) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: NumberLong(2)}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); + ASSERT_EQUALS("{a}", getModifiedPaths()); } TEST_F(SetNodeTest, IdentityOpOnDeserializedIsNotANoOp) { @@ -249,6 +258,7 @@ TEST_F(SetNodeTest, IdentityOpOnDeserializedIsNotANoOp) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: {b : NumberInt(2)}}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); + ASSERT_EQUALS("{a}", getModifiedPaths()); } TEST_F(SetNodeTest, ApplyEmptyDocument) { @@ -265,6 +275,7 @@ TEST_F(SetNodeTest, ApplyEmptyDocument) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: 2}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); + ASSERT_EQUALS("{a}", getModifiedPaths()); } TEST_F(SetNodeTest, ApplyInPlace) { @@ -281,6 +292,7 @@ TEST_F(SetNodeTest, ApplyInPlace) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: 2}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); + ASSERT_EQUALS("{a}", getModifiedPaths()); } TEST_F(SetNodeTest, ApplyOverridePath) { @@ -297,6 +309,7 @@ TEST_F(SetNodeTest, ApplyOverridePath) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: 2}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); + ASSERT_EQUALS("{a}", getModifiedPaths()); } TEST_F(SetNodeTest, ApplyChangeType) { @@ -313,6 +326,7 @@ TEST_F(SetNodeTest, ApplyChangeType) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: 2}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); + ASSERT_EQUALS("{a}", getModifiedPaths()); } TEST_F(SetNodeTest, ApplyNewPath) { @@ -329,6 +343,7 @@ TEST_F(SetNodeTest, ApplyNewPath) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{b: 1, a: 2}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); + ASSERT_EQUALS("{a}", getModifiedPaths()); } TEST_F(SetNodeTest, ApplyLog) { @@ -344,6 +359,7 @@ TEST_F(SetNodeTest, ApplyLog) { ASSERT_TRUE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(countChildren(getLogDoc().root()), 1u); ASSERT_EQUALS(fromjson("{$set: {a: 2}}"), getLogDoc()); + ASSERT_EQUALS("{a}", getModifiedPaths()); } TEST_F(SetNodeTest, ApplyNoOpDottedPath) { @@ -360,6 +376,7 @@ TEST_F(SetNodeTest, ApplyNoOpDottedPath) { ASSERT_FALSE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: {b : 2}}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); + ASSERT_EQUALS("{a.b}", getModifiedPaths()); } TEST_F(SetNodeTest, TypeChangeOnDottedPathIsNotANoOp) { @@ -376,6 +393,7 @@ TEST_F(SetNodeTest, TypeChangeOnDottedPathIsNotANoOp) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: {b : NumberLong(2)}}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); + ASSERT_EQUALS("{a.b}", getModifiedPaths()); } TEST_F(SetNodeTest, ApplyPathNotViable) { @@ -422,6 +440,7 @@ TEST_F(SetNodeTest, ApplyInPlaceDottedPath) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: {b: 2}}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); + ASSERT_EQUALS("{a.b}", getModifiedPaths()); } TEST_F(SetNodeTest, ApplyChangeTypeDottedPath) { @@ -438,6 +457,7 @@ TEST_F(SetNodeTest, ApplyChangeTypeDottedPath) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: {b: 2}}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); + ASSERT_EQUALS("{a.b}", getModifiedPaths()); } TEST_F(SetNodeTest, ApplyChangePath) { @@ -454,6 +474,7 @@ TEST_F(SetNodeTest, ApplyChangePath) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: {b: 2}}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); + ASSERT_EQUALS("{a.b}", getModifiedPaths()); } TEST_F(SetNodeTest, ApplyExtendPath) { @@ -471,6 +492,7 @@ TEST_F(SetNodeTest, ApplyExtendPath) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: {c: 1, b: 2}}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); + ASSERT_EQUALS("{a.b}", getModifiedPaths()); } TEST_F(SetNodeTest, ApplyNewDottedPath) { @@ -487,6 +509,7 @@ TEST_F(SetNodeTest, ApplyNewDottedPath) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{c: 1, a: {b: 2}}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); + ASSERT_EQUALS("{a.b}", getModifiedPaths()); } TEST_F(SetNodeTest, ApplyEmptyDoc) { @@ -503,6 +526,7 @@ TEST_F(SetNodeTest, ApplyEmptyDoc) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: {b: 2}}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); + ASSERT_EQUALS("{a.b}", getModifiedPaths()); } TEST_F(SetNodeTest, ApplyFieldWithDot) { @@ -519,6 +543,7 @@ TEST_F(SetNodeTest, ApplyFieldWithDot) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{'a.b':4, a: {b: 2}}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); + ASSERT_EQUALS("{a.b}", getModifiedPaths()); } TEST_F(SetNodeTest, ApplyNoOpArrayIndex) { @@ -535,6 +560,7 @@ TEST_F(SetNodeTest, ApplyNoOpArrayIndex) { ASSERT_FALSE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: [{b: 0},{b: 1},{b: 2}]}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); + ASSERT_EQUALS("{a.2.b}", getModifiedPaths()); } TEST_F(SetNodeTest, TypeChangeInArrayIsNotANoOp) { @@ -551,6 +577,7 @@ TEST_F(SetNodeTest, TypeChangeInArrayIsNotANoOp) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: [{b: 0},{b: 1},{b: NumberInt(2)}]}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); + ASSERT_EQUALS("{a.2.b}", getModifiedPaths()); } TEST_F(SetNodeTest, ApplyNonViablePath) { @@ -582,6 +609,7 @@ TEST_F(SetNodeTest, ApplyInPlaceArrayIndex) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: [{b: 0},{b: 1},{b: 2}]}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); + ASSERT_EQUALS("{a.2.b}", getModifiedPaths()); } TEST_F(SetNodeTest, ApplyNormalArray) { @@ -599,6 +627,7 @@ TEST_F(SetNodeTest, ApplyNormalArray) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: [{b: 0},{b: 1},{b: 2}]}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); + ASSERT_EQUALS("{a}", getModifiedPaths()); } TEST_F(SetNodeTest, ApplyPaddingArray) { @@ -616,6 +645,7 @@ TEST_F(SetNodeTest, ApplyPaddingArray) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: [{b: 0},null,{b: 2}]}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); + ASSERT_EQUALS("{a}", getModifiedPaths()); } TEST_F(SetNodeTest, ApplyNumericObject) { @@ -633,6 +663,7 @@ TEST_F(SetNodeTest, ApplyNumericObject) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: {b: 0, '2': {b: 2}}}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); + ASSERT_EQUALS("{a.2.b}", getModifiedPaths()); } TEST_F(SetNodeTest, ApplyNumericField) { @@ -649,6 +680,7 @@ TEST_F(SetNodeTest, ApplyNumericField) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: {'2': {b: 2}}}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); + ASSERT_EQUALS("{a.2.b}", getModifiedPaths()); } TEST_F(SetNodeTest, ApplyExtendNumericField) { @@ -666,6 +698,7 @@ TEST_F(SetNodeTest, ApplyExtendNumericField) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: {'2': {c: 1, b: 2}}}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); + ASSERT_EQUALS("{a.2.b}", getModifiedPaths()); } TEST_F(SetNodeTest, ApplyEmptyObject) { @@ -683,6 +716,7 @@ TEST_F(SetNodeTest, ApplyEmptyObject) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: {'2': {b: 2}}}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); + ASSERT_EQUALS("{a.2.b}", getModifiedPaths()); } TEST_F(SetNodeTest, ApplyEmptyArray) { @@ -700,6 +734,7 @@ TEST_F(SetNodeTest, ApplyEmptyArray) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: [null, null, {b: 2}]}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); + ASSERT_EQUALS("{a}", getModifiedPaths()); } TEST_F(SetNodeTest, ApplyLogDottedPath) { @@ -716,6 +751,7 @@ TEST_F(SetNodeTest, ApplyLogDottedPath) { ASSERT_FALSE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(countChildren(getLogDoc().root()), 1u); ASSERT_EQUALS(fromjson("{$set: {'a.2.b': 2}}"), getLogDoc()); + ASSERT_EQUALS("{a}", getModifiedPaths()); } TEST_F(SetNodeTest, LogEmptyArray) { @@ -732,6 +768,7 @@ TEST_F(SetNodeTest, LogEmptyArray) { ASSERT_FALSE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(countChildren(getLogDoc().root()), 1u); ASSERT_EQUALS(fromjson("{$set: {'a.2.b': 2}}"), getLogDoc()); + ASSERT_EQUALS("{a}", getModifiedPaths()); } TEST_F(SetNodeTest, LogEmptyObject) { @@ -748,6 +785,7 @@ TEST_F(SetNodeTest, LogEmptyObject) { ASSERT_FALSE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(countChildren(getLogDoc().root()), 1u); ASSERT_EQUALS(fromjson("{$set: {'a.2.b': 2}}"), getLogDoc()); + ASSERT_EQUALS("{a.2.b}", getModifiedPaths()); } TEST_F(SetNodeTest, ApplyNoOpComplex) { @@ -764,6 +802,7 @@ TEST_F(SetNodeTest, ApplyNoOpComplex) { ASSERT_FALSE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: [{b: {c: 0, d: 0}}, {b: {c: 1, d: 1}}]}}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); + ASSERT_EQUALS("{a.1.b}", getModifiedPaths()); } TEST_F(SetNodeTest, ApplySameStructure) { @@ -780,6 +819,7 @@ TEST_F(SetNodeTest, ApplySameStructure) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: [{b: {c: 0, d: 0}}, {b: {c: 1, d: 1}}]}}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); + ASSERT_EQUALS("{a.1.b}", getModifiedPaths()); } TEST_F(SetNodeTest, NonViablePathWithoutRepl) { @@ -813,6 +853,7 @@ TEST_F(SetNodeTest, SingleFieldFromReplication) { ASSERT_FALSE(result.indexesAffected); ASSERT_EQUALS(fromjson("{_id:1, a: 1}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); + ASSERT_EQUALS("{a.1.b}", getModifiedPaths()); } TEST_F(SetNodeTest, SingleFieldNoIdFromReplication) { @@ -831,6 +872,7 @@ TEST_F(SetNodeTest, SingleFieldNoIdFromReplication) { ASSERT_FALSE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: 1}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); + ASSERT_EQUALS("{a.1.b}", getModifiedPaths()); } TEST_F(SetNodeTest, NestedFieldFromReplication) { @@ -849,6 +891,7 @@ TEST_F(SetNodeTest, NestedFieldFromReplication) { ASSERT_FALSE(result.indexesAffected); ASSERT_EQUALS(fromjson("{_id:1, a: {a: 1}}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); + ASSERT_EQUALS("{a.a.1.b}", getModifiedPaths()); } TEST_F(SetNodeTest, DoubleNestedFieldFromReplication) { @@ -867,6 +910,7 @@ TEST_F(SetNodeTest, DoubleNestedFieldFromReplication) { ASSERT_FALSE(result.indexesAffected); ASSERT_EQUALS(fromjson("{_id:1, a: {b: {c: 1}}}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); + ASSERT_EQUALS("{a.b.c.d}", getModifiedPaths()); } TEST_F(SetNodeTest, NestedFieldNoIdFromReplication) { @@ -885,6 +929,7 @@ TEST_F(SetNodeTest, NestedFieldNoIdFromReplication) { ASSERT_FALSE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: {a: 1}}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); + ASSERT_EQUALS("{a.a.1.b}", getModifiedPaths()); } TEST_F(SetNodeTest, ReplayArrayFieldNotAppendedIntermediateFromReplication) { @@ -903,6 +948,7 @@ TEST_F(SetNodeTest, ReplayArrayFieldNotAppendedIntermediateFromReplication) { ASSERT_FALSE(result.indexesAffected); ASSERT_EQUALS(fromjson("{_id: 0, a: [1, {b: [1]}]}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); + ASSERT_EQUALS("{a.0.b}", getModifiedPaths()); } TEST_F(SetNodeTest, Set6) { @@ -921,6 +967,7 @@ TEST_F(SetNodeTest, Set6) { ASSERT_TRUE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(countChildren(getLogDoc().root()), 1u); ASSERT_EQUALS(fromjson("{$set: {'r.a': 2}}"), getLogDoc()); + ASSERT_EQUALS("{r.a}", getModifiedPaths()); } TEST_F(SetNodeTest, Set6FromRepl) { @@ -940,6 +987,7 @@ TEST_F(SetNodeTest, Set6FromRepl) { ASSERT_TRUE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(countChildren(getLogDoc().root()), 1u); ASSERT_EQUALS(fromjson("{$set: {'r.a': 2}}"), getLogDoc()); + ASSERT_EQUALS("{r.a}", getModifiedPaths()); } TEST_F(SetNodeTest, ApplySetModToEphemeralDocument) { @@ -1043,6 +1091,7 @@ TEST_F(SetNodeTest, ApplyCanCreateDollarPrefixedFieldNameWhenValidateForStorageI ASSERT_FALSE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(countChildren(getLogDoc().root()), 1u); ASSERT_EQUALS(fromjson("{$set: {$bad: 1}}"), getLogDoc()); + ASSERT_EQUALS("{$bad}", getModifiedPaths()); } TEST_F(SetNodeTest, ApplyCannotOverwriteImmutablePath) { @@ -1078,6 +1127,7 @@ TEST_F(SetNodeTest, ApplyCanPerformNoopOnImmutablePath) { ASSERT_TRUE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(countChildren(getLogDoc().root()), 0u); ASSERT_EQUALS(fromjson("{}"), getLogDoc()); + ASSERT_EQUALS("{a.b}", getModifiedPaths()); } TEST_F(SetNodeTest, ApplyCannotOverwritePrefixToRemoveImmutablePath) { @@ -1129,6 +1179,7 @@ TEST_F(SetNodeTest, ApplyCanPerformNoopOnPrefixOfImmutablePath) { ASSERT_TRUE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(countChildren(getLogDoc().root()), 0u); ASSERT_EQUALS(fromjson("{}"), getLogDoc()); + ASSERT_EQUALS("{a}", getModifiedPaths()); } TEST_F(SetNodeTest, ApplyCanOverwritePrefixToCreateImmutablePath) { @@ -1148,6 +1199,7 @@ TEST_F(SetNodeTest, ApplyCanOverwritePrefixToCreateImmutablePath) { ASSERT_FALSE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(countChildren(getLogDoc().root()), 1u); ASSERT_EQUALS(fromjson("{$set: {a: {b: 2}}}"), getLogDoc()); + ASSERT_EQUALS("{a}", getModifiedPaths()); } TEST_F(SetNodeTest, ApplyCanOverwritePrefixOfImmutablePathIfNoopOnImmutablePath) { @@ -1167,6 +1219,7 @@ TEST_F(SetNodeTest, ApplyCanOverwritePrefixOfImmutablePathIfNoopOnImmutablePath) ASSERT_FALSE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(countChildren(getLogDoc().root()), 1u); ASSERT_EQUALS(fromjson("{$set: {a: {b: 2, c: 3}}}"), getLogDoc()); + ASSERT_EQUALS("{a}", getModifiedPaths()); } TEST_F(SetNodeTest, ApplyCannotOverwriteSuffixOfImmutablePath) { @@ -1202,6 +1255,7 @@ TEST_F(SetNodeTest, ApplyCanPerformNoopOnSuffixOfImmutablePath) { ASSERT_TRUE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(countChildren(getLogDoc().root()), 0u); ASSERT_EQUALS(fromjson("{}"), getLogDoc()); + ASSERT_EQUALS("{a.b.c}", getModifiedPaths()); } TEST_F(SetNodeTest, ApplyCannotCreateFieldAtEndOfImmutablePath) { @@ -1256,6 +1310,7 @@ TEST_F(SetNodeTest, ApplyCanCreateImmutablePath) { ASSERT_FALSE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(countChildren(getLogDoc().root()), 1u); ASSERT_EQUALS(fromjson("{$set: {'a.b': 2}}"), getLogDoc()); + ASSERT_EQUALS("{a.b}", getModifiedPaths()); } TEST_F(SetNodeTest, ApplyCanCreatePrefixOfImmutablePath) { @@ -1275,6 +1330,7 @@ TEST_F(SetNodeTest, ApplyCanCreatePrefixOfImmutablePath) { ASSERT_FALSE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(countChildren(getLogDoc().root()), 1u); ASSERT_EQUALS(fromjson("{$set: {a: 2}}"), getLogDoc()); + ASSERT_EQUALS("{a}", getModifiedPaths()); } TEST_F(SetNodeTest, ApplySetFieldInNonExistentArrayElementAffectsIndexOnSiblingField) { @@ -1294,6 +1350,7 @@ TEST_F(SetNodeTest, ApplySetFieldInNonExistentArrayElementAffectsIndexOnSiblingF ASSERT_FALSE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(countChildren(getLogDoc().root()), 1u); ASSERT_EQUALS(fromjson("{$set: {'a.1.c': 2}}"), getLogDoc()); + ASSERT_EQUALS("{a}", getModifiedPaths()); } TEST_F(SetNodeTest, ApplySetFieldInExistingArrayElementDoesNotAffectIndexOnSiblingField) { @@ -1313,6 +1370,7 @@ TEST_F(SetNodeTest, ApplySetFieldInExistingArrayElementDoesNotAffectIndexOnSibli ASSERT_FALSE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(countChildren(getLogDoc().root()), 1u); ASSERT_EQUALS(fromjson("{$set: {'a.0.c': 2}}"), getLogDoc()); + ASSERT_EQUALS("{a.0.c}", getModifiedPaths()); } TEST_F(SetNodeTest, ApplySetFieldInNonExistentNumericFieldDoesNotAffectIndexOnSiblingField) { @@ -1333,6 +1391,7 @@ TEST_F(SetNodeTest, ApplySetFieldInNonExistentNumericFieldDoesNotAffectIndexOnSi ASSERT_FALSE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(countChildren(getLogDoc().root()), 1u); ASSERT_EQUALS(fromjson("{$set: {'a.1.c': 2}}"), getLogDoc()); + ASSERT_EQUALS("{a.1.c}", getModifiedPaths()); } TEST_F(SetNodeTest, ApplySetOnInsertIsNoopWhenInsertIsFalse) { @@ -1350,6 +1409,7 @@ TEST_F(SetNodeTest, ApplySetOnInsertIsNoopWhenInsertIsFalse) { ASSERT_EQUALS(fromjson("{}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(fromjson("{}"), getLogDoc()); + ASSERT_EQUALS("{a}", getModifiedPaths()); } TEST_F(SetNodeTest, ApplySetOnInsertIsAppliedWhenInsertIsTrue) { @@ -1368,6 +1428,7 @@ TEST_F(SetNodeTest, ApplySetOnInsertIsAppliedWhenInsertIsTrue) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: 2}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); + ASSERT_EQUALS("{a}", getModifiedPaths()); } TEST_F(SetNodeTest, ApplySetOnInsertExistingPath) { @@ -1386,6 +1447,7 @@ TEST_F(SetNodeTest, ApplySetOnInsertExistingPath) { ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: 2}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); + ASSERT_EQUALS("{a}", getModifiedPaths()); } } // namespace diff --git a/src/mongo/db/update/unset_node_test.cpp b/src/mongo/db/update/unset_node_test.cpp index 5443357b4bb..2553be23712 100644 --- a/src/mongo/db/update/unset_node_test.cpp +++ b/src/mongo/db/update/unset_node_test.cpp @@ -87,6 +87,7 @@ TEST_F(UnsetNodeTest, UnsetNoOp) { ASSERT_EQUALS(fromjson("{b: 5}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(fromjson("{}"), getLogDoc()); + ASSERT_EQUALS(getModifiedPaths(), "{a}"); } TEST_F(UnsetNodeTest, UnsetNoOpDottedPath) { @@ -105,6 +106,7 @@ TEST_F(UnsetNodeTest, UnsetNoOpDottedPath) { ASSERT_EQUALS(fromjson("{a: 5}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(fromjson("{}"), getLogDoc()); + ASSERT_EQUALS(getModifiedPaths(), "{a.b}"); } TEST_F(UnsetNodeTest, UnsetNoOpThroughArray) { @@ -123,6 +125,7 @@ TEST_F(UnsetNodeTest, UnsetNoOpThroughArray) { ASSERT_EQUALS(fromjson("{a:[{b:1}]}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(fromjson("{}"), getLogDoc()); + ASSERT_EQUALS(getModifiedPaths(), "{a.b}"); } TEST_F(UnsetNodeTest, UnsetNoOpEmptyDoc) { @@ -140,6 +143,7 @@ TEST_F(UnsetNodeTest, UnsetNoOpEmptyDoc) { ASSERT_EQUALS(fromjson("{}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(fromjson("{}"), getLogDoc()); + ASSERT_EQUALS(getModifiedPaths(), "{a}"); } TEST_F(UnsetNodeTest, UnsetTopLevelPath) { @@ -157,6 +161,7 @@ TEST_F(UnsetNodeTest, UnsetTopLevelPath) { ASSERT_EQUALS(fromjson("{}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(fromjson("{$unset: {a: true}}"), getLogDoc()); + ASSERT_EQUALS(getModifiedPaths(), "{a}"); } TEST_F(UnsetNodeTest, UnsetNestedPath) { @@ -174,6 +179,7 @@ TEST_F(UnsetNodeTest, UnsetNestedPath) { ASSERT_EQUALS(fromjson("{a: {b: {}}}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(fromjson("{$unset: {'a.b.c': true}}"), getLogDoc()); + ASSERT_EQUALS(getModifiedPaths(), "{a.b.c}"); } TEST_F(UnsetNodeTest, UnsetObject) { @@ -191,6 +197,7 @@ TEST_F(UnsetNodeTest, UnsetObject) { ASSERT_EQUALS(fromjson("{a: {}}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(fromjson("{$unset: {'a.b': true}}"), getLogDoc()); + ASSERT_EQUALS(getModifiedPaths(), "{a.b}"); } TEST_F(UnsetNodeTest, UnsetArrayElement) { @@ -208,6 +215,7 @@ TEST_F(UnsetNodeTest, UnsetArrayElement) { ASSERT_EQUALS(fromjson("{a:[null], b:1}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(fromjson("{$unset: {'a.0': true}}"), getLogDoc()); + ASSERT_EQUALS(getModifiedPaths(), "{a.0}"); } TEST_F(UnsetNodeTest, UnsetPositional) { @@ -226,6 +234,7 @@ TEST_F(UnsetNodeTest, UnsetPositional) { ASSERT_EQUALS(fromjson("{a: [0, null, 2]}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(fromjson("{$unset: {'a.1': true}}"), getLogDoc()); + ASSERT_EQUALS(getModifiedPaths(), "{a.1}"); } TEST_F(UnsetNodeTest, UnsetEntireArray) { @@ -243,6 +252,7 @@ TEST_F(UnsetNodeTest, UnsetEntireArray) { ASSERT_EQUALS(fromjson("{}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(fromjson("{$unset: {a: true}}"), getLogDoc()); + ASSERT_EQUALS(getModifiedPaths(), "{a}"); } TEST_F(UnsetNodeTest, UnsetFromObjectInArray) { @@ -260,6 +270,7 @@ TEST_F(UnsetNodeTest, UnsetFromObjectInArray) { ASSERT_EQUALS(fromjson("{a:[{}]}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(fromjson("{$unset: {'a.0.b': true}}"), getLogDoc()); + ASSERT_EQUALS(getModifiedPaths(), "{a.0.b}"); } TEST_F(UnsetNodeTest, CanUnsetInvalidField) { @@ -277,6 +288,7 @@ TEST_F(UnsetNodeTest, CanUnsetInvalidField) { ASSERT_EQUALS(fromjson("{b: 1, a: [{}]}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(fromjson("{$unset: {'a.0.$b': true}}"), getLogDoc()); + ASSERT_EQUALS(getModifiedPaths(), "{a.0.$b}"); } TEST_F(UnsetNodeTest, ApplyNoIndexDataNoLogBuilder) { @@ -293,6 +305,7 @@ TEST_F(UnsetNodeTest, ApplyNoIndexDataNoLogBuilder) { ASSERT_FALSE(result.indexesAffected); ASSERT_EQUALS(fromjson("{}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); + ASSERT_EQUALS(getModifiedPaths(), "{a}"); } TEST_F(UnsetNodeTest, ApplyDoesNotAffectIndexes) { @@ -310,6 +323,7 @@ TEST_F(UnsetNodeTest, ApplyDoesNotAffectIndexes) { ASSERT_EQUALS(fromjson("{}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(fromjson("{$unset: {a: true}}"), getLogDoc()); + ASSERT_EQUALS(getModifiedPaths(), "{a}"); } TEST_F(UnsetNodeTest, ApplyFieldWithDot) { @@ -327,6 +341,7 @@ TEST_F(UnsetNodeTest, ApplyFieldWithDot) { ASSERT_EQUALS(fromjson("{'a.b':4, a: {}}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(fromjson("{$unset: {'a.b': true}}"), getLogDoc()); + ASSERT_EQUALS(getModifiedPaths(), "{a.b}"); } TEST_F(UnsetNodeTest, ApplyCannotRemoveRequiredPartOfDBRef) { @@ -361,6 +376,7 @@ TEST_F(UnsetNodeTest, ApplyCanRemoveRequiredPartOfDBRefIfValidateForStorageIsFal ASSERT_EQUALS(updated, doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(fromjson("{$unset: {'a.$id': true}}"), getLogDoc()); + ASSERT_EQUALS(getModifiedPaths(), "{a.$id}"); } TEST_F(UnsetNodeTest, ApplyCannotRemoveImmutablePath) { @@ -428,6 +444,7 @@ TEST_F(UnsetNodeTest, ApplyCanRemoveImmutablePathIfNoop) { ASSERT_EQUALS(fromjson("{a: {b: 1}}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(fromjson("{}"), getLogDoc()); + ASSERT_EQUALS(getModifiedPaths(), "{a.b.c}"); } } // namespace diff --git a/src/mongo/db/update/update_array_node.cpp b/src/mongo/db/update/update_array_node.cpp index dc10ce92a74..655260f92e7 100644 --- a/src/mongo/db/update/update_array_node.cpp +++ b/src/mongo/db/update/update_array_node.cpp @@ -155,6 +155,11 @@ UpdateNode::ApplyResult UpdateArrayNode::apply(ApplyParams applyParams) const { ++i; } + // If no elements match the array filter, report the path to the array itself as modified. + if (applyParams.modifiedPaths && matchingElements.size() == 0) { + applyParams.modifiedPaths->keepShortest(*applyParams.pathTaken); + } + // If the child updates have not been logged, log the updated array elements. if (!childrenShouldLogThemselves && applyParams.logBuilder) { if (nModified > 1) { diff --git a/src/mongo/db/update/update_array_node_test.cpp b/src/mongo/db/update/update_array_node_test.cpp index 1b7b5934eb0..45a01d425b9 100644 --- a/src/mongo/db/update/update_array_node_test.cpp +++ b/src/mongo/db/update/update_array_node_test.cpp @@ -122,6 +122,7 @@ TEST_F(UpdateArrayNodeTest, UpdateIsAppliedToAllMatchingElements) { ASSERT_EQUALS(fromjson("{a: [2, 1, 2]}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(fromjson("{$set: {a: [2, 1, 2]}}"), getLogDoc()); + ASSERT_EQUALS("{a.0, a.2}", getModifiedPaths()); } DEATH_TEST_F(UpdateArrayNodeTest, @@ -170,6 +171,7 @@ TEST_F(UpdateArrayNodeTest, UpdateForEmptyIdentifierIsAppliedToAllArrayElements) ASSERT_EQUALS(fromjson("{a: [1, 1, 1]}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(fromjson("{$set: {a: [1, 1, 1]}}"), getLogDoc()); + ASSERT_EQUALS("{a.0, a.1, a.2}", getModifiedPaths()); } TEST_F(UpdateArrayNodeTest, ApplyMultipleUpdatesToArrayElement) { @@ -217,6 +219,7 @@ TEST_F(UpdateArrayNodeTest, ApplyMultipleUpdatesToArrayElement) { ASSERT_EQUALS(fromjson("{a: [{b: 1, c: 1, d: 1}]}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(fromjson("{$set: {'a.0.b': 1, 'a.0.c': 1, 'a.0.d': 1}}"), getLogDoc()); + ASSERT_EQUALS("{a.0.b, a.0.c, a.0.d}", getModifiedPaths()); } TEST_F(UpdateArrayNodeTest, ApplyMultipleUpdatesToArrayElementsUsingMergedChildrenCache) { @@ -255,6 +258,7 @@ TEST_F(UpdateArrayNodeTest, ApplyMultipleUpdatesToArrayElementsUsingMergedChildr ASSERT_EQUALS(fromjson("{a: [{b: 1, c: 1}, {b: 1, c: 1}]}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(fromjson("{$set: {a: [{b: 1, c: 1}, {b: 1, c: 1}]}}"), getLogDoc()); + ASSERT_EQUALS("{a.0.b, a.0.c, a.1.b, a.1.c}", getModifiedPaths()); } TEST_F(UpdateArrayNodeTest, ApplyMultipleUpdatesToArrayElementsWithoutMergedChildrenCache) { @@ -302,6 +306,7 @@ TEST_F(UpdateArrayNodeTest, ApplyMultipleUpdatesToArrayElementsWithoutMergedChil ASSERT_EQUALS(fromjson("{a: [{b: 2, c: 2, d: 1}, {b: 1, c: 2, d: 2}]}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(fromjson("{$set: {a: [{b: 2, c: 2, d: 1}, {b: 1, c: 2, d: 2}]}}"), getLogDoc()); + ASSERT_EQUALS("{a.0.b, a.0.c, a.1.c, a.1.d}", getModifiedPaths()); } TEST_F(UpdateArrayNodeTest, ApplyMultipleUpdatesToArrayElementWithEmptyIdentifiers) { @@ -331,6 +336,7 @@ TEST_F(UpdateArrayNodeTest, ApplyMultipleUpdatesToArrayElementWithEmptyIdentifie ASSERT_EQUALS(fromjson("{a: [{b: 1, c: 1}]}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(fromjson("{$set: {'a.0.b': 1, 'a.0.c': 1}}"), getLogDoc()); + ASSERT_EQUALS("{a.0.b, a.0.c}", getModifiedPaths()); } TEST_F(UpdateArrayNodeTest, ApplyNestedArrayUpdates) { @@ -342,18 +348,10 @@ TEST_F(UpdateArrayNodeTest, ApplyNestedArrayUpdates) { boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); std::map<StringData, std::unique_ptr<ExpressionWithPlaceholder>> arrayFilters; - auto parsedFilterI = assertGet(MatchExpressionParser::parse(arrayFilterI, expCtx - - )); - auto parsedFilterJ = assertGet(MatchExpressionParser::parse(arrayFilterJ, expCtx - - )); - auto parsedFilterK = assertGet(MatchExpressionParser::parse(arrayFilterK, expCtx - - )); - auto parsedFilterL = assertGet(MatchExpressionParser::parse(arrayFilterL, expCtx - - )); + auto parsedFilterI = assertGet(MatchExpressionParser::parse(arrayFilterI, expCtx)); + auto parsedFilterJ = assertGet(MatchExpressionParser::parse(arrayFilterJ, expCtx)); + auto parsedFilterK = assertGet(MatchExpressionParser::parse(arrayFilterK, expCtx)); + auto parsedFilterL = assertGet(MatchExpressionParser::parse(arrayFilterL, expCtx)); arrayFilters["i"] = assertGet(ExpressionWithPlaceholder::make(std::move(parsedFilterI))); arrayFilters["j"] = assertGet(ExpressionWithPlaceholder::make(std::move(parsedFilterJ))); @@ -383,6 +381,7 @@ TEST_F(UpdateArrayNodeTest, ApplyNestedArrayUpdates) { ASSERT_EQUALS(fromjson("{a: [{x: 0, b: [{c: 1, d: 1}]}]}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(fromjson("{$set: {'a.0.b.0.c': 1, 'a.0.b.0.d': 1}}"), getLogDoc()); + ASSERT_EQUALS("{a.0.b.0.c, a.0.b.0.d}", getModifiedPaths()); } TEST_F(UpdateArrayNodeTest, ApplyUpdatesWithMergeConflictToArrayElementFails) { @@ -392,12 +391,8 @@ TEST_F(UpdateArrayNodeTest, ApplyUpdatesWithMergeConflictToArrayElementFails) { boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); std::map<StringData, std::unique_ptr<ExpressionWithPlaceholder>> arrayFilters; - auto parsedFilterI = assertGet(MatchExpressionParser::parse(arrayFilterI, expCtx - - )); - auto parsedFilterJ = assertGet(MatchExpressionParser::parse(arrayFilterJ, expCtx - - )); + auto parsedFilterI = assertGet(MatchExpressionParser::parse(arrayFilterI, expCtx)); + auto parsedFilterJ = assertGet(MatchExpressionParser::parse(arrayFilterJ, expCtx)); arrayFilters["i"] = assertGet(ExpressionWithPlaceholder::make(std::move(parsedFilterI))); arrayFilters["j"] = assertGet(ExpressionWithPlaceholder::make(std::move(parsedFilterJ))); @@ -432,12 +427,8 @@ TEST_F(UpdateArrayNodeTest, ApplyUpdatesWithEmptyIdentifiersWithMergeConflictToA boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); std::map<StringData, std::unique_ptr<ExpressionWithPlaceholder>> arrayFilters; - auto parsedFilterI = assertGet(MatchExpressionParser::parse(arrayFilterI, expCtx - - )); - auto parsedFilterJ = assertGet(MatchExpressionParser::parse(arrayFilterJ, expCtx - - )); + auto parsedFilterI = assertGet(MatchExpressionParser::parse(arrayFilterI, expCtx)); + auto parsedFilterJ = assertGet(MatchExpressionParser::parse(arrayFilterJ, expCtx)); arrayFilters["i"] = assertGet(ExpressionWithPlaceholder::make(std::move(parsedFilterI))); arrayFilters["j"] = assertGet(ExpressionWithPlaceholder::make(std::move(parsedFilterJ))); @@ -474,18 +465,10 @@ TEST_F(UpdateArrayNodeTest, ApplyNestedArrayUpdatesWithMergeConflictFails) { boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); std::map<StringData, std::unique_ptr<ExpressionWithPlaceholder>> arrayFilters; - auto parsedFilterI = assertGet(MatchExpressionParser::parse(arrayFilterI, expCtx - - )); - auto parsedFilterJ = assertGet(MatchExpressionParser::parse(arrayFilterJ, expCtx - - )); - auto parsedFilterK = assertGet(MatchExpressionParser::parse(arrayFilterK, expCtx - - )); - auto parsedFilterL = assertGet(MatchExpressionParser::parse(arrayFilterL, expCtx - - )); + auto parsedFilterI = assertGet(MatchExpressionParser::parse(arrayFilterI, expCtx)); + auto parsedFilterJ = assertGet(MatchExpressionParser::parse(arrayFilterJ, expCtx)); + auto parsedFilterK = assertGet(MatchExpressionParser::parse(arrayFilterK, expCtx)); + auto parsedFilterL = assertGet(MatchExpressionParser::parse(arrayFilterL, expCtx)); arrayFilters["i"] = assertGet(ExpressionWithPlaceholder::make(std::move(parsedFilterI))); arrayFilters["j"] = assertGet(ExpressionWithPlaceholder::make(std::move(parsedFilterJ))); @@ -520,9 +503,7 @@ TEST_F(UpdateArrayNodeTest, NoArrayElementsMatch) { auto arrayFilter = fromjson("{'i': 0}"); boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); std::map<StringData, std::unique_ptr<ExpressionWithPlaceholder>> arrayFilters; - auto parsedFilter = assertGet(MatchExpressionParser::parse(arrayFilter, expCtx - - )); + auto parsedFilter = assertGet(MatchExpressionParser::parse(arrayFilter, expCtx)); arrayFilters["i"] = assertGet(ExpressionWithPlaceholder::make(std::move(parsedFilter))); std::set<std::string> foundIdentifiers; UpdateObjectNode root; @@ -541,16 +522,15 @@ TEST_F(UpdateArrayNodeTest, NoArrayElementsMatch) { ASSERT_EQUALS(fromjson("{a: [2, 2, 2]}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(fromjson("{}"), getLogDoc()); + ASSERT_EQUALS("{a}", getModifiedPaths()); } TEST_F(UpdateArrayNodeTest, UpdatesToAllArrayElementsAreNoops) { auto update = fromjson("{$set: {'a.$[i]': 1}}"); - auto arrayFilter = fromjson("{'i': 0}"); + auto arrayFilter = fromjson("{'i': 1}"); boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); std::map<StringData, std::unique_ptr<ExpressionWithPlaceholder>> arrayFilters; - auto parsedFilter = assertGet(MatchExpressionParser::parse(arrayFilter, expCtx - - )); + auto parsedFilter = assertGet(MatchExpressionParser::parse(arrayFilter, expCtx)); arrayFilters["i"] = assertGet(ExpressionWithPlaceholder::make(std::move(parsedFilter))); std::set<std::string> foundIdentifiers; UpdateObjectNode root; @@ -569,6 +549,7 @@ TEST_F(UpdateArrayNodeTest, UpdatesToAllArrayElementsAreNoops) { ASSERT_EQUALS(fromjson("{a: [1, 1, 1]}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(fromjson("{}"), getLogDoc()); + ASSERT_EQUALS("{a.0, a.1, a.2}", getModifiedPaths()); } TEST_F(UpdateArrayNodeTest, NoArrayElementAffectsIndexes) { @@ -576,9 +557,7 @@ TEST_F(UpdateArrayNodeTest, NoArrayElementAffectsIndexes) { auto arrayFilter = fromjson("{'i.c': 0}"); boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); std::map<StringData, std::unique_ptr<ExpressionWithPlaceholder>> arrayFilters; - auto parsedFilter = assertGet(MatchExpressionParser::parse(arrayFilter, expCtx - - )); + auto parsedFilter = assertGet(MatchExpressionParser::parse(arrayFilter, expCtx)); arrayFilters["i"] = assertGet(ExpressionWithPlaceholder::make(std::move(parsedFilter))); std::set<std::string> foundIdentifiers; UpdateObjectNode root; @@ -597,6 +576,7 @@ TEST_F(UpdateArrayNodeTest, NoArrayElementAffectsIndexes) { ASSERT_EQUALS(fromjson("{a: [{c: 0, b: 0}, {c: 0, b: 0}, {c: 0, b: 0}]}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(fromjson("{$set: {a: [{c: 0, b: 0}, {c: 0, b: 0}, {c: 0, b: 0}]}}"), getLogDoc()); + ASSERT_EQUALS("{a.0.b, a.1.b, a.2.b}", getModifiedPaths()); } TEST_F(UpdateArrayNodeTest, WhenOneElementIsMatchedLogElementUpdateDirectly) { @@ -604,9 +584,7 @@ TEST_F(UpdateArrayNodeTest, WhenOneElementIsMatchedLogElementUpdateDirectly) { auto arrayFilter = fromjson("{'i.c': 0}"); boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); std::map<StringData, std::unique_ptr<ExpressionWithPlaceholder>> arrayFilters; - auto parsedFilter = assertGet(MatchExpressionParser::parse(arrayFilter, expCtx - - )); + auto parsedFilter = assertGet(MatchExpressionParser::parse(arrayFilter, expCtx)); arrayFilters["i"] = assertGet(ExpressionWithPlaceholder::make(std::move(parsedFilter))); std::set<std::string> foundIdentifiers; UpdateObjectNode root; @@ -625,6 +603,7 @@ TEST_F(UpdateArrayNodeTest, WhenOneElementIsMatchedLogElementUpdateDirectly) { ASSERT_EQUALS(fromjson("{a: [{c: 1}, {c: 0, b: 0}, {c: 1}]}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(fromjson("{$set: {'a.1.b': 0}}"), getLogDoc()); + ASSERT_EQUALS("{a.1.b}", getModifiedPaths()); } TEST_F(UpdateArrayNodeTest, WhenOneElementIsModifiedLogElement) { @@ -632,9 +611,7 @@ TEST_F(UpdateArrayNodeTest, WhenOneElementIsModifiedLogElement) { auto arrayFilter = fromjson("{'i.c': 0}"); boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); std::map<StringData, std::unique_ptr<ExpressionWithPlaceholder>> arrayFilters; - auto parsedFilter = assertGet(MatchExpressionParser::parse(arrayFilter, expCtx - - )); + auto parsedFilter = assertGet(MatchExpressionParser::parse(arrayFilter, expCtx)); arrayFilters["i"] = assertGet(ExpressionWithPlaceholder::make(std::move(parsedFilter))); std::set<std::string> foundIdentifiers; UpdateObjectNode root; @@ -653,6 +630,7 @@ TEST_F(UpdateArrayNodeTest, WhenOneElementIsModifiedLogElement) { ASSERT_EQUALS(fromjson("{a: [{c: 0, b: 0}, {c: 0, b: 0}, {c: 1}]}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(fromjson("{$set: {'a.1': {c: 0, b: 0}}}"), getLogDoc()); + ASSERT_EQUALS("{a.0.b, a.1.b}", getModifiedPaths()); } TEST_F(UpdateArrayNodeTest, ArrayUpdateOnEmptyArrayIsANoop) { @@ -676,6 +654,7 @@ TEST_F(UpdateArrayNodeTest, ArrayUpdateOnEmptyArrayIsANoop) { ASSERT_EQUALS(fromjson("{a: []}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(fromjson("{}"), getLogDoc()); + ASSERT_EQUALS("{a}", getModifiedPaths()); } TEST_F(UpdateArrayNodeTest, ApplyPositionalInsideArrayUpdate) { @@ -683,9 +662,7 @@ TEST_F(UpdateArrayNodeTest, ApplyPositionalInsideArrayUpdate) { auto arrayFilter = fromjson("{'i.c': 0}"); boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); std::map<StringData, std::unique_ptr<ExpressionWithPlaceholder>> arrayFilters; - auto parsedFilter = assertGet(MatchExpressionParser::parse(arrayFilter, expCtx - - )); + auto parsedFilter = assertGet(MatchExpressionParser::parse(arrayFilter, expCtx)); arrayFilters["i"] = assertGet(ExpressionWithPlaceholder::make(std::move(parsedFilter))); std::set<std::string> foundIdentifiers; UpdateObjectNode root; @@ -705,6 +682,7 @@ TEST_F(UpdateArrayNodeTest, ApplyPositionalInsideArrayUpdate) { ASSERT_EQUALS(fromjson("{a: [{b: [0, 1], c: 0}]}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(fromjson("{$set: {'a.0.b.1': 1}}"), getLogDoc()); + ASSERT_EQUALS("{a.0.b.1}", getModifiedPaths()); } TEST_F(UpdateArrayNodeTest, ApplyArrayUpdateFromReplication) { @@ -712,9 +690,7 @@ TEST_F(UpdateArrayNodeTest, ApplyArrayUpdateFromReplication) { auto arrayFilter = fromjson("{'i': 0}"); boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); std::map<StringData, std::unique_ptr<ExpressionWithPlaceholder>> arrayFilters; - auto parsedFilter = assertGet(MatchExpressionParser::parse(arrayFilter, expCtx - - )); + auto parsedFilter = assertGet(MatchExpressionParser::parse(arrayFilter, expCtx)); arrayFilters["i"] = assertGet(ExpressionWithPlaceholder::make(std::move(parsedFilter))); std::set<std::string> foundIdentifiers; UpdateObjectNode root; @@ -734,6 +710,7 @@ TEST_F(UpdateArrayNodeTest, ApplyArrayUpdateFromReplication) { ASSERT_EQUALS(fromjson("{a: [0]}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(fromjson("{}"), getLogDoc()); + ASSERT_EQUALS("{a.0.b}", getModifiedPaths()); } TEST_F(UpdateArrayNodeTest, ApplyArrayUpdateNotFromReplication) { @@ -741,9 +718,7 @@ TEST_F(UpdateArrayNodeTest, ApplyArrayUpdateNotFromReplication) { auto arrayFilter = fromjson("{'i': 0}"); boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); std::map<StringData, std::unique_ptr<ExpressionWithPlaceholder>> arrayFilters; - auto parsedFilter = assertGet(MatchExpressionParser::parse(arrayFilter, expCtx - - )); + auto parsedFilter = assertGet(MatchExpressionParser::parse(arrayFilter, expCtx)); arrayFilters["i"] = assertGet(ExpressionWithPlaceholder::make(std::move(parsedFilter))); std::set<std::string> foundIdentifiers; UpdateObjectNode root; @@ -767,9 +742,7 @@ TEST_F(UpdateArrayNodeTest, ApplyArrayUpdateWithoutLogBuilderOrIndexData) { auto arrayFilter = fromjson("{'i': 0}"); boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); std::map<StringData, std::unique_ptr<ExpressionWithPlaceholder>> arrayFilters; - auto parsedFilter = assertGet(MatchExpressionParser::parse(arrayFilter, expCtx - - )); + auto parsedFilter = assertGet(MatchExpressionParser::parse(arrayFilter, expCtx)); arrayFilters["i"] = assertGet(ExpressionWithPlaceholder::make(std::move(parsedFilter))); std::set<std::string> foundIdentifiers; UpdateObjectNode root; @@ -787,6 +760,7 @@ TEST_F(UpdateArrayNodeTest, ApplyArrayUpdateWithoutLogBuilderOrIndexData) { ASSERT_FALSE(result.noop); ASSERT_EQUALS(fromjson("{a: [1]}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); + ASSERT_EQUALS("{a.0}", getModifiedPaths()); } } // namespace diff --git a/src/mongo/db/update/update_driver.cpp b/src/mongo/db/update/update_driver.cpp index 90764403dd8..1b6e5cfcfd8 100644 --- a/src/mongo/db/update/update_driver.cpp +++ b/src/mongo/db/update/update_driver.cpp @@ -245,7 +245,8 @@ Status UpdateDriver::update(StringData matchedField, bool validateForStorage, const FieldRefSet& immutablePaths, BSONObj* logOpRec, - bool* docWasModified) { + bool* docWasModified, + FieldRefSetWithStorage* modifiedPaths) { // TODO: assert that update() is called at most once in a !_multi case. _affectIndices = (isDocReplacement() && (_indexedFields != NULL)); @@ -259,6 +260,10 @@ Status UpdateDriver::update(StringData matchedField, applyParams.fromOplogApplication = _fromOplogApplication; applyParams.validateForStorage = validateForStorage; applyParams.indexData = _indexedFields; + applyParams.modifiedPaths = modifiedPaths; + // The supplied 'modifiedPaths' must be an empty set. + invariant(!modifiedPaths || modifiedPaths->empty()); + if (_logOp && logOpRec) { applyParams.logBuilder = &logBuilder; } diff --git a/src/mongo/db/update/update_driver.h b/src/mongo/db/update/update_driver.h index 77868cf03da..bd2c94f7cc5 100644 --- a/src/mongo/db/update/update_driver.h +++ b/src/mongo/db/update/update_driver.h @@ -95,13 +95,22 @@ public: * If 'validateForStorage' is true, ensures that modified elements do not violate depth or DBRef * constraints. Ensures that no paths in 'immutablePaths' are modified (though they may be * created, if they do not yet exist). + * + * If 'modifiedPaths' is not null, this method will populate it with the set of paths that were + * either modified at runtime or present statically in the update modifiers. For arrays, the + * set will include only the path to the array if the length has changed. All paths encode array + * indexes explicitly. + * + * The caller must either provide a null pointer, or a non-null pointer to an empty field ref + * set. */ Status update(StringData matchedField, mutablebson::Document* doc, bool validateForStorage, const FieldRefSet& immutablePaths, BSONObj* logOpRec = nullptr, - bool* docWasModified = nullptr); + bool* docWasModified = nullptr, + FieldRefSetWithStorage* modifiedPaths = nullptr); // // Accessors diff --git a/src/mongo/db/update/update_driver_test.cpp b/src/mongo/db/update/update_driver_test.cpp index 00e06229406..04cce2c5005 100644 --- a/src/mongo/db/update/update_driver_test.cpp +++ b/src/mongo/db/update/update_driver_test.cpp @@ -60,6 +60,7 @@ namespace mongo { namespace { using mongoutils::str::stream; +using unittest::assertGet; TEST(Parse, Normal) { boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); @@ -528,5 +529,98 @@ TEST_F(CreateFromQuery, NotFullShardKeyRepl) { driverRepl().populateDocumentWithQueryFields(opCtx(), query, immutablePaths, doc())); } +using mutablebson::Document; + +class ModifiedPathsTestFixture : public mongo::unittest::Test { +public: + std::string getModifiedPaths(Document* doc, + BSONObj updateSpec, + StringData matchedField = StringData(), + std::vector<BSONObj> arrayFilterSpec = {}) { + boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); + UpdateDriver driver(expCtx); + std::map<StringData, std::unique_ptr<ExpressionWithPlaceholder>> arrayFilters; + for (const auto& filter : arrayFilterSpec) { + auto parsedFilter = assertGet(MatchExpressionParser::parse(filter, expCtx)); + auto expr = assertGet(ExpressionWithPlaceholder::make(std::move(parsedFilter))); + ASSERT(expr->getPlaceholder()); + arrayFilters[expr->getPlaceholder().get()] = std::move(expr); + } + driver.parse(updateSpec, arrayFilters); + + const bool validateForStorage = true; + const FieldRefSet emptyImmutablePaths; + FieldRefSetWithStorage modifiedPaths; + ASSERT_OK(driver.update(matchedField, + doc, + validateForStorage, + emptyImmutablePaths, + nullptr, + nullptr, + &modifiedPaths)); + + return modifiedPaths.toString(); + } +}; + +TEST_F(ModifiedPathsTestFixture, SetFieldInRoot) { + BSONObj spec = fromjson("{$set: {a: 1}}"); + Document doc(fromjson("{a: 0}")); + ASSERT_EQ(getModifiedPaths(&doc, spec), "{a}"); +} + +TEST_F(ModifiedPathsTestFixture, IncFieldInRoot) { + BSONObj spec = fromjson("{$inc: {a: 1}}"); + Document doc(fromjson("{a: 0}")); + ASSERT_EQ(getModifiedPaths(&doc, spec), "{a}"); +} + +TEST_F(ModifiedPathsTestFixture, UnsetFieldInRoot) { + BSONObj spec = fromjson("{$unset: {a: ''}}"); + Document doc(fromjson("{a: 0}")); + ASSERT_EQ(getModifiedPaths(&doc, spec), "{a}"); +} + +TEST_F(ModifiedPathsTestFixture, UpdateArrayElement) { + BSONObj spec = fromjson("{$set: {'a.0.b': 1}}"); + Document doc(fromjson("{a: [{b: 0}]}")); + ASSERT_EQ(getModifiedPaths(&doc, spec), "{a.0.b}"); +} + +TEST_F(ModifiedPathsTestFixture, SetBeyondTheEndOfArrayShouldReturnPathToArray) { + BSONObj spec = fromjson("{$set: {'a.1.b': 1}}"); + Document doc(fromjson("{a: [{b: 0}]}")); + ASSERT_EQ(getModifiedPaths(&doc, spec), "{a}"); +} + +TEST_F(ModifiedPathsTestFixture, InsertingAndUpdatingArrayShouldReturnPathToArray) { + BSONObj spec = fromjson("{$set: {'a.0.b': 1, 'a.1.c': 2}}"); + Document doc(fromjson("{a: [{b: 0}]}")); + ASSERT_EQ(getModifiedPaths(&doc, spec), "{a}"); + + spec = fromjson("{$set: {'a.10.b': 1, 'a.1.c': 2}}"); + Document doc2(fromjson("{a: [{b: 0}, {b: 0}]}")); + ASSERT_EQ(getModifiedPaths(&doc2, spec), "{a}"); +} + +TEST_F(ModifiedPathsTestFixture, UpdateWithPositionalOperator) { + BSONObj spec = fromjson("{$set: {'a.$': 1}}"); + Document doc(fromjson("{a: [0, 1, 2]}")); + ASSERT_EQ(getModifiedPaths(&doc, spec, "0"_sd), "{a.0}"); +} + +TEST_F(ModifiedPathsTestFixture, UpdateWithPositionalOperatorToNestedField) { + BSONObj spec = fromjson("{$set: {'a.$.b': 1}}"); + Document doc(fromjson("{a: [{b: 1}, {b: 2}]}")); + ASSERT_EQ(getModifiedPaths(&doc, spec, "1"_sd), "{a.1.b}"); +} + +TEST_F(ModifiedPathsTestFixture, ArrayFilterThatMatchesNoElements) { + BSONObj spec = fromjson("{$set: {'a.$[i]': 1}}"); + BSONObj arrayFilter = fromjson("{i: 0}"); + Document doc(fromjson("{a: [1, 2, 3]}")); + ASSERT_EQ(getModifiedPaths(&doc, spec, ""_sd, {arrayFilter}), "{a}"); +} + } // namespace } // namespace mongo diff --git a/src/mongo/db/update/update_node.h b/src/mongo/db/update/update_node.h index 11cc451d139..9a53aa7ba50 100644 --- a/src/mongo/db/update/update_node.h +++ b/src/mongo/db/update/update_node.h @@ -123,6 +123,9 @@ public: // If provided, UpdateNode::apply will log the update here. LogBuilder* logBuilder = nullptr; + + // If provided, UpdateNode::apply will populate this with a path to each modified field. + FieldRefSetWithStorage* modifiedPaths = nullptr; }; /** diff --git a/src/mongo/db/update/update_node_test_fixture.h b/src/mongo/db/update/update_node_test_fixture.h index f578bde73b8..635ccb5b300 100644 --- a/src/mongo/db/update/update_node_test_fixture.h +++ b/src/mongo/db/update/update_node_test_fixture.h @@ -64,6 +64,7 @@ protected: _indexData.reset(); _logDoc.reset(); _logBuilder = stdx::make_unique<LogBuilder>(_logDoc.root()); + _modifiedPaths.clear(); } UpdateNode::ApplyParams getApplyParams(mutablebson::Element element) { @@ -76,6 +77,7 @@ protected: applyParams.validateForStorage = _validateForStorage; applyParams.indexData = _indexData.get(); applyParams.logBuilder = _logBuilder.get(); + applyParams.modifiedPaths = &_modifiedPaths; return applyParams; } @@ -126,6 +128,10 @@ protected: return _logDoc; } + std::string getModifiedPaths() { + return _modifiedPaths.toString(); + } + private: std::vector<std::unique_ptr<FieldRef>> _immutablePathsVector; FieldRefSet _immutablePaths; @@ -138,6 +144,7 @@ private: std::unique_ptr<UpdateIndexData> _indexData; mutablebson::Document _logDoc; std::unique_ptr<LogBuilder> _logBuilder; + FieldRefSetWithStorage _modifiedPaths; }; } // namespace mongo diff --git a/src/mongo/db/update/update_object_node_test.cpp b/src/mongo/db/update/update_object_node_test.cpp index 4694e57ad32..5e7242d64f6 100644 --- a/src/mongo/db/update/update_object_node_test.cpp +++ b/src/mongo/db/update/update_object_node_test.cpp @@ -1776,6 +1776,7 @@ TEST_F(UpdateObjectNodeTest, ApplyCreateField) { ASSERT_EQUALS(fromjson("{a: 5, b: 6}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(fromjson("{$set: {b: 6}}"), getLogDoc()); + ASSERT_EQUALS(getModifiedPaths(), "{b}"); } TEST_F(UpdateObjectNodeTest, ApplyExistingField) { @@ -1799,6 +1800,7 @@ TEST_F(UpdateObjectNodeTest, ApplyExistingField) { ASSERT_EQUALS(fromjson("{a: 6}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(fromjson("{$set: {a: 6}}"), getLogDoc()); + ASSERT_EQUALS(getModifiedPaths(), "{a}"); } TEST_F(UpdateObjectNodeTest, ApplyExistingAndNonexistingFields) { @@ -1840,6 +1842,7 @@ TEST_F(UpdateObjectNodeTest, ApplyExistingAndNonexistingFields) { ASSERT_BSONOBJ_EQ(fromjson("{a: 5, c: 7, b: 6, d: 8}"), doc.getObject()); ASSERT_FALSE(doc.isInPlaceModeEnabled()); ASSERT_BSONOBJ_EQ(fromjson("{$set: {a: 5, b: 6, c: 7, d: 8}}"), getLogDoc().getObject()); + ASSERT_EQUALS(getModifiedPaths(), "{a, b, c, d}"); } TEST_F(UpdateObjectNodeTest, ApplyExistingNestedPaths) { @@ -1882,6 +1885,7 @@ TEST_F(UpdateObjectNodeTest, ApplyExistingNestedPaths) { ASSERT_TRUE(doc.isInPlaceModeEnabled()); ASSERT_BSONOBJ_EQ(fromjson("{$set: {'a.b': 6, 'a.c': 7, 'b.d': 8, 'b.e': 9}}"), getLogDoc().getObject()); + ASSERT_EQUALS(getModifiedPaths(), "{a.b, a.c, b.d, b.e}"); } TEST_F(UpdateObjectNodeTest, ApplyCreateNestedPaths) { @@ -1924,6 +1928,7 @@ TEST_F(UpdateObjectNodeTest, ApplyCreateNestedPaths) { ASSERT_FALSE(doc.isInPlaceModeEnabled()); ASSERT_BSONOBJ_EQ(fromjson("{$set: {'a.b': 6, 'a.c': 7, 'b.d': 8, 'b.e': 9}}"), getLogDoc().getObject()); + ASSERT_EQUALS(getModifiedPaths(), "{a.b, a.c, b.d, b.e}"); } TEST_F(UpdateObjectNodeTest, ApplyCreateDeeplyNestedPaths) { @@ -1960,6 +1965,7 @@ TEST_F(UpdateObjectNodeTest, ApplyCreateDeeplyNestedPaths) { ASSERT_FALSE(doc.isInPlaceModeEnabled()); ASSERT_BSONOBJ_EQ(fromjson("{$set: {'a.b.c.d': 6, 'a.b.c.e': 7, 'a.f': 8}}"), getLogDoc().getObject()); + ASSERT_EQUALS(getModifiedPaths(), "{a.b.c.d, a.b.c.e, a.f}"); } TEST_F(UpdateObjectNodeTest, ChildrenShouldBeAppliedInAlphabeticalOrder) { @@ -2007,6 +2013,7 @@ TEST_F(UpdateObjectNodeTest, ChildrenShouldBeAppliedInAlphabeticalOrder) { ASSERT_BSONOBJ_EQ(fromjson("{z: 9, a: 5, b: 8, c: 7, d: 6}"), doc.getObject()); ASSERT_FALSE(doc.isInPlaceModeEnabled()); ASSERT_BSONOBJ_EQ(fromjson("{$set: {a: 5, b: 8, c: 7, d: 6, z: 9}}"), getLogDoc().getObject()); + ASSERT_EQUALS(getModifiedPaths(), "{a, b, c, d, z}"); } TEST_F(UpdateObjectNodeTest, CollatorShouldNotAffectUpdateOrder) { @@ -2075,6 +2082,7 @@ TEST_F(UpdateObjectNodeTest, ApplyNoop) { ASSERT_BSONOBJ_EQ(fromjson("{a: 5, b: 6, c: 7}"), doc.getObject()); ASSERT_TRUE(doc.isInPlaceModeEnabled()); ASSERT_BSONOBJ_EQ(fromjson("{}"), getLogDoc().getObject()); + ASSERT_EQUALS(getModifiedPaths(), "{a, b, c}"); } TEST_F(UpdateObjectNodeTest, ApplySomeChildrenNoops) { @@ -2112,6 +2120,7 @@ TEST_F(UpdateObjectNodeTest, ApplySomeChildrenNoops) { ASSERT_BSONOBJ_EQ(fromjson("{a: 5, b: 6, c: 7}"), doc.getObject()); ASSERT_TRUE(doc.isInPlaceModeEnabled()); ASSERT_BSONOBJ_EQ(fromjson("{$set: {b: 6}}"), getLogDoc().getObject()); + ASSERT_EQUALS(getModifiedPaths(), "{a, b, c}"); } TEST_F(UpdateObjectNodeTest, ApplyBlockingElement) { @@ -2129,6 +2138,7 @@ TEST_F(UpdateObjectNodeTest, ApplyBlockingElement) { mutablebson::Document doc(fromjson("{a: 0}")); addIndexedPath("a"); + ASSERT_EQUALS(getModifiedPaths(), "{}"); ASSERT_THROWS_CODE_AND_WHAT(root.apply(getApplyParams(doc.root())), AssertionException, ErrorCodes::PathNotViable, @@ -2180,6 +2190,7 @@ TEST_F(UpdateObjectNodeTest, ApplyPositionalMissingMatchedField) { mutablebson::Document doc(fromjson("{}")); addIndexedPath("a"); + ASSERT_EQUALS(getModifiedPaths(), "{}"); ASSERT_THROWS_CODE_AND_WHAT( root.apply(getApplyParams(doc.root())), AssertionException, @@ -2215,6 +2226,7 @@ TEST_F(UpdateObjectNodeTest, ApplyMergePositionalChild) { ASSERT_BSONOBJ_EQ(fromjson("{a: [{b: 5, c: 6}]}"), doc.getObject()); ASSERT_TRUE(doc.isInPlaceModeEnabled()); ASSERT_BSONOBJ_EQ(fromjson("{$set: {'a.0.b': 5, 'a.0.c': 6}}"), getLogDoc().getObject()); + ASSERT_EQUALS(getModifiedPaths(), "{a.0.b, a.0.c}"); } TEST_F(UpdateObjectNodeTest, ApplyOrderMergedPositionalChild) { @@ -2258,6 +2270,7 @@ TEST_F(UpdateObjectNodeTest, ApplyOrderMergedPositionalChild) { ASSERT_FALSE(doc.isInPlaceModeEnabled()); ASSERT_BSONOBJ_EQ(fromjson("{$set: {'a.0': 7, 'a.1.b': 6, 'a.1.c': 8, 'a.2': 5}}"), getLogDoc().getObject()); + ASSERT_EQUALS(getModifiedPaths(), "{a.0, a.1.b, a.1.c, a.2}"); } TEST_F(UpdateObjectNodeTest, ApplyMergeConflictWithPositionalChild) { @@ -2282,6 +2295,7 @@ TEST_F(UpdateObjectNodeTest, ApplyMergeConflictWithPositionalChild) { mutablebson::Document doc(fromjson("{}")); setMatchedField("0"); addIndexedPath("a"); + ASSERT_EQUALS(getModifiedPaths(), "{}"); ASSERT_THROWS_CODE_AND_WHAT(root.apply(getApplyParams(doc.root())), AssertionException, ErrorCodes::ConflictingUpdateOperators, @@ -2322,6 +2336,7 @@ TEST_F(UpdateObjectNodeTest, ApplyDoNotMergePositionalChild) { ASSERT_BSONOBJ_EQ(fromjson("{a: {'0': 5, '1': 7, '2': 6}}"), doc.getObject()); ASSERT_FALSE(doc.isInPlaceModeEnabled()); ASSERT_BSONOBJ_EQ(fromjson("{$set: {'a.0': 5, 'a.1': 7, 'a.2': 6}}"), getLogDoc().getObject()); + ASSERT_EQUALS(getModifiedPaths(), "{a.0, a.1, a.2}"); } TEST_F(UpdateObjectNodeTest, ApplyPositionalChildLast) { @@ -2358,6 +2373,7 @@ TEST_F(UpdateObjectNodeTest, ApplyPositionalChildLast) { ASSERT_BSONOBJ_EQ(fromjson("{a: {'0': 6, '1': 7, '2': 5}}"), doc.getObject()); ASSERT_FALSE(doc.isInPlaceModeEnabled()); ASSERT_BSONOBJ_EQ(fromjson("{$set: {'a.0': 6, 'a.1': 7, 'a.2': 5}}"), getLogDoc().getObject()); + ASSERT_EQUALS(getModifiedPaths(), "{a.0, a.1, a.2}"); } TEST_F(UpdateObjectNodeTest, ApplyUseStoredMergedPositional) { @@ -2388,6 +2404,7 @@ TEST_F(UpdateObjectNodeTest, ApplyUseStoredMergedPositional) { ASSERT_BSONOBJ_EQ(fromjson("{a: [{b: 5, c: 6}]}"), doc.getObject()); ASSERT_TRUE(doc.isInPlaceModeEnabled()); ASSERT_BSONOBJ_EQ(fromjson("{$set: {'a.0.b': 5, 'a.0.c': 6}}"), getLogDoc().getObject()); + ASSERT_EQUALS(getModifiedPaths(), "{a.0.b, a.0.c}"); mutablebson::Document doc2(fromjson("{a: [{b: 0, c: 0}]}")); resetApplyParams(); @@ -2399,6 +2416,7 @@ TEST_F(UpdateObjectNodeTest, ApplyUseStoredMergedPositional) { ASSERT_BSONOBJ_EQ(fromjson("{a: [{b: 5, c: 6}]}"), doc2.getObject()); ASSERT_TRUE(doc2.isInPlaceModeEnabled()); ASSERT_BSONOBJ_EQ(fromjson("{$set: {'a.0.b': 5, 'a.0.c': 6}}"), getLogDoc().getObject()); + ASSERT_EQUALS(getModifiedPaths(), "{a.0.b, a.0.c}"); } TEST_F(UpdateObjectNodeTest, ApplyDoNotUseStoredMergedPositional) { @@ -2436,6 +2454,7 @@ TEST_F(UpdateObjectNodeTest, ApplyDoNotUseStoredMergedPositional) { ASSERT_TRUE(doc.isInPlaceModeEnabled()); ASSERT_BSONOBJ_EQ(fromjson("{$set: {'a.0.b': 5, 'a.0.c': 6, 'a.1.d': 7}}"), getLogDoc().getObject()); + ASSERT_EQUALS(getModifiedPaths(), "{a.0.b, a.0.c, a.1.d}"); mutablebson::Document doc2(fromjson("{a: [{b: 0, c: 0}, {c: 0, d: 0}]}")); resetApplyParams(); @@ -2448,6 +2467,7 @@ TEST_F(UpdateObjectNodeTest, ApplyDoNotUseStoredMergedPositional) { ASSERT_TRUE(doc2.isInPlaceModeEnabled()); ASSERT_BSONOBJ_EQ(fromjson("{$set: {'a.0.b': 5, 'a.1.c': 6, 'a.1.d': 7}}"), getLogDoc().getObject()); + ASSERT_EQUALS(getModifiedPaths(), "{a.0.b, a.1.c, a.1.d}"); } /** @@ -2476,6 +2496,7 @@ TEST_F(UpdateObjectNodeTest, ApplyToArrayByIndexWithLeadingZero) { ASSERT_BSONOBJ_EQ(fromjson("{a: [0, 0, 2, 0, 0]}"), doc.getObject()); ASSERT_TRUE(doc.isInPlaceModeEnabled()); ASSERT_BSONOBJ_EQ(fromjson("{$set: {'a.02': 2}}"), getLogDoc().getObject()); + ASSERT_EQUALS(getModifiedPaths(), "{a.02}"); } /** @@ -2542,6 +2563,7 @@ TEST_F(UpdateObjectNodeTest, ApplyMultipleUpdatesToDocumentInArray) { ASSERT_BSONOBJ_EQ(fromjson("{a: [null, null, {b: 1, c: 1}]}"), doc.getObject()); ASSERT_FALSE(doc.isInPlaceModeEnabled()); ASSERT_BSONOBJ_EQ(fromjson("{$set: {'a.2.b': 1, 'a.2.c': 1}}"), getLogDoc().getObject()); + ASSERT_EQUALS(getModifiedPaths(), "{a}"); } TEST_F(UpdateObjectNodeTest, ApplyUpdateToNonViablePathInArray) { @@ -2591,6 +2613,7 @@ TEST_F(UpdateObjectNodeTest, SetAndPopModifiersWithCommonPrefixApplySuccessfully ASSERT_BSONOBJ_EQ(fromjson("{a: {b: 5, c: [2, 3, 4]}}"), doc.getObject()); ASSERT_FALSE(doc.isInPlaceModeEnabled()); ASSERT_BSONOBJ_EQ(fromjson("{$set: {'a.b': 5, 'a.c': [2, 3, 4]}}"), getLogDoc().getObject()); + ASSERT_EQUALS(getModifiedPaths(), "{a.b, a.c}"); } TEST(ParseRenameTest, RenameToStringWithEmbeddedNullFails) { |