summaryrefslogtreecommitdiff
path: root/src/mongo/db/pipeline
diff options
context:
space:
mode:
Diffstat (limited to 'src/mongo/db/pipeline')
-rw-r--r--src/mongo/db/pipeline/document.cpp51
-rw-r--r--src/mongo/db/pipeline/document.h12
-rw-r--r--src/mongo/db/pipeline/document_metadata_fields.cpp14
-rw-r--r--src/mongo/db/pipeline/document_metadata_fields.h19
-rw-r--r--src/mongo/db/pipeline/document_metadata_fields_test.cpp28
-rw-r--r--src/mongo/db/pipeline/document_source_change_stream_test.cpp5
-rw-r--r--src/mongo/db/pipeline/document_source_change_stream_transform.cpp3
-rw-r--r--src/mongo/db/pipeline/document_source_check_invalidate.cpp3
-rw-r--r--src/mongo/db/pipeline/document_source_geo_near_cursor.cpp3
-rw-r--r--src/mongo/db/pipeline/document_source_sample_from_random_cursor.cpp3
-rw-r--r--src/mongo/db/pipeline/document_source_sort.cpp11
-rw-r--r--src/mongo/db/pipeline/document_value_test.cpp2
-rw-r--r--src/mongo/db/pipeline/expression.cpp5
-rw-r--r--src/mongo/db/pipeline/expression_context.h4
-rw-r--r--src/mongo/db/pipeline/expression_test.cpp6
-rw-r--r--src/mongo/db/pipeline/pipeline_d.cpp5
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) {