bool isIndependentOf(const MatchExpression& expr, const std::set<std::string>& pathSet) { if (expr.isLogical()) { // Any logical expression is independent of 'pathSet' if all its children are independent of // 'pathSet'. for (size_t i = 0; i < expr.numChildren(); i++) { if (!isIndependentOf(*expr.getChild(i), pathSet)) { return false; } } return true; } // At this point, we know 'expr' is a leaf. If it is an elemMatch, we do not attempt to // determine if it is independent or not, and instead just return false. return !isElemMatch(expr) && isLeafIndependentOf(expr.path(), pathSet); }
/** * Traverse the subtree rooted at 'node' to remove invalid RelevantTag assignments to text index * 'idx', which has prefix paths 'prefixPaths'. */ static void stripInvalidAssignmentsToTextIndex(MatchExpression* node, size_t idx, const unordered_set<StringData, StringData::Hasher>& prefixPaths) { // If we're here, there are prefixPaths and node is either: // 1. a text pred which we can't use as we have nothing over its prefix, or // 2. a non-text pred which we can't use as we don't have a text pred AND-related. if (Indexability::nodeCanUseIndexOnOwnField(node)) { removeIndexRelevantTag(node, idx); return; } // Do not traverse tree beyond negation node. if (node->matchType() == MatchExpression::NOT || node->matchType() == MatchExpression::NOR) { return; } // For anything to use a text index with prefixes, we require that: // 1. The text pred exists in an AND, // 2. The non-text preds that use the text index's prefixes are also in that AND. if (node->matchType() != MatchExpression::AND) { // It's an OR or some kind of array operator. for (size_t i = 0; i < node->numChildren(); ++i) { stripInvalidAssignmentsToTextIndex(node->getChild(i), idx, prefixPaths); } return; } // If we're here, we're an AND. Determine whether the children satisfy the index prefix for // the text index. invariant(node->matchType() == MatchExpression::AND); bool hasText = false; // The AND must have an EQ predicate for each prefix path. When we encounter a child with a // tag we remove it from childrenPrefixPaths. All children exist if this set is empty at // the end. unordered_set<StringData, StringData::Hasher> childrenPrefixPaths = prefixPaths; for (size_t i = 0; i < node->numChildren(); ++i) { MatchExpression* child = node->getChild(i); RelevantTag* tag = static_cast<RelevantTag*>(child->getTag()); if (NULL == tag) { // 'child' could be a logical operator. Maybe there are some assignments hiding // inside. stripInvalidAssignmentsToTextIndex(child, idx, prefixPaths); continue; } bool inFirst = tag->first.end() != std::find(tag->first.begin(), tag->first.end(), idx); bool inNotFirst = tag->notFirst.end() != std::find(tag->notFirst.begin(), tag->notFirst.end(), idx); if (inFirst || inNotFirst) { // Great! 'child' was assigned to our index. if (child->matchType() == MatchExpression::TEXT) { hasText = true; } else { childrenPrefixPaths.erase(child->path()); // One fewer prefix we're looking for, possibly. Note that we could have a // suffix assignment on the index and wind up here. In this case the erase // above won't do anything since a suffix isn't a prefix. } } else { // Recurse on the children to ensure that they're not hiding any assignments // to idx. stripInvalidAssignmentsToTextIndex(child, idx, prefixPaths); } } // Our prereqs for using the text index were not satisfied so we remove the assignments from // all children of the AND. if (!hasText || !childrenPrefixPaths.empty()) { for (size_t i = 0; i < node->numChildren(); ++i) { stripInvalidAssignmentsToTextIndex(node->getChild(i), idx, prefixPaths); } } }
Status UpdateDriver::populateDocumentWithQueryFields(const CanonicalQuery* query, mutablebson::Document& doc) const { MatchExpression* root = query->root(); MatchExpression::MatchType rootType = root->matchType(); // These copies are needed until we apply the modifiers at the end. std::vector<BSONObj> copies; // We only care about equality and "and"ed equality fields, everything else is ignored if (rootType != MatchExpression::EQ && rootType != MatchExpression::AND) return Status::OK(); if (isDocReplacement()) { BSONElement idElem = query->getQueryObj().getField("_id"); // Replacement mods need the _id field copied explicitly. if (idElem.ok()) { mb::Element elem = doc.makeElement(idElem); return doc.root().pushFront(elem); } return Status::OK(); } // Create a new UpdateDriver to create the base doc from the query Options opts; opts.logOp = false; opts.multi = false; opts.upsert = true; opts.modOptions = modOptions(); UpdateDriver insertDriver(opts); insertDriver.setContext(ModifierInterface::ExecInfo::INSERT_CONTEXT); // If we are a single equality match query if (root->matchType() == MatchExpression::EQ) { EqualityMatchExpression* eqMatch = static_cast<EqualityMatchExpression*>(root); const BSONElement matchData = eqMatch->getData(); BSONElement childElem = matchData; // Make copy to new path if not the same field name (for cases like $all) if (!root->path().empty() && matchData.fieldNameStringData() != root->path()) { BSONObjBuilder copyBuilder; copyBuilder.appendAs(eqMatch->getData(), root->path()); const BSONObj copy = copyBuilder.obj(); copies.push_back(copy); childElem = copy[root->path()]; } // Add this element as a $set modifier Status s = insertDriver.addAndParse(modifiertable::MOD_SET, childElem); if (!s.isOK()) return s; } else { // parse query $set mods, including only equality stuff for (size_t i = 0; i < root->numChildren(); ++i) { MatchExpression* child = root->getChild(i); if (child->matchType() == MatchExpression::EQ) { EqualityMatchExpression* eqMatch = static_cast<EqualityMatchExpression*>(child); const BSONElement matchData = eqMatch->getData(); BSONElement childElem = matchData; // Make copy to new path if not the same field name (for cases like $all) if (!child->path().empty() && matchData.fieldNameStringData() != child->path()) { BSONObjBuilder copyBuilder; copyBuilder.appendAs(eqMatch->getData(), child->path()); const BSONObj copy = copyBuilder.obj(); copies.push_back(copy); childElem = copy[child->path()]; } // Add this element as a $set modifier Status s = insertDriver.addAndParse(modifiertable::MOD_SET, childElem); if (!s.isOK()) return s; } } } // update the document with base field Status s = insertDriver.update(StringData(), &doc); copies.clear(); if (!s.isOK()) { return Status(ErrorCodes::UnsupportedFormat, str::stream() << "Cannot create base during" " insert of update. Caused by :" << s.toString()); } return Status::OK(); }