SortKeyGenerator::SortKeyGenerator(OperationContext* txn, const BSONObj& sortSpec, const BSONObj& queryObj) { _hasBounds = false; _sortHasMeta = false; _rawSortSpec = sortSpec; // 'sortSpec' can be a mix of $meta and index key expressions. We pick it apart so that // we only generate Btree keys for the index key expressions. // The Btree key fields go in here. We pass this fake index key pattern to the Btree // key generator below as part of generating sort keys for the docs. BSONObjBuilder btreeBob; BSONObjIterator it(sortSpec); while (it.more()) { BSONElement elt = it.next(); if (elt.isNumber()) { // Btree key. elt (should be) foo: 1 or foo: -1. btreeBob.append(elt); } else if (LiteParsedQuery::isTextScoreMeta(elt)) { _sortHasMeta = true; } else { // Sort spec. should have been validated before here. verify(false); } } // The fake index key pattern used to generate Btree keys. _btreeObj = btreeBob.obj(); // If we're just sorting by meta, don't bother with all the key stuff. if (_btreeObj.isEmpty()) { return; } // We'll need to treat arrays as if we were to create an index over them. that is, // we may need to unnest the first level and consider each array element to decide // the sort order. std::vector<const char*> fieldNames; std::vector<BSONElement> fixed; BSONObjIterator btreeIt(_btreeObj); while (btreeIt.more()) { BSONElement patternElt = btreeIt.next(); fieldNames.push_back(patternElt.fieldName()); fixed.push_back(BSONElement()); } // TODO SERVER-23095: change nullptr to the appropriate CollationInterface*. _keyGen.reset(new BtreeKeyGeneratorV1(fieldNames, fixed, false /* not sparse */, nullptr)); // The bounds checker only works on the Btree part of the sort key. getBoundsForSort(txn, queryObj, _btreeObj); if (_hasBounds) { _boundsChecker.reset(new IndexBoundsChecker(&_bounds, _btreeObj, 1 /* == order */)); } }
Status SortKeyGenerator::getSortKey(const WorkingSetMember& member, BSONObj* objOut) const { BSONObj btreeKeyToUse; Status btreeStatus = getBtreeKey(member.obj.value(), &btreeKeyToUse); if (!btreeStatus.isOK()) { return btreeStatus; } if (!_sortHasMeta) { *objOut = btreeKeyToUse; return Status::OK(); } BSONObjBuilder mergedKeyBob; // Merge metadata into the key. BSONObjIterator it(_rawSortSpec); BSONObjIterator btreeIt(btreeKeyToUse); while (it.more()) { BSONElement elt = it.next(); if (elt.isNumber()) { // Merge btree key elt. mergedKeyBob.append(btreeIt.next()); } else if (LiteParsedQuery::isTextScoreMeta(elt)) { // Add text score metadata double score = 0.0; if (member.hasComputed(WSM_COMPUTED_TEXT_SCORE)) { const TextScoreComputedData* scoreData = static_cast<const TextScoreComputedData*>( member.getComputed(WSM_COMPUTED_TEXT_SCORE)); score = scoreData->getScore(); } mergedKeyBob.append("$metaTextScore", score); } } *objOut = mergedKeyBob.obj(); return Status::OK(); }