summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMihai Andrei <mihai.andrei@10gen.com>2020-11-10 09:39:11 -0500
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2020-11-25 20:21:24 +0000
commit64a0e0863b7e9390d87a58eaaa5ef6d05d18bd9d (patch)
treedf1c894c87650ec57ba2edcb34c2ddd57eca9327
parentec8251932634665d3778fcbec87def64e77222ff (diff)
downloadmongo-64a0e0863b7e9390d87a58eaaa5ef6d05d18bd9d.tar.gz
SERVER-49744 Per-document scoring metadata for $search
(cherry picked from commit 6ef44c9d2db276e2bd7f5a49dea9324be3d5fd02)
-rw-r--r--src/mongo/db/exec/document_value/document.cpp8
-rw-r--r--src/mongo/db/exec/document_value/document.h1
-rw-r--r--src/mongo/db/exec/document_value/document_metadata_fields.cpp16
-rw-r--r--src/mongo/db/exec/document_value/document_metadata_fields.h19
-rw-r--r--src/mongo/db/exec/document_value/document_metadata_fields_test.cpp23
-rw-r--r--src/mongo/db/exec/document_value/document_value_test.cpp49
-rw-r--r--src/mongo/db/exec/exclusion_projection_executor_test.cpp16
-rw-r--r--src/mongo/db/exec/inclusion_projection_executor_test.cpp16
-rw-r--r--src/mongo/db/index/sort_key_generator_test.cpp9
-rw-r--r--src/mongo/db/pipeline/expression.cpp13
-rw-r--r--src/mongo/db/pipeline/expression_test.cpp14
11 files changed, 168 insertions, 16 deletions
diff --git a/src/mongo/db/exec/document_value/document.cpp b/src/mongo/db/exec/document_value/document.cpp
index 1990c1dbc5a..358cad5a300 100644
--- a/src/mongo/db/exec/document_value/document.cpp
+++ b/src/mongo/db/exec/document_value/document.cpp
@@ -53,7 +53,8 @@ const StringDataSet Document::allMetadataFieldNames{Document::metaFieldTextScore
Document::metaFieldGeoNearPoint,
Document::metaFieldSearchScore,
Document::metaFieldSearchHighlights,
- Document::metaFieldIndexKey};
+ Document::metaFieldIndexKey,
+ Document::metaFieldSearchScoreDetails};
DocumentStorageIterator::DocumentStorageIterator(DocumentStorage* storage, BSONObjIterator bsonIt)
: _bsonIt(std::move(bsonIt)),
@@ -398,6 +399,8 @@ void DocumentStorage::loadLazyMetadata() const {
_metadataFields.setGeoNearPoint(val);
} else if (fieldName == Document::metaFieldIndexKey) {
_metadataFields.setIndexKey(elem.Obj());
+ } else if (fieldName == Document::metaFieldSearchScoreDetails) {
+ _metadataFields.setSearchScoreDetails(elem.Obj());
}
}
}
@@ -469,6 +472,7 @@ constexpr StringData Document::metaFieldGeoNearDistance;
constexpr StringData Document::metaFieldGeoNearPoint;
constexpr StringData Document::metaFieldSearchScore;
constexpr StringData Document::metaFieldSearchHighlights;
+constexpr StringData Document::metaFieldSearchScoreDetails;
BSONObj Document::toBsonWithMetaData(SortKeyFormat sortKeyFormat) const {
BSONObjBuilder bb;
@@ -509,6 +513,8 @@ BSONObj Document::toBsonWithMetaData(SortKeyFormat sortKeyFormat) const {
metadata().getSearchHighlights().addToBsonObj(&bb, metaFieldSearchHighlights);
if (metadata().hasIndexKey())
bb.append(metaFieldIndexKey, metadata().getIndexKey());
+ if (metadata().hasSearchScoreDetails())
+ bb.append(metaFieldSearchScoreDetails, metadata().getSearchScoreDetails());
return bb.obj();
}
diff --git a/src/mongo/db/exec/document_value/document.h b/src/mongo/db/exec/document_value/document.h
index 832dd78196d..b0dbe6e87ff 100644
--- a/src/mongo/db/exec/document_value/document.h
+++ b/src/mongo/db/exec/document_value/document.h
@@ -119,6 +119,7 @@ public:
static constexpr StringData metaFieldGeoNearPoint = "$pt"_sd;
static constexpr StringData metaFieldSearchScore = "$searchScore"_sd;
static constexpr StringData metaFieldSearchHighlights = "$searchHighlights"_sd;
+ static constexpr StringData metaFieldSearchScoreDetails = "$searchScoreDetails"_sd;
static constexpr StringData metaFieldIndexKey = "$indexKey"_sd;
static const StringDataSet allMetadataFieldNames;
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 16bda6ce44a..b00b3a53185 100644
--- a/src/mongo/db/exec/document_value/document_metadata_fields.cpp
+++ b/src/mongo/db/exec/document_value/document_metadata_fields.cpp
@@ -82,6 +82,9 @@ void DocumentMetadataFields::mergeWith(const DocumentMetadataFields& other) {
if (!hasIndexKey() && other.hasIndexKey()) {
setIndexKey(other.getIndexKey());
}
+ if (!hasSearchScoreDetails() && other.hasSearchScoreDetails()) {
+ setSearchScoreDetails(other.getSearchScoreDetails());
+ }
}
void DocumentMetadataFields::copyFrom(const DocumentMetadataFields& other) {
@@ -109,6 +112,9 @@ void DocumentMetadataFields::copyFrom(const DocumentMetadataFields& other) {
if (other.hasIndexKey()) {
setIndexKey(other.getIndexKey());
}
+ if (other.hasSearchScoreDetails()) {
+ setSearchScoreDetails(other.getSearchScoreDetails());
+ }
}
size_t DocumentMetadataFields::getApproximateSize() const {
@@ -129,6 +135,7 @@ size_t DocumentMetadataFields::getApproximateSize() const {
size += _holder->searchHighlights.getApproximateSize();
size -= sizeof(_holder->searchHighlights);
size += _holder->indexKey.objsize();
+ size += _holder->searchScoreDetails.objsize();
return size;
}
@@ -173,6 +180,10 @@ void DocumentMetadataFields::serializeForSorter(BufBuilder& buf) const {
buf.appendNum(static_cast<char>(MetaType::kIndexKey + 1));
getIndexKey().appendSelfToBufBuilder(buf);
}
+ if (hasSearchScoreDetails()) {
+ buf.appendNum(static_cast<char>(MetaType::kSearchScoreDetails + 1));
+ getSearchScoreDetails().appendSelfToBufBuilder(buf);
+ }
buf.appendNum(static_cast<char>(0));
}
@@ -201,6 +212,9 @@ void DocumentMetadataFields::deserializeForSorter(BufReader& buf, DocumentMetada
} else if (marker == static_cast<char>(MetaType::kIndexKey) + 1) {
out->setIndexKey(
BSONObj::deserializeForSorter(buf, BSONObj::SorterDeserializeSettings()));
+ } else if (marker == static_cast<char>(MetaType::kSearchScoreDetails) + 1) {
+ out->setSearchScoreDetails(
+ BSONObj::deserializeForSorter(buf, BSONObj::SorterDeserializeSettings()));
} else {
uasserted(28744, "Unrecognized marker, unable to deserialize buffer");
}
@@ -270,6 +284,8 @@ const char* DocumentMetadataFields::typeNameToDebugString(DocumentMetadataFields
return "sort key";
case DocumentMetadataFields::kTextScore:
return "text score";
+ case DocumentMetadataFields::kSearchScoreDetails:
+ return "$search score details";
default:
MONGO_UNREACHABLE;
}
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 83482a32abc..c529da03d81 100644
--- a/src/mongo/db/exec/document_value/document_metadata_fields.h
+++ b/src/mongo/db/exec/document_value/document_metadata_fields.h
@@ -64,6 +64,7 @@ public:
kSearchScore,
kSortKey,
kTextScore,
+ kSearchScoreDetails,
// New fields must be added before the kNumFields sentinel.
kNumFields
@@ -316,6 +317,23 @@ public:
_holder->recordId = rid;
}
+ bool hasSearchScoreDetails() const {
+ return _holder && _holder->metaFields.test(MetaType::kSearchScoreDetails);
+ }
+
+ BSONObj getSearchScoreDetails() const {
+ invariant(hasSearchScoreDetails());
+ return _holder->searchScoreDetails;
+ }
+
+ void setSearchScoreDetails(BSONObj details) {
+ if (!_holder) {
+ _holder = std::make_unique<MetadataHolder>();
+ }
+ _holder->metaFields.set(MetaType::kSearchScoreDetails);
+ _holder->searchScoreDetails = details.getOwned();
+ }
+
void serializeForSorter(BufBuilder& buf) const;
private:
@@ -337,6 +355,7 @@ private:
Value searchHighlights;
BSONObj indexKey;
RecordId recordId;
+ BSONObj searchScoreDetails;
};
// 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 b19127ef226..e4a23df0688 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,8 @@ TEST(DocumentMetadataFieldsTest, AllMetadataRoundtripsThroughSerialization) {
metadata.setSearchScore(5.4);
metadata.setSearchHighlights(Value{"foo"_sd});
metadata.setIndexKey(BSON("b" << 1));
+ metadata.setSearchScoreDetails(BSON("scoreDetails"
+ << "foo"));
BufBuilder builder;
metadata.serializeForSorter(builder);
@@ -64,6 +66,9 @@ 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_BSONOBJ_EQ(deserialized.getSearchScoreDetails(),
+ BSON("scoreDetails"
+ << "foo"));
}
TEST(DocumentMetadataFieldsTest, HasMethodsReturnFalseForEmptyMetadata) {
@@ -77,6 +82,7 @@ TEST(DocumentMetadataFieldsTest, HasMethodsReturnFalseForEmptyMetadata) {
ASSERT_FALSE(metadata.hasSearchScore());
ASSERT_FALSE(metadata.hasSearchHighlights());
ASSERT_FALSE(metadata.hasIndexKey());
+ ASSERT_FALSE(metadata.hasSearchScoreDetails());
}
TEST(DocumentMetadataFieldsTest, HasMethodsReturnTrueForInitializedMetadata) {
@@ -114,6 +120,11 @@ TEST(DocumentMetadataFieldsTest, HasMethodsReturnTrueForInitializedMetadata) {
ASSERT_FALSE(metadata.hasIndexKey());
metadata.setIndexKey(BSON("b" << 1));
ASSERT_TRUE(metadata.hasIndexKey());
+
+ ASSERT_FALSE(metadata.hasSearchScoreDetails());
+ metadata.setSearchScoreDetails(BSON("scoreDetails"
+ << "foo"));
+ ASSERT_TRUE(metadata.hasSearchScoreDetails());
}
TEST(DocumentMetadataFieldsTest, MoveConstructor) {
@@ -126,6 +137,8 @@ TEST(DocumentMetadataFieldsTest, MoveConstructor) {
metadata.setSearchScore(5.4);
metadata.setSearchHighlights(Value{"foo"_sd});
metadata.setIndexKey(BSON("b" << 1));
+ metadata.setSearchScoreDetails(BSON("scoreDetails"
+ << "foo"));
DocumentMetadataFields moveConstructed(std::move(metadata));
ASSERT_TRUE(moveConstructed);
@@ -138,6 +151,9 @@ 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_BSONOBJ_EQ(moveConstructed.getSearchScoreDetails(),
+ BSON("scoreDetails"
+ << "foo"));
ASSERT_FALSE(metadata);
}
@@ -152,6 +168,8 @@ TEST(DocumentMetadataFieldsTest, MoveAssignmentOperator) {
metadata.setSearchScore(5.4);
metadata.setSearchHighlights(Value{"foo"_sd});
metadata.setIndexKey(BSON("b" << 1));
+ metadata.setSearchScoreDetails(BSON("scoreDetails"
+ << "foo"));
DocumentMetadataFields moveAssigned;
moveAssigned.setTextScore(12.3);
@@ -167,6 +185,9 @@ 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_BSONOBJ_EQ(moveAssigned.getSearchScoreDetails(),
+ BSON("scoreDetails"
+ << "foo"));
ASSERT_FALSE(metadata);
}
@@ -219,6 +240,7 @@ TEST(DocumentMetadataFieldsTest, MergeWithOnlyCopiesMetadataThatDestinationDoesN
ASSERT_FALSE(destination.hasSearchScore());
ASSERT_FALSE(destination.hasSearchHighlights());
ASSERT_FALSE(destination.hasIndexKey());
+ ASSERT_FALSE(destination.hasSearchScoreDetails());
}
TEST(DocumentMetadataFieldsTest, CopyFromCopiesAllMetadataThatSourceHas) {
@@ -243,6 +265,7 @@ TEST(DocumentMetadataFieldsTest, CopyFromCopiesAllMetadataThatSourceHas) {
ASSERT_FALSE(destination.hasSearchScore());
ASSERT_FALSE(destination.hasSearchHighlights());
ASSERT_FALSE(destination.hasIndexKey());
+ ASSERT_FALSE(destination.hasSearchScoreDetails());
}
} // namespace mongo
diff --git a/src/mongo/db/exec/document_value/document_value_test.cpp b/src/mongo/db/exec/document_value/document_value_test.cpp
index b81c8803478..74c42cefe14 100644
--- a/src/mongo/db/exec/document_value/document_value_test.cpp
+++ b/src/mongo/db/exec/document_value/document_value_test.cpp
@@ -768,6 +768,30 @@ TEST(MetaFields, SearchHighlightsBasic) {
ASSERT_VALUE_EQ(doc2.metadata().getSearchHighlights(), otherHighlights);
}
+TEST(MetaFields, SearchScoreDetailsBasic) {
+ // Documents should not have a value for searchScoreDetails until it is set.
+ ASSERT_FALSE(Document().metadata().hasSearchScoreDetails());
+
+ // Setting the searchScoreDetails field should work as expected.
+ MutableDocument docBuilder;
+ BSONObj details = BSON("scoreDetails"
+ << "foo");
+ docBuilder.metadata().setSearchScoreDetails(details);
+ Document doc = docBuilder.freeze();
+ ASSERT_TRUE(doc.metadata().hasSearchScoreDetails());
+ ASSERT_BSONOBJ_EQ(doc.metadata().getSearchScoreDetails(), details);
+
+ // Setting the searchScoreDetails twice should keep the second value.
+ MutableDocument docBuilder2;
+ BSONObj otherDetails = BSON("scoreDetails"
+ << "bar");
+ docBuilder2.metadata().setSearchScoreDetails(details);
+ docBuilder2.metadata().setSearchScoreDetails(otherDetails);
+ Document doc2 = docBuilder2.freeze();
+ ASSERT_TRUE(doc2.metadata().hasSearchScoreDetails());
+ ASSERT_BSONOBJ_EQ(doc2.metadata().getSearchScoreDetails(), otherDetails);
+}
+
TEST(MetaFields, IndexKeyMetadataSerializesCorrectly) {
Document doc{BSON("a" << 1)};
MutableDocument mutableDoc{doc};
@@ -796,7 +820,9 @@ TEST(MetaFields, CopyMetadataFromCopiesAllMetadata) {
<< BSON_ARRAY(1 << 2) << "f" << 1 << "$searchScore" << 5.4 << "g" << 1
<< "$searchHighlights"
<< "foo"
- << "h" << 1 << "$indexKey" << BSON("y" << 1)));
+ << "h" << 1 << "$indexKey" << BSON("y" << 1) << "$searchScoreDetails"
+ << BSON("scoreDetails"
+ << "foo")));
MutableDocument destination{};
destination.copyMetaDataFrom(source);
@@ -810,6 +836,9 @@ TEST(MetaFields, CopyMetadataFromCopiesAllMetadata) {
ASSERT_EQ(result.metadata().getSearchScore(), 5.4);
ASSERT_VALUE_EQ(result.metadata().getSearchHighlights(), Value{"foo"_sd});
ASSERT_BSONOBJ_EQ(result.metadata().getIndexKey(), BSON("y" << 1));
+ ASSERT_BSONOBJ_EQ(result.metadata().getSearchScoreDetails(),
+ BSON("scoreDetails"
+ << "foo"));
}
class SerializationTest : public unittest::Test {
@@ -846,6 +875,10 @@ protected:
if (input.metadata().hasIndexKey()) {
ASSERT_BSONOBJ_EQ(output.metadata().getIndexKey(), input.metadata().getIndexKey());
}
+ if (input.metadata().hasSearchScoreDetails()) {
+ ASSERT_BSONOBJ_EQ(output.metadata().getSearchScoreDetails(),
+ input.metadata().getSearchScoreDetails());
+ }
ASSERT(output.toBson().binaryEqual(input.toBson()));
}
@@ -858,6 +891,8 @@ TEST_F(SerializationTest, MetaSerializationNoVals) {
docBuilder.metadata().setSearchScore(30.0);
docBuilder.metadata().setSearchHighlights(DOC_ARRAY("abc"_sd
<< "def"_sd));
+ docBuilder.metadata().setSearchScoreDetails(BSON("scoreDetails"
+ << "foo"));
assertRoundTrips(docBuilder.freeze());
}
@@ -870,6 +905,8 @@ TEST_F(SerializationTest, MetaSerializationWithVals) {
docBuilder.metadata().setSearchHighlights(DOC_ARRAY("abc"_sd
<< "def"_sd));
docBuilder.metadata().setIndexKey(BSON("key" << 42));
+ docBuilder.metadata().setSearchScoreDetails(BSON("scoreDetails"
+ << "foo"));
assertRoundTrips(docBuilder.freeze());
}
@@ -890,6 +927,8 @@ TEST(MetaFields, ToAndFromBson) {
docBuilder.metadata().setSearchScore(30.0);
docBuilder.metadata().setSearchHighlights(DOC_ARRAY("abc"_sd
<< "def"_sd));
+ docBuilder.metadata().setSearchScoreDetails(BSON("scoreDetails"
+ << "foo"));
Document doc = docBuilder.freeze();
BSONObj obj = doc.toBsonWithMetaData(SortKeyFormat::k42SortKey);
ASSERT_EQ(10.0, obj[Document::metaFieldTextScore].Double());
@@ -898,11 +937,17 @@ TEST(MetaFields, ToAndFromBson) {
ASSERT_BSONOBJ_EQ(obj[Document::metaFieldSearchHighlights].embeddedObject(),
BSON_ARRAY("abc"_sd
<< "def"_sd));
+ ASSERT_BSONOBJ_EQ(obj[Document::metaFieldSearchScoreDetails].Obj(),
+ BSON("scoreDetails"
+ << "foo"));
Document fromBson = Document::fromBsonWithMetaData(obj);
ASSERT_TRUE(fromBson.metadata().hasTextScore());
ASSERT_TRUE(fromBson.metadata().hasRandVal());
ASSERT_EQ(10.0, fromBson.metadata().getTextScore());
ASSERT_EQ(20, fromBson.metadata().getRandVal());
+ ASSERT_BSONOBJ_EQ(BSON("scoreDetails"
+ << "foo"),
+ fromBson.metadata().getSearchScoreDetails());
}
TEST(MetaFields, MetaFieldsIncludedInDocumentApproximateSize) {
@@ -921,7 +966,7 @@ TEST(MetaFields, MetaFieldsIncludedInDocumentApproximateSize) {
ASSERT_GT(bigMetadataDocSize, smallMetadataDocSize);
// Do a sanity check on the amount of space taken by metadata in document 2.
- ASSERT_LT(doc2.getMetadataApproximateSize(), 250U);
+ ASSERT_LT(doc2.getMetadataApproximateSize(), 300U);
Document emptyDoc;
ASSERT_LT(emptyDoc.getMetadataApproximateSize(), 100U);
diff --git a/src/mongo/db/exec/exclusion_projection_executor_test.cpp b/src/mongo/db/exec/exclusion_projection_executor_test.cpp
index cdf7a305bf8..0a74a413120 100644
--- a/src/mongo/db/exec/exclusion_projection_executor_test.cpp
+++ b/src/mongo/db/exec/exclusion_projection_executor_test.cpp
@@ -326,7 +326,8 @@ TEST(ExclusionProjectionExecutionTest, ShouldEvaluateMetaExpressions) {
"h: {$meta: 'geoNearPoint'}, "
"i: {$meta: 'recordId'}, "
"j: {$meta: 'indexKey'}, "
- "k: {$meta: 'sortKey'}}"));
+ "k: {$meta: 'sortKey'}, "
+ "l: {$meta: 'searchScoreDetails'}}"));
MutableDocument inputDocBuilder(Document{{"a", 1}, {"b", 2}});
inputDocBuilder.metadata().setTextScore(0.0);
@@ -338,13 +339,16 @@ TEST(ExclusionProjectionExecutionTest, ShouldEvaluateMetaExpressions) {
inputDocBuilder.metadata().setRecordId(RecordId{6});
inputDocBuilder.metadata().setIndexKey(BSON("foo" << 7));
inputDocBuilder.metadata().setSortKey(Value{Document{{"bar", 8}}}, true);
+ inputDocBuilder.metadata().setSearchScoreDetails(BSON("scoreDetails"
+ << "foo"));
Document inputDoc = inputDocBuilder.freeze();
auto result = exclusion->applyTransformation(inputDoc);
ASSERT_DOCUMENT_EQ(result,
Document{fromjson("{b: 2, c: 0.0, d: 1.0, e: 2.0, f: 'foo', g: 3.0, "
- "h: [4, 5], i: 6, j: {foo: 7}, k: [{bar: 8}]}")});
+ "h: [4, 5], i: 6, j: {foo: 7}, k: [{bar: 8}],"
+ "l: {scoreDetails: 'foo'}}")});
}
TEST(ExclusionProjectionExecutionTest, ShouldAddMetaExpressionsToDependencies) {
@@ -357,17 +361,19 @@ TEST(ExclusionProjectionExecutionTest, ShouldAddMetaExpressionsToDependencies) {
"h: {$meta: 'geoNearPoint'}, "
"i: {$meta: 'recordId'}, "
"j: {$meta: 'indexKey'}, "
- "k: {$meta: 'sortKey'}}"));
+ "k: {$meta: 'sortKey'}, "
+ "l: {$meta: 'searchScoreDetails'}}"));
DepsTracker deps;
exclusion->addDependencies(&deps);
ASSERT_EQ(deps.fields.size(), 0UL);
- // We do not add the dependencies for SEARCH_SCORE or SEARCH_HIGHLIGHTS because those values
- // are not stored in the collection (or in mongod at all).
+ // We do not add the dependencies for searchScore, searchHighlights, or searchScoreDetails
+ // because those values are not stored in the collection (or in mongod at all).
ASSERT_FALSE(deps.metadataDeps()[DocumentMetadataFields::kSearchScore]);
ASSERT_FALSE(deps.metadataDeps()[DocumentMetadataFields::kSearchHighlights]);
+ ASSERT_FALSE(deps.metadataDeps()[DocumentMetadataFields::kSearchScoreDetails]);
ASSERT_TRUE(deps.metadataDeps()[DocumentMetadataFields::kTextScore]);
ASSERT_TRUE(deps.metadataDeps()[DocumentMetadataFields::kRandVal]);
diff --git a/src/mongo/db/exec/inclusion_projection_executor_test.cpp b/src/mongo/db/exec/inclusion_projection_executor_test.cpp
index dd66aa31122..7b78d7e56b5 100644
--- a/src/mongo/db/exec/inclusion_projection_executor_test.cpp
+++ b/src/mongo/db/exec/inclusion_projection_executor_test.cpp
@@ -779,17 +779,19 @@ TEST_F(InclusionProjectionExecutionTestWithFallBackToDefault,
"h: {$meta: 'geoNearPoint'}, "
"i: {$meta: 'recordId'}, "
"j: {$meta: 'indexKey'}, "
- "k: {$meta: 'sortKey'}}"));
+ "k: {$meta: 'sortKey'}, "
+ "l: {$meta: 'searchScoreDetails'}}"));
DepsTracker deps;
inclusion->addDependencies(&deps);
ASSERT_EQ(deps.fields.size(), 2UL);
- // We do not add the dependencies for SEARCH_SCORE or SEARCH_HIGHLIGHTS because those
- // values are not stored in the collection (or in mongod at all).
+ // We do not add the dependencies for searchScore, searchHighlights, or searchScoreDetails
+ // because those values are not stored in the collection (or in mongod at all).
ASSERT_FALSE(deps.metadataDeps()[DocumentMetadataFields::kSearchScore]);
ASSERT_FALSE(deps.metadataDeps()[DocumentMetadataFields::kSearchHighlights]);
+ ASSERT_FALSE(deps.metadataDeps()[DocumentMetadataFields::kSearchScoreDetails]);
ASSERT_TRUE(deps.metadataDeps()[DocumentMetadataFields::kTextScore]);
ASSERT_TRUE(deps.metadataDeps()[DocumentMetadataFields::kRandVal]);
@@ -810,7 +812,8 @@ TEST_F(InclusionProjectionExecutionTestWithFallBackToDefault, ShouldEvaluateMeta
"h: {$meta: 'geoNearPoint'}, "
"i: {$meta: 'recordId'}, "
"j: {$meta: 'indexKey'}, "
- "k: {$meta: 'sortKey'}}"));
+ "k: {$meta: 'sortKey'}, "
+ "l: {$meta: 'searchScoreDetails'}}"));
MutableDocument inputDocBuilder(Document{{"a", 1}});
inputDocBuilder.metadata().setTextScore(0.0);
@@ -822,13 +825,16 @@ TEST_F(InclusionProjectionExecutionTestWithFallBackToDefault, ShouldEvaluateMeta
inputDocBuilder.metadata().setRecordId(RecordId{6});
inputDocBuilder.metadata().setIndexKey(BSON("foo" << 7));
inputDocBuilder.metadata().setSortKey(Value{Document{{"bar", 8}}}, true);
+ inputDocBuilder.metadata().setSearchScoreDetails(BSON("scoreDetails"
+ << "foo"));
Document inputDoc = inputDocBuilder.freeze();
auto result = inclusion->applyTransformation(inputDoc);
ASSERT_DOCUMENT_EQ(result,
Document{fromjson("{a: 1, c: 0.0, d: 1.0, e: 2.0, f: 'foo', g: 3.0, "
- "h: [4, 5], i: 6, j: {foo: 7}, k: [{bar: 8}]}")});
+ "h: [4, 5], i: 6, j: {foo: 7}, k: [{bar: 8}], "
+ "l: {scoreDetails: 'foo'}}")});
}
//
diff --git a/src/mongo/db/index/sort_key_generator_test.cpp b/src/mongo/db/index/sort_key_generator_test.cpp
index 17c66fe7d59..abda98684ba 100644
--- a/src/mongo/db/index/sort_key_generator_test.cpp
+++ b/src/mongo/db/index/sort_key_generator_test.cpp
@@ -231,6 +231,15 @@ TEST(SortKeyGeneratorTest, SortPatternComponentWithSearchHighlightsMetaKeywordUa
"$meta sort by 'searchHighlights' metadata is not supported");
}
+TEST(SortKeyGeneratorTest, SortPatternComponentWithSearchScoreDetailsMetaKeywordUasserts) {
+ ASSERT_THROWS_CODE_AND_WHAT(makeSortKeyGen(BSON("a" << BSON("$meta"
+ << "searchScoreDetails")),
+ nullptr),
+ AssertionException,
+ 31138,
+ "Illegal $meta sort: $meta: \"searchScoreDetails\"");
+}
+
TEST(SortKeyGeneratorTest, CanGenerateKeysForTextScoreMetaSort) {
auto sortKeyGen = makeSortKeyGen(BSON("a" << BSON("$meta"
<< "textScore")),
diff --git a/src/mongo/db/pipeline/expression.cpp b/src/mongo/db/pipeline/expression.cpp
index 8d72d2da5dc..9534eebc108 100644
--- a/src/mongo/db/pipeline/expression.cpp
+++ b/src/mongo/db/pipeline/expression.cpp
@@ -2619,6 +2619,7 @@ const std::string geoNearPointName = "geoNearPoint";
const std::string recordIdName = "recordId";
const std::string indexKeyName = "indexKey";
const std::string sortKeyName = "sortKey";
+const std::string searchScoreDetailsName = "searchScoreDetails";
using MetaType = DocumentMetadataFields::MetaType;
const StringMap<DocumentMetadataFields::MetaType> kMetaNameToMetaType = {
@@ -2629,6 +2630,7 @@ const StringMap<DocumentMetadataFields::MetaType> kMetaNameToMetaType = {
{recordIdName, MetaType::kRecordId},
{searchHighlightsName, MetaType::kSearchHighlights},
{searchScoreName, MetaType::kSearchScore},
+ {searchScoreDetailsName, MetaType::kSearchScoreDetails},
{sortKeyName, MetaType::kSortKey},
{textScoreName, MetaType::kTextScore},
};
@@ -2641,6 +2643,7 @@ const stdx::unordered_map<DocumentMetadataFields::MetaType, StringData> kMetaTyp
{MetaType::kRecordId, recordIdName},
{MetaType::kSearchHighlights, searchHighlightsName},
{MetaType::kSearchScore, searchScoreName},
+ {MetaType::kSearchScoreDetails, searchScoreDetailsName},
{MetaType::kSortKey, sortKeyName},
{MetaType::kTextScore, textScoreName},
};
@@ -2730,6 +2733,9 @@ Value ExpressionMeta::evaluate(const Document& root, Variables* variables) const
} else {
return Value();
}
+ case MetaType::kSearchScoreDetails:
+ return metadata.hasSearchScoreDetails() ? Value(metadata.getSearchScoreDetails())
+ : Value();
default:
MONGO_UNREACHABLE;
}
@@ -2737,9 +2743,10 @@ Value ExpressionMeta::evaluate(const Document& root, Variables* variables) const
}
void ExpressionMeta::_doAddDependencies(DepsTracker* deps) const {
- if (_metaType == MetaType::kSearchScore || _metaType == MetaType::kSearchHighlights) {
- // We do not add the dependencies for SEARCH_SCORE or SEARCH_HIGHLIGHTS because those values
- // are not stored in the collection (or in mongod at all).
+ if (_metaType == MetaType::kSearchScore || _metaType == MetaType::kSearchHighlights ||
+ _metaType == MetaType::kSearchScoreDetails) {
+ // We do not add the dependencies for searchScore, searchHighlights, or searchScoreDetails
+ // because those values are not stored in the collection (or in mongod at all).
return;
}
diff --git a/src/mongo/db/pipeline/expression_test.cpp b/src/mongo/db/pipeline/expression_test.cpp
index c9c2092acc4..5502c1a64a9 100644
--- a/src/mongo/db/pipeline/expression_test.cpp
+++ b/src/mongo/db/pipeline/expression_test.cpp
@@ -2683,6 +2683,20 @@ TEST(ExpressionMetaTest, ExpressionMetaTextScore) {
Value val = expressionMeta->evaluate(doc.freeze(), &expCtx->variables);
ASSERT_EQ(val.getDouble(), 1.23);
}
+
+TEST(ExpressionMetaTest, ExpressionMetaSearchScoreDetails) {
+ intrusive_ptr<ExpressionContextForTest> expCtx(new ExpressionContextForTest());
+ BSONObj expr = fromjson("{$meta: \"searchScoreDetails\"}");
+ auto expressionMeta =
+ ExpressionMeta::parse(expCtx, expr.firstElement(), expCtx->variablesParseState);
+
+ auto details = BSON("scoreDetails"
+ << "foo");
+ MutableDocument doc;
+ doc.metadata().setSearchScoreDetails(details);
+ Value val = expressionMeta->evaluate(doc.freeze(), &expCtx->variables);
+ ASSERT_DOCUMENT_EQ(val.getDocument(), Document(details));
+}
} // namespace expression_meta_test
namespace ExpressionRegexTest {