diff options
Diffstat (limited to 'src/mongo/db/ops/modifier_add_to_set_test.cpp')
-rw-r--r-- | src/mongo/db/ops/modifier_add_to_set_test.cpp | 678 |
1 files changed, 337 insertions, 341 deletions
diff --git a/src/mongo/db/ops/modifier_add_to_set_test.cpp b/src/mongo/db/ops/modifier_add_to_set_test.cpp index 45741b4c000..43335cb4847 100644 --- a/src/mongo/db/ops/modifier_add_to_set_test.cpp +++ b/src/mongo/db/ops/modifier_add_to_set_test.cpp @@ -40,354 +40,350 @@ namespace { - using mongo::BSONObj; - using mongo::LogBuilder; - using mongo::ModifierAddToSet; - using mongo::ModifierInterface; - using mongo::Status; - using mongo::StringData; - using mongo::fromjson; - using mongo::mutablebson::Document; - using mongo::mutablebson::Element; - - /** Helper to build and manipulate a $addToSet mod. */ - class Mod { - public: - Mod() : _mod() {} - - explicit Mod(BSONObj modObj) - : _modObj(modObj) - , _mod() { - ASSERT_OK(_mod.init(_modObj["$addToSet"].embeddedObject().firstElement(), - ModifierInterface::Options::normal())); - } - - Status prepare(Element root, - StringData matchedField, - ModifierInterface::ExecInfo* execInfo) { - return _mod.prepare(root, matchedField, execInfo); - } - - Status apply() const { - return _mod.apply(); - } - - Status log(LogBuilder* logBuilder) const { - return _mod.log(logBuilder); - } - - ModifierAddToSet& mod() { - return _mod; - } - - private: - BSONObj _modObj; - ModifierAddToSet _mod; - }; - - TEST(Init, FailToInitWithInvalidValue) { - BSONObj modObj; - ModifierAddToSet mod; - - modObj = fromjson("{ $addToSet : { a : { 'x.$.y' : 'bad' } } }"); - ASSERT_NOT_OK(mod.init(modObj["$addToSet"].embeddedObject().firstElement(), - ModifierInterface::Options::normal())); - modObj = fromjson("{ $addToSet : { a : { $each : [ { 'x.$.y' : 'bad' } ] } } }"); - ASSERT_NOT_OK(mod.init(modObj["$addToSet"].embeddedObject().firstElement(), - ModifierInterface::Options::normal())); - - // An int is not valid after $each - modObj = fromjson("{ $addToSet : { a : { $each : 0 } } }"); - ASSERT_NOT_OK(mod.init(modObj["$addToSet"].embeddedObject().firstElement(), - ModifierInterface::Options::normal())); - - // An object is not valid after $each - modObj = fromjson("{ $addToSet : { a : { $each : { a : 1 } } } }"); - ASSERT_NOT_OK(mod.init(modObj["$addToSet"].embeddedObject().firstElement(), - ModifierInterface::Options::normal())); +using mongo::BSONObj; +using mongo::LogBuilder; +using mongo::ModifierAddToSet; +using mongo::ModifierInterface; +using mongo::Status; +using mongo::StringData; +using mongo::fromjson; +using mongo::mutablebson::Document; +using mongo::mutablebson::Element; + +/** Helper to build and manipulate a $addToSet mod. */ +class Mod { +public: + Mod() : _mod() {} + + explicit Mod(BSONObj modObj) : _modObj(modObj), _mod() { + ASSERT_OK(_mod.init(_modObj["$addToSet"].embeddedObject().firstElement(), + ModifierInterface::Options::normal())); } - TEST(Init, ParsesSimple) { - Mod(fromjson("{ $addToSet : { a : 1 } }")); - Mod(fromjson("{ $addToSet : { a : 'foo' } }")); - Mod(fromjson("{ $addToSet : { a : {} } }")); - Mod(fromjson("{ $addToSet : { a : { x : 1 } } }")); - Mod(fromjson("{ $addToSet : { a : [] } }")); - Mod(fromjson("{ $addToSet : { a : [1, 2] } } }")); - Mod(fromjson("{ $addToSet : { 'a.b' : 1 } }")); - Mod(fromjson("{ $addToSet : { 'a.b' : 'foo' } }")); - Mod(fromjson("{ $addToSet : { 'a.b' : {} } }")); - Mod(fromjson("{ $addToSet : { 'a.b' : { x : 1} } }")); - Mod(fromjson("{ $addToSet : { 'a.b' : [] } }")); - Mod(fromjson("{ $addToSet : { 'a.b' : [1, 2] } } }")); + Status prepare(Element root, StringData matchedField, ModifierInterface::ExecInfo* execInfo) { + return _mod.prepare(root, matchedField, execInfo); } - TEST(Init, ParsesEach) { - Mod(fromjson("{ $addToSet : { a : { $each : [] } } }")); - Mod(fromjson("{ $addToSet : { a : { $each : [ 1 ] } } }")); - Mod(fromjson("{ $addToSet : { a : { $each : [ 1, 2 ] } } }")); - Mod(fromjson("{ $addToSet : { a : { $each : [ 1, 2, 1 ] } } }")); - Mod(fromjson("{ $addToSet : { a : { $each : [ {} ] } } }")); - Mod(fromjson("{ $addToSet : { a : { $each : [ { x : 1 } ] } } }")); - Mod(fromjson("{ $addToSet : { a : { $each : [ { x : 1 }, { y : 2 } ] } } }")); - Mod(fromjson("{ $addToSet : { a : { $each : [ { x : 1 }, { y : 2 }, { x : 1 } ] } } }")); + Status apply() const { + return _mod.apply(); } - TEST(SimpleMod, PrepareOKTargetNotFound) { - Document doc(fromjson("{}")); - Mod mod(fromjson("{ $addToSet : { a : 1 } }")); - - ModifierInterface::ExecInfo execInfo; - ASSERT_OK(mod.prepare(doc.root(), "", &execInfo)); - ASSERT_EQUALS(execInfo.fieldRef[0]->dottedField(), "a"); - ASSERT_FALSE(execInfo.noOp); - } - - TEST(SimpleMod, PrepareOKTargetFound) { - Document doc(fromjson("{ a : [ 1 ] }")); - Mod mod(fromjson("{ $addToSet : { a : 1 } }")); - - ModifierInterface::ExecInfo execInfo; - ASSERT_OK(mod.prepare(doc.root(), "", &execInfo)); - ASSERT_EQUALS(execInfo.fieldRef[0]->dottedField(), "a"); - ASSERT_TRUE(execInfo.noOp); - - Document logDoc; - LogBuilder logBuilder(logDoc.root()); - ASSERT_OK(mod.log(&logBuilder)); - ASSERT_EQUALS(fromjson("{ $set : { a : [ 1 ] } }"), logDoc); - } - - TEST(SimpleMod, PrepareInvalidTargetNumber) { - Document doc(fromjson("{ a : 1 }")); - Mod mod(fromjson("{ $addToSet : { a : 1 } }")); - - ModifierInterface::ExecInfo execInfo; - ASSERT_NOT_OK(mod.prepare(doc.root(), "", &execInfo)); - } - - TEST(SimpleMod, PrepareInvalidTarget) { - Document doc(fromjson("{ a : {} }")); - Mod mod(fromjson("{ $addToSet : { a : 1 } }")); - - ModifierInterface::ExecInfo execInfo; - ASSERT_NOT_OK(mod.prepare(doc.root(), "", &execInfo)); - } - - TEST(SimpleMod, ApplyAndLogEmptyDocument) { - Document doc(fromjson("{}")); - Mod mod(fromjson("{ $addToSet : { a : 1 } }")); - - ModifierInterface::ExecInfo execInfo; - ASSERT_OK(mod.prepare(doc.root(), "", &execInfo)); - ASSERT_EQUALS(execInfo.fieldRef[0]->dottedField(), "a"); - ASSERT_FALSE(execInfo.noOp); - - ASSERT_OK(mod.apply()); - ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{ a : [ 1 ] }"), doc); - - Document logDoc; - LogBuilder logBuilder(logDoc.root()); - ASSERT_OK(mod.log(&logBuilder)); - ASSERT_EQUALS(fromjson("{ $set : { a : [ 1 ] } }"), logDoc); - } - - TEST(SimpleMod, ApplyAndLogEmptyArray) { - Document doc(fromjson("{ a : [] }")); - Mod mod(fromjson("{ $addToSet : { a : 1 } }")); - - ModifierInterface::ExecInfo execInfo; - ASSERT_OK(mod.prepare(doc.root(), "", &execInfo)); - ASSERT_EQUALS(execInfo.fieldRef[0]->dottedField(), "a"); - ASSERT_FALSE(execInfo.noOp); - - ASSERT_OK(mod.apply()); - ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{ a : [ 1 ] }"), doc); - - Document logDoc; - LogBuilder logBuilder(logDoc.root()); - ASSERT_OK(mod.log(&logBuilder)); - ASSERT_EQUALS(fromjson("{ $set : { a : [ 1 ] } }"), logDoc); - } - - TEST(SimpleEachMod, ApplyAndLogEmptyDocument) { - Document doc(fromjson("{}")); - Mod mod(fromjson("{ $addToSet : { a : { $each : [1, 2, 3] } } }")); - - ModifierInterface::ExecInfo execInfo; - ASSERT_OK(mod.prepare(doc.root(), "", &execInfo)); - ASSERT_EQUALS(execInfo.fieldRef[0]->dottedField(), "a"); - ASSERT_FALSE(execInfo.noOp); - - ASSERT_OK(mod.apply()); - ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{ a : [ 1, 2, 3 ] }"), doc); - - Document logDoc; - LogBuilder logBuilder(logDoc.root()); - ASSERT_OK(mod.log(&logBuilder)); - ASSERT_EQUALS(fromjson("{ $set : { a : [ 1, 2, 3 ] } }"), logDoc); - } - - TEST(SimpleEachMod, ApplyAndLogEmptyArray) { - Document doc(fromjson("{ a : [] }")); - Mod mod(fromjson("{ $addToSet : { a : { $each : [1, 2, 3] } } }")); - - ModifierInterface::ExecInfo execInfo; - ASSERT_OK(mod.prepare(doc.root(), "", &execInfo)); - ASSERT_EQUALS(execInfo.fieldRef[0]->dottedField(), "a"); - ASSERT_FALSE(execInfo.noOp); - - ASSERT_OK(mod.apply()); - ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{ a : [ 1, 2, 3 ] }"), doc); - - Document logDoc; - LogBuilder logBuilder(logDoc.root()); - ASSERT_OK(mod.log(&logBuilder)); - ASSERT_EQUALS(fromjson("{ $set : { a : [ 1, 2, 3 ] } }"), logDoc); + Status log(LogBuilder* logBuilder) const { + return _mod.log(logBuilder); } - TEST(SimpleMod, ApplyAndLogPopulatedArray) { - Document doc(fromjson("{ a : [ 'x' ] }")); - Mod mod(fromjson("{ $addToSet : { a : 1 } }")); - - ModifierInterface::ExecInfo execInfo; - ASSERT_OK(mod.prepare(doc.root(), "", &execInfo)); - ASSERT_EQUALS(execInfo.fieldRef[0]->dottedField(), "a"); - ASSERT_FALSE(execInfo.noOp); - - ASSERT_OK(mod.apply()); - ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{ a : [ 'x', 1 ] }"), doc); - - Document logDoc; - LogBuilder logBuilder(logDoc.root()); - ASSERT_OK(mod.log(&logBuilder)); - ASSERT_EQUALS(fromjson("{ $set : { a : [ 'x', 1 ] } }"), logDoc); - } - - TEST(SimpleEachMod, ApplyAndLogPopulatedArray) { - Document doc(fromjson("{ a : [ 'x' ] }")); - Mod mod(fromjson("{ $addToSet : { a : { $each : [1, 2, 3] } } }")); - - ModifierInterface::ExecInfo execInfo; - ASSERT_OK(mod.prepare(doc.root(), "", &execInfo)); - ASSERT_EQUALS(execInfo.fieldRef[0]->dottedField(), "a"); - ASSERT_FALSE(execInfo.noOp); - - ASSERT_OK(mod.apply()); - ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{ a : [ 'x', 1, 2, 3 ] }"), doc); - - Document logDoc; - LogBuilder logBuilder(logDoc.root()); - ASSERT_OK(mod.log(&logBuilder)); - ASSERT_EQUALS(fromjson("{ $set : { a : [ 'x', 1, 2, 3 ] } }"), logDoc); - } - - TEST(NoOp, AddOneExistingIsNoOp) { - Document doc(fromjson("{ a : [ 1, 2, 3 ] }")); - Mod mod(fromjson("{ $addToSet : { a : 1 } }")); - ModifierInterface::ExecInfo execInfo; - ASSERT_OK(mod.prepare(doc.root(), "", &execInfo)); - ASSERT_EQUALS(execInfo.fieldRef[0]->dottedField(), "a"); - ASSERT_TRUE(execInfo.noOp); - - Document logDoc; - LogBuilder logBuilder(logDoc.root()); - ASSERT_OK(mod.log(&logBuilder)); - ASSERT_EQUALS(fromjson("{ $set : { a : [ 1, 2, 3 ] } }"), logDoc); - } - - TEST(NoOp, AddSeveralExistingIsNoOp) { - Document doc(fromjson("{ a : [ 1, 2, 3 ] }")); - Mod mod(fromjson("{ $addToSet : { a : { $each : [1, 2] } } }")); - ModifierInterface::ExecInfo execInfo; - ASSERT_OK(mod.prepare(doc.root(), "", &execInfo)); - ASSERT_EQUALS(execInfo.fieldRef[0]->dottedField(), "a"); - ASSERT_TRUE(execInfo.noOp); - - Document logDoc; - LogBuilder logBuilder(logDoc.root()); - ASSERT_OK(mod.log(&logBuilder)); - ASSERT_EQUALS(fromjson("{ $set : { a : [ 1, 2, 3 ] } }"), logDoc); - } - - TEST(NoOp, AddAllExistingIsNoOp) { - Document doc(fromjson("{ a : [ 1, 2, 3 ] }")); - Mod mod(fromjson("{ $addToSet : { a : { $each : [1, 2, 3] } } }")); - ModifierInterface::ExecInfo execInfo; - ASSERT_OK(mod.prepare(doc.root(), "", &execInfo)); - ASSERT_EQUALS(execInfo.fieldRef[0]->dottedField(), "a"); - ASSERT_TRUE(execInfo.noOp); - - Document logDoc; - LogBuilder logBuilder(logDoc.root()); - ASSERT_OK(mod.log(&logBuilder)); - ASSERT_EQUALS(fromjson("{ $set : { a : [ 1, 2, 3 ] } }"), logDoc); - } - - TEST(Deduplication, ExistingDuplicatesArePreserved) { - Document doc(fromjson("{ a : [ 1, 1, 2, 1, 2, 2 ] }")); - Mod mod(fromjson("{ $addToSet : { a : 3 } }")); - - ModifierInterface::ExecInfo execInfo; - ASSERT_OK(mod.prepare(doc.root(), "", &execInfo)); - ASSERT_EQUALS(execInfo.fieldRef[0]->dottedField(), "a"); - ASSERT_FALSE(execInfo.noOp); - - ASSERT_OK(mod.apply()); - ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{ a : [ 1, 1, 2, 1, 2, 2, 3] }"), doc); - - Document logDoc; - LogBuilder logBuilder(logDoc.root()); - ASSERT_OK(mod.log(&logBuilder)); - ASSERT_EQUALS(fromjson("{ $set : { a : [ 1, 1, 2, 1, 2, 2, 3] } }"), logDoc); - } - - TEST(Deduplication, NewDuplicatesAreElided) { - Document doc(fromjson("{ a : [ 1, 1, 2, 1, 2, 2 ] }")); - Mod mod(fromjson("{ $addToSet : { a : { $each : [ 4, 1, 3, 2, 3, 1, 3, 3, 2, 4] } } }")); - - ModifierInterface::ExecInfo execInfo; - ASSERT_OK(mod.prepare(doc.root(), "", &execInfo)); - ASSERT_EQUALS(execInfo.fieldRef[0]->dottedField(), "a"); - ASSERT_FALSE(execInfo.noOp); - - ASSERT_OK(mod.apply()); - ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{ a : [ 1, 1, 2, 1, 2, 2, 4, 3] }"), doc); - - Document logDoc; - LogBuilder logBuilder(logDoc.root()); - ASSERT_OK(mod.log(&logBuilder)); - ASSERT_EQUALS(fromjson("{ $set : { a : [ 1, 1, 2, 1, 2, 2, 4, 3] } }"), logDoc); - } - - TEST(Regressions, SERVER_12848) { - // Proof that the mod works ok (the real issue was in validate). - - Document doc(fromjson("{ _id : 1, a : [ 1, [ ] ] }")); - Mod mod(fromjson("{ $addToSet : { 'a.1' : 1 } }")); - - ModifierInterface::ExecInfo execInfo; - ASSERT_OK(mod.prepare(doc.root(), "", &execInfo)); - - ASSERT_EQUALS(execInfo.fieldRef[0]->dottedField(), "a.1"); - ASSERT_FALSE(execInfo.noOp); - - ASSERT_OK(mod.apply()); - ASSERT_FALSE(doc.isInPlaceModeEnabled()); - ASSERT_EQUALS(fromjson("{ _id : 1, a : [ 1, [ 1 ] ] }"), doc); - - Document logDoc; - LogBuilder logBuilder(logDoc.root()); - ASSERT_OK(mod.log(&logBuilder)); - ASSERT_EQUALS(fromjson("{ $set : { 'a.1' : [ 1 ] } }"), logDoc); + ModifierAddToSet& mod() { + return _mod; } -} // namespace +private: + BSONObj _modObj; + ModifierAddToSet _mod; +}; + +TEST(Init, FailToInitWithInvalidValue) { + BSONObj modObj; + ModifierAddToSet mod; + + modObj = fromjson("{ $addToSet : { a : { 'x.$.y' : 'bad' } } }"); + ASSERT_NOT_OK(mod.init(modObj["$addToSet"].embeddedObject().firstElement(), + ModifierInterface::Options::normal())); + modObj = fromjson("{ $addToSet : { a : { $each : [ { 'x.$.y' : 'bad' } ] } } }"); + ASSERT_NOT_OK(mod.init(modObj["$addToSet"].embeddedObject().firstElement(), + ModifierInterface::Options::normal())); + + // An int is not valid after $each + modObj = fromjson("{ $addToSet : { a : { $each : 0 } } }"); + ASSERT_NOT_OK(mod.init(modObj["$addToSet"].embeddedObject().firstElement(), + ModifierInterface::Options::normal())); + + // An object is not valid after $each + modObj = fromjson("{ $addToSet : { a : { $each : { a : 1 } } } }"); + ASSERT_NOT_OK(mod.init(modObj["$addToSet"].embeddedObject().firstElement(), + ModifierInterface::Options::normal())); +} + +TEST(Init, ParsesSimple) { + Mod(fromjson("{ $addToSet : { a : 1 } }")); + Mod(fromjson("{ $addToSet : { a : 'foo' } }")); + Mod(fromjson("{ $addToSet : { a : {} } }")); + Mod(fromjson("{ $addToSet : { a : { x : 1 } } }")); + Mod(fromjson("{ $addToSet : { a : [] } }")); + Mod(fromjson("{ $addToSet : { a : [1, 2] } } }")); + Mod(fromjson("{ $addToSet : { 'a.b' : 1 } }")); + Mod(fromjson("{ $addToSet : { 'a.b' : 'foo' } }")); + Mod(fromjson("{ $addToSet : { 'a.b' : {} } }")); + Mod(fromjson("{ $addToSet : { 'a.b' : { x : 1} } }")); + Mod(fromjson("{ $addToSet : { 'a.b' : [] } }")); + Mod(fromjson("{ $addToSet : { 'a.b' : [1, 2] } } }")); +} + +TEST(Init, ParsesEach) { + Mod(fromjson("{ $addToSet : { a : { $each : [] } } }")); + Mod(fromjson("{ $addToSet : { a : { $each : [ 1 ] } } }")); + Mod(fromjson("{ $addToSet : { a : { $each : [ 1, 2 ] } } }")); + Mod(fromjson("{ $addToSet : { a : { $each : [ 1, 2, 1 ] } } }")); + Mod(fromjson("{ $addToSet : { a : { $each : [ {} ] } } }")); + Mod(fromjson("{ $addToSet : { a : { $each : [ { x : 1 } ] } } }")); + Mod(fromjson("{ $addToSet : { a : { $each : [ { x : 1 }, { y : 2 } ] } } }")); + Mod(fromjson("{ $addToSet : { a : { $each : [ { x : 1 }, { y : 2 }, { x : 1 } ] } } }")); +} + +TEST(SimpleMod, PrepareOKTargetNotFound) { + Document doc(fromjson("{}")); + Mod mod(fromjson("{ $addToSet : { a : 1 } }")); + + ModifierInterface::ExecInfo execInfo; + ASSERT_OK(mod.prepare(doc.root(), "", &execInfo)); + ASSERT_EQUALS(execInfo.fieldRef[0]->dottedField(), "a"); + ASSERT_FALSE(execInfo.noOp); +} + +TEST(SimpleMod, PrepareOKTargetFound) { + Document doc(fromjson("{ a : [ 1 ] }")); + Mod mod(fromjson("{ $addToSet : { a : 1 } }")); + + ModifierInterface::ExecInfo execInfo; + ASSERT_OK(mod.prepare(doc.root(), "", &execInfo)); + ASSERT_EQUALS(execInfo.fieldRef[0]->dottedField(), "a"); + ASSERT_TRUE(execInfo.noOp); + + Document logDoc; + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(mod.log(&logBuilder)); + ASSERT_EQUALS(fromjson("{ $set : { a : [ 1 ] } }"), logDoc); +} + +TEST(SimpleMod, PrepareInvalidTargetNumber) { + Document doc(fromjson("{ a : 1 }")); + Mod mod(fromjson("{ $addToSet : { a : 1 } }")); + + ModifierInterface::ExecInfo execInfo; + ASSERT_NOT_OK(mod.prepare(doc.root(), "", &execInfo)); +} + +TEST(SimpleMod, PrepareInvalidTarget) { + Document doc(fromjson("{ a : {} }")); + Mod mod(fromjson("{ $addToSet : { a : 1 } }")); + + ModifierInterface::ExecInfo execInfo; + ASSERT_NOT_OK(mod.prepare(doc.root(), "", &execInfo)); +} + +TEST(SimpleMod, ApplyAndLogEmptyDocument) { + Document doc(fromjson("{}")); + Mod mod(fromjson("{ $addToSet : { a : 1 } }")); + + ModifierInterface::ExecInfo execInfo; + ASSERT_OK(mod.prepare(doc.root(), "", &execInfo)); + ASSERT_EQUALS(execInfo.fieldRef[0]->dottedField(), "a"); + ASSERT_FALSE(execInfo.noOp); + + ASSERT_OK(mod.apply()); + ASSERT_FALSE(doc.isInPlaceModeEnabled()); + ASSERT_EQUALS(fromjson("{ a : [ 1 ] }"), doc); + + Document logDoc; + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(mod.log(&logBuilder)); + ASSERT_EQUALS(fromjson("{ $set : { a : [ 1 ] } }"), logDoc); +} + +TEST(SimpleMod, ApplyAndLogEmptyArray) { + Document doc(fromjson("{ a : [] }")); + Mod mod(fromjson("{ $addToSet : { a : 1 } }")); + + ModifierInterface::ExecInfo execInfo; + ASSERT_OK(mod.prepare(doc.root(), "", &execInfo)); + ASSERT_EQUALS(execInfo.fieldRef[0]->dottedField(), "a"); + ASSERT_FALSE(execInfo.noOp); + + ASSERT_OK(mod.apply()); + ASSERT_FALSE(doc.isInPlaceModeEnabled()); + ASSERT_EQUALS(fromjson("{ a : [ 1 ] }"), doc); + + Document logDoc; + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(mod.log(&logBuilder)); + ASSERT_EQUALS(fromjson("{ $set : { a : [ 1 ] } }"), logDoc); +} + +TEST(SimpleEachMod, ApplyAndLogEmptyDocument) { + Document doc(fromjson("{}")); + Mod mod(fromjson("{ $addToSet : { a : { $each : [1, 2, 3] } } }")); + + ModifierInterface::ExecInfo execInfo; + ASSERT_OK(mod.prepare(doc.root(), "", &execInfo)); + ASSERT_EQUALS(execInfo.fieldRef[0]->dottedField(), "a"); + ASSERT_FALSE(execInfo.noOp); + + ASSERT_OK(mod.apply()); + ASSERT_FALSE(doc.isInPlaceModeEnabled()); + ASSERT_EQUALS(fromjson("{ a : [ 1, 2, 3 ] }"), doc); + + Document logDoc; + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(mod.log(&logBuilder)); + ASSERT_EQUALS(fromjson("{ $set : { a : [ 1, 2, 3 ] } }"), logDoc); +} + +TEST(SimpleEachMod, ApplyAndLogEmptyArray) { + Document doc(fromjson("{ a : [] }")); + Mod mod(fromjson("{ $addToSet : { a : { $each : [1, 2, 3] } } }")); + + ModifierInterface::ExecInfo execInfo; + ASSERT_OK(mod.prepare(doc.root(), "", &execInfo)); + ASSERT_EQUALS(execInfo.fieldRef[0]->dottedField(), "a"); + ASSERT_FALSE(execInfo.noOp); + + ASSERT_OK(mod.apply()); + ASSERT_FALSE(doc.isInPlaceModeEnabled()); + ASSERT_EQUALS(fromjson("{ a : [ 1, 2, 3 ] }"), doc); + + Document logDoc; + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(mod.log(&logBuilder)); + ASSERT_EQUALS(fromjson("{ $set : { a : [ 1, 2, 3 ] } }"), logDoc); +} + +TEST(SimpleMod, ApplyAndLogPopulatedArray) { + Document doc(fromjson("{ a : [ 'x' ] }")); + Mod mod(fromjson("{ $addToSet : { a : 1 } }")); + + ModifierInterface::ExecInfo execInfo; + ASSERT_OK(mod.prepare(doc.root(), "", &execInfo)); + ASSERT_EQUALS(execInfo.fieldRef[0]->dottedField(), "a"); + ASSERT_FALSE(execInfo.noOp); + + ASSERT_OK(mod.apply()); + ASSERT_FALSE(doc.isInPlaceModeEnabled()); + ASSERT_EQUALS(fromjson("{ a : [ 'x', 1 ] }"), doc); + + Document logDoc; + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(mod.log(&logBuilder)); + ASSERT_EQUALS(fromjson("{ $set : { a : [ 'x', 1 ] } }"), logDoc); +} + +TEST(SimpleEachMod, ApplyAndLogPopulatedArray) { + Document doc(fromjson("{ a : [ 'x' ] }")); + Mod mod(fromjson("{ $addToSet : { a : { $each : [1, 2, 3] } } }")); + + ModifierInterface::ExecInfo execInfo; + ASSERT_OK(mod.prepare(doc.root(), "", &execInfo)); + ASSERT_EQUALS(execInfo.fieldRef[0]->dottedField(), "a"); + ASSERT_FALSE(execInfo.noOp); + + ASSERT_OK(mod.apply()); + ASSERT_FALSE(doc.isInPlaceModeEnabled()); + ASSERT_EQUALS(fromjson("{ a : [ 'x', 1, 2, 3 ] }"), doc); + + Document logDoc; + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(mod.log(&logBuilder)); + ASSERT_EQUALS(fromjson("{ $set : { a : [ 'x', 1, 2, 3 ] } }"), logDoc); +} + +TEST(NoOp, AddOneExistingIsNoOp) { + Document doc(fromjson("{ a : [ 1, 2, 3 ] }")); + Mod mod(fromjson("{ $addToSet : { a : 1 } }")); + ModifierInterface::ExecInfo execInfo; + ASSERT_OK(mod.prepare(doc.root(), "", &execInfo)); + ASSERT_EQUALS(execInfo.fieldRef[0]->dottedField(), "a"); + ASSERT_TRUE(execInfo.noOp); + + Document logDoc; + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(mod.log(&logBuilder)); + ASSERT_EQUALS(fromjson("{ $set : { a : [ 1, 2, 3 ] } }"), logDoc); +} + +TEST(NoOp, AddSeveralExistingIsNoOp) { + Document doc(fromjson("{ a : [ 1, 2, 3 ] }")); + Mod mod(fromjson("{ $addToSet : { a : { $each : [1, 2] } } }")); + ModifierInterface::ExecInfo execInfo; + ASSERT_OK(mod.prepare(doc.root(), "", &execInfo)); + ASSERT_EQUALS(execInfo.fieldRef[0]->dottedField(), "a"); + ASSERT_TRUE(execInfo.noOp); + + Document logDoc; + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(mod.log(&logBuilder)); + ASSERT_EQUALS(fromjson("{ $set : { a : [ 1, 2, 3 ] } }"), logDoc); +} + +TEST(NoOp, AddAllExistingIsNoOp) { + Document doc(fromjson("{ a : [ 1, 2, 3 ] }")); + Mod mod(fromjson("{ $addToSet : { a : { $each : [1, 2, 3] } } }")); + ModifierInterface::ExecInfo execInfo; + ASSERT_OK(mod.prepare(doc.root(), "", &execInfo)); + ASSERT_EQUALS(execInfo.fieldRef[0]->dottedField(), "a"); + ASSERT_TRUE(execInfo.noOp); + + Document logDoc; + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(mod.log(&logBuilder)); + ASSERT_EQUALS(fromjson("{ $set : { a : [ 1, 2, 3 ] } }"), logDoc); +} + +TEST(Deduplication, ExistingDuplicatesArePreserved) { + Document doc(fromjson("{ a : [ 1, 1, 2, 1, 2, 2 ] }")); + Mod mod(fromjson("{ $addToSet : { a : 3 } }")); + + ModifierInterface::ExecInfo execInfo; + ASSERT_OK(mod.prepare(doc.root(), "", &execInfo)); + ASSERT_EQUALS(execInfo.fieldRef[0]->dottedField(), "a"); + ASSERT_FALSE(execInfo.noOp); + + ASSERT_OK(mod.apply()); + ASSERT_FALSE(doc.isInPlaceModeEnabled()); + ASSERT_EQUALS(fromjson("{ a : [ 1, 1, 2, 1, 2, 2, 3] }"), doc); + + Document logDoc; + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(mod.log(&logBuilder)); + ASSERT_EQUALS(fromjson("{ $set : { a : [ 1, 1, 2, 1, 2, 2, 3] } }"), logDoc); +} + +TEST(Deduplication, NewDuplicatesAreElided) { + Document doc(fromjson("{ a : [ 1, 1, 2, 1, 2, 2 ] }")); + Mod mod(fromjson("{ $addToSet : { a : { $each : [ 4, 1, 3, 2, 3, 1, 3, 3, 2, 4] } } }")); + + ModifierInterface::ExecInfo execInfo; + ASSERT_OK(mod.prepare(doc.root(), "", &execInfo)); + ASSERT_EQUALS(execInfo.fieldRef[0]->dottedField(), "a"); + ASSERT_FALSE(execInfo.noOp); + + ASSERT_OK(mod.apply()); + ASSERT_FALSE(doc.isInPlaceModeEnabled()); + ASSERT_EQUALS(fromjson("{ a : [ 1, 1, 2, 1, 2, 2, 4, 3] }"), doc); + + Document logDoc; + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(mod.log(&logBuilder)); + ASSERT_EQUALS(fromjson("{ $set : { a : [ 1, 1, 2, 1, 2, 2, 4, 3] } }"), logDoc); +} + +TEST(Regressions, SERVER_12848) { + // Proof that the mod works ok (the real issue was in validate). + + Document doc(fromjson("{ _id : 1, a : [ 1, [ ] ] }")); + Mod mod(fromjson("{ $addToSet : { 'a.1' : 1 } }")); + + ModifierInterface::ExecInfo execInfo; + ASSERT_OK(mod.prepare(doc.root(), "", &execInfo)); + + ASSERT_EQUALS(execInfo.fieldRef[0]->dottedField(), "a.1"); + ASSERT_FALSE(execInfo.noOp); + + ASSERT_OK(mod.apply()); + ASSERT_FALSE(doc.isInPlaceModeEnabled()); + ASSERT_EQUALS(fromjson("{ _id : 1, a : [ 1, [ 1 ] ] }"), doc); + + Document logDoc; + LogBuilder logBuilder(logDoc.root()); + ASSERT_OK(mod.log(&logBuilder)); + ASSERT_EQUALS(fromjson("{ $set : { 'a.1' : [ 1 ] } }"), logDoc); +} + +} // namespace |