TEST_F(UpdateArrayNodeTest, UpdateIsAppliedToAllMatchingElements) { auto update = fromjson("{$set: {'a.$[i]': 2}}"); auto arrayFilter = fromjson("{i: 0}"); boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); std::map<StringData, std::unique_ptr<ExpressionWithPlaceholder>> arrayFilters; auto parsedFilter = assertGet(MatchExpressionParser::parse(arrayFilter, expCtx)); arrayFilters["i"] = assertGet(ExpressionWithPlaceholder::make(std::move(parsedFilter))); std::set<std::string> foundIdentifiers; UpdateObjectNode root; ASSERT_OK(UpdateObjectNode::parseAndMerge(&root, modifiertable::ModifierType::MOD_SET, update["$set"]["a.$[i]"], expCtx, arrayFilters, foundIdentifiers)); mutablebson::Document doc(fromjson("{a: [0, 1, 0]}")); addIndexedPath("a"); auto result = root.apply(getApplyParams(doc.root())); ASSERT_TRUE(result.indexesAffected); ASSERT_FALSE(result.noop); ASSERT_EQUALS(fromjson("{a: [2, 1, 2]}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(fromjson("{$set: {a: [2, 1, 2]}}"), getLogDoc()); ASSERT_EQUALS("{a.0, a.2}", getModifiedPaths()); }
void PathGroups::addPath(QString flnm) { QFile fpath(flnm); fpath.open(QFile::ReadOnly); QTextStream fd(&fpath); QString line = fd.readLine(); if (line.contains("#indexed", Qt::CaseInsensitive)) { addIndexedPath(flnm); return; } PathGroupGrabber *pg = new PathGroupGrabber(); while (! fd.atEnd()) { QStringList list = line.split(" ", QString::SkipEmptyParts); if (list.count() == 1) { int npts = list[0].toInt(); QList<Vec> pts; for(int i=0; i<npts; i++) { if (fd.atEnd()) break; else { QString line = fd.readLine(); QStringList list = line.split(" ", QString::SkipEmptyParts); if (list.count() == 3) { float x = list[0].toFloat(); float y = list[1].toFloat(); float z = list[2].toFloat(); pts.append(Vec(x,y,z)); } } } if (pts.count() > 0) pg->addPoints(pts); } line = fd.readLine(); } m_paths.append(pg); makePathConnections(); }
TEST_F(PopNodeTest, NoopWhenFirstPathComponentDoesNotExist) { auto update = fromjson("{$pop: {'a.b': 1}}"); boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); PopNode popNode; ASSERT_OK(popNode.init(update["$pop"]["a.b"], expCtx)); mmb::Document doc(fromjson("{b: [1, 2, 3]}")); setPathToCreate("a.b"); addIndexedPath("a.b"); auto result = popNode.apply(getApplyParams(doc.root())); ASSERT_TRUE(result.noop); ASSERT_FALSE(result.indexesAffected); ASSERT_EQUALS(fromjson("{b: [1, 2, 3]}"), doc); ASSERT_EQUALS(fromjson("{}"), getLogDoc()); }
TEST_F(PopNodeTest, NoopWhenNumericalPathComponentExceedsArrayLength) { auto update = fromjson("{$pop: {'a.0': 1}}"); boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); PopNode popNode; ASSERT_OK(popNode.init(update["$pop"]["a.0"], expCtx)); mmb::Document doc(fromjson("{a: []}")); setPathToCreate("0"); setPathTaken("a"); addIndexedPath("a.0"); auto result = popNode.apply(getApplyParams(doc.root()["a"])); ASSERT_TRUE(result.noop); ASSERT_FALSE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: []}"), doc); ASSERT_EQUALS(fromjson("{}"), getLogDoc()); }
TEST_F(UnsetNodeTest, ApplyFieldWithDot) { auto update = fromjson("{$unset: {'a.b': 1}}"); const CollatorInterface* collator = nullptr; UnsetNode node; ASSERT_OK(node.init(update["$unset"]["a.b"], collator)); mutablebson::Document doc(fromjson("{'a.b':4, a: {b: 2}}")); setPathTaken("a.b"); addIndexedPath("a"); auto result = node.apply(getApplyParams(doc.root()["a"]["b"])); ASSERT_FALSE(result.noop); ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{'a.b':4, a: {}}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(fromjson("{$unset: {'a.b': true}}"), getLogDoc()); }
TEST_F(UnsetNodeTest, UnsetNoOpEmptyDoc) { auto update = fromjson("{$unset: {a: 1}}"); const CollatorInterface* collator = nullptr; UnsetNode node; ASSERT_OK(node.init(update["$unset"]["a"], collator)); mutablebson::Document doc(fromjson("{}")); setPathToCreate("a"); addIndexedPath("a"); auto result = node.apply(getApplyParams(doc.root())); ASSERT_TRUE(result.noop); ASSERT_FALSE(result.indexesAffected); ASSERT_EQUALS(fromjson("{}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(fromjson("{}"), getLogDoc()); }
TEST_F(PopNodeTest, NoopWhenPathPartiallyExists) { auto update = fromjson("{$pop: {'a.b.c': 1}}"); boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); PopNode popNode; ASSERT_OK(popNode.init(update["$pop"]["a.b.c"], expCtx)); mmb::Document doc(fromjson("{a: {}}")); setPathToCreate("b.c"); setPathTaken("a"); addIndexedPath("a.b.c"); auto result = popNode.apply(getApplyParams(doc.root()["a"])); ASSERT_TRUE(result.noop); ASSERT_FALSE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: {}}"), doc); ASSERT_EQUALS(fromjson("{}"), getLogDoc()); }
TEST_F(PopNodeTest, ThrowsWhenPathIsBlockedByAScalar) { auto update = fromjson("{$pop: {'a.b': 1}}"); boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); PopNode popNode; ASSERT_OK(popNode.init(update["$pop"]["a.b"], expCtx)); mmb::Document doc(fromjson("{a: 'foo'}")); setPathToCreate("b"); setPathTaken("a"); addIndexedPath("a.b"); ASSERT_THROWS_CODE_AND_WHAT( popNode.apply(getApplyParams(doc.root()["a"])), AssertionException, ErrorCodes::PathNotViable, "Cannot use the part (b) of (a.b) to traverse the element ({a: \"foo\"})"); }
TEST_F(CompareNodeTest, ApplyMaxNumberIsLess) { auto update = fromjson("{$max: {a: 0}}"); const CollatorInterface* collator = nullptr; CompareNode node(CompareNode::CompareMode::kMax); ASSERT_OK(node.init(update["$max"]["a"], collator)); mutablebson::Document doc(fromjson("{a: 1}")); setPathTaken("a"); addIndexedPath("a"); auto result = node.apply(getApplyParams(doc.root()["a"])); ASSERT_TRUE(result.noop); ASSERT_FALSE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: 1}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(fromjson("{}"), getLogDoc()); }
TEST_F(CompareNodeTest, ApplyMinRespectsCollation) { auto update = fromjson("{$min: {a: 'dba'}}"); const CollatorInterfaceMock collator(CollatorInterfaceMock::MockType::kReverseString); CompareNode node(CompareNode::CompareMode::kMin); ASSERT_OK(node.init(update["$min"]["a"], &collator)); mutablebson::Document doc(fromjson("{a: 'cbc'}")); setPathTaken("a"); addIndexedPath("a"); auto result = node.apply(getApplyParams(doc.root()["a"])); ASSERT_FALSE(result.noop); ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: 'dba'}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(fromjson("{$set: {a: 'dba'}}"), getLogDoc()); }
TEST_F(CompareNodeTest, ApplyMinSameValIntZero) { auto update = BSON("$min" << BSON("a" << 0LL)); const CollatorInterface* collator = nullptr; CompareNode node(CompareNode::CompareMode::kMin); ASSERT_OK(node.init(update["$min"]["a"], collator)); mutablebson::Document doc(fromjson("{a: 0.0}")); setPathTaken("a"); addIndexedPath("a"); auto result = node.apply(getApplyParams(doc.root()["a"])); ASSERT_TRUE(result.noop); ASSERT_FALSE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: 0.0}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(fromjson("{}"), getLogDoc()); }
TEST_F(UnsetNodeTest, UnsetPositional) { auto update = fromjson("{$unset: {'a.$': 1}}"); const CollatorInterface* collator = nullptr; UnsetNode node; ASSERT_OK(node.init(update["$unset"]["a.$"], collator)); mutablebson::Document doc(fromjson("{a: [0, 1, 2]}")); setPathTaken("a.1"); setMatchedField("1"); addIndexedPath("a"); auto result = node.apply(getApplyParams(doc.root()["a"]["1"])); ASSERT_FALSE(result.noop); ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: [0, null, 2]}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(fromjson("{$unset: {'a.1': true}}"), getLogDoc()); }
TEST_F(UnsetNodeTest, ApplyFieldWithDot) { auto update = fromjson("{$unset: {'a.b': 1}}"); boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); UnsetNode node; ASSERT_OK(node.init(update["$unset"]["a.b"], expCtx)); mutablebson::Document doc(fromjson("{'a.b':4, a: {b: 2}}")); setPathTaken("a.b"); addIndexedPath("a"); auto result = node.apply(getApplyParams(doc.root()["a"]["b"])); ASSERT_FALSE(result.noop); ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{'a.b':4, a: {}}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(fromjson("{$unset: {'a.b': true}}"), getLogDoc()); ASSERT_EQUALS(getModifiedPaths(), "{a.b}"); }
TEST_F(UnsetNodeTest, UnsetNoOpEmptyDoc) { auto update = fromjson("{$unset: {a: 1}}"); boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); UnsetNode node; ASSERT_OK(node.init(update["$unset"]["a"], expCtx)); mutablebson::Document doc(fromjson("{}")); setPathToCreate("a"); addIndexedPath("a"); auto result = node.apply(getApplyParams(doc.root())); ASSERT_TRUE(result.noop); ASSERT_FALSE(result.indexesAffected); ASSERT_EQUALS(fromjson("{}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(fromjson("{}"), getLogDoc()); ASSERT_EQUALS(getModifiedPaths(), "{a}"); }
TEST_F(UnsetNodeTest, UnsetPositional) { auto update = fromjson("{$unset: {'a.$': 1}}"); boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); UnsetNode node; ASSERT_OK(node.init(update["$unset"]["a.$"], expCtx)); mutablebson::Document doc(fromjson("{a: [0, 1, 2]}")); setPathTaken("a.1"); setMatchedField("1"); addIndexedPath("a"); auto result = node.apply(getApplyParams(doc.root()["a"][1])); ASSERT_FALSE(result.noop); ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: [0, null, 2]}"), doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(fromjson("{$unset: {'a.1': true}}"), getLogDoc()); ASSERT_EQUALS(getModifiedPaths(), "{a.1}"); }
TEST_F(UnsetNodeTest, ApplyCanRemoveRequiredPartOfDBRefIfValidateForStorageIsFalse) { auto update = fromjson("{$unset: {'a.$id': true}}"); const CollatorInterface* collator = nullptr; UnsetNode node; ASSERT_OK(node.init(update["$unset"]["a.$id"], collator)); mutablebson::Document doc(fromjson("{a: {$ref: 'c', $id: 0}}")); setPathTaken("a.$id"); addIndexedPath("a"); setValidateForStorage(false); auto result = node.apply(getApplyParams(doc.root()["a"]["$id"])); ASSERT_FALSE(result.noop); ASSERT_TRUE(result.indexesAffected); auto updated = BSON("a" << BSON("$ref" << "c")); ASSERT_EQUALS(updated, doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(fromjson("{$unset: {'a.$id': true}}"), getLogDoc()); }
TEST_F(UnsetNodeTest, ApplyCanRemoveRequiredPartOfDBRefIfValidateForStorageIsFalse) { auto update = fromjson("{$unset: {'a.$id': true}}"); boost::intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest()); UnsetNode node; ASSERT_OK(node.init(update["$unset"]["a.$id"], expCtx)); mutablebson::Document doc(fromjson("{a: {$ref: 'c', $id: 0}}")); setPathTaken("a.$id"); addIndexedPath("a"); setValidateForStorage(false); auto result = node.apply(getApplyParams(doc.root()["a"]["$id"])); ASSERT_FALSE(result.noop); ASSERT_TRUE(result.indexesAffected); auto updated = BSON("a" << BSON("$ref" << "c")); ASSERT_EQUALS(updated, doc); ASSERT_FALSE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(fromjson("{$unset: {'a.$id': true}}"), getLogDoc()); ASSERT_EQUALS(getModifiedPaths(), "{a.$id}"); }
TEST_F(CompareNodeTest, ApplyMaxRespectsCollationFromSetCollator) { auto update = fromjson("{$max: {a: 'abd'}}"); const CollatorInterface* binaryComparisonCollator = nullptr; CompareNode node(CompareNode::CompareMode::kMax); ASSERT_OK(node.init(update["$max"]["a"], binaryComparisonCollator)); const CollatorInterfaceMock reverseStringCollator( CollatorInterfaceMock::MockType::kReverseString); node.setCollator(&reverseStringCollator); mutablebson::Document doc(fromjson("{a: 'cbc'}")); setPathTaken("a"); addIndexedPath("a"); auto result = node.apply(getApplyParams(doc.root()["a"])); ASSERT_FALSE(result.noop); ASSERT_TRUE(result.indexesAffected); ASSERT_EQUALS(fromjson("{a: 'abd'}"), doc); ASSERT_TRUE(doc.isInPlaceModeEnabled()); ASSERT_EQUALS(fromjson("{$set: {a: 'abd'}}"), getLogDoc()); }