// static void IndexBoundsBuilder::translate(const MatchExpression* expr, int direction, OrderedIntervalList* oilOut, bool* exactOut) { Interval interval; bool exact = false; if (expr->isLeaf()) { if (MatchExpression::EQ == expr->matchType()) { const EqualityMatchExpression* node = static_cast<const EqualityMatchExpression*>(expr); // We have to copy the data out of the parse tree and stuff it into the index bounds. // BSONValue will be useful here. BSONObj dataObj = objFromElement(node->getData()); if (dataObj.couldBeArray()) { // XXX: build better bounds warning() << "building lazy bounds for " << expr->toString() << endl; interval = allValues(); exact = false; } else { verify(dataObj.isOwned()); interval = makePointInterval(dataObj); exact = true; } } else if (MatchExpression::LTE == expr->matchType()) { const LTEMatchExpression* node = static_cast<const LTEMatchExpression*>(expr); BSONObjBuilder bob; bob.appendMinKey(""); bob.append(node->getData()); BSONObj dataObj = bob.obj(); verify(dataObj.isOwned()); interval = makeRangeInterval(dataObj, true, true); exact = true; } else if (MatchExpression::LT == expr->matchType()) { const LTMatchExpression* node = static_cast<const LTMatchExpression*>(expr); BSONObjBuilder bob; bob.appendMinKey(""); bob.append(node->getData()); BSONObj dataObj = bob.obj(); verify(dataObj.isOwned()); interval = makeRangeInterval(dataObj, true, false); exact = true; } else if (MatchExpression::GT == expr->matchType()) { const GTMatchExpression* node = static_cast<const GTMatchExpression*>(expr); BSONObjBuilder bob; bob.append(node->getData()); bob.appendMaxKey(""); BSONObj dataObj = bob.obj(); verify(dataObj.isOwned()); interval = makeRangeInterval(dataObj, false, true); exact = true; } else if (MatchExpression::GTE == expr->matchType()) { const GTEMatchExpression* node = static_cast<const GTEMatchExpression*>(expr); BSONObjBuilder bob; bob.append(node->getData()); bob.appendMaxKey(""); BSONObj dataObj = bob.obj(); verify(dataObj.isOwned()); interval = makeRangeInterval(dataObj, true, true); exact = true; } else { // XXX: build better bounds warning() << "building lazy bounds for " << expr->toString() << endl; interval = allValues(); exact = false; } } else { // XXX: build better bounds verify(expr->isArray()); warning() << "building lazy bounds for " << expr->toString() << endl; interval = allValues(); exact = false; } if (-1 == direction) { reverseInterval(&interval); } oilOut->intervals.push_back(interval); *exactOut = exact; }
Status ProjectionExec::transform(WorkingSetMember* member) const { if (_hasReturnKey) { BSONObj keyObj; if (member->hasComputed(WSM_INDEX_KEY)) { const IndexKeyComputedData* key = static_cast<const IndexKeyComputedData*>(member->getComputed(WSM_INDEX_KEY)); keyObj = key->getKey(); } member->obj = Snapshotted<BSONObj>(SnapshotId(), keyObj.getOwned()); member->keyData.clear(); member->loc = RecordId(); member->transitionToOwnedObj(); return Status::OK(); } BSONObjBuilder bob; if (member->hasObj()) { MatchDetails matchDetails; // If it's a positional projection we need a MatchDetails. if (transformRequiresDetails()) { matchDetails.requestElemMatchKey(); verify(NULL != _queryExpression); verify(_queryExpression->matchesBSON(member->obj.value(), &matchDetails)); } Status projStatus = transform(member->obj.value(), &bob, &matchDetails); if (!projStatus.isOK()) { return projStatus; } } else { verify(!requiresDocument()); // Go field by field. if (_includeID) { BSONElement elt; // Sometimes the _id field doesn't exist... if (member->getFieldDotted("_id", &elt) && !elt.eoo()) { bob.appendAs(elt, "_id"); } } BSONObjIterator it(_source); while (it.more()) { BSONElement specElt = it.next(); if (mongoutils::str::equals("_id", specElt.fieldName())) { continue; } BSONElement keyElt; // We can project a field that doesn't exist. We just ignore it. if (member->getFieldDotted(specElt.fieldName(), &keyElt) && !keyElt.eoo()) { bob.appendAs(keyElt, specElt.fieldName()); } } } for (MetaMap::const_iterator it = _meta.begin(); it != _meta.end(); ++it) { if (META_GEONEAR_DIST == it->second) { if (member->hasComputed(WSM_COMPUTED_GEO_DISTANCE)) { const GeoDistanceComputedData* dist = static_cast<const GeoDistanceComputedData*>( member->getComputed(WSM_COMPUTED_GEO_DISTANCE)); bob.append(it->first, dist->getDist()); } else { return Status(ErrorCodes::InternalError, "near loc dist requested but no data available"); } } else if (META_GEONEAR_POINT == it->second) { if (member->hasComputed(WSM_GEO_NEAR_POINT)) { const GeoNearPointComputedData* point = static_cast<const GeoNearPointComputedData*>( member->getComputed(WSM_GEO_NEAR_POINT)); BSONObj ptObj = point->getPoint(); if (ptObj.couldBeArray()) { bob.appendArray(it->first, ptObj); } else { bob.append(it->first, ptObj); } } else { return Status(ErrorCodes::InternalError, "near loc proj requested but no data available"); } } else if (META_TEXT_SCORE == it->second) { if (member->hasComputed(WSM_COMPUTED_TEXT_SCORE)) { const TextScoreComputedData* score = static_cast<const TextScoreComputedData*>( member->getComputed(WSM_COMPUTED_TEXT_SCORE)); bob.append(it->first, score->getScore()); } else { bob.append(it->first, 0.0); } } else if (META_RECORDID == it->second) { bob.append(it->first, static_cast<long long>(member->loc.repr())); } } BSONObj newObj = bob.obj(); member->obj = Snapshotted<BSONObj>(SnapshotId(), newObj); member->keyData.clear(); member->loc = RecordId(); member->transitionToOwnedObj(); return Status::OK(); }
Status ProjectionExec::transform(WorkingSetMember* member) const { if (_hasReturnKey) { BSONObjBuilder builder; if (member->hasComputed(WSM_INDEX_KEY)) { const IndexKeyComputedData* key = static_cast<const IndexKeyComputedData*>(member->getComputed(WSM_INDEX_KEY)); builder.appendElements(key->getKey()); } // Must be possible to do both returnKey meta-projection and sortKey meta-projection so that // mongos can support returnKey. for (auto fieldName : _sortKeyMetaFields) { auto sortKeyMetaStatus = addSortKeyMetaProj(fieldName, *member, &builder); if (!sortKeyMetaStatus.isOK()) { return sortKeyMetaStatus; } } member->obj = Snapshotted<BSONObj>(SnapshotId(), builder.obj()); member->keyData.clear(); member->recordId = RecordId(); member->transitionToOwnedObj(); return Status::OK(); } BSONObjBuilder bob; if (member->hasObj()) { MatchDetails matchDetails; // If it's a positional projection we need a MatchDetails. if (transformRequiresDetails()) { matchDetails.requestElemMatchKey(); verify(NULL != _queryExpression); verify(_queryExpression->matchesBSON(member->obj.value(), &matchDetails)); } Status projStatus = transform(member->obj.value(), &bob, &matchDetails); if (!projStatus.isOK()) { return projStatus; } } else { invariant(!_include); // Go field by field. if (_includeID) { BSONElement elt; // Sometimes the _id field doesn't exist... if (member->getFieldDotted("_id", &elt) && !elt.eoo()) { bob.appendAs(elt, "_id"); } } mmb::Document projectedDoc; for (auto&& specElt : _source) { if (mongoutils::str::equals("_id", specElt.fieldName())) { continue; } // $meta sortKey is the only meta-projection which is allowed to operate on index keys // rather than the full document. auto metaIt = _meta.find(specElt.fieldName()); if (metaIt != _meta.end()) { invariant(metaIt->second == META_SORT_KEY); continue; } // $meta sortKey is also the only element with an Object value in the projection spec // that can operate on index keys rather than the full document. invariant(BSONType::Object != specElt.type()); BSONElement keyElt; // We can project a field that doesn't exist. We just ignore it. if (member->getFieldDotted(specElt.fieldName(), &keyElt) && !keyElt.eoo()) { FieldRef projectedFieldPath{specElt.fieldNameStringData()}; auto setElementStatus = pathsupport::setElementAtPath(projectedFieldPath, keyElt, &projectedDoc); if (!setElementStatus.isOK()) { return setElementStatus; } } } bob.appendElements(projectedDoc.getObject()); } for (MetaMap::const_iterator it = _meta.begin(); it != _meta.end(); ++it) { if (META_GEONEAR_DIST == it->second) { if (member->hasComputed(WSM_COMPUTED_GEO_DISTANCE)) { const GeoDistanceComputedData* dist = static_cast<const GeoDistanceComputedData*>( member->getComputed(WSM_COMPUTED_GEO_DISTANCE)); bob.append(it->first, dist->getDist()); } else { return Status(ErrorCodes::InternalError, "near loc dist requested but no data available"); } } else if (META_GEONEAR_POINT == it->second) { if (member->hasComputed(WSM_GEO_NEAR_POINT)) { const GeoNearPointComputedData* point = static_cast<const GeoNearPointComputedData*>( member->getComputed(WSM_GEO_NEAR_POINT)); BSONObj ptObj = point->getPoint(); if (ptObj.couldBeArray()) { bob.appendArray(it->first, ptObj); } else { bob.append(it->first, ptObj); } } else { return Status(ErrorCodes::InternalError, "near loc proj requested but no data available"); } } else if (META_TEXT_SCORE == it->second) { if (member->hasComputed(WSM_COMPUTED_TEXT_SCORE)) { const TextScoreComputedData* score = static_cast<const TextScoreComputedData*>( member->getComputed(WSM_COMPUTED_TEXT_SCORE)); bob.append(it->first, score->getScore()); } else { bob.append(it->first, 0.0); } } else if (META_SORT_KEY == it->second) { auto sortKeyMetaStatus = addSortKeyMetaProj(it->first, *member, &bob); if (!sortKeyMetaStatus.isOK()) { return sortKeyMetaStatus; } } else if (META_RECORDID == it->second) { bob.append(it->first, static_cast<long long>(member->recordId.repr())); } } BSONObj newObj = bob.obj(); member->obj = Snapshotted<BSONObj>(SnapshotId(), newObj); member->keyData.clear(); member->recordId = RecordId(); member->transitionToOwnedObj(); return Status::OK(); }