/** * Encodes parsed projection into cache key. * Does a simple toString() on each projected field * in the BSON object. * Orders the encoded elements in the projection by field name. * This handles all the special projection types ($meta, $elemMatch, etc.) */ void PlanCache::encodeKeyForProj(const BSONObj& projObj, StringBuilder* keyBuilder) const { if (projObj.isEmpty()) { return; } *keyBuilder << kEncodeProjectionSection; // Sorts the BSON elements by field name using a map. std::map<StringData, BSONElement> elements; BSONObjIterator it(projObj); while (it.more()) { BSONElement elt = it.next(); StringData fieldName = elt.fieldNameStringData(); elements[fieldName] = elt; } // Read elements in order of field name for (std::map<StringData, BSONElement>::const_iterator i = elements.begin(); i != elements.end(); ++i) { const BSONElement& elt = (*i).second; // BSONElement::toString() arguments // includeFieldName - skip field name (appending after toString() result). false. // full: choose less verbose representation of child/data values. false. encodeUserString(elt.toString(false, false), keyBuilder); encodeUserString(elt.fieldName(), keyBuilder); } }
/** * Encodes sort order into cache key. * Sort order is normalized because it provided by * QueryRequest. */ void PlanCache::encodeKeyForSort(const BSONObj& sortObj, StringBuilder* keyBuilder) const { if (sortObj.isEmpty()) { return; } *keyBuilder << kEncodeSortSection; BSONObjIterator it(sortObj); while (it.more()) { BSONElement elt = it.next(); // $meta text score if (QueryRequest::isTextScoreMeta(elt)) { *keyBuilder << "t"; } // Ascending else if (elt.numberInt() == 1) { *keyBuilder << "a"; } // Descending else { *keyBuilder << "d"; } encodeUserString(elt.fieldName(), keyBuilder); // Sort argument separator if (it.more()) { *keyBuilder << ","; } } }
/** * Encodes parsed projection into cache key. * Does a simple toString() on each projected field * in the BSON object. * Orders the encoded elements in the projection by field name. * This handles all the special projection types ($meta, $elemMatch, etc.) */ void PlanCache::encodeKeyForProj(const BSONObj& projObj, StringBuilder* keyBuilder) const { // Sorts the BSON elements by field name using a map. std::map<StringData, BSONElement> elements; BSONObjIterator it(projObj); while (it.more()) { BSONElement elt = it.next(); StringData fieldName = elt.fieldNameStringData(); // Internal callers may add $-prefixed fields to the projection. These are not part of a // user query, and therefore are not considered part of the cache key. if (fieldName[0] == '$') { continue; } elements[fieldName] = elt; } if (!elements.empty()) { *keyBuilder << kEncodeProjectionSection; } // Read elements in order of field name for (std::map<StringData, BSONElement>::const_iterator i = elements.begin(); i != elements.end(); ++i) { const BSONElement& elt = (*i).second; if (elt.type() != BSONType::Object) { // For inclusion/exclusion projections, we encode as "i" or "e". *keyBuilder << (elt.trueValue() ? "i" : "e"); } else { // For projection operators, we use the verbatim string encoding of the element. encodeUserString(elt.toString(false, // includeFieldName false), // full keyBuilder); } encodeUserString(elt.fieldName(), keyBuilder); } }
/** * Traverses expression tree pre-order. * Appends an encoding of each node's match type and path name * to the output stream. */ void PlanCache::encodeKeyForMatch(const MatchExpression* tree, StringBuilder* keyBuilder) const { // Encode match type and path. *keyBuilder << encodeMatchType(tree->matchType()); encodeUserString(tree->path(), keyBuilder); // GEO and GEO_NEAR require additional encoding. if (MatchExpression::GEO == tree->matchType()) { encodeGeoMatchExpression(static_cast<const GeoMatchExpression*>(tree), keyBuilder); } else if (MatchExpression::GEO_NEAR == tree->matchType()) { encodeGeoNearMatchExpression(static_cast<const GeoNearMatchExpression*>(tree), keyBuilder); } // Encode indexability. const IndexabilityDiscriminators& discriminators = _indexabilityState.getDiscriminators(tree->path()); if (!discriminators.empty()) { *keyBuilder << kEncodeDiscriminatorsBegin; // For each discriminator on this path, append the character '0' or '1'. for (const IndexabilityDiscriminator& discriminator : discriminators) { *keyBuilder << discriminator(tree); } *keyBuilder << kEncodeDiscriminatorsEnd; } // Traverse child nodes. // Enclose children in []. if (tree->numChildren() > 0) { *keyBuilder << kEncodeChildrenBegin; } // Use comma to separate children encoding. for (size_t i = 0; i < tree->numChildren(); ++i) { if (i > 0) { *keyBuilder << kEncodeChildrenSeparator; } encodeKeyForMatch(tree->getChild(i), keyBuilder); } if (tree->numChildren() > 0) { *keyBuilder << kEncodeChildrenEnd; } }