diff options
Diffstat (limited to 'src/mongo/db/pipeline')
-rw-r--r-- | src/mongo/db/pipeline/document.cpp | 51 | ||||
-rw-r--r-- | src/mongo/db/pipeline/document.h | 12 | ||||
-rw-r--r-- | src/mongo/db/pipeline/document_metadata_fields.cpp | 14 | ||||
-rw-r--r-- | src/mongo/db/pipeline/document_metadata_fields.h | 19 | ||||
-rw-r--r-- | src/mongo/db/pipeline/document_metadata_fields_test.cpp | 28 | ||||
-rw-r--r-- | src/mongo/db/pipeline/document_source_change_stream_test.cpp | 5 | ||||
-rw-r--r-- | src/mongo/db/pipeline/document_source_change_stream_transform.cpp | 3 | ||||
-rw-r--r-- | src/mongo/db/pipeline/document_source_check_invalidate.cpp | 3 | ||||
-rw-r--r-- | src/mongo/db/pipeline/document_source_geo_near_cursor.cpp | 3 | ||||
-rw-r--r-- | src/mongo/db/pipeline/document_source_sample_from_random_cursor.cpp | 3 | ||||
-rw-r--r-- | src/mongo/db/pipeline/document_source_sort.cpp | 11 | ||||
-rw-r--r-- | src/mongo/db/pipeline/document_value_test.cpp | 2 | ||||
-rw-r--r-- | src/mongo/db/pipeline/expression.cpp | 5 | ||||
-rw-r--r-- | src/mongo/db/pipeline/expression_context.h | 4 | ||||
-rw-r--r-- | src/mongo/db/pipeline/expression_test.cpp | 6 | ||||
-rw-r--r-- | src/mongo/db/pipeline/pipeline_d.cpp | 5 |
16 files changed, 126 insertions, 48 deletions
diff --git a/src/mongo/db/pipeline/document.cpp b/src/mongo/db/pipeline/document.cpp index cf7c58a819e..9ffab53c34c 100644 --- a/src/mongo/db/pipeline/document.cpp +++ b/src/mongo/db/pipeline/document.cpp @@ -36,6 +36,7 @@ #include "mongo/bson/bson_depth.h" #include "mongo/db/jsobj.h" #include "mongo/db/pipeline/field_path.h" +#include "mongo/db/pipeline/resume_token.h" #include "mongo/util/str.h" namespace mongo { @@ -380,7 +381,37 @@ void DocumentStorage::loadLazyMetadata() const { } else if (fieldName == Document::metaFieldRandVal) { _metadataFields.setRandVal(elem.Double()); } else if (fieldName == Document::metaFieldSortKey) { - _metadataFields.setSortKey(elem.Obj()); + auto bsonSortKey = elem.Obj(); + + bool isSingleElementKey = false; + + BSONObjIterator sortKeyIt(bsonSortKey); + uassert(31282, "Empty sort key in metadata", sortKeyIt.more()); + auto firstElementName = sortKeyIt.next().fieldNameStringData(); + + // If the sort key has exactly one field, we say it is a "single element key." + boost::optional<StringData> secondElementName; + if (!sortKeyIt.more()) { + isSingleElementKey = true; + } else { + secondElementName = sortKeyIt.next().fieldNameStringData(); + } + + // If the sort key looks like {_data: ...} or {_data: ..., _typeBits: ...}, we know + // that it came from a change stream, and we also treat it as a "single element + // key." + if (!sortKeyIt.more() && (firstElementName == ResumeToken::kDataFieldName) && + (!secondElementName || secondElementName == ResumeToken::kTypeBitsFieldName)) { + // TODO (SERVER-43361): In 4.2 and earlier, the "sort key" for a change stream + // document gets serialized differently than sort keys for normal pipeline + // documents. + isSingleElementKey = true; + _metadataFields.setSortKey(Value(bsonSortKey), isSingleElementKey); + } else { + _metadataFields.setSortKey( + DocumentMetadataFields::deserializeSortKey(isSingleElementKey, bsonSortKey), + isSingleElementKey); + } } else if (fieldName == Document::metaFieldGeoNearDistance) { _metadataFields.setGeoNearDistance(elem.Double()); } else if (fieldName == Document::metaFieldGeoNearPoint) { @@ -459,15 +490,27 @@ constexpr StringData Document::metaFieldGeoNearPoint; constexpr StringData Document::metaFieldSearchScore; constexpr StringData Document::metaFieldSearchHighlights; -BSONObj Document::toBsonWithMetaData() const { +BSONObj Document::toBsonWithMetaData(bool use42ChangeStreamSortKeys) const { BSONObjBuilder bb; toBson(&bb); if (metadata().hasTextScore()) bb.append(metaFieldTextScore, metadata().getTextScore()); if (metadata().hasRandVal()) bb.append(metaFieldRandVal, metadata().getRandVal()); - if (metadata().hasSortKey()) - bb.append(metaFieldSortKey, metadata().getSortKey()); + if (metadata().hasSortKey()) { + if (use42ChangeStreamSortKeys) { + // TODO (SERVER-43361): In 4.2 and earlier, the "sort key" for a change stream document + // gets serialized differently than sort keys for normal pipeline documents. Once we no + // longer need to support that format, we can remove the 'use42ChangeStreamSortKeys' + // flag and this special case along with it. + invariant(metadata().isSingleElementKey()); + metadata().getSortKey().addToBsonObj(&bb, metaFieldSortKey); + } else { + bb.append(metaFieldSortKey, + DocumentMetadataFields::serializeSortKey(metadata().isSingleElementKey(), + metadata().getSortKey())); + } + } if (metadata().hasGeoNearDistance()) bb.append(metaFieldGeoNearDistance, metadata().getGeoNearDistance()); if (metadata().hasGeoNearPoint()) diff --git a/src/mongo/db/pipeline/document.h b/src/mongo/db/pipeline/document.h index fefbcbcdd67..3be4444c95d 100644 --- a/src/mongo/db/pipeline/document.h +++ b/src/mongo/db/pipeline/document.h @@ -222,10 +222,14 @@ public: BSONObj toBson() const; /** - * Like toBson, but includes metadata at the top-level. - * Output is parseable by fromBsonWithMetaData - */ - BSONObj toBsonWithMetaData() const; + * Like the 'toBson()' method, but includes metadata at the top-level. When + * 'use42ChangeStreamSortKeys' is true, we assume that any Value in the "sortKey" metadata + * represents the resume token, which gets assigned directly to the "$sortKey" field. Otherwise, + * the "$sortKey" field gets assigned using DocumentMetadataFields::serializeSortKey(). Output + * is parseable by the 'fromBsonWithMetaData()' method. Note that parsing is able to infer the + * value of 'use42ChangeStreamSortKeys' from the format of the '$sortKey' field. + */ + BSONObj toBsonWithMetaData(bool use42ChangeStreamSortKeys = false) const; /** * Like Document(BSONObj) but treats top-level fields with special names as metadata. diff --git a/src/mongo/db/pipeline/document_metadata_fields.cpp b/src/mongo/db/pipeline/document_metadata_fields.cpp index 73120cd68ed..62617144b03 100644 --- a/src/mongo/db/pipeline/document_metadata_fields.cpp +++ b/src/mongo/db/pipeline/document_metadata_fields.cpp @@ -65,7 +65,7 @@ void DocumentMetadataFields::mergeWith(const DocumentMetadataFields& other) { setRandVal(other.getRandVal()); } if (!hasSortKey() && other.hasSortKey()) { - setSortKey(other.getSortKey()); + setSortKey(other.getSortKey(), other.isSingleElementKey()); } if (!hasGeoNearDistance() && other.hasGeoNearDistance()) { setGeoNearDistance(other.getGeoNearDistance()); @@ -92,7 +92,7 @@ void DocumentMetadataFields::copyFrom(const DocumentMetadataFields& other) { setRandVal(other.getRandVal()); } if (other.hasSortKey()) { - setSortKey(other.getSortKey()); + setSortKey(other.getSortKey(), other.isSingleElementKey()); } if (other.hasGeoNearDistance()) { setGeoNearDistance(other.getGeoNearDistance()); @@ -121,7 +121,7 @@ size_t DocumentMetadataFields::getApproximateSize() const { size_t size = sizeof(MetadataHolder); // Count the "deep" portion of the metadata values. - size += _holder->sortKey.objsize(); + size += _holder->sortKey.getApproximateSize(); size += _holder->geoNearPoint.getApproximateSize(); // Size of Value is double counted - once in sizeof(MetadataFields) and once in // getApproximateSize() @@ -150,7 +150,8 @@ void DocumentMetadataFields::serializeForSorter(BufBuilder& buf) const { } if (hasSortKey()) { buf.appendNum(static_cast<char>(MetaType::kSortKey + 1)); - getSortKey().appendSelfToBufBuilder(buf); + buf.appendChar(isSingleElementKey() ? 1 : 0); + getSortKey().serializeForSorter(buf); } if (hasGeoNearDistance()) { buf.appendNum(static_cast<char>(MetaType::kGeoNearDist + 1)); @@ -184,8 +185,9 @@ void DocumentMetadataFields::deserializeForSorter(BufReader& buf, DocumentMetada } else if (marker == static_cast<char>(MetaType::kRandVal) + 1) { out->setRandVal(buf.read<LittleEndian<double>>()); } else if (marker == static_cast<char>(MetaType::kSortKey) + 1) { - out->setSortKey( - BSONObj::deserializeForSorter(buf, BSONObj::SorterDeserializeSettings())); + char isSingleElementKey = buf.read<char>(); + out->setSortKey(Value::deserializeForSorter(buf, Value::SorterDeserializeSettings()), + isSingleElementKey); } else if (marker == static_cast<char>(MetaType::kGeoNearDist) + 1) { out->setGeoNearDistance(buf.read<LittleEndian<double>>()); } else if (marker == static_cast<char>(MetaType::kGeoNearPoint) + 1) { diff --git a/src/mongo/db/pipeline/document_metadata_fields.h b/src/mongo/db/pipeline/document_metadata_fields.h index 609aa8c4c5c..59db8dc941f 100644 --- a/src/mongo/db/pipeline/document_metadata_fields.h +++ b/src/mongo/db/pipeline/document_metadata_fields.h @@ -170,18 +170,23 @@ public: return _holder && _holder->metaFields.test(MetaType::kSortKey); } - BSONObj getSortKey() const { + Value getSortKey() const { invariant(hasSortKey()); return _holder->sortKey; } - void setSortKey(BSONObj sortKey) { + void setSortKey(Value sortKey, bool isSingleElementKey) { if (!_holder) { _holder = std::make_unique<MetadataHolder>(); } _holder->metaFields.set(MetaType::kSortKey); - _holder->sortKey = sortKey.getOwned(); + _holder->isSingleElementKey = isSingleElementKey; + _holder->sortKey = std::move(sortKey); + } + + bool isSingleElementKey() const { + return _holder && _holder->isSingleElementKey; } bool hasGeoNearDistance() const { @@ -298,9 +303,15 @@ private: // A simple data struct housing all possible metadata fields. struct MetadataHolder { std::bitset<MetaType::kNumFields> metaFields; + + // True when the sort key corresponds to a single-element sort pattern, meaning that + // comparisons should treat the sort key value as a single element, even if it is an array. + // Only relevant when 'kSortKey' is set. + bool isSingleElementKey; + double textScore{0.0}; double randVal{0.0}; - BSONObj sortKey; + Value sortKey; double geoNearDistance{0.0}; Value geoNearPoint; double searchScore{0.0}; diff --git a/src/mongo/db/pipeline/document_metadata_fields_test.cpp b/src/mongo/db/pipeline/document_metadata_fields_test.cpp index 5e46ea7de7f..fc9c8be8779 100644 --- a/src/mongo/db/pipeline/document_metadata_fields_test.cpp +++ b/src/mongo/db/pipeline/document_metadata_fields_test.cpp @@ -42,7 +42,7 @@ TEST(DocumentMetadataFieldsTest, AllMetadataRoundtripsThroughSerialization) { DocumentMetadataFields metadata; metadata.setTextScore(9.9); metadata.setRandVal(42.0); - metadata.setSortKey(BSON("a" << 1)); + metadata.setSortKey(Value(1), /* isSingleElementKey = */ true); metadata.setGeoNearDistance(3.2); metadata.setGeoNearPoint(Value{BSON_ARRAY(1 << 2)}); metadata.setSearchScore(5.4); @@ -57,7 +57,8 @@ TEST(DocumentMetadataFieldsTest, AllMetadataRoundtripsThroughSerialization) { ASSERT_EQ(deserialized.getTextScore(), 9.9); ASSERT_EQ(deserialized.getRandVal(), 42.0); - ASSERT_BSONOBJ_EQ(deserialized.getSortKey(), BSON("a" << 1)); + ASSERT_VALUE_EQ(deserialized.getSortKey(), Value(1)); + ASSERT_TRUE(deserialized.isSingleElementKey()); ASSERT_EQ(deserialized.getGeoNearDistance(), 3.2); ASSERT_VALUE_EQ(deserialized.getGeoNearPoint(), Value{BSON_ARRAY(1 << 2)}); ASSERT_EQ(deserialized.getSearchScore(), 5.4); @@ -90,8 +91,9 @@ TEST(DocumentMetadataFieldsTest, HasMethodsReturnTrueForInitializedMetadata) { ASSERT_TRUE(metadata.hasRandVal()); ASSERT_FALSE(metadata.hasSortKey()); - metadata.setSortKey(BSON("a" << 1)); + metadata.setSortKey(Value(1), /* isSingleElementKey = */ true); ASSERT_TRUE(metadata.hasSortKey()); + ASSERT_TRUE(metadata.isSingleElementKey()); ASSERT_FALSE(metadata.hasGeoNearDistance()); metadata.setGeoNearDistance(3.2); @@ -118,7 +120,7 @@ TEST(DocumentMetadataFieldsTest, MoveConstructor) { DocumentMetadataFields metadata; metadata.setTextScore(9.9); metadata.setRandVal(42.0); - metadata.setSortKey(BSON("a" << 1)); + metadata.setSortKey(Value(1), /* isSingleElementKey = */ true); metadata.setGeoNearDistance(3.2); metadata.setGeoNearPoint(Value{BSON_ARRAY(1 << 2)}); metadata.setSearchScore(5.4); @@ -129,7 +131,8 @@ TEST(DocumentMetadataFieldsTest, MoveConstructor) { ASSERT_TRUE(moveConstructed); ASSERT_EQ(moveConstructed.getTextScore(), 9.9); ASSERT_EQ(moveConstructed.getRandVal(), 42.0); - ASSERT_BSONOBJ_EQ(moveConstructed.getSortKey(), BSON("a" << 1)); + ASSERT_VALUE_EQ(moveConstructed.getSortKey(), Value(1)); + ASSERT_TRUE(moveConstructed.isSingleElementKey()); ASSERT_EQ(moveConstructed.getGeoNearDistance(), 3.2); ASSERT_VALUE_EQ(moveConstructed.getGeoNearPoint(), Value{BSON_ARRAY(1 << 2)}); ASSERT_EQ(moveConstructed.getSearchScore(), 5.4); @@ -143,7 +146,7 @@ TEST(DocumentMetadataFieldsTest, MoveAssignmentOperator) { DocumentMetadataFields metadata; metadata.setTextScore(9.9); metadata.setRandVal(42.0); - metadata.setSortKey(BSON("a" << 1)); + metadata.setSortKey(Value(1), /* isSingleElementKey = */ true); metadata.setGeoNearDistance(3.2); metadata.setGeoNearPoint(Value{BSON_ARRAY(1 << 2)}); metadata.setSearchScore(5.4); @@ -157,7 +160,8 @@ TEST(DocumentMetadataFieldsTest, MoveAssignmentOperator) { ASSERT_EQ(moveAssigned.getTextScore(), 9.9); ASSERT_EQ(moveAssigned.getRandVal(), 42.0); - ASSERT_BSONOBJ_EQ(moveAssigned.getSortKey(), BSON("a" << 1)); + ASSERT_VALUE_EQ(moveAssigned.getSortKey(), Value(1)); + ASSERT_TRUE(moveAssigned.isSingleElementKey()); ASSERT_EQ(moveAssigned.getGeoNearDistance(), 3.2); ASSERT_VALUE_EQ(moveAssigned.getGeoNearPoint(), Value{BSON_ARRAY(1 << 2)}); ASSERT_EQ(moveAssigned.getSearchScore(), 5.4); @@ -197,7 +201,7 @@ TEST(DocumentMetadataFieldsTest, MergeWithOnlyCopiesMetadataThatDestinationDoesN DocumentMetadataFields source; source.setTextScore(9.9); source.setRandVal(42.0); - source.setSortKey(BSON("a" << 1)); + source.setSortKey(Value(1), /* isSingleElementKey = */ true); source.setGeoNearDistance(3.2); DocumentMetadataFields destination; @@ -208,7 +212,8 @@ TEST(DocumentMetadataFieldsTest, MergeWithOnlyCopiesMetadataThatDestinationDoesN ASSERT_EQ(destination.getTextScore(), 12.3); ASSERT_EQ(destination.getRandVal(), 84.0); - ASSERT_BSONOBJ_EQ(destination.getSortKey(), BSON("a" << 1)); + ASSERT_VALUE_EQ(destination.getSortKey(), Value(1)); + ASSERT_TRUE(destination.isSingleElementKey()); ASSERT_EQ(destination.getGeoNearDistance(), 3.2); ASSERT_FALSE(destination.hasGeoNearPoint()); ASSERT_FALSE(destination.hasSearchScore()); @@ -220,7 +225,7 @@ TEST(DocumentMetadataFieldsTest, CopyFromCopiesAllMetadataThatSourceHas) { DocumentMetadataFields source; source.setTextScore(9.9); source.setRandVal(42.0); - source.setSortKey(BSON("a" << 1)); + source.setSortKey(Value(1), /* isSingleElementKey = */ true); source.setGeoNearDistance(3.2); DocumentMetadataFields destination; @@ -231,7 +236,8 @@ TEST(DocumentMetadataFieldsTest, CopyFromCopiesAllMetadataThatSourceHas) { ASSERT_EQ(destination.getTextScore(), 9.9); ASSERT_EQ(destination.getRandVal(), 42.0); - ASSERT_BSONOBJ_EQ(destination.getSortKey(), BSON("a" << 1)); + ASSERT_VALUE_EQ(destination.getSortKey(), Value(1)); + ASSERT_TRUE(destination.isSingleElementKey()); ASSERT_EQ(destination.getGeoNearDistance(), 3.2); ASSERT_FALSE(destination.hasGeoNearPoint()); ASSERT_FALSE(destination.hasSearchScore()); diff --git a/src/mongo/db/pipeline/document_source_change_stream_test.cpp b/src/mongo/db/pipeline/document_source_change_stream_test.cpp index e9248465431..592e3190786 100644 --- a/src/mongo/db/pipeline/document_source_change_stream_test.cpp +++ b/src/mongo/db/pipeline/document_source_change_stream_test.cpp @@ -1687,11 +1687,10 @@ TEST_F(ChangeStreamStageTest, UsesResumeTokenAsSortKeyIfNeedsMergeIsFalse) { auto next = stages.back()->getNext(); - auto expectedSortKey = - makeResumeToken(kDefaultTs, testUuid(), BSON("x" << 2 << "_id" << 1)).toBson(); + auto expectedSortKey = makeResumeToken(kDefaultTs, testUuid(), BSON("x" << 2 << "_id" << 1)); ASSERT_TRUE(next.isAdvanced()); - ASSERT_BSONOBJ_EQ(next.releaseDocument().metadata().getSortKey(), expectedSortKey); + ASSERT_VALUE_EQ(next.releaseDocument().metadata().getSortKey(), Value(expectedSortKey)); } // diff --git a/src/mongo/db/pipeline/document_source_change_stream_transform.cpp b/src/mongo/db/pipeline/document_source_change_stream_transform.cpp index 5a558a924b9..a147e5271c3 100644 --- a/src/mongo/db/pipeline/document_source_change_stream_transform.cpp +++ b/src/mongo/db/pipeline/document_source_change_stream_transform.cpp @@ -328,7 +328,8 @@ Document DocumentSourceChangeStreamTransform::applyTransformation(const Document // We set the resume token as the document's sort key in both the sharded and non-sharded cases, // since we will subsequently rely upon it to generate a correct postBatchResumeToken. - doc.metadata().setSortKey(resumeToken.toBson()); + const bool isSingleElementKey = true; + doc.metadata().setSortKey(Value{resumeToken}, isSingleElementKey); // "invalidate" and "newShardDetected" entries have fewer fields. if (operationType == DocumentSourceChangeStream::kInvalidateOpType || diff --git a/src/mongo/db/pipeline/document_source_check_invalidate.cpp b/src/mongo/db/pipeline/document_source_check_invalidate.cpp index 5b286b585b0..330ef4d02df 100644 --- a/src/mongo/db/pipeline/document_source_check_invalidate.cpp +++ b/src/mongo/db/pipeline/document_source_check_invalidate.cpp @@ -105,7 +105,8 @@ DocumentSource::GetNextResult DocumentSourceCheckInvalidate::doGetNext() { // We set the resume token as the document's sort key in both the sharded and non-sharded // cases, since we will later rely upon it to generate a correct postBatchResumeToken. We // must therefore update the sort key to match the new resume token that we generated above. - result.metadata().setSortKey(resumeTokenDoc.toBson()); + const bool isSingleElementKey = true; + result.metadata().setSortKey(Value{resumeTokenDoc}, isSingleElementKey); _queuedInvalidate = result.freeze(); } diff --git a/src/mongo/db/pipeline/document_source_geo_near_cursor.cpp b/src/mongo/db/pipeline/document_source_geo_near_cursor.cpp index 24253846166..1fa3e8d673c 100644 --- a/src/mongo/db/pipeline/document_source_geo_near_cursor.cpp +++ b/src/mongo/db/pipeline/document_source_geo_near_cursor.cpp @@ -105,7 +105,8 @@ Document DocumentSourceGeoNearCursor::transformBSONObjToDocument(const BSONObj& // In a cluster, $geoNear will be merged via $sort, so add the sort key. if (pExpCtx->needsMerge) { - output.metadata().setSortKey(BSON("" << distance)); + const bool isSingleElementKey = true; + output.metadata().setSortKey(Value(distance), isSingleElementKey); } return output.freeze(); diff --git a/src/mongo/db/pipeline/document_source_sample_from_random_cursor.cpp b/src/mongo/db/pipeline/document_source_sample_from_random_cursor.cpp index ba2c99cbbfb..31389b9d44d 100644 --- a/src/mongo/db/pipeline/document_source_sample_from_random_cursor.cpp +++ b/src/mongo/db/pipeline/document_source_sample_from_random_cursor.cpp @@ -94,7 +94,8 @@ DocumentSource::GetNextResult DocumentSourceSampleFromRandomCursor::doGetNext() if (pExpCtx->needsMerge) { // This stage will be merged by sorting results according to this random metadata field, but // the merging logic expects to sort by the sort key metadata. - md.metadata().setSortKey(BSON("" << _randMetaFieldVal)); + const bool isSingleElementKey = true; + md.metadata().setSortKey(Value(_randMetaFieldVal), isSingleElementKey); } return md.freeze(); } diff --git a/src/mongo/db/pipeline/document_source_sort.cpp b/src/mongo/db/pipeline/document_source_sort.cpp index 027d88037f8..5bce2d6acd0 100644 --- a/src/mongo/db/pipeline/document_source_sort.cpp +++ b/src/mongo/db/pipeline/document_source_sort.cpp @@ -215,20 +215,17 @@ bool DocumentSourceSort::usedDisk() { } std::pair<Value, Document> DocumentSourceSort::extractSortKey(Document&& doc) const { - Value inMemorySortKey = _sortKeyGen->computeSortKeyFromDocument(doc); + Value sortKey = _sortKeyGen->computeSortKeyFromDocument(doc); if (pExpCtx->needsMerge) { // If this sort stage is part of a merged pipeline, make sure that each Document's sort key // gets saved with its metadata. - auto serializedSortKey = DocumentMetadataFields::serializeSortKey( - _sortKeyGen->isSingleElementKey(), inMemorySortKey); - MutableDocument toBeSorted(std::move(doc)); - toBeSorted.metadata().setSortKey(serializedSortKey); + toBeSorted.metadata().setSortKey(sortKey, _sortKeyGen->isSingleElementKey()); - return std::make_pair(std::move(inMemorySortKey), toBeSorted.freeze()); + return std::make_pair(std::move(sortKey), toBeSorted.freeze()); } else { - return std::make_pair(std::move(inMemorySortKey), std::move(doc)); + return std::make_pair(std::move(sortKey), std::move(doc)); } } diff --git a/src/mongo/db/pipeline/document_value_test.cpp b/src/mongo/db/pipeline/document_value_test.cpp index 43ed44e5c9a..872743679a4 100644 --- a/src/mongo/db/pipeline/document_value_test.cpp +++ b/src/mongo/db/pipeline/document_value_test.cpp @@ -622,7 +622,7 @@ TEST(MetaFields, CopyMetadataFromCopiesAllMetadata) { ASSERT_EQ(result.metadata().getTextScore(), 9.9); ASSERT_EQ(result.metadata().getRandVal(), 42.0); - ASSERT_BSONOBJ_EQ(result.metadata().getSortKey(), BSON("x" << 1)); + ASSERT_VALUE_EQ(result.metadata().getSortKey(), Value(1)); ASSERT_EQ(result.metadata().getGeoNearDistance(), 3.2); ASSERT_VALUE_EQ(result.metadata().getGeoNearPoint(), Value{BSON_ARRAY(1 << 2)}); ASSERT_EQ(result.metadata().getSearchScore(), 5.4); diff --git a/src/mongo/db/pipeline/expression.cpp b/src/mongo/db/pipeline/expression.cpp index 9d21614c1ed..2ac2bd0402f 100644 --- a/src/mongo/db/pipeline/expression.cpp +++ b/src/mongo/db/pipeline/expression.cpp @@ -2623,7 +2623,10 @@ Value ExpressionMeta::evaluate(const Document& root, Variables* variables) const case MetaType::kIndexKey: return metadata.hasIndexKey() ? Value(metadata.getIndexKey()) : Value(); case MetaType::kSortKey: - return metadata.hasSortKey() ? Value(metadata.getSortKey()) : Value(); + return metadata.hasSortKey() + ? Value(DocumentMetadataFields::serializeSortKey(metadata.isSingleElementKey(), + metadata.getSortKey())) + : Value(); default: MONGO_UNREACHABLE; } diff --git a/src/mongo/db/pipeline/expression_context.h b/src/mongo/db/pipeline/expression_context.h index dbac88c28e1..e7e4c7ac2a7 100644 --- a/src/mongo/db/pipeline/expression_context.h +++ b/src/mongo/db/pipeline/expression_context.h @@ -277,6 +277,10 @@ public: boost::optional<ServerGlobalParams::FeatureCompatibility::Version> maxFeatureCompatibilityVersion; + // True if this ExpressionContext is associated with a Change Stream that should serialize its + // "$sortKey" using the 4.2 format. + bool use42ChangeStreamSortKeys = false; + protected: static const int kInterruptCheckPeriod = 128; diff --git a/src/mongo/db/pipeline/expression_test.cpp b/src/mongo/db/pipeline/expression_test.cpp index 9ed9e3a03b8..3043399787a 100644 --- a/src/mongo/db/pipeline/expression_test.cpp +++ b/src/mongo/db/pipeline/expression_test.cpp @@ -6199,10 +6199,10 @@ TEST(ExpressionMetaTest, ExpressionMetaSortKey) { ExpressionMeta::parse(expCtx, expr.firstElement(), expCtx->variablesParseState); MutableDocument doc; - BSONObj sortKey = BSON("" << 1 << "" << 2); - doc.metadata().setSortKey(sortKey); + Value sortKey = Value({Value(1), Value(2)}); + doc.metadata().setSortKey(sortKey, /* isSingleElementSortKey = */ false); Value val = expressionMeta->evaluate(doc.freeze(), &expCtx->variables); - ASSERT_DOCUMENT_EQ(val.getDocument(), Document(sortKey)); + ASSERT_BSONOBJ_EQ(val.getDocument().toBson(), BSON("" << 1 << "" << 2)); } TEST(ExpressionMetaTest, ExpressionMetaTextScore) { diff --git a/src/mongo/db/pipeline/pipeline_d.cpp b/src/mongo/db/pipeline/pipeline_d.cpp index 14584438e03..4b183916e35 100644 --- a/src/mongo/db/pipeline/pipeline_d.cpp +++ b/src/mongo/db/pipeline/pipeline_d.cpp @@ -631,6 +631,11 @@ StatusWith<std::unique_ptr<PlanExecutor, PlanExecutor::Deleter>> PipelineD::prep if (pipeline->peekFront() && pipeline->peekFront()->constraints().isChangeStreamStage()) { invariant(expCtx->tailableMode == TailableModeEnum::kTailableAndAwaitData); plannerOpts |= QueryPlannerParams::TRACK_LATEST_OPLOG_TS; + + // TODO (SERVER-42713): When we change the format of Change Stream sort keys for 4.4, this + // function will determine whether we use the new format, based on the AggregationRequest + // parameters. For now, we always use the old 4.2 format. + expCtx->use42ChangeStreamSortKeys = true; } if (rewrittenGroupStage) { |