From 676206d10e8a626e9c5e39045468dbe0bb2bd75a Mon Sep 17 00:00:00 2001 From: Tess Avitabile Date: Mon, 5 Jun 2017 14:22:26 -0400 Subject: SERVER-28763 Create UpdateArrayNode --- src/mongo/db/update/update_object_node_test.cpp | 1734 +++++++++++++++++++---- 1 file changed, 1484 insertions(+), 250 deletions(-) (limited to 'src/mongo/db/update/update_object_node_test.cpp') diff --git a/src/mongo/db/update/update_object_node_test.cpp b/src/mongo/db/update/update_object_node_test.cpp index f0bd1e0d1c8..5f0d0b6d134 100644 --- a/src/mongo/db/update/update_object_node_test.cpp +++ b/src/mongo/db/update/update_object_node_test.cpp @@ -33,7 +33,10 @@ #include "mongo/bson/mutable/algorithm.h" #include "mongo/bson/mutable/mutable_bson_test_utils.h" #include "mongo/db/json.h" +#include "mongo/db/matcher/extensions_callback_noop.h" #include "mongo/db/query/collation/collator_interface_mock.h" +#include "mongo/db/update/update_array_node.h" +#include "mongo/unittest/death_test.h" #include "mongo/unittest/unittest.h" namespace mongo { @@ -45,9 +48,15 @@ using mongo::mutablebson::Element; TEST(UpdateObjectNodeTest, InvalidPathFailsToParse) { auto update = fromjson("{$set: {'': 5}}"); const CollatorInterface* collator = nullptr; + std::map> arrayFilters; + std::set foundIdentifiers; UpdateObjectNode root; - auto result = UpdateObjectNode::parseAndMerge( - &root, modifiertable::ModifierType::MOD_SET, update["$set"][""], collator); + auto result = UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_SET, + update["$set"][""], + collator, + arrayFilters, + foundIdentifiers); ASSERT_NOT_OK(result); ASSERT_EQ(result.getStatus().code(), ErrorCodes::EmptyFieldName); ASSERT_EQ(result.getStatus().reason(), "An empty update path is not valid."); @@ -56,41 +65,71 @@ TEST(UpdateObjectNodeTest, InvalidPathFailsToParse) { TEST(UpdateObjectNodeTest, ValidIncPathParsesSuccessfully) { auto update = fromjson("{$inc: {'a.b': 5}}"); const CollatorInterface* collator = nullptr; + std::map> arrayFilters; + std::set foundIdentifiers; UpdateObjectNode root; - ASSERT_OK(UpdateObjectNode::parseAndMerge( - &root, modifiertable::ModifierType::MOD_INC, update["$inc"]["a.b"], collator)); + ASSERT_OK(UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_INC, + update["$inc"]["a.b"], + collator, + arrayFilters, + foundIdentifiers)); } TEST(UpdateObjectNodeTest, ValidMulPathParsesSuccessfully) { auto update = fromjson("{$mul: {'a.b': 5}}"); const CollatorInterface* collator = nullptr; + std::map> arrayFilters; + std::set foundIdentifiers; UpdateObjectNode root; - ASSERT_OK(UpdateObjectNode::parseAndMerge( - &root, modifiertable::ModifierType::MOD_MUL, update["$mul"]["a.b"], collator)); + ASSERT_OK(UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_MUL, + update["$mul"]["a.b"], + collator, + arrayFilters, + foundIdentifiers)); } TEST(UpdateObjectNodeTest, ValidSetPathParsesSuccessfully) { auto update = fromjson("{$set: {'a.b': 5}}"); const CollatorInterface* collator = nullptr; + std::map> arrayFilters; + std::set foundIdentifiers; UpdateObjectNode root; - ASSERT_OK(UpdateObjectNode::parseAndMerge( - &root, modifiertable::ModifierType::MOD_SET, update["$set"]["a.b"], collator)); + ASSERT_OK(UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_SET, + update["$set"]["a.b"], + collator, + arrayFilters, + foundIdentifiers)); } TEST(UpdateObjectNodeTest, ValidUnsetPathParsesSuccessfully) { auto update = fromjson("{$unset: {'a.b': 5}}"); const CollatorInterface* collator = nullptr; + std::map> arrayFilters; + std::set foundIdentifiers; UpdateObjectNode root; - ASSERT_OK(UpdateObjectNode::parseAndMerge( - &root, modifiertable::ModifierType::MOD_UNSET, update["$unset"]["a.b"], collator)); + ASSERT_OK(UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_UNSET, + update["$unset"]["a.b"], + collator, + arrayFilters, + foundIdentifiers)); } TEST(UpdateObjectNodeTest, MultiplePositionalElementsFailToParse) { auto update = fromjson("{$set: {'a.$.b.$': 5}}"); const CollatorInterface* collator = nullptr; + std::map> arrayFilters; + std::set foundIdentifiers; UpdateObjectNode root; - auto result = UpdateObjectNode::parseAndMerge( - &root, modifiertable::ModifierType::MOD_SET, update["$set"]["a.$.b.$"], collator); + auto result = UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_SET, + update["$set"]["a.$.b.$"], + collator, + arrayFilters, + foundIdentifiers); ASSERT_NOT_OK(result); ASSERT_EQ(result.getStatus().code(), ErrorCodes::BadValue); ASSERT_EQ(result.getStatus().reason(), @@ -100,9 +139,15 @@ TEST(UpdateObjectNodeTest, MultiplePositionalElementsFailToParse) { TEST(UpdateObjectNodeTest, ParsingSetsPositionalTrue) { auto update = fromjson("{$set: {'a.$.b': 5}}"); const CollatorInterface* collator = nullptr; + std::map> arrayFilters; + std::set foundIdentifiers; UpdateObjectNode root; - auto result = UpdateObjectNode::parseAndMerge( - &root, modifiertable::ModifierType::MOD_SET, update["$set"]["a.$.b"], collator); + auto result = UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_SET, + update["$set"]["a.$.b"], + collator, + arrayFilters, + foundIdentifiers); ASSERT_OK(result); ASSERT_TRUE(result.getValue()); } @@ -110,9 +155,15 @@ TEST(UpdateObjectNodeTest, ParsingSetsPositionalTrue) { TEST(UpdateObjectNodeTest, ParsingSetsPositionalFalse) { auto update = fromjson("{$set: {'a.b': 5}}"); const CollatorInterface* collator = nullptr; + std::map> arrayFilters; + std::set foundIdentifiers; UpdateObjectNode root; - auto result = UpdateObjectNode::parseAndMerge( - &root, modifiertable::ModifierType::MOD_SET, update["$set"]["a.b"], collator); + auto result = UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_SET, + update["$set"]["a.b"], + collator, + arrayFilters, + foundIdentifiers); ASSERT_OK(result); ASSERT_FALSE(result.getValue()); } @@ -120,9 +171,15 @@ TEST(UpdateObjectNodeTest, ParsingSetsPositionalFalse) { TEST(UpdateObjectNodeTest, PositionalElementFirstPositionFailsToParse) { auto update = fromjson("{$set: {'$': 5}}"); const CollatorInterface* collator = nullptr; + std::map> arrayFilters; + std::set foundIdentifiers; UpdateObjectNode root; - auto result = UpdateObjectNode::parseAndMerge( - &root, modifiertable::ModifierType::MOD_SET, update["$set"]["$"], collator); + auto result = UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_SET, + update["$set"]["$"], + collator, + arrayFilters, + foundIdentifiers); ASSERT_NOT_OK(result); ASSERT_EQ(result.getStatus().code(), ErrorCodes::BadValue); ASSERT_EQ(result.getStatus().reason(), @@ -133,9 +190,15 @@ TEST(UpdateObjectNodeTest, PositionalElementFirstPositionFailsToParse) { TEST(UpdateObjectNodeTest, PushFailsToParse) { auto update = fromjson("{$push: {a: 5}}"); const CollatorInterface* collator = nullptr; + std::map> arrayFilters; + std::set foundIdentifiers; UpdateObjectNode root; - auto result = UpdateObjectNode::parseAndMerge( - &root, modifiertable::ModifierType::MOD_PUSH, update["$push"]["a"], collator); + auto result = UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_PUSH, + update["$push"]["a"], + collator, + arrayFilters, + foundIdentifiers); ASSERT_NOT_OK(result); ASSERT_EQ(result.getStatus().code(), ErrorCodes::FailedToParse); ASSERT_EQ(result.getStatus().reason(), "Cannot construct modifier of type 10"); @@ -144,11 +207,21 @@ TEST(UpdateObjectNodeTest, PushFailsToParse) { TEST(UpdateObjectNodeTest, TwoModifiersOnSameFieldFailToParse) { auto update = fromjson("{$set: {a: 5}}"); const CollatorInterface* collator = nullptr; + std::map> arrayFilters; + std::set foundIdentifiers; UpdateObjectNode root; - ASSERT_OK(UpdateObjectNode::parseAndMerge( - &root, modifiertable::ModifierType::MOD_SET, update["$set"]["a"], collator)); - auto result = UpdateObjectNode::parseAndMerge( - &root, modifiertable::ModifierType::MOD_SET, update["$set"]["a"], collator); + ASSERT_OK(UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_SET, + update["$set"]["a"], + collator, + arrayFilters, + foundIdentifiers)); + auto result = UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_SET, + update["$set"]["a"], + collator, + arrayFilters, + foundIdentifiers); ASSERT_NOT_OK(result); ASSERT_EQ(result.getStatus().code(), ErrorCodes::ConflictingUpdateOperators); ASSERT_EQ(result.getStatus().reason(), "Updating the path 'a' would create a conflict at 'a'"); @@ -157,21 +230,41 @@ TEST(UpdateObjectNodeTest, TwoModifiersOnSameFieldFailToParse) { TEST(UpdateObjectNodeTest, TwoModifiersOnDifferentFieldsParseSuccessfully) { auto update = fromjson("{$set: {a: 5, b: 6}}"); const CollatorInterface* collator = nullptr; + std::map> arrayFilters; + std::set foundIdentifiers; UpdateObjectNode root; - ASSERT_OK(UpdateObjectNode::parseAndMerge( - &root, modifiertable::ModifierType::MOD_SET, update["$set"]["a"], collator)); - ASSERT_OK(UpdateObjectNode::parseAndMerge( - &root, modifiertable::ModifierType::MOD_SET, update["$set"]["b"], collator)); + ASSERT_OK(UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_SET, + update["$set"]["a"], + collator, + arrayFilters, + foundIdentifiers)); + ASSERT_OK(UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_SET, + update["$set"]["b"], + collator, + arrayFilters, + foundIdentifiers)); } TEST(UpdateObjectNodeTest, TwoModifiersWithSameDottedPathFailToParse) { auto update = fromjson("{$set: {'a.b': 5}}"); const CollatorInterface* collator = nullptr; + std::map> arrayFilters; + std::set foundIdentifiers; UpdateObjectNode root; - ASSERT_OK(UpdateObjectNode::parseAndMerge( - &root, modifiertable::ModifierType::MOD_SET, update["$set"]["a.b"], collator)); - auto result = UpdateObjectNode::parseAndMerge( - &root, modifiertable::ModifierType::MOD_SET, update["$set"]["a.b"], collator); + ASSERT_OK(UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_SET, + update["$set"]["a.b"], + collator, + arrayFilters, + foundIdentifiers)); + auto result = UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_SET, + update["$set"]["a.b"], + collator, + arrayFilters, + foundIdentifiers); ASSERT_NOT_OK(result); ASSERT_EQ(result.getStatus().code(), ErrorCodes::ConflictingUpdateOperators); ASSERT_EQ(result.getStatus().reason(), @@ -181,11 +274,21 @@ TEST(UpdateObjectNodeTest, TwoModifiersWithSameDottedPathFailToParse) { TEST(UpdateObjectNodeTest, FirstModifierPrefixOfSecondFailToParse) { auto update = fromjson("{$set: {a: 5, 'a.b': 6}}"); const CollatorInterface* collator = nullptr; + std::map> arrayFilters; + std::set foundIdentifiers; UpdateObjectNode root; - ASSERT_OK(UpdateObjectNode::parseAndMerge( - &root, modifiertable::ModifierType::MOD_SET, update["$set"]["a"], collator)); - auto result = UpdateObjectNode::parseAndMerge( - &root, modifiertable::ModifierType::MOD_SET, update["$set"]["a.b"], collator); + ASSERT_OK(UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_SET, + update["$set"]["a"], + collator, + arrayFilters, + foundIdentifiers)); + auto result = UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_SET, + update["$set"]["a.b"], + collator, + arrayFilters, + foundIdentifiers); ASSERT_NOT_OK(result); ASSERT_EQ(result.getStatus().code(), ErrorCodes::ConflictingUpdateOperators); ASSERT_EQ(result.getStatus().reason(), @@ -195,11 +298,21 @@ TEST(UpdateObjectNodeTest, FirstModifierPrefixOfSecondFailToParse) { TEST(UpdateObjectNodeTest, FirstModifierDottedPrefixOfSecondFailsToParse) { auto update = fromjson("{$set: {'a.b': 5, 'a.b.c': 6}}"); const CollatorInterface* collator = nullptr; + std::map> arrayFilters; + std::set foundIdentifiers; UpdateObjectNode root; - ASSERT_OK(UpdateObjectNode::parseAndMerge( - &root, modifiertable::ModifierType::MOD_SET, update["$set"]["a.b"], collator)); - auto result = UpdateObjectNode::parseAndMerge( - &root, modifiertable::ModifierType::MOD_SET, update["$set"]["a.b.c"], collator); + ASSERT_OK(UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_SET, + update["$set"]["a.b"], + collator, + arrayFilters, + foundIdentifiers)); + auto result = UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_SET, + update["$set"]["a.b.c"], + collator, + arrayFilters, + foundIdentifiers); ASSERT_NOT_OK(result); ASSERT_EQ(result.getStatus().code(), ErrorCodes::ConflictingUpdateOperators); ASSERT_EQ(result.getStatus().reason(), @@ -209,11 +322,21 @@ TEST(UpdateObjectNodeTest, FirstModifierDottedPrefixOfSecondFailsToParse) { TEST(UpdateObjectNodeTest, SecondModifierPrefixOfFirstFailsToParse) { auto update = fromjson("{$set: {'a.b': 5, a: 6}}"); const CollatorInterface* collator = nullptr; + std::map> arrayFilters; + std::set foundIdentifiers; UpdateObjectNode root; - ASSERT_OK(UpdateObjectNode::parseAndMerge( - &root, modifiertable::ModifierType::MOD_SET, update["$set"]["a.b"], collator)); - auto result = UpdateObjectNode::parseAndMerge( - &root, modifiertable::ModifierType::MOD_SET, update["$set"]["a"], collator); + ASSERT_OK(UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_SET, + update["$set"]["a.b"], + collator, + arrayFilters, + foundIdentifiers)); + auto result = UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_SET, + update["$set"]["a"], + collator, + arrayFilters, + foundIdentifiers); ASSERT_NOT_OK(result); ASSERT_EQ(result.getStatus().code(), ErrorCodes::ConflictingUpdateOperators); ASSERT_EQ(result.getStatus().reason(), "Updating the path 'a' would create a conflict at 'a'"); @@ -222,11 +345,21 @@ TEST(UpdateObjectNodeTest, SecondModifierPrefixOfFirstFailsToParse) { TEST(UpdateObjectNodeTest, SecondModifierDottedPrefixOfFirstFailsToParse) { auto update = fromjson("{$set: {'a.b.c': 5, 'a.b': 6}}"); const CollatorInterface* collator = nullptr; + std::map> arrayFilters; + std::set foundIdentifiers; UpdateObjectNode root; - ASSERT_OK(UpdateObjectNode::parseAndMerge( - &root, modifiertable::ModifierType::MOD_SET, update["$set"]["a.b.c"], collator)); - auto result = UpdateObjectNode::parseAndMerge( - &root, modifiertable::ModifierType::MOD_SET, update["$set"]["a.b"], collator); + ASSERT_OK(UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_SET, + update["$set"]["a.b.c"], + collator, + arrayFilters, + foundIdentifiers)); + auto result = UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_SET, + update["$set"]["a.b"], + collator, + arrayFilters, + foundIdentifiers); ASSERT_NOT_OK(result); ASSERT_EQ(result.getStatus().code(), ErrorCodes::ConflictingUpdateOperators); ASSERT_EQ(result.getStatus().reason(), @@ -236,41 +369,81 @@ TEST(UpdateObjectNodeTest, SecondModifierDottedPrefixOfFirstFailsToParse) { TEST(UpdateObjectNodeTest, ModifiersWithCommonPrefixParseSuccessfully) { auto update = fromjson("{$set: {'a.b': 5, 'a.c': 6}}"); const CollatorInterface* collator = nullptr; + std::map> arrayFilters; + std::set foundIdentifiers; UpdateObjectNode root; - ASSERT_OK(UpdateObjectNode::parseAndMerge( - &root, modifiertable::ModifierType::MOD_SET, update["$set"]["a.b"], collator)); - ASSERT_OK(UpdateObjectNode::parseAndMerge( - &root, modifiertable::ModifierType::MOD_SET, update["$set"]["a.c"], collator)); + ASSERT_OK(UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_SET, + update["$set"]["a.b"], + collator, + arrayFilters, + foundIdentifiers)); + ASSERT_OK(UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_SET, + update["$set"]["a.c"], + collator, + arrayFilters, + foundIdentifiers)); } TEST(UpdateObjectNodeTest, ModifiersWithCommonDottedPrefixParseSuccessfully) { auto update = fromjson("{$set: {'a.b.c': 5, 'a.b.d': 6}}"); const CollatorInterface* collator = nullptr; + std::map> arrayFilters; + std::set foundIdentifiers; UpdateObjectNode root; - ASSERT_OK(UpdateObjectNode::parseAndMerge( - &root, modifiertable::ModifierType::MOD_SET, update["$set"]["a.b.c"], collator)); - ASSERT_OK(UpdateObjectNode::parseAndMerge( - &root, modifiertable::ModifierType::MOD_SET, update["$set"]["a.b.d"], collator)); + ASSERT_OK(UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_SET, + update["$set"]["a.b.c"], + collator, + arrayFilters, + foundIdentifiers)); + ASSERT_OK(UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_SET, + update["$set"]["a.b.d"], + collator, + arrayFilters, + foundIdentifiers)); } TEST(UpdateObjectNodeTest, ModifiersWithCommonPrefixDottedSuffixParseSuccessfully) { auto update = fromjson("{$set: {'a.b.c': 5, 'a.d.e': 6}}"); const CollatorInterface* collator = nullptr; + std::map> arrayFilters; + std::set foundIdentifiers; UpdateObjectNode root; - ASSERT_OK(UpdateObjectNode::parseAndMerge( - &root, modifiertable::ModifierType::MOD_SET, update["$set"]["a.b.c"], collator)); - ASSERT_OK(UpdateObjectNode::parseAndMerge( - &root, modifiertable::ModifierType::MOD_SET, update["$set"]["a.d.e"], collator)); + ASSERT_OK(UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_SET, + update["$set"]["a.b.c"], + collator, + arrayFilters, + foundIdentifiers)); + ASSERT_OK(UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_SET, + update["$set"]["a.d.e"], + collator, + arrayFilters, + foundIdentifiers)); } TEST(UpdateObjectNodeTest, TwoModifiersOnSamePositionalFieldFailToParse) { auto update = fromjson("{$set: {'a.$': 5}}"); const CollatorInterface* collator = nullptr; + std::map> arrayFilters; + std::set foundIdentifiers; UpdateObjectNode root; - ASSERT_OK(UpdateObjectNode::parseAndMerge( - &root, modifiertable::ModifierType::MOD_SET, update["$set"]["a.$"], collator)); - auto result = UpdateObjectNode::parseAndMerge( - &root, modifiertable::ModifierType::MOD_SET, update["$set"]["a.$"], collator); + ASSERT_OK(UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_SET, + update["$set"]["a.$"], + collator, + arrayFilters, + foundIdentifiers)); + auto result = UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_SET, + update["$set"]["a.$"], + collator, + arrayFilters, + foundIdentifiers); ASSERT_NOT_OK(result); ASSERT_EQ(result.getStatus().code(), ErrorCodes::ConflictingUpdateOperators); ASSERT_EQ(result.getStatus().reason(), @@ -280,31 +453,61 @@ TEST(UpdateObjectNodeTest, TwoModifiersOnSamePositionalFieldFailToParse) { TEST(UpdateObjectNodeTest, PositionalFieldsWithDifferentPrefixesParseSuccessfully) { auto update = fromjson("{$set: {'a.$': 5, 'b.$': 6}}"); const CollatorInterface* collator = nullptr; + std::map> arrayFilters; + std::set foundIdentifiers; UpdateObjectNode root; - ASSERT_OK(UpdateObjectNode::parseAndMerge( - &root, modifiertable::ModifierType::MOD_SET, update["$set"]["a.$"], collator)); - ASSERT_OK(UpdateObjectNode::parseAndMerge( - &root, modifiertable::ModifierType::MOD_SET, update["$set"]["b.$"], collator)); + ASSERT_OK(UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_SET, + update["$set"]["a.$"], + collator, + arrayFilters, + foundIdentifiers)); + ASSERT_OK(UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_SET, + update["$set"]["b.$"], + collator, + arrayFilters, + foundIdentifiers)); } TEST(UpdateObjectNodeTest, PositionalAndNonpositionalFieldWithCommonPrefixParseSuccessfully) { auto update = fromjson("{$set: {'a.$': 5, 'a.0': 6}}"); const CollatorInterface* collator = nullptr; + std::map> arrayFilters; + std::set foundIdentifiers; UpdateObjectNode root; - ASSERT_OK(UpdateObjectNode::parseAndMerge( - &root, modifiertable::ModifierType::MOD_SET, update["$set"]["a.$"], collator)); - ASSERT_OK(UpdateObjectNode::parseAndMerge( - &root, modifiertable::ModifierType::MOD_SET, update["$set"]["a.0"], collator)); + ASSERT_OK(UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_SET, + update["$set"]["a.$"], + collator, + arrayFilters, + foundIdentifiers)); + ASSERT_OK(UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_SET, + update["$set"]["a.0"], + collator, + arrayFilters, + foundIdentifiers)); } TEST(UpdateObjectNodeTest, TwoModifiersWithSamePositionalDottedPathFailToParse) { auto update = fromjson("{$set: {'a.$.b': 5}}"); const CollatorInterface* collator = nullptr; + std::map> arrayFilters; + std::set foundIdentifiers; UpdateObjectNode root; - ASSERT_OK(UpdateObjectNode::parseAndMerge( - &root, modifiertable::ModifierType::MOD_SET, update["$set"]["a.$.b"], collator)); - auto result = UpdateObjectNode::parseAndMerge( - &root, modifiertable::ModifierType::MOD_SET, update["$set"]["a.$.b"], collator); + ASSERT_OK(UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_SET, + update["$set"]["a.$.b"], + collator, + arrayFilters, + foundIdentifiers)); + auto result = UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_SET, + update["$set"]["a.$.b"], + collator, + arrayFilters, + foundIdentifiers); ASSERT_NOT_OK(result); ASSERT_EQ(result.getStatus().code(), ErrorCodes::ConflictingUpdateOperators); ASSERT_EQ(result.getStatus().reason(), @@ -314,11 +517,21 @@ TEST(UpdateObjectNodeTest, TwoModifiersWithSamePositionalDottedPathFailToParse) TEST(UpdateObjectNodeTest, FirstModifierPositionalPrefixOfSecondFailsToParse) { auto update = fromjson("{$set: {'a.$': 5, 'a.$.b': 6}}"); const CollatorInterface* collator = nullptr; + std::map> arrayFilters; + std::set foundIdentifiers; UpdateObjectNode root; - ASSERT_OK(UpdateObjectNode::parseAndMerge( - &root, modifiertable::ModifierType::MOD_SET, update["$set"]["a.$"], collator)); - auto result = UpdateObjectNode::parseAndMerge( - &root, modifiertable::ModifierType::MOD_SET, update["$set"]["a.$.b"], collator); + ASSERT_OK(UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_SET, + update["$set"]["a.$"], + collator, + arrayFilters, + foundIdentifiers)); + auto result = UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_SET, + update["$set"]["a.$.b"], + collator, + arrayFilters, + foundIdentifiers); ASSERT_NOT_OK(result); ASSERT_EQ(result.getStatus().code(), ErrorCodes::ConflictingUpdateOperators); ASSERT_EQ(result.getStatus().reason(), @@ -328,11 +541,21 @@ TEST(UpdateObjectNodeTest, FirstModifierPositionalPrefixOfSecondFailsToParse) { TEST(UpdateObjectNodeTest, SecondModifierPositionalPrefixOfFirstFailsToParse) { auto update = fromjson("{$set: {'a.$.b': 5, 'a.$': 6}}"); const CollatorInterface* collator = nullptr; + std::map> arrayFilters; + std::set foundIdentifiers; UpdateObjectNode root; - ASSERT_OK(UpdateObjectNode::parseAndMerge( - &root, modifiertable::ModifierType::MOD_SET, update["$set"]["a.$.b"], collator)); - auto result = UpdateObjectNode::parseAndMerge( - &root, modifiertable::ModifierType::MOD_SET, update["$set"]["a.$"], collator); + ASSERT_OK(UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_SET, + update["$set"]["a.$.b"], + collator, + arrayFilters, + foundIdentifiers)); + auto result = UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_SET, + update["$set"]["a.$"], + collator, + arrayFilters, + foundIdentifiers); ASSERT_NOT_OK(result); ASSERT_EQ(result.getStatus().code(), ErrorCodes::ConflictingUpdateOperators); ASSERT_EQ(result.getStatus().reason(), @@ -342,37 +565,460 @@ TEST(UpdateObjectNodeTest, SecondModifierPositionalPrefixOfFirstFailsToParse) { TEST(UpdateObjectNodeTest, FirstModifierFieldPrefixOfSecondParsesSuccessfully) { auto update = fromjson("{$set: {'a': 5, 'ab': 6}}"); const CollatorInterface* collator = nullptr; + std::map> arrayFilters; + std::set foundIdentifiers; UpdateObjectNode root; - ASSERT_OK(UpdateObjectNode::parseAndMerge( - &root, modifiertable::ModifierType::MOD_SET, update["$set"]["a"], collator)); - ASSERT_OK(UpdateObjectNode::parseAndMerge( - &root, modifiertable::ModifierType::MOD_SET, update["$set"]["ab"], collator)); + ASSERT_OK(UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_SET, + update["$set"]["a"], + collator, + arrayFilters, + foundIdentifiers)); + ASSERT_OK(UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_SET, + update["$set"]["ab"], + collator, + arrayFilters, + foundIdentifiers)); } TEST(UpdateObjectNodeTest, SecondModifierFieldPrefixOfSecondParsesSuccessfully) { auto update = fromjson("{$set: {'ab': 5, 'a': 6}}"); const CollatorInterface* collator = nullptr; + std::map> arrayFilters; + std::set foundIdentifiers; UpdateObjectNode root; - ASSERT_OK(UpdateObjectNode::parseAndMerge( - &root, modifiertable::ModifierType::MOD_SET, update["$set"]["ab"], collator)); - ASSERT_OK(UpdateObjectNode::parseAndMerge( - &root, modifiertable::ModifierType::MOD_SET, update["$set"]["a"], collator)); + ASSERT_OK(UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_SET, + update["$set"]["ab"], + collator, + arrayFilters, + foundIdentifiers)); + ASSERT_OK(UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_SET, + update["$set"]["a"], + collator, + arrayFilters, + foundIdentifiers)); +} + +TEST(UpdateObjectNodeTest, IdentifierWithoutArrayFilterFailsToParse) { + auto update = fromjson("{$set: {'a.$[i]': 5}}"); + const CollatorInterface* collator = nullptr; + std::map> arrayFilters; + std::set foundIdentifiers; + UpdateObjectNode root; + auto result = UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_SET, + update["$set"]["a.$[i]"], + collator, + arrayFilters, + foundIdentifiers); + ASSERT_NOT_OK(result); + ASSERT_EQ(result.getStatus().code(), ErrorCodes::BadValue); + ASSERT_EQ(result.getStatus().reason(), + "No array filter found for identifier 'i' in path 'a.$[i]'"); +} + +TEST(UpdateObjectNodeTest, IdentifierInMiddleOfPathWithoutArrayFilterFailsToParse) { + auto update = fromjson("{$set: {'a.$[i].b': 5}}"); + const CollatorInterface* collator = nullptr; + std::map> arrayFilters; + std::set foundIdentifiers; + UpdateObjectNode root; + auto result = UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_SET, + update["$set"]["a.$[i].b"], + collator, + arrayFilters, + foundIdentifiers); + ASSERT_NOT_OK(result); + ASSERT_EQ(result.getStatus().code(), ErrorCodes::BadValue); + ASSERT_EQ(result.getStatus().reason(), + "No array filter found for identifier 'i' in path 'a.$[i].b'"); +} + +TEST(UpdateObjectNodeTest, EmptyIdentifierParsesSuccessfully) { + auto update = fromjson("{$set: {'a.$[]': 5}}"); + const CollatorInterface* collator = nullptr; + std::map> arrayFilters; + std::set foundIdentifiers; + UpdateObjectNode root; + ASSERT_OK(UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_SET, + update["$set"]["a.$[]"], + collator, + arrayFilters, + foundIdentifiers)); + ASSERT_TRUE(foundIdentifiers.empty()); +} + +TEST(UpdateObjectNodeTest, EmptyIdentifierInMiddleOfPathParsesSuccessfully) { + auto update = fromjson("{$set: {'a.$[].b': 5}}"); + const CollatorInterface* collator = nullptr; + std::map> arrayFilters; + std::set foundIdentifiers; + UpdateObjectNode root; + ASSERT_OK(UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_SET, + update["$set"]["a.$[].b"], + collator, + arrayFilters, + foundIdentifiers)); + ASSERT_TRUE(foundIdentifiers.empty()); +} + +TEST(UpdateObjectNodeTest, IdentifierWithArrayFilterParsesSuccessfully) { + auto update = fromjson("{$set: {'a.$[i]': 5}}"); + auto arrayFilter = fromjson("{i: 0}"); + const CollatorInterface* collator = nullptr; + std::map> arrayFilters; + arrayFilters["i"] = + uassertStatusOK(ArrayFilter::parse(arrayFilter, ExtensionsCallbackNoop(), collator)); + std::set foundIdentifiers; + UpdateObjectNode root; + ASSERT_OK(UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_SET, + update["$set"]["a.$[i]"], + collator, + arrayFilters, + foundIdentifiers)); + ASSERT_EQ(foundIdentifiers.size(), 1U); + ASSERT_TRUE(foundIdentifiers.find("i") != foundIdentifiers.end()); +} + +TEST(UpdateObjectNodeTest, IdentifierWithArrayFilterInMiddleOfPathParsesSuccessfully) { + auto update = fromjson("{$set: {'a.$[i].b': 5}}"); + auto arrayFilter = fromjson("{i: 0}"); + const CollatorInterface* collator = nullptr; + std::map> arrayFilters; + arrayFilters["i"] = + uassertStatusOK(ArrayFilter::parse(arrayFilter, ExtensionsCallbackNoop(), collator)); + std::set foundIdentifiers; + UpdateObjectNode root; + ASSERT_OK(UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_SET, + update["$set"]["a.$[i].b"], + collator, + arrayFilters, + foundIdentifiers)); + ASSERT_EQ(foundIdentifiers.size(), 1U); + ASSERT_TRUE(foundIdentifiers.find("i") != foundIdentifiers.end()); +} + +TEST(UpdateObjectNodeTest, IdentifierInFirstPositionFailsToParse) { + auto update = fromjson("{$set: {'$[i]': 5}}"); + auto arrayFilter = fromjson("{i: 0}"); + const CollatorInterface* collator = nullptr; + std::map> arrayFilters; + arrayFilters["i"] = + uassertStatusOK(ArrayFilter::parse(arrayFilter, ExtensionsCallbackNoop(), collator)); + std::set foundIdentifiers; + UpdateObjectNode root; + auto result = UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_SET, + update["$set"]["$[i]"], + collator, + arrayFilters, + foundIdentifiers); + ASSERT_NOT_OK(result); + ASSERT_EQ(result.getStatus().code(), ErrorCodes::BadValue); + ASSERT_EQ(result.getStatus().reason(), + "Cannot have array filter identifier (i.e. '$[]') element in the first position " + "in path '$[i]'"); +} + +TEST(UpdateObjectNodeTest, IdentifierInFirstPositionWithSuffixFailsToParse) { + auto update = fromjson("{$set: {'$[i].a': 5}}"); + auto arrayFilter = fromjson("{i: 0}"); + const CollatorInterface* collator = nullptr; + std::map> arrayFilters; + arrayFilters["i"] = + uassertStatusOK(ArrayFilter::parse(arrayFilter, ExtensionsCallbackNoop(), collator)); + std::set foundIdentifiers; + UpdateObjectNode root; + auto result = UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_SET, + update["$set"]["$[i].a"], + collator, + arrayFilters, + foundIdentifiers); + ASSERT_NOT_OK(result); + ASSERT_EQ(result.getStatus().code(), ErrorCodes::BadValue); + ASSERT_EQ(result.getStatus().reason(), + "Cannot have array filter identifier (i.e. '$[]') element in the first position " + "in path '$[i].a'"); +} + +TEST(UpdateObjectNodeTest, CreateObjectNodeInSamePositionAsArrayNodeFailsToParse) { + auto update = fromjson("{$set: {'a.$[i]': 5, 'a.0': 6}}"); + auto arrayFilter = fromjson("{i: 0}"); + const CollatorInterface* collator = nullptr; + std::map> arrayFilters; + arrayFilters["i"] = + uassertStatusOK(ArrayFilter::parse(arrayFilter, ExtensionsCallbackNoop(), collator)); + std::set foundIdentifiers; + UpdateObjectNode root; + ASSERT_OK(UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_SET, + update["$set"]["a.$[i]"], + collator, + arrayFilters, + foundIdentifiers)); + ASSERT_EQ(foundIdentifiers.size(), 1U); + ASSERT_TRUE(foundIdentifiers.find("i") != foundIdentifiers.end()); + auto result = UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_SET, + update["$set"]["a.0"], + collator, + arrayFilters, + foundIdentifiers); + ASSERT_NOT_OK(result); + ASSERT_EQ(result.getStatus().code(), ErrorCodes::ConflictingUpdateOperators); + ASSERT_EQ(result.getStatus().reason(), + "Updating the path 'a.0' would create a conflict at 'a'"); +} + +TEST(UpdateObjectNodeTest, CreateArrayNodeInSamePositionAsObjectNodeFailsToParse) { + auto update = fromjson("{$set: {'a.0': 5, 'a.$[i]': 6}}"); + auto arrayFilter = fromjson("{i: 0}"); + const CollatorInterface* collator = nullptr; + std::map> arrayFilters; + arrayFilters["i"] = + uassertStatusOK(ArrayFilter::parse(arrayFilter, ExtensionsCallbackNoop(), collator)); + std::set foundIdentifiers; + UpdateObjectNode root; + ASSERT_OK(UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_SET, + update["$set"]["a.0"], + collator, + arrayFilters, + foundIdentifiers)); + auto result = UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_SET, + update["$set"]["a.$[i]"], + collator, + arrayFilters, + foundIdentifiers); + ASSERT_NOT_OK(result); + ASSERT_EQ(result.getStatus().code(), ErrorCodes::ConflictingUpdateOperators); + ASSERT_EQ(result.getStatus().reason(), + "Updating the path 'a.$[i]' would create a conflict at 'a'"); +} + +TEST(UpdateObjectNodeTest, CreateLeafNodeInSamePositionAsArrayNodeFailsToParse) { + auto update = fromjson("{$set: {'a.$[i]': 5, a: 6}}"); + auto arrayFilter = fromjson("{i: 0}"); + const CollatorInterface* collator = nullptr; + std::map> arrayFilters; + arrayFilters["i"] = + uassertStatusOK(ArrayFilter::parse(arrayFilter, ExtensionsCallbackNoop(), collator)); + std::set foundIdentifiers; + UpdateObjectNode root; + ASSERT_OK(UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_SET, + update["$set"]["a.$[i]"], + collator, + arrayFilters, + foundIdentifiers)); + ASSERT_EQ(foundIdentifiers.size(), 1U); + ASSERT_TRUE(foundIdentifiers.find("i") != foundIdentifiers.end()); + auto result = UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_SET, + update["$set"]["a"], + collator, + arrayFilters, + foundIdentifiers); + ASSERT_NOT_OK(result); + ASSERT_EQ(result.getStatus().code(), ErrorCodes::ConflictingUpdateOperators); + ASSERT_EQ(result.getStatus().reason(), "Updating the path 'a' would create a conflict at 'a'"); +} + +TEST(UpdateObjectNodeTest, CreateArrayNodeInSamePositionAsLeafNodeFailsToParse) { + auto update = fromjson("{$set: {a: 5, 'a.$[i]': 6}}"); + auto arrayFilter = fromjson("{i: 0}"); + const CollatorInterface* collator = nullptr; + std::map> arrayFilters; + arrayFilters["i"] = + uassertStatusOK(ArrayFilter::parse(arrayFilter, ExtensionsCallbackNoop(), collator)); + std::set foundIdentifiers; + UpdateObjectNode root; + ASSERT_OK(UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_SET, + update["$set"]["a"], + collator, + arrayFilters, + foundIdentifiers)); + auto result = UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_SET, + update["$set"]["a.$[i]"], + collator, + arrayFilters, + foundIdentifiers); + ASSERT_NOT_OK(result); + ASSERT_EQ(result.getStatus().code(), ErrorCodes::ConflictingUpdateOperators); + ASSERT_EQ(result.getStatus().reason(), + "Updating the path 'a.$[i]' would create a conflict at 'a'"); +} + +TEST(UpdateObjectNodeTest, CreateTwoChildrenOfArrayNodeParsesSuccessfully) { + auto update = fromjson("{$set: {'a.$[i]': 5, 'a.$[j]': 6}}"); + auto arrayFilterI = fromjson("{i: 0}"); + auto arrayFilterJ = fromjson("{j: 0}"); + const CollatorInterface* collator = nullptr; + std::map> arrayFilters; + arrayFilters["i"] = + uassertStatusOK(ArrayFilter::parse(arrayFilterI, ExtensionsCallbackNoop(), collator)); + arrayFilters["j"] = + uassertStatusOK(ArrayFilter::parse(arrayFilterJ, ExtensionsCallbackNoop(), collator)); + std::set foundIdentifiers; + UpdateObjectNode root; + ASSERT_OK(UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_SET, + update["$set"]["a.$[i]"], + collator, + arrayFilters, + foundIdentifiers)); + ASSERT_EQ(foundIdentifiers.size(), 1U); + ASSERT_TRUE(foundIdentifiers.find("i") != foundIdentifiers.end()); + ASSERT_OK(UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_SET, + update["$set"]["a.$[j]"], + collator, + arrayFilters, + foundIdentifiers)); + ASSERT_EQ(foundIdentifiers.size(), 2U); + ASSERT_TRUE(foundIdentifiers.find("i") != foundIdentifiers.end()); + ASSERT_TRUE(foundIdentifiers.find("j") != foundIdentifiers.end()); +} + +TEST(UpdateObjectNodeTest, ConflictAtArrayNodeChildFailsToParse) { + auto update1 = fromjson("{$set: {'a.$[i]': 5}}"); + auto update2 = fromjson("{$set: {'a.$[i]': 6}}"); + auto arrayFilterI = fromjson("{i: 0}"); + const CollatorInterface* collator = nullptr; + std::map> arrayFilters; + arrayFilters["i"] = + uassertStatusOK(ArrayFilter::parse(arrayFilterI, ExtensionsCallbackNoop(), collator)); + std::set foundIdentifiers; + UpdateObjectNode root; + ASSERT_OK(UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_SET, + update1["$set"]["a.$[i]"], + collator, + arrayFilters, + foundIdentifiers)); + ASSERT_EQ(foundIdentifiers.size(), 1U); + ASSERT_TRUE(foundIdentifiers.find("i") != foundIdentifiers.end()); + auto result = UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_SET, + update2["$set"]["a.$[i]"], + collator, + arrayFilters, + foundIdentifiers); + ASSERT_NOT_OK(result); + ASSERT_EQ(result.getStatus().code(), ErrorCodes::ConflictingUpdateOperators); + ASSERT_EQ(result.getStatus().reason(), + "Updating the path 'a.$[i]' would create a conflict at 'a.$[i]'"); +} + +TEST(UpdateObjectNodeTest, ConflictThroughArrayNodeChildFailsToParse) { + auto update = fromjson("{$set: {'a.$[i].b': 5, 'a.$[i].b.c': 6}}"); + auto arrayFilterI = fromjson("{i: 0}"); + const CollatorInterface* collator = nullptr; + std::map> arrayFilters; + arrayFilters["i"] = + uassertStatusOK(ArrayFilter::parse(arrayFilterI, ExtensionsCallbackNoop(), collator)); + std::set foundIdentifiers; + UpdateObjectNode root; + ASSERT_OK(UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_SET, + update["$set"]["a.$[i].b"], + collator, + arrayFilters, + foundIdentifiers)); + ASSERT_EQ(foundIdentifiers.size(), 1U); + ASSERT_TRUE(foundIdentifiers.find("i") != foundIdentifiers.end()); + auto result = UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_SET, + update["$set"]["a.$[i].b.c"], + collator, + arrayFilters, + foundIdentifiers); + ASSERT_NOT_OK(result); + ASSERT_EQ(result.getStatus().code(), ErrorCodes::ConflictingUpdateOperators); + ASSERT_EQ(result.getStatus().reason(), + "Updating the path 'a.$[i].b.c' would create a conflict at 'a.$[i].b'"); +} + +TEST(UpdateObjectNodeTest, NoConflictDueToDifferentArrayNodeChildrenParsesSuccessfully) { + auto update = fromjson("{$set: {'a.$[i].b': 5, 'a.$[j].b.c': 6}}"); + auto arrayFilterI = fromjson("{i: 0}"); + auto arrayFilterJ = fromjson("{j: 0}"); + const CollatorInterface* collator = nullptr; + std::map> arrayFilters; + arrayFilters["i"] = + uassertStatusOK(ArrayFilter::parse(arrayFilterI, ExtensionsCallbackNoop(), collator)); + arrayFilters["j"] = + uassertStatusOK(ArrayFilter::parse(arrayFilterJ, ExtensionsCallbackNoop(), collator)); + std::set foundIdentifiers; + UpdateObjectNode root; + ASSERT_OK(UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_SET, + update["$set"]["a.$[i].b"], + collator, + arrayFilters, + foundIdentifiers)); + ASSERT_EQ(foundIdentifiers.size(), 1U); + ASSERT_TRUE(foundIdentifiers.find("i") != foundIdentifiers.end()); + ASSERT_OK(UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_SET, + update["$set"]["a.$[j].b.c"], + collator, + arrayFilters, + foundIdentifiers)); + ASSERT_EQ(foundIdentifiers.size(), 2U); + ASSERT_TRUE(foundIdentifiers.find("i") != foundIdentifiers.end()); + ASSERT_TRUE(foundIdentifiers.find("j") != foundIdentifiers.end()); +} + +TEST(UpdateObjectNodeTest, MultipleArrayNodesAlongPathParsesSuccessfully) { + auto update = fromjson("{$set: {'a.$[i].$[j].$[i]': 5}}"); + auto arrayFilterI = fromjson("{i: 0}"); + auto arrayFilterJ = fromjson("{j: 0}"); + const CollatorInterface* collator = nullptr; + std::map> arrayFilters; + arrayFilters["i"] = + uassertStatusOK(ArrayFilter::parse(arrayFilterI, ExtensionsCallbackNoop(), collator)); + arrayFilters["j"] = + uassertStatusOK(ArrayFilter::parse(arrayFilterJ, ExtensionsCallbackNoop(), collator)); + std::set foundIdentifiers; + UpdateObjectNode root; + ASSERT_OK(UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_SET, + update["$set"]["a.$[i].$[j].$[i]"], + collator, + arrayFilters, + foundIdentifiers)); + ASSERT_EQ(foundIdentifiers.size(), 2U); + ASSERT_TRUE(foundIdentifiers.find("i") != foundIdentifiers.end()); + ASSERT_TRUE(foundIdentifiers.find("j") != foundIdentifiers.end()); } /** * Used to test if the fields in an input UpdateObjectNode match an expected set of fields. */ static bool fieldsMatch(const std::vector& expectedFields, - const UpdateObjectNode& node) { + const UpdateInternalNode& node) { for (const std::string& fieldName : expectedFields) { if (!node.getChild(fieldName)) { return false; } } - // There are no expected fields that aren't in the UpdateObjectNode. There is no way to check - // if UpdateObjectNodes contains any fields that are not in the expected set, because the - // UpdateObjectNodes API does not expose its list of child fields in any way other than + // There are no expected fields that aren't in the UpdateInternalNode. There is no way to check + // if UpdateInternalNodes contains any fields that are not in the expected set, because the + // UpdateInternalNodes API does not expose its list of child fields in any way other than // getChild(). return true; } @@ -382,11 +1028,21 @@ TEST(UpdateObjectNodeTest, DistinctFieldsMergeCorrectly) { auto setUpdate2 = fromjson("{$set: {'ab': 6}}"); FieldRef fakeFieldRef("root"); const CollatorInterface* collator = nullptr; + std::map> arrayFilters; + std::set foundIdentifiers; UpdateObjectNode setRoot1, setRoot2; - ASSERT_OK(UpdateObjectNode::parseAndMerge( - &setRoot1, modifiertable::ModifierType::MOD_SET, setUpdate1["$set"]["a"], collator)); - ASSERT_OK(UpdateObjectNode::parseAndMerge( - &setRoot2, modifiertable::ModifierType::MOD_SET, setUpdate2["$set"]["ab"], collator)); + ASSERT_OK(UpdateObjectNode::parseAndMerge(&setRoot1, + modifiertable::ModifierType::MOD_SET, + setUpdate1["$set"]["a"], + collator, + arrayFilters, + foundIdentifiers)); + ASSERT_OK(UpdateObjectNode::parseAndMerge(&setRoot2, + modifiertable::ModifierType::MOD_SET, + setUpdate2["$set"]["ab"], + collator, + arrayFilters, + foundIdentifiers)); auto result = UpdateNode::createUpdateNodeByMerging(setRoot1, setRoot2, &fakeFieldRef); ASSERT_TRUE(result); @@ -402,11 +1058,21 @@ TEST(UpdateObjectNodeTest, NestedMergeSucceeds) { auto setUpdate2 = fromjson("{$set: {'a.d': 6}}"); FieldRef fakeFieldRef("root"); const CollatorInterface* collator = nullptr; + std::map> arrayFilters; + std::set foundIdentifiers; UpdateObjectNode setRoot1, setRoot2; - ASSERT_OK(UpdateObjectNode::parseAndMerge( - &setRoot1, modifiertable::ModifierType::MOD_SET, setUpdate1["$set"]["a.c"], collator)); - ASSERT_OK(UpdateObjectNode::parseAndMerge( - &setRoot2, modifiertable::ModifierType::MOD_SET, setUpdate2["$set"]["a.d"], collator)); + ASSERT_OK(UpdateObjectNode::parseAndMerge(&setRoot1, + modifiertable::ModifierType::MOD_SET, + setUpdate1["$set"]["a.c"], + collator, + arrayFilters, + foundIdentifiers)); + ASSERT_OK(UpdateObjectNode::parseAndMerge(&setRoot2, + modifiertable::ModifierType::MOD_SET, + setUpdate2["$set"]["a.d"], + collator, + arrayFilters, + foundIdentifiers)); auto result = UpdateNode::createUpdateNodeByMerging(setRoot1, setRoot2, &fakeFieldRef); ASSERT_TRUE(result); @@ -428,11 +1094,21 @@ TEST(UpdateObjectNodeTest, DoublyNestedMergeSucceeds) { auto setUpdate2 = fromjson("{$set: {'a.b.d': 6}}"); FieldRef fakeFieldRef("root"); const CollatorInterface* collator = nullptr; + std::map> arrayFilters; + std::set foundIdentifiers; UpdateObjectNode setRoot1, setRoot2; - ASSERT_OK(UpdateObjectNode::parseAndMerge( - &setRoot1, modifiertable::ModifierType::MOD_SET, setUpdate1["$set"]["a.b.c"], collator)); - ASSERT_OK(UpdateObjectNode::parseAndMerge( - &setRoot2, modifiertable::ModifierType::MOD_SET, setUpdate2["$set"]["a.b.d"], collator)); + ASSERT_OK(UpdateObjectNode::parseAndMerge(&setRoot1, + modifiertable::ModifierType::MOD_SET, + setUpdate1["$set"]["a.b.c"], + collator, + arrayFilters, + foundIdentifiers)); + ASSERT_OK(UpdateObjectNode::parseAndMerge(&setRoot2, + modifiertable::ModifierType::MOD_SET, + setUpdate2["$set"]["a.b.d"], + collator, + arrayFilters, + foundIdentifiers)); auto result = UpdateNode::createUpdateNodeByMerging(setRoot1, setRoot2, &fakeFieldRef); ASSERT_TRUE(result); @@ -460,11 +1136,21 @@ TEST(UpdateObjectNodeTest, FieldAndPositionalMergeCorrectly) { auto setUpdate2 = fromjson("{$set: {'a.$': 6}}"); FieldRef fakeFieldRef("root"); const CollatorInterface* collator = nullptr; + std::map> arrayFilters; + std::set foundIdentifiers; UpdateObjectNode setRoot1, setRoot2; - ASSERT_OK(UpdateObjectNode::parseAndMerge( - &setRoot1, modifiertable::ModifierType::MOD_SET, setUpdate1["$set"]["a.b"], collator)); - ASSERT_OK(UpdateObjectNode::parseAndMerge( - &setRoot2, modifiertable::ModifierType::MOD_SET, setUpdate2["$set"]["a.$"], collator)); + ASSERT_OK(UpdateObjectNode::parseAndMerge(&setRoot1, + modifiertable::ModifierType::MOD_SET, + setUpdate1["$set"]["a.b"], + collator, + arrayFilters, + foundIdentifiers)); + ASSERT_OK(UpdateObjectNode::parseAndMerge(&setRoot2, + modifiertable::ModifierType::MOD_SET, + setUpdate2["$set"]["a.$"], + collator, + arrayFilters, + foundIdentifiers)); auto result = UpdateNode::createUpdateNodeByMerging(setRoot1, setRoot2, &fakeFieldRef); ASSERT_TRUE(result); @@ -487,11 +1173,21 @@ TEST(UpdateObjectNodeTest, MergeThroughPositionalSucceeds) { auto setUpdate2 = fromjson("{$set: {'a.$.c': 6}}"); FieldRef fakeFieldRef("root"); const CollatorInterface* collator = nullptr; + std::map> arrayFilters; + std::set foundIdentifiers; UpdateObjectNode setRoot1, setRoot2; - ASSERT_OK(UpdateObjectNode::parseAndMerge( - &setRoot1, modifiertable::ModifierType::MOD_SET, setUpdate1["$set"]["a.$.b"], collator)); - ASSERT_OK(UpdateObjectNode::parseAndMerge( - &setRoot2, modifiertable::ModifierType::MOD_SET, setUpdate2["$set"]["a.$.c"], collator)); + ASSERT_OK(UpdateObjectNode::parseAndMerge(&setRoot1, + modifiertable::ModifierType::MOD_SET, + setUpdate1["$set"]["a.$.b"], + collator, + arrayFilters, + foundIdentifiers)); + ASSERT_OK(UpdateObjectNode::parseAndMerge(&setRoot2, + modifiertable::ModifierType::MOD_SET, + setUpdate2["$set"]["a.$.c"], + collator, + arrayFilters, + foundIdentifiers)); auto result = UpdateNode::createUpdateNodeByMerging(setRoot1, setRoot2, &fakeFieldRef); ASSERT_TRUE(result); @@ -519,11 +1215,21 @@ TEST(UpdateObjectNodeTest, TopLevelConflictFails) { auto setUpdate2 = fromjson("{$set: {'a': 6}}"); FieldRef fakeFieldRef("root"); const CollatorInterface* collator = nullptr; + std::map> arrayFilters; + std::set foundIdentifiers; UpdateObjectNode setRoot1, setRoot2; - ASSERT_OK(UpdateObjectNode::parseAndMerge( - &setRoot1, modifiertable::ModifierType::MOD_SET, setUpdate1["$set"]["a"], collator)); - ASSERT_OK(UpdateObjectNode::parseAndMerge( - &setRoot2, modifiertable::ModifierType::MOD_SET, setUpdate2["$set"]["a"], collator)); + ASSERT_OK(UpdateObjectNode::parseAndMerge(&setRoot1, + modifiertable::ModifierType::MOD_SET, + setUpdate1["$set"]["a"], + collator, + arrayFilters, + foundIdentifiers)); + ASSERT_OK(UpdateObjectNode::parseAndMerge(&setRoot2, + modifiertable::ModifierType::MOD_SET, + setUpdate2["$set"]["a"], + collator, + arrayFilters, + foundIdentifiers)); std::unique_ptr result; ASSERT_THROWS_CODE_AND_WHAT( @@ -538,11 +1244,21 @@ TEST(UpdateObjectNodeTest, NestedConflictFails) { auto setUpdate2 = fromjson("{$set: {'a.b': 6}}"); FieldRef fakeFieldRef("root"); const CollatorInterface* collator = nullptr; + std::map> arrayFilters; + std::set foundIdentifiers; UpdateObjectNode setRoot1, setRoot2; - ASSERT_OK(UpdateObjectNode::parseAndMerge( - &setRoot1, modifiertable::ModifierType::MOD_SET, setUpdate1["$set"]["a.b"], collator)); - ASSERT_OK(UpdateObjectNode::parseAndMerge( - &setRoot2, modifiertable::ModifierType::MOD_SET, setUpdate2["$set"]["a.b"], collator)); + ASSERT_OK(UpdateObjectNode::parseAndMerge(&setRoot1, + modifiertable::ModifierType::MOD_SET, + setUpdate1["$set"]["a.b"], + collator, + arrayFilters, + foundIdentifiers)); + ASSERT_OK(UpdateObjectNode::parseAndMerge(&setRoot2, + modifiertable::ModifierType::MOD_SET, + setUpdate2["$set"]["a.b"], + collator, + arrayFilters, + foundIdentifiers)); std::unique_ptr result; ASSERT_THROWS_CODE_AND_WHAT( @@ -557,11 +1273,21 @@ TEST(UpdateObjectNodeTest, LeftPrefixMergeFails) { auto setUpdate2 = fromjson("{$set: {'a.b.c': 6}}"); FieldRef fakeFieldRef("root"); const CollatorInterface* collator = nullptr; + std::map> arrayFilters; + std::set foundIdentifiers; UpdateObjectNode setRoot1, setRoot2; - ASSERT_OK(UpdateObjectNode::parseAndMerge( - &setRoot1, modifiertable::ModifierType::MOD_SET, setUpdate1["$set"]["a.b"], collator)); - ASSERT_OK(UpdateObjectNode::parseAndMerge( - &setRoot2, modifiertable::ModifierType::MOD_SET, setUpdate2["$set"]["a.b.c"], collator)); + ASSERT_OK(UpdateObjectNode::parseAndMerge(&setRoot1, + modifiertable::ModifierType::MOD_SET, + setUpdate1["$set"]["a.b"], + collator, + arrayFilters, + foundIdentifiers)); + ASSERT_OK(UpdateObjectNode::parseAndMerge(&setRoot2, + modifiertable::ModifierType::MOD_SET, + setUpdate2["$set"]["a.b.c"], + collator, + arrayFilters, + foundIdentifiers)); std::unique_ptr result; ASSERT_THROWS_CODE_AND_WHAT( @@ -576,11 +1302,21 @@ TEST(UpdateObjectNodeTest, RightPrefixMergeFails) { auto setUpdate2 = fromjson("{$set: {'a.b': 6}}"); FieldRef fakeFieldRef("root"); const CollatorInterface* collator = nullptr; + std::map> arrayFilters; + std::set foundIdentifiers; UpdateObjectNode setRoot1, setRoot2; - ASSERT_OK(UpdateObjectNode::parseAndMerge( - &setRoot1, modifiertable::ModifierType::MOD_SET, setUpdate1["$set"]["a.b.c"], collator)); - ASSERT_OK(UpdateObjectNode::parseAndMerge( - &setRoot2, modifiertable::ModifierType::MOD_SET, setUpdate2["$set"]["a.b"], collator)); + ASSERT_OK(UpdateObjectNode::parseAndMerge(&setRoot1, + modifiertable::ModifierType::MOD_SET, + setUpdate1["$set"]["a.b.c"], + collator, + arrayFilters, + foundIdentifiers)); + ASSERT_OK(UpdateObjectNode::parseAndMerge(&setRoot2, + modifiertable::ModifierType::MOD_SET, + setUpdate2["$set"]["a.b"], + collator, + arrayFilters, + foundIdentifiers)); std::unique_ptr result; ASSERT_THROWS_CODE_AND_WHAT( @@ -595,11 +1331,21 @@ TEST(UpdateObjectNodeTest, LeftPrefixMergeThroughPositionalFails) { auto setUpdate2 = fromjson("{$set: {'a.$.c.d': 6}}"); FieldRef fakeFieldRef("root"); const CollatorInterface* collator = nullptr; + std::map> arrayFilters; + std::set foundIdentifiers; UpdateObjectNode setRoot1, setRoot2; - ASSERT_OK(UpdateObjectNode::parseAndMerge( - &setRoot1, modifiertable::ModifierType::MOD_SET, setUpdate1["$set"]["a.$.c"], collator)); - ASSERT_OK(UpdateObjectNode::parseAndMerge( - &setRoot2, modifiertable::ModifierType::MOD_SET, setUpdate2["$set"]["a.$.c.d"], collator)); + ASSERT_OK(UpdateObjectNode::parseAndMerge(&setRoot1, + modifiertable::ModifierType::MOD_SET, + setUpdate1["$set"]["a.$.c"], + collator, + arrayFilters, + foundIdentifiers)); + ASSERT_OK(UpdateObjectNode::parseAndMerge(&setRoot2, + modifiertable::ModifierType::MOD_SET, + setUpdate2["$set"]["a.$.c.d"], + collator, + arrayFilters, + foundIdentifiers)); std::unique_ptr result; ASSERT_THROWS_CODE_AND_WHAT( @@ -614,11 +1360,21 @@ TEST(UpdateObjectNodeTest, RightPrefixMergeThroughPositionalFails) { auto setUpdate2 = fromjson("{$set: {'a.$.c': 6}}"); FieldRef fakeFieldRef("root"); const CollatorInterface* collator = nullptr; + std::map> arrayFilters; + std::set foundIdentifiers; UpdateObjectNode setRoot1, setRoot2; - ASSERT_OK(UpdateObjectNode::parseAndMerge( - &setRoot1, modifiertable::ModifierType::MOD_SET, setUpdate1["$set"]["a.$.c.d"], collator)); - ASSERT_OK(UpdateObjectNode::parseAndMerge( - &setRoot2, modifiertable::ModifierType::MOD_SET, setUpdate2["$set"]["a.$.c"], collator)); + ASSERT_OK(UpdateObjectNode::parseAndMerge(&setRoot1, + modifiertable::ModifierType::MOD_SET, + setUpdate1["$set"]["a.$.c.d"], + collator, + arrayFilters, + foundIdentifiers)); + ASSERT_OK(UpdateObjectNode::parseAndMerge(&setRoot2, + modifiertable::ModifierType::MOD_SET, + setUpdate2["$set"]["a.$.c"], + collator, + arrayFilters, + foundIdentifiers)); std::unique_ptr result; ASSERT_THROWS_CODE_AND_WHAT( @@ -633,11 +1389,21 @@ TEST(UpdateObjectNodeTest, MergeWithConflictingPositionalFails) { auto setUpdate2 = fromjson("{$set: {'a.$': 6}}"); FieldRef fakeFieldRef("root"); const CollatorInterface* collator = nullptr; + std::map> arrayFilters; + std::set foundIdentifiers; UpdateObjectNode setRoot1, setRoot2; - ASSERT_OK(UpdateObjectNode::parseAndMerge( - &setRoot1, modifiertable::ModifierType::MOD_SET, setUpdate1["$set"]["a.$"], collator)); - ASSERT_OK(UpdateObjectNode::parseAndMerge( - &setRoot2, modifiertable::ModifierType::MOD_SET, setUpdate2["$set"]["a.$"], collator)); + ASSERT_OK(UpdateObjectNode::parseAndMerge(&setRoot1, + modifiertable::ModifierType::MOD_SET, + setUpdate1["$set"]["a.$"], + collator, + arrayFilters, + foundIdentifiers)); + ASSERT_OK(UpdateObjectNode::parseAndMerge(&setRoot2, + modifiertable::ModifierType::MOD_SET, + setUpdate2["$set"]["a.$"], + collator, + arrayFilters, + foundIdentifiers)); std::unique_ptr result; ASSERT_THROWS_CODE_AND_WHAT( @@ -647,12 +1413,234 @@ TEST(UpdateObjectNodeTest, MergeWithConflictingPositionalFails) { "Update created a conflict at 'root.a.$'"); } +DEATH_TEST(UpdateObjectNodeTest, + MergingArrayNodesWithDifferentArrayFiltersFails, + "Invariant failure &leftNode._arrayFilters == &rightNode._arrayFilters") { + auto setUpdate1 = fromjson("{$set: {'a.$[i]': 5}}"); + auto setUpdate2 = fromjson("{$set: {'a.$[j]': 6}}"); + FieldRef fakeFieldRef("root"); + const CollatorInterface* collator = nullptr; + auto arrayFilterI = fromjson("{i: 0}"); + std::map> arrayFilters1; + arrayFilters1["i"] = + uassertStatusOK(ArrayFilter::parse(arrayFilterI, ExtensionsCallbackNoop(), collator)); + auto arrayFilterJ = fromjson("{j: 0}"); + std::map> arrayFilters2; + arrayFilters2["j"] = + uassertStatusOK(ArrayFilter::parse(arrayFilterJ, ExtensionsCallbackNoop(), collator)); + std::set foundIdentifiers; + UpdateObjectNode setRoot1, setRoot2; + ASSERT_OK(UpdateObjectNode::parseAndMerge(&setRoot1, + modifiertable::ModifierType::MOD_SET, + setUpdate1["$set"]["a.$[i]"], + collator, + arrayFilters1, + foundIdentifiers)); + ASSERT_OK(UpdateObjectNode::parseAndMerge(&setRoot2, + modifiertable::ModifierType::MOD_SET, + setUpdate2["$set"]["a.$[j]"], + collator, + arrayFilters2, + foundIdentifiers)); + + UpdateNode::createUpdateNodeByMerging(setRoot1, setRoot2, &fakeFieldRef); +} + +TEST(UpdateObjectNodeTest, MergingArrayNodeWithObjectNodeFails) { + auto setUpdate1 = fromjson("{$set: {'a.$[i]': 5}}"); + auto setUpdate2 = fromjson("{$set: {'a.b': 6}}"); + FieldRef fakeFieldRef("root"); + const CollatorInterface* collator = nullptr; + auto arrayFilter = fromjson("{i: 0}"); + std::map> arrayFilters; + arrayFilters["i"] = + uassertStatusOK(ArrayFilter::parse(arrayFilter, ExtensionsCallbackNoop(), collator)); + std::set foundIdentifiers; + UpdateObjectNode setRoot1, setRoot2; + ASSERT_OK(UpdateObjectNode::parseAndMerge(&setRoot1, + modifiertable::ModifierType::MOD_SET, + setUpdate1["$set"]["a.$[i]"], + collator, + arrayFilters, + foundIdentifiers)); + ASSERT_OK(UpdateObjectNode::parseAndMerge(&setRoot2, + modifiertable::ModifierType::MOD_SET, + setUpdate2["$set"]["a.b"], + collator, + arrayFilters, + foundIdentifiers)); + + std::unique_ptr result; + ASSERT_THROWS_CODE_AND_WHAT( + result = UpdateNode::createUpdateNodeByMerging(setRoot1, setRoot2, &fakeFieldRef), + UserException, + ErrorCodes::ConflictingUpdateOperators, + "Update created a conflict at 'root.a'"); +} + +TEST(UpdateObjectNodeTest, MergingArrayNodeWithLeafNodeFails) { + auto setUpdate1 = fromjson("{$set: {'a.$[i]': 5}}"); + auto setUpdate2 = fromjson("{$set: {'a': 6}}"); + FieldRef fakeFieldRef("root"); + const CollatorInterface* collator = nullptr; + auto arrayFilter = fromjson("{i: 0}"); + std::map> arrayFilters; + arrayFilters["i"] = + uassertStatusOK(ArrayFilter::parse(arrayFilter, ExtensionsCallbackNoop(), collator)); + std::set foundIdentifiers; + UpdateObjectNode setRoot1, setRoot2; + ASSERT_OK(UpdateObjectNode::parseAndMerge(&setRoot1, + modifiertable::ModifierType::MOD_SET, + setUpdate1["$set"]["a.$[i]"], + collator, + arrayFilters, + foundIdentifiers)); + ASSERT_OK(UpdateObjectNode::parseAndMerge(&setRoot2, + modifiertable::ModifierType::MOD_SET, + setUpdate2["$set"]["a"], + collator, + arrayFilters, + foundIdentifiers)); + + std::unique_ptr result; + ASSERT_THROWS_CODE_AND_WHAT( + result = UpdateNode::createUpdateNodeByMerging(setRoot1, setRoot2, &fakeFieldRef), + UserException, + ErrorCodes::ConflictingUpdateOperators, + "Update created a conflict at 'root.a'"); +} + +TEST(UpdateObjectNodeTest, MergingTwoArrayNodesSucceeds) { + auto setUpdate1 = fromjson("{$set: {'a.$[i]': 5}}"); + auto setUpdate2 = fromjson("{$set: {'a.$[j]': 6}}"); + FieldRef fakeFieldRef("root"); + const CollatorInterface* collator = nullptr; + auto arrayFilterI = fromjson("{i: 0}"); + auto arrayFilterJ = fromjson("{j: 0}"); + std::map> arrayFilters; + arrayFilters["i"] = + uassertStatusOK(ArrayFilter::parse(arrayFilterI, ExtensionsCallbackNoop(), collator)); + arrayFilters["j"] = + uassertStatusOK(ArrayFilter::parse(arrayFilterJ, ExtensionsCallbackNoop(), collator)); + std::set foundIdentifiers; + UpdateObjectNode setRoot1, setRoot2; + ASSERT_OK(UpdateObjectNode::parseAndMerge(&setRoot1, + modifiertable::ModifierType::MOD_SET, + setUpdate1["$set"]["a.$[i]"], + collator, + arrayFilters, + foundIdentifiers)); + ASSERT_OK(UpdateObjectNode::parseAndMerge(&setRoot2, + modifiertable::ModifierType::MOD_SET, + setUpdate2["$set"]["a.$[j]"], + collator, + arrayFilters, + foundIdentifiers)); + + auto result = UpdateNode::createUpdateNodeByMerging(setRoot1, setRoot2, &fakeFieldRef); + ASSERT_TRUE(result); + + ASSERT_TRUE(result->type == UpdateNode::Type::Object); + ASSERT_TRUE(typeid(*result) == typeid(UpdateObjectNode&)); + auto mergedRootNode = static_cast(result.get()); + ASSERT_TRUE(fieldsMatch({"a"}, *mergedRootNode)); + + ASSERT_TRUE(mergedRootNode->getChild("a")); + ASSERT_TRUE(mergedRootNode->getChild("a")->type == UpdateNode::Type::Array); + ASSERT_TRUE(typeid(*mergedRootNode->getChild("a")) == typeid(UpdateArrayNode&)); + auto aNode = static_cast(mergedRootNode->getChild("a")); + ASSERT_TRUE(fieldsMatch({"i", "j"}, *aNode)); +} + +TEST(UpdateObjectNodeTest, MergeConflictThroughArrayNodesFails) { + auto setUpdate1 = fromjson("{$set: {'a.$[i].b.c': 5}}"); + auto setUpdate2 = fromjson("{$set: {'a.$[i].b': 6}}"); + FieldRef fakeFieldRef("root"); + const CollatorInterface* collator = nullptr; + auto arrayFilter = fromjson("{i: 0}"); + std::map> arrayFilters; + arrayFilters["i"] = + uassertStatusOK(ArrayFilter::parse(arrayFilter, ExtensionsCallbackNoop(), collator)); + std::set foundIdentifiers; + UpdateObjectNode setRoot1, setRoot2; + ASSERT_OK(UpdateObjectNode::parseAndMerge(&setRoot1, + modifiertable::ModifierType::MOD_SET, + setUpdate1["$set"]["a.$[i].b.c"], + collator, + arrayFilters, + foundIdentifiers)); + ASSERT_OK(UpdateObjectNode::parseAndMerge(&setRoot2, + modifiertable::ModifierType::MOD_SET, + setUpdate2["$set"]["a.$[i].b"], + collator, + arrayFilters, + foundIdentifiers)); + + std::unique_ptr result; + ASSERT_THROWS_CODE_AND_WHAT( + result = UpdateNode::createUpdateNodeByMerging(setRoot1, setRoot2, &fakeFieldRef), + UserException, + ErrorCodes::ConflictingUpdateOperators, + "Update created a conflict at 'root.a.$[i].b'"); +} + +TEST(UpdateObjectNodeTest, NoMergeConflictThroughArrayNodesSucceeds) { + auto setUpdate1 = fromjson("{$set: {'a.$[i].b': 5}}"); + auto setUpdate2 = fromjson("{$set: {'a.$[i].c': 6}}"); + FieldRef fakeFieldRef("root"); + const CollatorInterface* collator = nullptr; + auto arrayFilter = fromjson("{i: 0}"); + std::map> arrayFilters; + arrayFilters["i"] = + uassertStatusOK(ArrayFilter::parse(arrayFilter, ExtensionsCallbackNoop(), collator)); + std::set foundIdentifiers; + UpdateObjectNode setRoot1, setRoot2; + ASSERT_OK(UpdateObjectNode::parseAndMerge(&setRoot1, + modifiertable::ModifierType::MOD_SET, + setUpdate1["$set"]["a.$[i].b"], + collator, + arrayFilters, + foundIdentifiers)); + ASSERT_OK(UpdateObjectNode::parseAndMerge(&setRoot2, + modifiertable::ModifierType::MOD_SET, + setUpdate2["$set"]["a.$[i].c"], + collator, + arrayFilters, + foundIdentifiers)); + + auto result = UpdateNode::createUpdateNodeByMerging(setRoot1, setRoot2, &fakeFieldRef); + ASSERT_TRUE(result); + + ASSERT_TRUE(result->type == UpdateNode::Type::Object); + ASSERT_TRUE(typeid(*result) == typeid(UpdateObjectNode&)); + auto mergedRootNode = static_cast(result.get()); + ASSERT_TRUE(fieldsMatch({"a"}, *mergedRootNode)); + + ASSERT_TRUE(mergedRootNode->getChild("a")); + ASSERT_TRUE(mergedRootNode->getChild("a")->type == UpdateNode::Type::Array); + ASSERT_TRUE(typeid(*mergedRootNode->getChild("a")) == typeid(UpdateArrayNode&)); + auto aNode = static_cast(mergedRootNode->getChild("a")); + ASSERT_TRUE(fieldsMatch({"i"}, *aNode)); + + ASSERT_TRUE(aNode->getChild("i")); + ASSERT_TRUE(aNode->getChild("i")->type == UpdateNode::Type::Object); + ASSERT_TRUE(typeid(*aNode->getChild("i")) == typeid(UpdateObjectNode&)); + auto iNode = static_cast(aNode->getChild("i")); + ASSERT_TRUE(fieldsMatch({"b", "c"}, *iNode)); +} + TEST(UpdateObjectNodeTest, ApplyCreateField) { auto setUpdate = fromjson("{$set: {b: 6}}"); const CollatorInterface* collator = nullptr; + std::map> arrayFilters; + std::set foundIdentifiers; UpdateObjectNode root; - ASSERT_OK(UpdateObjectNode::parseAndMerge( - &root, modifiertable::ModifierType::MOD_SET, setUpdate["$set"]["b"], collator)); + ASSERT_OK(UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_SET, + setUpdate["$set"]["b"], + collator, + arrayFilters, + foundIdentifiers)); Document doc(fromjson("{a: 5}")); FieldRef pathToCreate(""); @@ -684,9 +1672,15 @@ TEST(UpdateObjectNodeTest, ApplyCreateField) { TEST(UpdateObjectNodeTest, ApplyExistingField) { auto setUpdate = fromjson("{$set: {a: 6}}"); const CollatorInterface* collator = nullptr; + std::map> arrayFilters; + std::set foundIdentifiers; UpdateObjectNode root; - ASSERT_OK(UpdateObjectNode::parseAndMerge( - &root, modifiertable::ModifierType::MOD_SET, setUpdate["$set"]["a"], collator)); + ASSERT_OK(UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_SET, + setUpdate["$set"]["a"], + collator, + arrayFilters, + foundIdentifiers)); Document doc(fromjson("{a: 5}")); FieldRef pathToCreate(""); @@ -718,15 +1712,33 @@ TEST(UpdateObjectNodeTest, ApplyExistingField) { TEST(UpdateObjectNodeTest, ApplyExistingAndNonexistingFields) { auto setUpdate = fromjson("{$set: {a: 5, b: 6, c: 7, d: 8}}"); const CollatorInterface* collator = nullptr; + std::map> arrayFilters; + std::set foundIdentifiers; UpdateObjectNode root; - ASSERT_OK(UpdateObjectNode::parseAndMerge( - &root, modifiertable::ModifierType::MOD_SET, setUpdate["$set"]["a"], collator)); - ASSERT_OK(UpdateObjectNode::parseAndMerge( - &root, modifiertable::ModifierType::MOD_SET, setUpdate["$set"]["b"], collator)); - ASSERT_OK(UpdateObjectNode::parseAndMerge( - &root, modifiertable::ModifierType::MOD_SET, setUpdate["$set"]["c"], collator)); - ASSERT_OK(UpdateObjectNode::parseAndMerge( - &root, modifiertable::ModifierType::MOD_SET, setUpdate["$set"]["d"], collator)); + ASSERT_OK(UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_SET, + setUpdate["$set"]["a"], + collator, + arrayFilters, + foundIdentifiers)); + ASSERT_OK(UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_SET, + setUpdate["$set"]["b"], + collator, + arrayFilters, + foundIdentifiers)); + ASSERT_OK(UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_SET, + setUpdate["$set"]["c"], + collator, + arrayFilters, + foundIdentifiers)); + ASSERT_OK(UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_SET, + setUpdate["$set"]["d"], + collator, + arrayFilters, + foundIdentifiers)); Document doc(fromjson("{a: 0, c: 0}")); FieldRef pathToCreate(""); @@ -758,15 +1770,33 @@ TEST(UpdateObjectNodeTest, ApplyExistingAndNonexistingFields) { TEST(UpdateObjectNodeTest, ApplyExistingNestedPaths) { auto setUpdate = fromjson("{$set: {'a.b': 6, 'a.c': 7, 'b.d': 8, 'b.e': 9}}"); const CollatorInterface* collator = nullptr; + std::map> arrayFilters; + std::set foundIdentifiers; UpdateObjectNode root; - ASSERT_OK(UpdateObjectNode::parseAndMerge( - &root, modifiertable::ModifierType::MOD_SET, setUpdate["$set"]["a.b"], collator)); - ASSERT_OK(UpdateObjectNode::parseAndMerge( - &root, modifiertable::ModifierType::MOD_SET, setUpdate["$set"]["a.c"], collator)); - ASSERT_OK(UpdateObjectNode::parseAndMerge( - &root, modifiertable::ModifierType::MOD_SET, setUpdate["$set"]["b.d"], collator)); - ASSERT_OK(UpdateObjectNode::parseAndMerge( - &root, modifiertable::ModifierType::MOD_SET, setUpdate["$set"]["b.e"], collator)); + ASSERT_OK(UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_SET, + setUpdate["$set"]["a.b"], + collator, + arrayFilters, + foundIdentifiers)); + ASSERT_OK(UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_SET, + setUpdate["$set"]["a.c"], + collator, + arrayFilters, + foundIdentifiers)); + ASSERT_OK(UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_SET, + setUpdate["$set"]["b.d"], + collator, + arrayFilters, + foundIdentifiers)); + ASSERT_OK(UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_SET, + setUpdate["$set"]["b.e"], + collator, + arrayFilters, + foundIdentifiers)); Document doc(fromjson("{a: {b: 5, c: 5}, b: {d: 5, e: 5}}")); FieldRef pathToCreate(""); @@ -799,15 +1829,33 @@ TEST(UpdateObjectNodeTest, ApplyExistingNestedPaths) { TEST(UpdateObjectNodeTest, ApplyCreateNestedPaths) { auto setUpdate = fromjson("{$set: {'a.b': 6, 'a.c': 7, 'b.d': 8, 'b.e': 9}}"); const CollatorInterface* collator = nullptr; + std::map> arrayFilters; + std::set foundIdentifiers; UpdateObjectNode root; - ASSERT_OK(UpdateObjectNode::parseAndMerge( - &root, modifiertable::ModifierType::MOD_SET, setUpdate["$set"]["a.b"], collator)); - ASSERT_OK(UpdateObjectNode::parseAndMerge( - &root, modifiertable::ModifierType::MOD_SET, setUpdate["$set"]["a.c"], collator)); - ASSERT_OK(UpdateObjectNode::parseAndMerge( - &root, modifiertable::ModifierType::MOD_SET, setUpdate["$set"]["b.d"], collator)); - ASSERT_OK(UpdateObjectNode::parseAndMerge( - &root, modifiertable::ModifierType::MOD_SET, setUpdate["$set"]["b.e"], collator)); + ASSERT_OK(UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_SET, + setUpdate["$set"]["a.b"], + collator, + arrayFilters, + foundIdentifiers)); + ASSERT_OK(UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_SET, + setUpdate["$set"]["a.c"], + collator, + arrayFilters, + foundIdentifiers)); + ASSERT_OK(UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_SET, + setUpdate["$set"]["b.d"], + collator, + arrayFilters, + foundIdentifiers)); + ASSERT_OK(UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_SET, + setUpdate["$set"]["b.e"], + collator, + arrayFilters, + foundIdentifiers)); Document doc(fromjson("{z: 0}")); FieldRef pathToCreate(""); @@ -840,13 +1888,27 @@ TEST(UpdateObjectNodeTest, ApplyCreateNestedPaths) { TEST(UpdateObjectNodeTest, ApplyCreateDeeplyNestedPaths) { auto setUpdate = fromjson("{$set: {'a.b.c.d': 6, 'a.b.c.e': 7, 'a.f': 8}}"); const CollatorInterface* collator = nullptr; + std::map> arrayFilters; + std::set foundIdentifiers; UpdateObjectNode root; - ASSERT_OK(UpdateObjectNode::parseAndMerge( - &root, modifiertable::ModifierType::MOD_SET, setUpdate["$set"]["a.b.c.d"], collator)); - ASSERT_OK(UpdateObjectNode::parseAndMerge( - &root, modifiertable::ModifierType::MOD_SET, setUpdate["$set"]["a.b.c.e"], collator)); - ASSERT_OK(UpdateObjectNode::parseAndMerge( - &root, modifiertable::ModifierType::MOD_SET, setUpdate["$set"]["a.f"], collator)); + ASSERT_OK(UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_SET, + setUpdate["$set"]["a.b.c.d"], + collator, + arrayFilters, + foundIdentifiers)); + ASSERT_OK(UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_SET, + setUpdate["$set"]["a.b.c.e"], + collator, + arrayFilters, + foundIdentifiers)); + ASSERT_OK(UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_SET, + setUpdate["$set"]["a.f"], + collator, + arrayFilters, + foundIdentifiers)); Document doc(fromjson("{z: 0}")); FieldRef pathToCreate(""); @@ -879,17 +1941,39 @@ TEST(UpdateObjectNodeTest, ApplyCreateDeeplyNestedPaths) { TEST(UpdateObjectNodeTest, ChildrenShouldBeAppliedInAlphabeticalOrder) { auto setUpdate = fromjson("{$set: {a: 5, d: 6, c: 7, b: 8, z: 9}}"); const CollatorInterface* collator = nullptr; + std::map> arrayFilters; + std::set foundIdentifiers; UpdateObjectNode root; - ASSERT_OK(UpdateObjectNode::parseAndMerge( - &root, modifiertable::ModifierType::MOD_SET, setUpdate["$set"]["a"], collator)); - ASSERT_OK(UpdateObjectNode::parseAndMerge( - &root, modifiertable::ModifierType::MOD_SET, setUpdate["$set"]["d"], collator)); - ASSERT_OK(UpdateObjectNode::parseAndMerge( - &root, modifiertable::ModifierType::MOD_SET, setUpdate["$set"]["c"], collator)); - ASSERT_OK(UpdateObjectNode::parseAndMerge( - &root, modifiertable::ModifierType::MOD_SET, setUpdate["$set"]["b"], collator)); - ASSERT_OK(UpdateObjectNode::parseAndMerge( - &root, modifiertable::ModifierType::MOD_SET, setUpdate["$set"]["z"], collator)); + ASSERT_OK(UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_SET, + setUpdate["$set"]["a"], + collator, + arrayFilters, + foundIdentifiers)); + ASSERT_OK(UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_SET, + setUpdate["$set"]["d"], + collator, + arrayFilters, + foundIdentifiers)); + ASSERT_OK(UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_SET, + setUpdate["$set"]["c"], + collator, + arrayFilters, + foundIdentifiers)); + ASSERT_OK(UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_SET, + setUpdate["$set"]["b"], + collator, + arrayFilters, + foundIdentifiers)); + ASSERT_OK(UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_SET, + setUpdate["$set"]["z"], + collator, + arrayFilters, + foundIdentifiers)); Document doc(fromjson("{z: 0, a: 0}")); FieldRef pathToCreate(""); @@ -921,11 +2005,21 @@ TEST(UpdateObjectNodeTest, ChildrenShouldBeAppliedInAlphabeticalOrder) { TEST(UpdateObjectNodeTest, CollatorShouldNotAffectUpdateOrder) { auto setUpdate = fromjson("{$set: {abc: 5, cba: 6}}"); CollatorInterfaceMock collator(CollatorInterfaceMock::MockType::kReverseString); + std::map> arrayFilters; + std::set foundIdentifiers; UpdateObjectNode root; - ASSERT_OK(UpdateObjectNode::parseAndMerge( - &root, modifiertable::ModifierType::MOD_SET, setUpdate["$set"]["abc"], &collator)); - ASSERT_OK(UpdateObjectNode::parseAndMerge( - &root, modifiertable::ModifierType::MOD_SET, setUpdate["$set"]["cba"], &collator)); + ASSERT_OK(UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_SET, + setUpdate["$set"]["abc"], + &collator, + arrayFilters, + foundIdentifiers)); + ASSERT_OK(UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_SET, + setUpdate["$set"]["cba"], + &collator, + arrayFilters, + foundIdentifiers)); Document doc(fromjson("{}")); FieldRef pathToCreate(""); @@ -957,13 +2051,27 @@ TEST(UpdateObjectNodeTest, CollatorShouldNotAffectUpdateOrder) { TEST(UpdateObjectNodeTest, ApplyNoop) { auto setUpdate = fromjson("{$set: {a: 5, b: 6, c: 7}}"); const CollatorInterface* collator = nullptr; + std::map> arrayFilters; + std::set foundIdentifiers; UpdateObjectNode root; - ASSERT_OK(UpdateObjectNode::parseAndMerge( - &root, modifiertable::ModifierType::MOD_SET, setUpdate["$set"]["a"], collator)); - ASSERT_OK(UpdateObjectNode::parseAndMerge( - &root, modifiertable::ModifierType::MOD_SET, setUpdate["$set"]["b"], collator)); - ASSERT_OK(UpdateObjectNode::parseAndMerge( - &root, modifiertable::ModifierType::MOD_SET, setUpdate["$set"]["c"], collator)); + ASSERT_OK(UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_SET, + setUpdate["$set"]["a"], + collator, + arrayFilters, + foundIdentifiers)); + ASSERT_OK(UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_SET, + setUpdate["$set"]["b"], + collator, + arrayFilters, + foundIdentifiers)); + ASSERT_OK(UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_SET, + setUpdate["$set"]["c"], + collator, + arrayFilters, + foundIdentifiers)); Document doc(fromjson("{a: 5, b: 6, c: 7}")); FieldRef pathToCreate(""); @@ -997,13 +2105,27 @@ TEST(UpdateObjectNodeTest, ApplyNoop) { TEST(UpdateObjectNodeTest, ApplySomeChildrenNoops) { auto setUpdate = fromjson("{$set: {a: 5, b: 6, c: 7}}"); const CollatorInterface* collator = nullptr; + std::map> arrayFilters; + std::set foundIdentifiers; UpdateObjectNode root; - ASSERT_OK(UpdateObjectNode::parseAndMerge( - &root, modifiertable::ModifierType::MOD_SET, setUpdate["$set"]["a"], collator)); - ASSERT_OK(UpdateObjectNode::parseAndMerge( - &root, modifiertable::ModifierType::MOD_SET, setUpdate["$set"]["b"], collator)); - ASSERT_OK(UpdateObjectNode::parseAndMerge( - &root, modifiertable::ModifierType::MOD_SET, setUpdate["$set"]["c"], collator)); + ASSERT_OK(UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_SET, + setUpdate["$set"]["a"], + collator, + arrayFilters, + foundIdentifiers)); + ASSERT_OK(UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_SET, + setUpdate["$set"]["b"], + collator, + arrayFilters, + foundIdentifiers)); + ASSERT_OK(UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_SET, + setUpdate["$set"]["c"], + collator, + arrayFilters, + foundIdentifiers)); Document doc(fromjson("{a: 5, b: 0, c: 7}")); FieldRef pathToCreate(""); @@ -1037,9 +2159,15 @@ TEST(UpdateObjectNodeTest, ApplySomeChildrenNoops) { TEST(UpdateObjectNodeTest, ApplyBlockingElement) { auto setUpdate = fromjson("{$set: {'a.b': 5}}"); const CollatorInterface* collator = nullptr; + std::map> arrayFilters; + std::set foundIdentifiers; UpdateObjectNode root; - ASSERT_OK(UpdateObjectNode::parseAndMerge( - &root, modifiertable::ModifierType::MOD_SET, setUpdate["$set"]["a.b"], collator)); + ASSERT_OK(UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_SET, + setUpdate["$set"]["a.b"], + collator, + arrayFilters, + foundIdentifiers)); Document doc(fromjson("{a: 0}")); FieldRef pathToCreate(""); @@ -1069,11 +2197,21 @@ TEST(UpdateObjectNodeTest, ApplyBlockingElement) { TEST(UpdateObjectNodeTest, ApplyBlockingElementFromReplication) { auto setUpdate = fromjson("{$set: {'a.b': 5, b: 6}}"); const CollatorInterface* collator = nullptr; + std::map> arrayFilters; + std::set foundIdentifiers; UpdateObjectNode root; - ASSERT_OK(UpdateObjectNode::parseAndMerge( - &root, modifiertable::ModifierType::MOD_SET, setUpdate["$set"]["a.b"], collator)); - ASSERT_OK(UpdateObjectNode::parseAndMerge( - &root, modifiertable::ModifierType::MOD_SET, setUpdate["$set"]["b"], collator)); + ASSERT_OK(UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_SET, + setUpdate["$set"]["a.b"], + collator, + arrayFilters, + foundIdentifiers)); + ASSERT_OK(UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_SET, + setUpdate["$set"]["b"], + collator, + arrayFilters, + foundIdentifiers)); Document doc(fromjson("{a: 0}")); FieldRef pathToCreate(""); @@ -1105,9 +2243,15 @@ TEST(UpdateObjectNodeTest, ApplyBlockingElementFromReplication) { TEST(UpdateObjectNodeTest, ApplyPositionalMissingMatchedField) { auto setUpdate = fromjson("{$set: {'a.$': 5}}"); const CollatorInterface* collator = nullptr; + std::map> arrayFilters; + std::set foundIdentifiers; UpdateObjectNode root; - ASSERT_OK(UpdateObjectNode::parseAndMerge( - &root, modifiertable::ModifierType::MOD_SET, setUpdate["$set"]["a.$"], collator)); + ASSERT_OK(UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_SET, + setUpdate["$set"]["a.$"], + collator, + arrayFilters, + foundIdentifiers)); Document doc(fromjson("{}")); FieldRef pathToCreate(""); @@ -1138,11 +2282,21 @@ TEST(UpdateObjectNodeTest, ApplyPositionalMissingMatchedField) { TEST(UpdateObjectNodeTest, ApplyMergePositionalChild) { auto setUpdate = fromjson("{$set: {'a.0.b': 5, 'a.$.c': 6}}"); const CollatorInterface* collator = nullptr; + std::map> arrayFilters; + std::set foundIdentifiers; UpdateObjectNode root; - ASSERT_OK(UpdateObjectNode::parseAndMerge( - &root, modifiertable::ModifierType::MOD_SET, setUpdate["$set"]["a.0.b"], collator)); - ASSERT_OK(UpdateObjectNode::parseAndMerge( - &root, modifiertable::ModifierType::MOD_SET, setUpdate["$set"]["a.$.c"], collator)); + ASSERT_OK(UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_SET, + setUpdate["$set"]["a.0.b"], + collator, + arrayFilters, + foundIdentifiers)); + ASSERT_OK(UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_SET, + setUpdate["$set"]["a.$.c"], + collator, + arrayFilters, + foundIdentifiers)); Document doc(fromjson("{a: [{b: 0, c: 0}]}")); FieldRef pathToCreate(""); @@ -1174,15 +2328,33 @@ TEST(UpdateObjectNodeTest, ApplyMergePositionalChild) { TEST(UpdateObjectNodeTest, ApplyOrderMergedPositionalChild) { auto setUpdate = fromjson("{$set: {'a.2': 5, 'a.1.b': 6, 'a.0': 7, 'a.$.c': 8}}"); const CollatorInterface* collator = nullptr; + std::map> arrayFilters; + std::set foundIdentifiers; UpdateObjectNode root; - ASSERT_OK(UpdateObjectNode::parseAndMerge( - &root, modifiertable::ModifierType::MOD_SET, setUpdate["$set"]["a.2"], collator)); - ASSERT_OK(UpdateObjectNode::parseAndMerge( - &root, modifiertable::ModifierType::MOD_SET, setUpdate["$set"]["a.1.b"], collator)); - ASSERT_OK(UpdateObjectNode::parseAndMerge( - &root, modifiertable::ModifierType::MOD_SET, setUpdate["$set"]["a.0"], collator)); - ASSERT_OK(UpdateObjectNode::parseAndMerge( - &root, modifiertable::ModifierType::MOD_SET, setUpdate["$set"]["a.$.c"], collator)); + ASSERT_OK(UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_SET, + setUpdate["$set"]["a.2"], + collator, + arrayFilters, + foundIdentifiers)); + ASSERT_OK(UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_SET, + setUpdate["$set"]["a.1.b"], + collator, + arrayFilters, + foundIdentifiers)); + ASSERT_OK(UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_SET, + setUpdate["$set"]["a.0"], + collator, + arrayFilters, + foundIdentifiers)); + ASSERT_OK(UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_SET, + setUpdate["$set"]["a.$.c"], + collator, + arrayFilters, + foundIdentifiers)); Document doc(fromjson("{}")); FieldRef pathToCreate(""); @@ -1215,11 +2387,21 @@ TEST(UpdateObjectNodeTest, ApplyOrderMergedPositionalChild) { TEST(UpdateObjectNodeTest, ApplyMergeConflictWithPositionalChild) { auto setUpdate = fromjson("{$set: {'a.0': 5, 'a.$': 6}}"); const CollatorInterface* collator = nullptr; + std::map> arrayFilters; + std::set foundIdentifiers; UpdateObjectNode root; - ASSERT_OK(UpdateObjectNode::parseAndMerge( - &root, modifiertable::ModifierType::MOD_SET, setUpdate["$set"]["a.0"], collator)); - ASSERT_OK(UpdateObjectNode::parseAndMerge( - &root, modifiertable::ModifierType::MOD_SET, setUpdate["$set"]["a.$"], collator)); + ASSERT_OK(UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_SET, + setUpdate["$set"]["a.0"], + collator, + arrayFilters, + foundIdentifiers)); + ASSERT_OK(UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_SET, + setUpdate["$set"]["a.$"], + collator, + arrayFilters, + foundIdentifiers)); Document doc(fromjson("{}")); FieldRef pathToCreate(""); @@ -1249,13 +2431,27 @@ TEST(UpdateObjectNodeTest, ApplyMergeConflictWithPositionalChild) { TEST(UpdateObjectNodeTest, ApplyDoNotMergePositionalChild) { auto setUpdate = fromjson("{$set: {'a.0': 5, 'a.2': 6, 'a.$': 7}}"); const CollatorInterface* collator = nullptr; + std::map> arrayFilters; + std::set foundIdentifiers; UpdateObjectNode root; - ASSERT_OK(UpdateObjectNode::parseAndMerge( - &root, modifiertable::ModifierType::MOD_SET, setUpdate["$set"]["a.0"], collator)); - ASSERT_OK(UpdateObjectNode::parseAndMerge( - &root, modifiertable::ModifierType::MOD_SET, setUpdate["$set"]["a.2"], collator)); - ASSERT_OK(UpdateObjectNode::parseAndMerge( - &root, modifiertable::ModifierType::MOD_SET, setUpdate["$set"]["a.$"], collator)); + ASSERT_OK(UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_SET, + setUpdate["$set"]["a.0"], + collator, + arrayFilters, + foundIdentifiers)); + ASSERT_OK(UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_SET, + setUpdate["$set"]["a.2"], + collator, + arrayFilters, + foundIdentifiers)); + ASSERT_OK(UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_SET, + setUpdate["$set"]["a.$"], + collator, + arrayFilters, + foundIdentifiers)); Document doc(fromjson("{}")); FieldRef pathToCreate(""); @@ -1287,13 +2483,27 @@ TEST(UpdateObjectNodeTest, ApplyDoNotMergePositionalChild) { TEST(UpdateObjectNodeTest, ApplyPositionalChildLast) { auto setUpdate = fromjson("{$set: {'a.$': 5, 'a.0': 6, 'a.1': 7}}"); const CollatorInterface* collator = nullptr; + std::map> arrayFilters; + std::set foundIdentifiers; UpdateObjectNode root; - ASSERT_OK(UpdateObjectNode::parseAndMerge( - &root, modifiertable::ModifierType::MOD_SET, setUpdate["$set"]["a.$"], collator)); - ASSERT_OK(UpdateObjectNode::parseAndMerge( - &root, modifiertable::ModifierType::MOD_SET, setUpdate["$set"]["a.0"], collator)); - ASSERT_OK(UpdateObjectNode::parseAndMerge( - &root, modifiertable::ModifierType::MOD_SET, setUpdate["$set"]["a.1"], collator)); + ASSERT_OK(UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_SET, + setUpdate["$set"]["a.$"], + collator, + arrayFilters, + foundIdentifiers)); + ASSERT_OK(UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_SET, + setUpdate["$set"]["a.0"], + collator, + arrayFilters, + foundIdentifiers)); + ASSERT_OK(UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_SET, + setUpdate["$set"]["a.1"], + collator, + arrayFilters, + foundIdentifiers)); Document doc(fromjson("{}")); FieldRef pathToCreate(""); @@ -1325,11 +2535,21 @@ TEST(UpdateObjectNodeTest, ApplyPositionalChildLast) { TEST(UpdateObjectNodeTest, ApplyUseStoredMergedPositional) { auto setUpdate = fromjson("{$set: {'a.0.b': 5, 'a.$.c': 6}}"); const CollatorInterface* collator = nullptr; + std::map> arrayFilters; + std::set foundIdentifiers; UpdateObjectNode root; - ASSERT_OK(UpdateObjectNode::parseAndMerge( - &root, modifiertable::ModifierType::MOD_SET, setUpdate["$set"]["a.0.b"], collator)); - ASSERT_OK(UpdateObjectNode::parseAndMerge( - &root, modifiertable::ModifierType::MOD_SET, setUpdate["$set"]["a.$.c"], collator)); + ASSERT_OK(UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_SET, + setUpdate["$set"]["a.0.b"], + collator, + arrayFilters, + foundIdentifiers)); + ASSERT_OK(UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_SET, + setUpdate["$set"]["a.$.c"], + collator, + arrayFilters, + foundIdentifiers)); Document doc(fromjson("{a: [{b: 0, c: 0}]}")); FieldRef pathToCreate(""); @@ -1379,13 +2599,27 @@ TEST(UpdateObjectNodeTest, ApplyUseStoredMergedPositional) { TEST(UpdateObjectNodeTest, ApplyDoNotUseStoredMergedPositional) { auto setUpdate = fromjson("{$set: {'a.0.b': 5, 'a.$.c': 6, 'a.1.d': 7}}"); const CollatorInterface* collator = nullptr; + std::map> arrayFilters; + std::set foundIdentifiers; UpdateObjectNode root; - ASSERT_OK(UpdateObjectNode::parseAndMerge( - &root, modifiertable::ModifierType::MOD_SET, setUpdate["$set"]["a.0.b"], collator)); - ASSERT_OK(UpdateObjectNode::parseAndMerge( - &root, modifiertable::ModifierType::MOD_SET, setUpdate["$set"]["a.$.c"], collator)); - ASSERT_OK(UpdateObjectNode::parseAndMerge( - &root, modifiertable::ModifierType::MOD_SET, setUpdate["$set"]["a.1.d"], collator)); + ASSERT_OK(UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_SET, + setUpdate["$set"]["a.0.b"], + collator, + arrayFilters, + foundIdentifiers)); + ASSERT_OK(UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_SET, + setUpdate["$set"]["a.$.c"], + collator, + arrayFilters, + foundIdentifiers)); + ASSERT_OK(UpdateObjectNode::parseAndMerge(&root, + modifiertable::ModifierType::MOD_SET, + setUpdate["$set"]["a.1.d"], + collator, + arrayFilters, + foundIdentifiers)); Document doc(fromjson("{a: [{b: 0, c: 0}, {c: 0, d: 0}]}")); FieldRef pathToCreate(""); -- cgit v1.2.1