summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/mongo/db/field_ref.h9
-rw-r--r--src/mongo/db/field_ref_set.cpp18
-rw-r--r--src/mongo/db/field_ref_set.h46
-rw-r--r--src/mongo/db/field_ref_set_test.cpp52
-rw-r--r--src/mongo/db/update/addtoset_node_test.cpp17
-rw-r--r--src/mongo/db/update/arithmetic_node_test.cpp50
-rw-r--r--src/mongo/db/update/modifier_node.cpp18
-rw-r--r--src/mongo/db/update/pop_node_test.cpp12
-rw-r--r--src/mongo/db/update/push_node_test.cpp37
-rw-r--r--src/mongo/db/update/rename_node.cpp6
-rw-r--r--src/mongo/db/update/rename_node_test.cpp15
-rw-r--r--src/mongo/db/update/set_node_test.cpp62
-rw-r--r--src/mongo/db/update/unset_node_test.cpp17
-rw-r--r--src/mongo/db/update/update_array_node.cpp5
-rw-r--r--src/mongo/db/update/update_array_node_test.cpp102
-rw-r--r--src/mongo/db/update/update_driver.cpp7
-rw-r--r--src/mongo/db/update/update_driver.h11
-rw-r--r--src/mongo/db/update/update_driver_test.cpp94
-rw-r--r--src/mongo/db/update/update_node.h3
-rw-r--r--src/mongo/db/update/update_node_test_fixture.h7
-rw-r--r--src/mongo/db/update/update_object_node_test.cpp23
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) {