diff --git a/src/mongo/db/exec/document_value/document_metadata_fields.cpp b/src/mongo/db/exec/document_value/document_metadata_fields.cpp index c7c2e4d202da8..3d28bcbd4edf4 100644 --- a/src/mongo/db/exec/document_value/document_metadata_fields.cpp +++ b/src/mongo/db/exec/document_value/document_metadata_fields.cpp @@ -33,6 +33,8 @@ #include "mongo/bson/bsonobjbuilder.h" +#include "mongo/s/shard_id.h" + namespace mongo { namespace { @@ -173,6 +175,10 @@ void DocumentMetadataFields::serializeForSorter(BufBuilder& buf) const { buf.appendNum(static_cast(MetaType::kIndexKey + 1)); getIndexKey().appendSelfToBufBuilder(buf); } + if (hasShardName()) { + buf.appendNum(static_cast(MetaType::kShardName + 1)); + buf.appendStr(getShardName()); + } buf.appendNum(static_cast(0)); } @@ -201,6 +207,8 @@ void DocumentMetadataFields::deserializeForSorter(BufReader& buf, DocumentMetada } else if (marker == static_cast(MetaType::kIndexKey) + 1) { out->setIndexKey( BSONObj::deserializeForSorter(buf, BSONObj::SorterDeserializeSettings())); + } else if (marker == static_cast(MetaType::kShardName) + 1) { + out->setShardName(ShardId(std::string(buf.readCStr()))); } else { uasserted(28744, "Unrecognized marker, unable to deserialize buffer"); } diff --git a/src/mongo/db/exec/document_value/document_metadata_fields.h b/src/mongo/db/exec/document_value/document_metadata_fields.h index ef4627b14c6b0..a340d26097375 100644 --- a/src/mongo/db/exec/document_value/document_metadata_fields.h +++ b/src/mongo/db/exec/document_value/document_metadata_fields.h @@ -34,6 +34,7 @@ #include "mongo/bson/bsonobj.h" #include "mongo/db/exec/document_value/value.h" #include "mongo/db/record_id.h" +#include "mongo/s/shard_id.h" namespace mongo { /** @@ -64,6 +65,7 @@ class DocumentMetadataFields { kSearchScore, kSortKey, kTextScore, + kShardName, // New fields must be added before the kNumFields sentinel. kNumFields @@ -302,6 +304,24 @@ class DocumentMetadataFields { _holder->recordId = rid; } + bool hasShardName() const { + return _holder && _holder->metaFields.test(MetaType::kShardName); + } + + ShardId getShardName() const { + invariant(hasShardName()); + return _holder->shardName; + } + + void setShardName(ShardId shardName) { + if (!_holder) { + _holder = std::make_unique(); + } + + _holder->metaFields.set(MetaType::kShardName); + _holder->shardName = shardName; + } + void serializeForSorter(BufBuilder& buf) const; private: @@ -323,6 +343,7 @@ class DocumentMetadataFields { Value searchHighlights; BSONObj indexKey; RecordId recordId; + ShardId shardName; }; // Null until the first setter is called, at which point a MetadataHolder struct is allocated. diff --git a/src/mongo/db/exec/document_value/document_metadata_fields_test.cpp b/src/mongo/db/exec/document_value/document_metadata_fields_test.cpp index b19127ef22634..e4571b0404c59 100644 --- a/src/mongo/db/exec/document_value/document_metadata_fields_test.cpp +++ b/src/mongo/db/exec/document_value/document_metadata_fields_test.cpp @@ -48,6 +48,7 @@ TEST(DocumentMetadataFieldsTest, AllMetadataRoundtripsThroughSerialization) { metadata.setSearchScore(5.4); metadata.setSearchHighlights(Value{"foo"_sd}); metadata.setIndexKey(BSON("b" << 1)); + metadata.setShardName("bar"); BufBuilder builder; metadata.serializeForSorter(builder); @@ -64,6 +65,7 @@ TEST(DocumentMetadataFieldsTest, AllMetadataRoundtripsThroughSerialization) { ASSERT_EQ(deserialized.getSearchScore(), 5.4); ASSERT_VALUE_EQ(deserialized.getSearchHighlights(), Value{"foo"_sd}); ASSERT_BSONOBJ_EQ(deserialized.getIndexKey(), BSON("b" << 1)); + ASSERT_EQ(deserialized.getShardName(), "bar"); } TEST(DocumentMetadataFieldsTest, HasMethodsReturnFalseForEmptyMetadata) { @@ -77,6 +79,7 @@ TEST(DocumentMetadataFieldsTest, HasMethodsReturnFalseForEmptyMetadata) { ASSERT_FALSE(metadata.hasSearchScore()); ASSERT_FALSE(metadata.hasSearchHighlights()); ASSERT_FALSE(metadata.hasIndexKey()); + ASSERT_FALSE(metadata.hasShardName()); } TEST(DocumentMetadataFieldsTest, HasMethodsReturnTrueForInitializedMetadata) { @@ -114,6 +117,10 @@ TEST(DocumentMetadataFieldsTest, HasMethodsReturnTrueForInitializedMetadata) { ASSERT_FALSE(metadata.hasIndexKey()); metadata.setIndexKey(BSON("b" << 1)); ASSERT_TRUE(metadata.hasIndexKey()); + + ASSERT_FALSE(metadata.hasShardName()); + metadata.setShardName("bar"); + ASSERT_TRUE(metadata.hasShardName()); } TEST(DocumentMetadataFieldsTest, MoveConstructor) { @@ -126,6 +133,7 @@ TEST(DocumentMetadataFieldsTest, MoveConstructor) { metadata.setSearchScore(5.4); metadata.setSearchHighlights(Value{"foo"_sd}); metadata.setIndexKey(BSON("b" << 1)); + metadata.setShardName("bar"); DocumentMetadataFields moveConstructed(std::move(metadata)); ASSERT_TRUE(moveConstructed); @@ -138,6 +146,7 @@ TEST(DocumentMetadataFieldsTest, MoveConstructor) { ASSERT_EQ(moveConstructed.getSearchScore(), 5.4); ASSERT_VALUE_EQ(moveConstructed.getSearchHighlights(), Value{"foo"_sd}); ASSERT_BSONOBJ_EQ(moveConstructed.getIndexKey(), BSON("b" << 1)); + ASSERT_EQ(moveConstructed.getShardName(), "bar"); ASSERT_FALSE(metadata); } @@ -152,6 +161,7 @@ TEST(DocumentMetadataFieldsTest, MoveAssignmentOperator) { metadata.setSearchScore(5.4); metadata.setSearchHighlights(Value{"foo"_sd}); metadata.setIndexKey(BSON("b" << 1)); + metadata.setShardName("bar"); DocumentMetadataFields moveAssigned; moveAssigned.setTextScore(12.3); @@ -167,6 +177,7 @@ TEST(DocumentMetadataFieldsTest, MoveAssignmentOperator) { ASSERT_EQ(moveAssigned.getSearchScore(), 5.4); ASSERT_VALUE_EQ(moveAssigned.getSearchHighlights(), Value{"foo"_sd}); ASSERT_BSONOBJ_EQ(moveAssigned.getIndexKey(), BSON("b" << 1)); + ASSERT_EQ(moveAssigned.getShardName(), "bar"); ASSERT_FALSE(metadata); } diff --git a/src/mongo/db/exec/projection_exec.cpp b/src/mongo/db/exec/projection_exec.cpp index bf212a0b44b6d..a49d8fa8b9cc4 100644 --- a/src/mongo/db/exec/projection_exec.cpp +++ b/src/mongo/db/exec/projection_exec.cpp @@ -116,6 +116,9 @@ ProjectionExec::ProjectionExec(OperationContext* opCtx, } else if (e2.valuestr() == QueryRequest::metaGeoNearDistance) { _meta[e.fieldName()] = META_GEONEAR_DIST; _needsGeoNearDistance = true; + } else if (e2.valuestr() == QueryRequest::metaShardName) { + _meta[e.fieldName()] = META_SHARD_NAME; + _needsShardName = true; } else { // This shouldn't happen, should be caught by parsing. MONGO_UNREACHABLE; @@ -202,7 +205,8 @@ StatusWith ProjectionExec::project(const BSONObj& in, Value geoNearPoint, const BSONObj& sortKey, const boost::optional textScore, - const int64_t recordId) const { + const int64_t recordId, + const StringData& shardName) const { BSONObjBuilder bob; MatchDetails matchDetails; @@ -217,7 +221,7 @@ StatusWith ProjectionExec::project(const BSONObj& in, if (!projStatus.isOK()) return projStatus; else - return {addMeta(std::move(bob), geoDistance, geoNearPoint, sortKey, textScore, recordId)}; + return {addMeta(std::move(bob), geoDistance, geoNearPoint, sortKey, textScore, recordId, shardName)}; } StatusWith ProjectionExec::projectCovered(const std::vector& keyData, @@ -225,7 +229,8 @@ StatusWith ProjectionExec::projectCovered(const std::vector textScore, - const int64_t recordId) const { + const int64_t recordId, + const StringData& shardName) const { invariant(!_include); BSONObjBuilder bob; // Go field by field. @@ -270,7 +275,7 @@ StatusWith ProjectionExec::projectCovered(const std::vector textScore, - const int64_t recordId) const { + const int64_t recordId, + const StringData& shardName) const { for (MetaMap::const_iterator it = _meta.begin(); it != _meta.end(); ++it) { switch (it->second) { case META_GEONEAR_DIST: @@ -299,6 +305,10 @@ BSONObj ProjectionExec::addMeta(BSONObjBuilder bob, bob.append(it->first, sortKey); break; } + case META_SHARD_NAME: { + bob.append(it->first, shardName); + break; + } case META_RECORDID: invariant(recordId != 0); bob.append(it->first, recordId); diff --git a/src/mongo/db/exec/projection_exec.h b/src/mongo/db/exec/projection_exec.h index 784fe2fb209cf..029d9ee94b624 100644 --- a/src/mongo/db/exec/projection_exec.h +++ b/src/mongo/db/exec/projection_exec.h @@ -63,6 +63,7 @@ class ProjectionExec { META_RECORDID, META_SORT_KEY, META_TEXT_SCORE, + META_SHARD_NAME, }; /** @@ -108,6 +109,13 @@ class ProjectionExec { return _needsTextScore; } + /** + * Indicates whether 'shardName' is going to be used in 'project()'. + */ + bool needsShardName() const { + return _needsShardName; + } + /** * Returns false if there are no meta fields to project. */ @@ -124,7 +132,8 @@ class ProjectionExec { Value geoNearPoint = Value{}, const BSONObj& sortKey = BSONObj(), const boost::optional textScore = boost::none, - const int64_t recordId = 0) const; + const int64_t recordId = 0, + const StringData& shardName = StringData("")) const; /** * Performs a projection given index 'KeyData' to directly retrieve results. This function @@ -137,7 +146,8 @@ class ProjectionExec { Value geoNearPoint = Value{}, const BSONObj& sortKey = BSONObj(), const boost::optional textScore = boost::none, - const int64_t recordId = 0) const; + const int64_t recordId = 0, + const StringData& shardName = StringData("")) const; /** * Determines if calls to the project method require that this object was created with the full @@ -157,7 +167,8 @@ class ProjectionExec { Value geoNearPoint, const BSONObj& sortKey, const boost::optional textScore, - const int64_t recordId) const; + const int64_t recordId, + const StringData& shardName) const; // // Initialization @@ -251,6 +262,7 @@ class ProjectionExec { bool _needsGeoNearDistance = false; bool _needsGeoNearPoint = false; bool _needsTextScore = false; + bool _needsShardName = false; // The collator this projection should use to compare strings. Needed for projection operators // that perform matching (e.g. elemMatch projection). If null, the collation is a simple binary diff --git a/src/mongo/db/exec/shard_filter.cpp b/src/mongo/db/exec/shard_filter.cpp index 3ca72e989a02a..bd048df21513f 100644 --- a/src/mongo/db/exec/shard_filter.cpp +++ b/src/mongo/db/exec/shard_filter.cpp @@ -38,6 +38,7 @@ #include "mongo/db/exec/filter.h" #include "mongo/db/exec/scoped_timer.h" #include "mongo/db/exec/working_set_common.h" +#include "mongo/db/s/sharding_state.h" #include "mongo/s/shard_key_pattern.h" #include "mongo/util/log.h" @@ -53,8 +54,10 @@ const char* ShardFilterStage::kStageType = "SHARDING_FILTER"; ShardFilterStage::ShardFilterStage(OperationContext* opCtx, ScopedCollectionMetadata metadata, WorkingSet* ws, - std::unique_ptr child) - : PlanStage(kStageType, opCtx), _ws(ws), _shardFilterer(std::move(metadata)) { + std::unique_ptr child, + bool wantShardName) + : PlanStage(kStageType, opCtx), _ws(ws), _shardFilterer(std::move(metadata)), + _wantShardName(wantShardName) { _children.emplace_back(std::move(child)); } @@ -104,6 +107,13 @@ PlanStage::StageState ShardFilterStage::doWork(WorkingSetID* out) { ++_specificStats.chunkSkips; return PlanStage::NEED_TIME; } + + if(wantShardName()) { + auto sharding = ShardingState::get(this->getOpCtx()); + + // Populate the working set member with the shard name and return it. + member->metadata().setShardName(sharding->shardId().toString()); + } } // If we're here either we have shard state and our doc passed, or we have no shard diff --git a/src/mongo/db/exec/shard_filter.h b/src/mongo/db/exec/shard_filter.h index 5fdb2a2a2476b..b29053af6b00e 100644 --- a/src/mongo/db/exec/shard_filter.h +++ b/src/mongo/db/exec/shard_filter.h @@ -74,7 +74,8 @@ class ShardFilterStage final : public PlanStage { ShardFilterStage(OperationContext* opCtx, ScopedCollectionMetadata metadata, WorkingSet* ws, - std::unique_ptr child); + std::unique_ptr child, + bool wantShardName); ~ShardFilterStage(); bool isEOF() final; @@ -91,6 +92,10 @@ class ShardFilterStage final : public PlanStage { static const char* kStageType; private: + bool wantShardName() const { + return _wantShardName; + } + WorkingSet* _ws; // Stats @@ -103,6 +108,8 @@ class ShardFilterStage final : public PlanStage { // ScopedCollectionMetadata for the entire query, it'd be possible for data which the query // needs to read to be deleted while it's still running. ShardFiltererImpl _shardFilterer; + + bool _wantShardName; }; } // namespace mongo diff --git a/src/mongo/db/pipeline/expression.cpp b/src/mongo/db/pipeline/expression.cpp index e69ff21aa5d12..d37d13e8ae4c3 100644 --- a/src/mongo/db/pipeline/expression.cpp +++ b/src/mongo/db/pipeline/expression.cpp @@ -27,7 +27,6 @@ * it in the license file. */ - #include "mongo/platform/basic.h" #include "mongo/db/pipeline/expression.h" @@ -2546,6 +2545,7 @@ const std::string geoNearPointName = "geoNearPoint"; const std::string recordIdName = "recordId"; const std::string indexKeyName = "indexKey"; const std::string sortKeyName = "sortKey"; +const std::string shardName = "shardName"; using MetaType = DocumentMetadataFields::MetaType; const StringMap kMetaNameToMetaType = { @@ -2558,6 +2558,7 @@ const StringMap kMetaNameToMetaType = { {searchScoreName, MetaType::kSearchScore}, {sortKeyName, MetaType::kSortKey}, {textScoreName, MetaType::kTextScore}, + {shardName, MetaType::kShardName}, }; const stdx::unordered_map kMetaTypeToMetaName = { @@ -2570,6 +2571,7 @@ const stdx::unordered_map kMetaTyp {MetaType::kSearchScore, searchScoreName}, {MetaType::kSortKey, sortKeyName}, {MetaType::kTextScore, textScoreName}, + {MetaType::kShardName, shardName}, }; } // namespace @@ -2643,6 +2645,10 @@ Value ExpressionMeta::evaluate(const Document& root, Variables* variables) const ? Value(DocumentMetadataFields::serializeSortKey(metadata.isSingleElementKey(), metadata.getSortKey())) : Value(); + case MetaType::kShardName: + return metadata.hasShardName() + ? Value(metadata.getShardName().toString()) + : Value(); default: MONGO_UNREACHABLE; } diff --git a/src/mongo/db/pipeline/pipeline_d.cpp b/src/mongo/db/pipeline/pipeline_d.cpp index 59bf5c596a08f..0fdc830bd360f 100644 --- a/src/mongo/db/pipeline/pipeline_d.cpp +++ b/src/mongo/db/pipeline/pipeline_d.cpp @@ -167,12 +167,12 @@ StatusWith> createRandomCursorEx sampleSize / (numRecords * kMaxSampleRatioForRandCursor), kMaxSampleRatioForRandCursor); // The trial plan is SHARDING_FILTER-MULTI_ITERATOR. auto randomCursorPlan = - std::make_unique(opCtx, shardMetadata, ws.get(), std::move(root)); + std::make_unique(opCtx, shardMetadata, ws.get(), std::move(root), false); // The backup plan is SHARDING_FILTER-COLLSCAN. std::unique_ptr collScanPlan = std::make_unique( opCtx, coll, CollectionScanParams{}, ws.get(), nullptr); collScanPlan = std::make_unique( - opCtx, shardMetadata, ws.get(), std::move(collScanPlan)); + opCtx, shardMetadata, ws.get(), std::move(collScanPlan), false); // Place a TRIAL stage at the root of the plan tree, and pass it the trial and backup plans. root = std::make_unique(opCtx, ws.get(), diff --git a/src/mongo/db/query/get_executor.cpp b/src/mongo/db/query/get_executor.cpp index ced708e90dcad..7a4f774fbdd72 100644 --- a/src/mongo/db/query/get_executor.cpp +++ b/src/mongo/db/query/get_executor.cpp @@ -382,12 +382,14 @@ StatusWith prepareExecution(OperationContext* opCtx, // Might have to filter out orphaned docs. if (plannerParams.options & QueryPlannerParams::INCLUDE_SHARD_FILTER) { + auto wantShardName = canonicalQuery->metadataDeps()[DocumentMetadataFields::kShardName]; root = std::make_unique( opCtx, CollectionShardingState::get(opCtx, canonicalQuery->nss()) ->getOrphansFilter(opCtx, collection), ws, - std::move(root)); + std::move(root), + wantShardName); } const auto* cqProjection = canonicalQuery->getProj(); @@ -418,7 +420,8 @@ StatusWith prepareExecution(OperationContext* opCtx, // document, so we don't support covered projections. However, we might use the // simple inclusion fast path. // Stuff the right data into the params depending on what proj impl we use. - if (!cqProjection->isSimple()) { + if (!cqProjection->isSimple() || + canonicalQuery->metadataDeps()[DocumentMetadataFields::kShardName]) { root = std::make_unique( canonicalQuery->getExpCtx(), canonicalQuery->getQueryRequest().getProj(), diff --git a/src/mongo/db/query/parsed_projection.cpp b/src/mongo/db/query/parsed_projection.cpp index 165a27e66bb61..ed839e877a152 100644 --- a/src/mongo/db/query/parsed_projection.cpp +++ b/src/mongo/db/query/parsed_projection.cpp @@ -161,7 +161,8 @@ Status ParsedProjection::make(OperationContext* opCtx, e2.valuestr() != QueryRequest::metaRecordId && e2.valuestr() != QueryRequest::metaGeoNearDistance && e2.valuestr() != QueryRequest::metaGeoNearPoint && - e2.valuestr() != QueryRequest::metaSortKey) { + e2.valuestr() != QueryRequest::metaSortKey && + e2.valuestr() != QueryRequest::metaShardName) { return Status(ErrorCodes::BadValue, "unsupported $meta operator: " + e2.str()); } diff --git a/src/mongo/db/query/planner_analysis.cpp b/src/mongo/db/query/planner_analysis.cpp index 27405653ae712..01ad93c767afe 100644 --- a/src/mongo/db/query/planner_analysis.cpp +++ b/src/mongo/db/query/planner_analysis.cpp @@ -384,7 +384,6 @@ std::unique_ptr analyzeProjection(const CanonicalQuery& query, } } } - return std::make_unique( addSortKeyGeneratorStageIfNeeded(query, hasSortStage, std::move(solnRoot)), *query.root(), @@ -762,7 +761,8 @@ std::unique_ptr QueryPlannerAnalysis::analyzeDataAccess( } } - ShardingFilterNode* sfn = new ShardingFilterNode(); + auto wantShardName = query.metadataDeps()[DocumentMetadataFields::kShardName]; + ShardingFilterNode* sfn = new ShardingFilterNode(wantShardName); sfn->children.push_back(solnRoot.release()); solnRoot.reset(sfn); } diff --git a/src/mongo/db/query/query_request.cpp b/src/mongo/db/query/query_request.cpp index d50d8a10f4db7..b2e9fdb8b3f93 100644 --- a/src/mongo/db/query/query_request.cpp +++ b/src/mongo/db/query/query_request.cpp @@ -61,6 +61,7 @@ const string QueryRequest::metaGeoNearPoint("geoNearPoint"); const string QueryRequest::metaRecordId("recordId"); const string QueryRequest::metaSortKey("sortKey"); const string QueryRequest::metaTextScore("textScore"); +const string QueryRequest::metaShardName("shardName"); const string QueryRequest::kAllowDiskUseField("allowDiskUse"); @@ -600,9 +601,10 @@ Status QueryRequest::validate() const { BSONObjIterator projIt(_proj); while (projIt.more()) { BSONElement projElt = projIt.next(); - if (isTextScoreMeta(projElt)) { + if (isTextScoreMeta(projElt) || isShardNameMeta(projElt)) { BSONElement sortElt = _sort[projElt.fieldName()]; - if (!sortElt.eoo() && !isTextScoreMeta(sortElt)) { + if (!sortElt.eoo() && ((isTextScoreMeta(projElt) && !isTextScoreMeta(sortElt)) || + (isShardNameMeta(projElt) && !isShardNameMeta(sortElt)))) { return Status(ErrorCodes::BadValue, "can't have a non-$meta sort on a $meta projection"); } @@ -617,9 +619,9 @@ Status QueryRequest::validate() const { BSONObjIterator sortIt(_sort); while (sortIt.more()) { BSONElement sortElt = sortIt.next(); - if (isTextScoreMeta(sortElt)) { + if (isTextScoreMeta(sortElt) || isShardNameMeta(sortElt)) { BSONElement projElt = _proj[sortElt.fieldName()]; - if (projElt.eoo() || !isTextScoreMeta(projElt)) { + if (projElt.eoo() || (isTextScoreMeta(sortElt) && !isTextScoreMeta(projElt))) { return Status(ErrorCodes::BadValue, "must have $meta projection for all $meta sort keys"); } @@ -704,8 +706,8 @@ StatusWith QueryRequest::parseMaxTimeMS(BSONElement maxTimeMSElt) { } // static -bool QueryRequest::isTextScoreMeta(BSONElement elt) { - // elt must be foo: {$meta: "textScore"} +bool QueryRequest::isMeta(BSONElement elt, const string& metaName) { + // elt must be foo: {$meta: "metaName"} if (mongo::Object != elt.type()) { return false; } @@ -722,7 +724,7 @@ bool QueryRequest::isTextScoreMeta(BSONElement elt) { if (mongo::String != metaElt.type()) { return false; } - if (QueryRequest::metaTextScore != metaElt.valuestr()) { + if (metaName != metaElt.valuestr()) { return false; } // must have exactly 1 element @@ -732,6 +734,18 @@ bool QueryRequest::isTextScoreMeta(BSONElement elt) { return true; } +// static +bool QueryRequest::isTextScoreMeta(BSONElement elt) { + // elt must be foo: {$meta: "textScore"} + return isMeta(elt, QueryRequest::metaTextScore); +} + +// static +bool QueryRequest::isShardNameMeta(BSONElement elt) { + // elt must be foo: {$meta: "shardName"} + return isMeta(elt, QueryRequest::metaShardName); +} + // static bool QueryRequest::isValidSortOrder(const BSONObj& sortObj) { BSONObjIterator i(sortObj); diff --git a/src/mongo/db/query/query_request.h b/src/mongo/db/query/query_request.h index a204e45c39b61..30ca49e5ebb6f 100644 --- a/src/mongo/db/query/query_request.h +++ b/src/mongo/db/query/query_request.h @@ -109,6 +109,12 @@ class QueryRequest { */ static bool isTextScoreMeta(BSONElement elt); + /** + * Helper function to identify shard name. + * Example: {a: {$meta: "shardName"}} + */ + static bool isShardNameMeta(BSONElement elt); + /** * Helper function to validate a sort object. * Returns true if each element satisfies one of: @@ -138,6 +144,7 @@ class QueryRequest { static const std::string metaRecordId; static const std::string metaSortKey; static const std::string metaTextScore; + static const std::string metaShardName; // Allow using disk during the find command. static const std::string kAllowDiskUseField; @@ -434,6 +441,12 @@ class QueryRequest { int queryOptions); private: + /** + * Helper function to identify meta with names. + * Example: {a: {$meta: "textScore"}} for metaName=metaTextScore. + */ + static bool isMeta(BSONElement elt, const std::string& metaName); + static StatusWith> parseFromFindCommand( std::unique_ptr qr, const BSONObj& cmdObj, bool isExplain); Status init(int ntoskip, diff --git a/src/mongo/db/query/query_solution.cpp b/src/mongo/db/query/query_solution.cpp index 320f54978a454..109711b05728e 100644 --- a/src/mongo/db/query/query_solution.cpp +++ b/src/mongo/db/query/query_solution.cpp @@ -1125,6 +1125,9 @@ QuerySolutionNode* GeoNear2DSphereNode::clone() const { void ShardingFilterNode::appendToString(str::stream* ss, int indent) const { addIndent(ss, indent); *ss << "SHARDING_FILTER\n"; + if (wantShardName()) { + *ss << "with shard name\n"; + } if (nullptr != filter) { addIndent(ss, indent + 1); StringBuilder sb; @@ -1139,7 +1142,7 @@ void ShardingFilterNode::appendToString(str::stream* ss, int indent) const { } QuerySolutionNode* ShardingFilterNode::clone() const { - ShardingFilterNode* copy = new ShardingFilterNode(); + ShardingFilterNode* copy = new ShardingFilterNode(this->wantShardName()); cloneBaseData(copy); return copy; } diff --git a/src/mongo/db/query/query_solution.h b/src/mongo/db/query/query_solution.h index 109241a2d54d4..17f489251760e 100644 --- a/src/mongo/db/query/query_solution.h +++ b/src/mongo/db/query/query_solution.h @@ -915,7 +915,7 @@ struct GeoNear2DSphereNode : public QuerySolutionNode { * through the pipeline. */ struct ShardingFilterNode : public QuerySolutionNode { - ShardingFilterNode() {} + ShardingFilterNode(bool wantShardName): _wantShardName(wantShardName) {} virtual ~ShardingFilterNode() {} virtual StageType getType() const { @@ -935,8 +935,14 @@ struct ShardingFilterNode : public QuerySolutionNode { const BSONObjSet& getSort() const { return children[0]->getSort(); } + bool wantShardName() const { + return _wantShardName; + } QuerySolutionNode* clone() const; + +private: + bool _wantShardName; }; /** diff --git a/src/mongo/db/query/stage_builder.cpp b/src/mongo/db/query/stage_builder.cpp index a3cc6aee999a8..6f008f5b2b4fc 100644 --- a/src/mongo/db/query/stage_builder.cpp +++ b/src/mongo/db/query/stage_builder.cpp @@ -276,7 +276,8 @@ std::unique_ptr buildStages(OperationContext* opCtx, auto css = CollectionShardingState::get(opCtx, collection->ns()); return std::make_unique( - opCtx, css->getOrphansFilter(opCtx, collection), ws, std::move(childStage)); + opCtx, css->getOrphansFilter(opCtx, collection), ws, std::move(childStage), + fn->wantShardName()); } case STAGE_DISTINCT_SCAN: { const DistinctNode* dn = static_cast(root);