summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlyssa Wagenmaker <alyssa.wagenmaker@mongodb.com>2023-09-01 18:02:11 +0000
committerEvergreen Agent <no-reply@evergreen.mongodb.com>2023-09-01 18:43:21 +0000
commit8e4b5670df9b9fe814e57cb5f3f8ee9407237b5a (patch)
tree2c2f75187377c70d9772c5fac171470fc2b8f7b0
parent69353845c8a545dc5a763ee199d9002e7cb95e6c (diff)
SERVER-79498 Introduce vectorSearch aggregation stager6.0.10-rc0r6.0.10
Co-authored-by: Chi-I Huang <chii.huang@mongodb.com> Co-authored-by: Hima Sheth <hima.sheth@mongodb.com>
-rw-r--r--buildscripts/resmokeconfig/suites/search.yml1
-rw-r--r--buildscripts/resmokeconfig/suites/search_auth.yml1
-rw-r--r--buildscripts/resmokeconfig/suites/search_pinned_connections_auth.yml1
-rw-r--r--buildscripts/resmokeconfig/suites/vector_search.yml12
-rw-r--r--buildscripts/resmokeconfig/suites/vector_search_auth.yml21
-rw-r--r--buildscripts/resmokeconfig/suites/vector_search_ssl.yml15
-rw-r--r--etc/evergreen.yml24
-rw-r--r--etc/evergreen_nightly.yml6
-rw-r--r--etc/evergreen_yml_components/definitions.yml33
-rw-r--r--etc/evergreen_yml_components/variants/atlas.yml3
-rw-r--r--etc/evergreen_yml_components/variants/misc_release.yml15
-rw-r--r--src/mongo/db/exec/document_value/document.cpp9
-rw-r--r--src/mongo/db/exec/document_value/document.h1
-rw-r--r--src/mongo/db/exec/document_value/document_metadata_fields.cpp14
-rw-r--r--src/mongo/db/exec/document_value/document_metadata_fields.h16
-rw-r--r--src/mongo/db/exec/document_value/document_metadata_fields_test.cpp14
-rw-r--r--src/mongo/db/exec/document_value/document_value_test.cpp14
-rw-r--r--src/mongo/db/exec/exclusion_projection_executor_test.cpp14
-rw-r--r--src/mongo/db/exec/inclusion_projection_executor_test.cpp14
-rw-r--r--src/mongo/db/pipeline/expression.cpp6
-rw-r--r--src/mongo/db/pipeline/expression_test.cpp12
-rw-r--r--src/mongo/db/query/query_feature_flags.idl5
22 files changed, 236 insertions, 15 deletions
diff --git a/buildscripts/resmokeconfig/suites/search.yml b/buildscripts/resmokeconfig/suites/search.yml
index ee3060f8436..4c003db89d1 100644
--- a/buildscripts/resmokeconfig/suites/search.yml
+++ b/buildscripts/resmokeconfig/suites/search.yml
@@ -3,6 +3,7 @@ test_kind: js_test
selector:
roots:
- src/mongo/db/modules/*/jstests/search/*.js
+ - src/mongo/db/modules/*/jstests/mongot/*.js
exclude_with_any_tags:
- requires_auth
diff --git a/buildscripts/resmokeconfig/suites/search_auth.yml b/buildscripts/resmokeconfig/suites/search_auth.yml
index edf79042a40..0afb113e42a 100644
--- a/buildscripts/resmokeconfig/suites/search_auth.yml
+++ b/buildscripts/resmokeconfig/suites/search_auth.yml
@@ -7,6 +7,7 @@ test_kind: js_test
selector:
roots:
- src/mongo/db/modules/*/jstests/search/*.js
+ - src/mongo/db/modules/*/jstests/mongot/*.js
executor:
config:
diff --git a/buildscripts/resmokeconfig/suites/search_pinned_connections_auth.yml b/buildscripts/resmokeconfig/suites/search_pinned_connections_auth.yml
index 0fd19d6e3f1..8bfb2fef169 100644
--- a/buildscripts/resmokeconfig/suites/search_pinned_connections_auth.yml
+++ b/buildscripts/resmokeconfig/suites/search_pinned_connections_auth.yml
@@ -7,6 +7,7 @@ test_kind: js_test
selector:
roots:
- src/mongo/db/modules/*/jstests/search/*.js
+ - src/mongo/db/modules/*/jstests/mongot/*.js
executor:
config:
diff --git a/buildscripts/resmokeconfig/suites/vector_search.yml b/buildscripts/resmokeconfig/suites/vector_search.yml
new file mode 100644
index 00000000000..effd621be8f
--- /dev/null
+++ b/buildscripts/resmokeconfig/suites/vector_search.yml
@@ -0,0 +1,12 @@
+test_kind: js_test
+
+selector:
+ roots:
+ - src/mongo/db/modules/*/jstests/vector_search/*.js
+ exclude_with_any_tags:
+ - requires_auth
+
+executor:
+ config:
+ shell_options:
+ nodb: ''
diff --git a/buildscripts/resmokeconfig/suites/vector_search_auth.yml b/buildscripts/resmokeconfig/suites/vector_search_auth.yml
new file mode 100644
index 00000000000..c025afe3619
--- /dev/null
+++ b/buildscripts/resmokeconfig/suites/vector_search_auth.yml
@@ -0,0 +1,21 @@
+config_variables:
+- &keyFile jstests/libs/authTestsKey
+- &keyFileData Thiskeyisonlyforrunningthesuitewithauthenticationdontuseitinanytestsdirectly
+
+test_kind: js_test
+
+selector:
+ roots:
+ - src/mongo/db/modules/*/jstests/vector_search/*.js
+
+executor:
+ config:
+ shell_options:
+ global_vars:
+ TestData:
+ auth: true
+ authMechanism: SCRAM-SHA-256
+ keyFile: *keyFile
+ keyFileData: *keyFileData
+ roleGraphInvalidationIsFatal: true
+ nodb: ''
diff --git a/buildscripts/resmokeconfig/suites/vector_search_ssl.yml b/buildscripts/resmokeconfig/suites/vector_search_ssl.yml
new file mode 100644
index 00000000000..e234ea490fd
--- /dev/null
+++ b/buildscripts/resmokeconfig/suites/vector_search_ssl.yml
@@ -0,0 +1,15 @@
+test_kind: js_test
+
+selector:
+ roots:
+ - src/mongo/db/modules/*/jstests/vector_search/ssl/*.js
+
+executor:
+ config:
+ shell_options:
+ nodb: ''
+ ssl: ''
+ tlsAllowInvalidHostnames: ''
+ tlsAllowInvalidCertificates: ''
+ tlsCAFile: jstests/libs/ca.pem
+ tlsCertificateKeyFile: jstests/libs/client.pem
diff --git a/etc/evergreen.yml b/etc/evergreen.yml
index ab83302fe7f..e1389dda5a7 100644
--- a/etc/evergreen.yml
+++ b/etc/evergreen.yml
@@ -189,6 +189,9 @@ variables:
- name: .crypt
- name: .publish_crypt
- name: unittest_shell_hang_analyzer_gen
+ - name: vector_search
+ - name: vector_search_auth
+ - name: vector_search_ssl
- name: selinux_rhel7_enterprise
- name: generate_buildid_to_debug_symbols_mapping
@@ -422,6 +425,9 @@ buildvariants:
- name: server_selection_json_test_TG
distros:
- rhel80-xlarge
+ - name: vector_search
+ - name: vector_search_auth
+ - name: vector_search_ssl
- <<: *enterprise-rhel80-dynamic-v4gcc-debug-experimental-template
name: enterprise-rhel80-dynamic-v4clang-debug-experimental
@@ -730,6 +736,9 @@ buildvariants:
- name: server_selection_json_test_TG
distros:
- rhel80-xlarge
+ - name: vector_search
+ - name: vector_search_auth
+ - name: vector_search_ssl
#- <<: *enterprise-rhel80-dynamic-v4gcc-debug-experimental-template
# name: enterprise-rhel80-dynamic-v4gcc-cxx20-debug-pm-1328-experimental
@@ -1417,6 +1426,9 @@ buildvariants:
- name: server_selection_json_test_TG
distros:
- rhel80-xlarge
+ - name: vector_search
+ - name: vector_search_auth
+ - name: vector_search_ssl
- name: generate_buildid_to_debug_symbols_mapping
- &enterprise-rhel-80-64-bit-dynamic-all-feature-flags-required-template
@@ -1554,6 +1566,9 @@ buildvariants:
# distros:
# - rhel80-large
- name: generate_buildid_to_debug_symbols_mapping
+ - name: vector_search
+ - name: vector_search_auth
+ - name: vector_search_ssl
- &enterprise-rhel-80-64-bit-dynamic-classic-engine
name: enterprise-rhel-80-64-bit-dynamic-classic-engine
@@ -1673,6 +1688,9 @@ buildvariants:
- name: snmp
- name: test_api_version_compatibility
- name: unittest_shell_hang_analyzer_gen
+ - name: vector_search
+ - name: vector_search_auth
+ - name: vector_search_ssl
- name: enterprise-rhel-80-64-bit-large-txns-format
display_name: "Enterprise RHEL 8.0 (large transactions format)"
@@ -1943,6 +1961,9 @@ buildvariants:
- name: sharding_auth_gen
- name: snmp
- name: .stitch
+ - name: vector_search
+ - name: vector_search_auth
+ - name: vector_search_ssl
- name: selinux_rhel8_enterprise
- name: generate_buildid_to_debug_symbols_mapping
@@ -2031,6 +2052,9 @@ buildvariants:
- name: sharding_auth_gen
- name: snmp
- name: .stitch
+ - name: vector_search
+ - name: vector_search_auth
+ - name: vector_search_ssl
- name: ubuntu1804-container
display_name: "Ubuntu 18.04 Container"
diff --git a/etc/evergreen_nightly.yml b/etc/evergreen_nightly.yml
index ae23d615fd8..8f028c9b632 100644
--- a/etc/evergreen_nightly.yml
+++ b/etc/evergreen_nightly.yml
@@ -302,6 +302,9 @@ buildvariants:
- name: server_selection_json_test_TG
distros:
- rhel80-xlarge
+ - name: vector_search
+ - name: vector_search_auth
+ - name: vector_search_ssl
- name: enterprise-ubuntu-dynamic-1804-clang-tidy-required
display_name: "! Enterprise Clang Tidy"
@@ -581,6 +584,9 @@ buildvariants:
- name: sharding_api_version_jscore_passthrough_gen
- name: snmp
- name: test_api_version_compatibility
+ - name: vector_search
+ - name: vector_search_auth
+ - name: vector_search_ssl
- name: security-daily-cron
modules:
diff --git a/etc/evergreen_yml_components/definitions.yml b/etc/evergreen_yml_components/definitions.yml
index 6c3bd375f56..3778bc1dbc2 100644
--- a/etc/evergreen_yml_components/definitions.yml
+++ b/etc/evergreen_yml_components/definitions.yml
@@ -3059,7 +3059,7 @@ tasks:
targets: archive-mongo-crypt-dev
task_compile_flags: >-
--allocator=system
- --enterprise-features=fle,search
+ --enterprise-features=fle,search,vector_search
--js-engine=none
--link-model=dynamic-sdk
--enable-http-client=off
@@ -3096,7 +3096,7 @@ tasks:
--dbg=on
--opt=off
--allocator=system
- --enterprise-features=fle,search
+ --enterprise-features=fle,search,vector_search
--js-engine=none
--link-model=dynamic-sdk
DESTDIR='$BUILD_ROOT/crypt-lib-$MONGO_VERSION'
@@ -3133,7 +3133,7 @@ tasks:
compiling_for_test: true
task_compile_flags: >-
--allocator=system
- --enterprise-features=fle,search
+ --enterprise-features=fle,search,vector_search
--js-engine=none
--link-model=static
DESTDIR='$BUILD_ROOT/crypt-lib-$MONGO_VERSION'
@@ -7534,6 +7534,33 @@ tasks:
resmoke_jobs_max: 1
- <<: *task_template
+ name: vector_search
+ tags: []
+ commands:
+ - func: "do setup"
+ - func: "run tests"
+ vars:
+ resmoke_jobs_max: 1
+
+- <<: *task_template
+ name: vector_search_auth
+ tags: []
+ commands:
+ - func: "do setup"
+ - func: "run tests"
+ vars:
+ resmoke_jobs_max: 1
+
+- <<: *task_template
+ name: vector_search_ssl
+ tags: []
+ commands:
+ - func: "do setup"
+ - func: "run tests"
+ vars:
+ resmoke_jobs_max: 1
+
+- <<: *task_template
name: cqf
tags: []
commands:
diff --git a/etc/evergreen_yml_components/variants/atlas.yml b/etc/evergreen_yml_components/variants/atlas.yml
index 1a8b1b0c85a..f7d6bd38be0 100644
--- a/etc/evergreen_yml_components/variants/atlas.yml
+++ b/etc/evergreen_yml_components/variants/atlas.yml
@@ -71,6 +71,9 @@ buildvariants:
- name: .crypt
- name: .publish_crypt
- name: unittest_shell_hang_analyzer_gen
+ - name: vector_search
+ - name: vector_search_auth
+ - name: vector_search_ssl
- name: selinux_rhel7_enterprise
- name: .publish
- name: generate_buildid_to_debug_symbols_mapping
diff --git a/etc/evergreen_yml_components/variants/misc_release.yml b/etc/evergreen_yml_components/variants/misc_release.yml
index 32a1da6d79f..fdb24ac12ed 100644
--- a/etc/evergreen_yml_components/variants/misc_release.yml
+++ b/etc/evergreen_yml_components/variants/misc_release.yml
@@ -269,6 +269,9 @@ buildvariants:
- amazon2-arm64-large
- name: .publish
- name: generate_buildid_to_debug_symbols_mapping
+ - name: vector_search
+ - name: vector_search_auth
+ - name: vector_search_ssl
- name: debian10
display_name: Debian 10
@@ -709,6 +712,9 @@ buildvariants:
- name: selinux_rhel8_enterprise
- name: .publish
- name: generate_buildid_to_debug_symbols_mapping
+ - name: vector_search
+ - name: vector_search_auth
+ - name: vector_search_ssl
- name: rhel-82-arm64
display_name: RHEL 8.2 arm64
@@ -809,6 +815,9 @@ buildvariants:
- name: .publish_crypt
- name: .publish
- name: generate_buildid_to_debug_symbols_mapping
+ - name: vector_search
+ - name: vector_search_auth
+ - name: vector_search_ssl
- name: rhel90
display_name: RHEL 9.0
@@ -927,6 +936,9 @@ buildvariants:
- name: .crypt
- name: .publish_crypt
- name: unittest_shell_hang_analyzer_gen
+ - name: vector_search
+ - name: vector_search_auth
+ - name: vector_search_ssl
- name: selinux_rhel9_enterprise
- name: .publish
- name: generate_buildid_to_debug_symbols_mapping
@@ -1027,6 +1039,9 @@ buildvariants:
distros:
- rhel90-arm64-large
- name: .publish_crypt
+ - name: vector_search
+ - name: vector_search_auth
+ - name: vector_search_ssl
- name: .publish
- name: generate_buildid_to_debug_symbols_mapping
diff --git a/src/mongo/db/exec/document_value/document.cpp b/src/mongo/db/exec/document_value/document.cpp
index fd50895c9a1..4488af81f3b 100644
--- a/src/mongo/db/exec/document_value/document.cpp
+++ b/src/mongo/db/exec/document_value/document.cpp
@@ -90,7 +90,8 @@ const StringDataSet Document::allMetadataFieldNames{Document::metaFieldTextScore
Document::metaFieldSearchHighlights,
Document::metaFieldSearchSortValues,
Document::metaFieldIndexKey,
- Document::metaFieldSearchScoreDetails};
+ Document::metaFieldSearchScoreDetails,
+ Document::metaFieldVectorSearchScore};
DocumentStorageIterator::DocumentStorageIterator(DocumentStorage* storage, BSONObjIterator bsonIt)
: _bsonIt(std::move(bsonIt)),
@@ -467,6 +468,8 @@ void DocumentStorage::loadLazyMetadata() const {
_metadataFields.setSearchScoreDetails(elem.Obj());
} else if (fieldName == Document::metaFieldSearchSortValues) {
_metadataFields.setSearchSortValues(elem.Obj());
+ } else if (fieldName == Document::metaFieldVectorSearchScore) {
+ _metadataFields.setVectorSearchScore(elem.Double());
}
}
}
@@ -537,6 +540,7 @@ constexpr StringData Document::metaFieldSearchScore;
constexpr StringData Document::metaFieldSearchHighlights;
constexpr StringData Document::metaFieldSearchScoreDetails;
constexpr StringData Document::metaFieldSearchSortValues;
+constexpr StringData Document::metaFieldVectorSearchScore;
void Document::toBsonWithMetaData(BSONObjBuilder* builder) const {
toBson(builder);
@@ -567,6 +571,9 @@ void Document::toBsonWithMetaData(BSONObjBuilder* builder) const {
if (metadata().hasSearchSortValues()) {
builder->append(metaFieldSearchSortValues, metadata().getSearchSortValues());
}
+ if (metadata().hasVectorSearchScore()) {
+ builder->append(metaFieldVectorSearchScore, metadata().getVectorSearchScore());
+ }
}
Document Document::fromBsonWithMetaData(const BSONObj& bson) {
diff --git a/src/mongo/db/exec/document_value/document.h b/src/mongo/db/exec/document_value/document.h
index ba4a50481bc..062778628a0 100644
--- a/src/mongo/db/exec/document_value/document.h
+++ b/src/mongo/db/exec/document_value/document.h
@@ -102,6 +102,7 @@ public:
static constexpr StringData metaFieldSearchScoreDetails = "$searchScoreDetails"_sd;
static constexpr StringData metaFieldSearchSortValues = "$searchSortValues"_sd;
static constexpr StringData metaFieldIndexKey = "$indexKey"_sd;
+ static constexpr StringData metaFieldVectorSearchScore = "$vectorSearchScore"_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 d33eee6c140..0532ed2e8cb 100644
--- a/src/mongo/db/exec/document_value/document_metadata_fields.cpp
+++ b/src/mongo/db/exec/document_value/document_metadata_fields.cpp
@@ -96,6 +96,9 @@ void DocumentMetadataFields::mergeWith(const DocumentMetadataFields& other) {
if (!hasSearchSortValues() && other.hasSearchSortValues()) {
setSearchSortValues(other.getSearchSortValues());
}
+ if (!hasVectorSearchScore() && other.hasVectorSearchScore()) {
+ setVectorSearchScore(other.getVectorSearchScore());
+ }
}
void DocumentMetadataFields::copyFrom(const DocumentMetadataFields& other) {
@@ -135,6 +138,9 @@ void DocumentMetadataFields::copyFrom(const DocumentMetadataFields& other) {
if (other.hasSearchSortValues()) {
setSearchSortValues(other.getSearchSortValues());
}
+ if (other.hasVectorSearchScore()) {
+ setVectorSearchScore(other.getVectorSearchScore());
+ }
}
size_t DocumentMetadataFields::getApproximateSize() const {
@@ -216,6 +222,10 @@ void DocumentMetadataFields::serializeForSorter(BufBuilder& buf) const {
buf.appendNum(static_cast<char>(MetaType::kSearchSortValues + 1));
getSearchSortValues().appendSelfToBufBuilder(buf);
}
+ if (hasVectorSearchScore()) {
+ buf.appendNum(static_cast<char>(MetaType::kVectorSearchScore + 1));
+ buf.appendNum(getVectorSearchScore());
+ }
buf.appendNum(static_cast<char>(0));
}
@@ -256,6 +266,8 @@ void DocumentMetadataFields::deserializeForSorter(BufReader& buf, DocumentMetada
} else if (marker == static_cast<char>(MetaType::kSearchSortValues) + 1) {
out->setSearchSortValues(
BSONObj::deserializeForSorter(buf, BSONObj::SorterDeserializeSettings()));
+ } else if (marker == static_cast<char>(MetaType::kVectorSearchScore) + 1) {
+ out->setVectorSearchScore(buf.read<LittleEndian<double>>());
} else {
uasserted(28744, "Unrecognized marker, unable to deserialize buffer");
}
@@ -317,6 +329,8 @@ const char* DocumentMetadataFields::typeNameToDebugString(DocumentMetadataFields
return "timeseries bucket max time";
case DocumentMetadataFields::kSearchSortValues:
return "$search sort values";
+ case DocumentMetadataFields::kVectorSearchScore:
+ return "$vectorSearch distance";
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 ede2f73229d..e8451677ea7 100644
--- a/src/mongo/db/exec/document_value/document_metadata_fields.h
+++ b/src/mongo/db/exec/document_value/document_metadata_fields.h
@@ -68,6 +68,7 @@ public:
kTimeseriesBucketMinTime,
kTimeseriesBucketMaxTime,
kSearchSortValues,
+ kVectorSearchScore,
// New fields must be added before the kNumFields sentinel.
kNumFields
@@ -330,6 +331,20 @@ public:
_holder->searchSortValues = vals.getOwned();
}
+ bool hasVectorSearchScore() const {
+ return _holder && _holder->metaFields.test(MetaType::kVectorSearchScore);
+ }
+
+ double getVectorSearchScore() const {
+ tassert(7828400, "vectorSearchScore must be present in metadata", hasVectorSearchScore());
+ return _holder->vectorSearchScore;
+ }
+
+ void setVectorSearchScore(double vectorSearchScore) {
+ _setCommon(MetaType::kVectorSearchScore);
+ _holder->vectorSearchScore = vectorSearchScore;
+ }
+
void serializeForSorter(BufBuilder& buf) const;
bool isModified() const {
@@ -376,6 +391,7 @@ private:
Date_t timeseriesBucketMinTime;
Date_t timeseriesBucketMaxTime;
BSONObj searchSortValues;
+ double vectorSearchScore{0.0};
};
// 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 4a76154f1f6..23ab2277c96 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
@@ -52,6 +52,7 @@ TEST(DocumentMetadataFieldsTest, AllMetadataRoundtripsThroughSerialization) {
metadata.setSearchScoreDetails(BSON("scoreDetails"
<< "foo"));
metadata.setSearchSortValues(BSON("a" << 1));
+ metadata.setVectorSearchScore(7.6);
BufBuilder builder;
metadata.serializeForSorter(builder);
@@ -72,6 +73,7 @@ TEST(DocumentMetadataFieldsTest, AllMetadataRoundtripsThroughSerialization) {
BSON("scoreDetails"
<< "foo"));
ASSERT_BSONOBJ_EQ(deserialized.getSearchSortValues(), BSON("a" << 1));
+ ASSERT_EQ(deserialized.getVectorSearchScore(), 7.6);
}
TEST(DocumentMetadataFieldsTest, HasMethodsReturnFalseForEmptyMetadata) {
@@ -87,6 +89,7 @@ TEST(DocumentMetadataFieldsTest, HasMethodsReturnFalseForEmptyMetadata) {
ASSERT_FALSE(metadata.hasIndexKey());
ASSERT_FALSE(metadata.hasSearchScoreDetails());
ASSERT_FALSE(metadata.hasSearchSortValues());
+ ASSERT_FALSE(metadata.hasVectorSearchScore());
}
TEST(DocumentMetadataFieldsTest, HasMethodsReturnTrueForInitializedMetadata) {
@@ -133,6 +136,10 @@ TEST(DocumentMetadataFieldsTest, HasMethodsReturnTrueForInitializedMetadata) {
ASSERT_FALSE(metadata.hasSearchSortValues());
metadata.setSearchSortValues(BSON("a" << 1));
ASSERT_TRUE(metadata.hasSearchSortValues());
+
+ ASSERT_FALSE(metadata.hasVectorSearchScore());
+ metadata.setVectorSearchScore(7.6);
+ ASSERT_TRUE(metadata.hasVectorSearchScore());
}
TEST(DocumentMetadataFieldsTest, MoveConstructor) {
@@ -148,6 +155,7 @@ TEST(DocumentMetadataFieldsTest, MoveConstructor) {
metadata.setSearchScoreDetails(BSON("scoreDetails"
<< "foo"));
metadata.setSearchSortValues(BSON("a" << 1));
+ metadata.setVectorSearchScore(7.6);
DocumentMetadataFields moveConstructed(std::move(metadata));
ASSERT_TRUE(moveConstructed);
@@ -164,6 +172,7 @@ TEST(DocumentMetadataFieldsTest, MoveConstructor) {
BSON("scoreDetails"
<< "foo"));
ASSERT_BSONOBJ_EQ(moveConstructed.getSearchSortValues(), BSON("a" << 1));
+ ASSERT_EQ(moveConstructed.getVectorSearchScore(), 7.6);
ASSERT_FALSE(metadata); // NOLINT(bugprone-use-after-move)
}
@@ -181,6 +190,7 @@ TEST(DocumentMetadataFieldsTest, MoveAssignmentOperator) {
metadata.setSearchScoreDetails(BSON("scoreDetails"
<< "foo"));
metadata.setSearchSortValues(BSON("a" << 1));
+ metadata.setVectorSearchScore(7.6);
DocumentMetadataFields moveAssigned;
moveAssigned.setTextScore(12.3);
@@ -200,6 +210,7 @@ TEST(DocumentMetadataFieldsTest, MoveAssignmentOperator) {
BSON("scoreDetails"
<< "foo"));
ASSERT_BSONOBJ_EQ(moveAssigned.getSearchSortValues(), BSON("a" << 1));
+ ASSERT_EQ(moveAssigned.getVectorSearchScore(), 7.6);
ASSERT_FALSE(metadata); // NOLINT(bugprone-use-after-move)
}
@@ -254,6 +265,7 @@ TEST(DocumentMetadataFieldsTest, MergeWithOnlyCopiesMetadataThatDestinationDoesN
ASSERT_FALSE(destination.hasIndexKey());
ASSERT_FALSE(destination.hasSearchScoreDetails());
ASSERT_FALSE(destination.hasSearchSortValues());
+ ASSERT_FALSE(destination.hasVectorSearchScore());
}
TEST(DocumentMetadataFieldsTest, CopyFromCopiesAllMetadataThatSourceHas) {
@@ -280,6 +292,7 @@ TEST(DocumentMetadataFieldsTest, CopyFromCopiesAllMetadataThatSourceHas) {
ASSERT_FALSE(destination.hasIndexKey());
ASSERT_FALSE(destination.hasSearchScoreDetails());
ASSERT_FALSE(destination.hasSearchSortValues());
+ ASSERT_FALSE(destination.hasVectorSearchScore());
}
TEST(DocumentMetadataFieldsTest, GetTimeseriesBucketMinTimeExists) {
@@ -323,6 +336,7 @@ TEST(DocumentMetadataFieldsTest, MetadataIsMarkedModifiedOnSetMetadataField) {
testFieldSetter([](DocumentMetadataFields& md) { md.setTimeseriesBucketMinTime(Date_t()); });
testFieldSetter([](DocumentMetadataFields& md) { md.setTimeseriesBucketMaxTime(Date_t()); });
testFieldSetter([](DocumentMetadataFields& md) { md.setSearchSortValues(BSON("a" << 1)); });
+ testFieldSetter([](DocumentMetadataFields& md) { md.setVectorSearchScore(60.0); });
}
TEST(DocumentMetadataFieldsTest, MetadataIsConstructedUnmodified) {
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 1b5c833f88b..ecc550f6ee0 100644
--- a/src/mongo/db/exec/document_value/document_value_test.cpp
+++ b/src/mongo/db/exec/document_value/document_value_test.cpp
@@ -907,7 +907,7 @@ TEST(MetaFields, CopyMetadataFromCopiesAllMetadata) {
<< "h" << 1 << "$indexKey" << BSON("y" << 1) << "$searchScoreDetails"
<< BSON("scoreDetails"
<< "foo")
- << "$searchSortValues" << BSON("a" << 1)));
+ << "$searchSortValues" << BSON("a" << 1) << "$vectorSearchScore" << 6.7));
MutableDocument destination{};
destination.copyMetaDataFrom(source);
@@ -925,6 +925,7 @@ TEST(MetaFields, CopyMetadataFromCopiesAllMetadata) {
BSON("scoreDetails"
<< "foo"));
ASSERT_BSONOBJ_EQ(result.metadata().getSearchSortValues(), BSON("a" << 1));
+ ASSERT_EQ(result.metadata().getVectorSearchScore(), 6.7);
}
class SerializationTest : public unittest::Test {
@@ -945,6 +946,8 @@ protected:
ASSERT_EQ(output.metadata().hasSearchScore(), input.metadata().hasSearchScore());
ASSERT_EQ(output.metadata().hasSearchHighlights(), input.metadata().hasSearchHighlights());
ASSERT_EQ(output.metadata().hasIndexKey(), input.metadata().hasIndexKey());
+ ASSERT_EQ(output.metadata().hasVectorSearchScore(),
+ input.metadata().hasVectorSearchScore());
if (input.metadata().hasTextScore()) {
ASSERT_EQ(output.metadata().getTextScore(), input.metadata().getTextScore());
}
@@ -965,6 +968,10 @@ protected:
ASSERT_BSONOBJ_EQ(output.metadata().getSearchScoreDetails(),
input.metadata().getSearchScoreDetails());
}
+ if (input.metadata().hasVectorSearchScore()) {
+ ASSERT_EQ(output.metadata().getVectorSearchScore(),
+ input.metadata().getVectorSearchScore());
+ }
ASSERT(output.toBson().binaryEqual(input.toBson()));
}
@@ -979,6 +986,7 @@ TEST_F(SerializationTest, MetaSerializationNoVals) {
<< "def"_sd));
docBuilder.metadata().setSearchScoreDetails(BSON("scoreDetails"
<< "foo"));
+ docBuilder.metadata().setVectorSearchScore(40.0);
assertRoundTrips(docBuilder.freeze());
}
@@ -993,6 +1001,7 @@ TEST_F(SerializationTest, MetaSerializationWithVals) {
docBuilder.metadata().setIndexKey(BSON("key" << 42));
docBuilder.metadata().setSearchScoreDetails(BSON("scoreDetails"
<< "foo"));
+ docBuilder.metadata().setVectorSearchScore(40.0);
assertRoundTrips(docBuilder.freeze());
}
@@ -1016,6 +1025,7 @@ TEST(MetaFields, ToAndFromBson) {
docBuilder.metadata().setSearchScoreDetails(BSON("scoreDetails"
<< "foo"));
docBuilder.metadata().setSearchSortValues(BSON("a" << 42));
+ docBuilder.metadata().setVectorSearchScore(40.0);
Document doc = docBuilder.freeze();
BSONObj obj = doc.toBsonWithMetaData();
ASSERT_EQ(10.0, obj[Document::metaFieldTextScore].Double());
@@ -1028,6 +1038,7 @@ TEST(MetaFields, ToAndFromBson) {
BSON("scoreDetails"
<< "foo"));
ASSERT_BSONOBJ_EQ(BSON("a" << 42), obj[Document::metaFieldSearchSortValues].Obj());
+ ASSERT_EQ(40.0, obj[Document::metaFieldVectorSearchScore].Double());
Document fromBson = Document::fromBsonWithMetaData(obj);
ASSERT_TRUE(fromBson.metadata().hasTextScore());
ASSERT_TRUE(fromBson.metadata().hasRandVal());
@@ -1037,6 +1048,7 @@ TEST(MetaFields, ToAndFromBson) {
<< "foo"),
fromBson.metadata().getSearchScoreDetails());
ASSERT_BSONOBJ_EQ(BSON("a" << 42), fromBson.metadata().getSearchSortValues());
+ ASSERT_EQ(40.0, fromBson.metadata().getVectorSearchScore());
}
TEST(MetaFields, ToAndFromBsonTrivialConvertibility) {
diff --git a/src/mongo/db/exec/exclusion_projection_executor_test.cpp b/src/mongo/db/exec/exclusion_projection_executor_test.cpp
index 983c5e1995a..c3866ef865f 100644
--- a/src/mongo/db/exec/exclusion_projection_executor_test.cpp
+++ b/src/mongo/db/exec/exclusion_projection_executor_test.cpp
@@ -363,7 +363,8 @@ TEST(ExclusionProjectionExecutionTest, ShouldEvaluateMetaExpressions) {
"i: {$meta: 'recordId'}, "
"j: {$meta: 'indexKey'}, "
"k: {$meta: 'sortKey'}, "
- "l: {$meta: 'searchScoreDetails'}}"));
+ "l: {$meta: 'searchScoreDetails'}, "
+ "m: {$meta: 'vectorSearchScore'}}"));
MutableDocument inputDocBuilder(Document{{"a", 1}, {"b", 2}});
inputDocBuilder.metadata().setTextScore(0.0);
@@ -377,6 +378,7 @@ TEST(ExclusionProjectionExecutionTest, ShouldEvaluateMetaExpressions) {
inputDocBuilder.metadata().setSortKey(Value{Document{{"bar", 8}}}, true);
inputDocBuilder.metadata().setSearchScoreDetails(BSON("scoreDetails"
<< "foo"));
+ inputDocBuilder.metadata().setVectorSearchScore(9.0);
Document inputDoc = inputDocBuilder.freeze();
auto result = exclusion->applyTransformation(inputDoc);
@@ -384,7 +386,7 @@ TEST(ExclusionProjectionExecutionTest, ShouldEvaluateMetaExpressions) {
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}],"
- "l: {scoreDetails: 'foo'}}")});
+ "l: {scoreDetails: 'foo'}, m: 9.0}")});
}
TEST(ExclusionProjectionExecutionTest, ShouldAddMetaExpressionsToDependencies) {
@@ -398,18 +400,20 @@ TEST(ExclusionProjectionExecutionTest, ShouldAddMetaExpressionsToDependencies) {
"i: {$meta: 'recordId'}, "
"j: {$meta: 'indexKey'}, "
"k: {$meta: 'sortKey'}, "
- "l: {$meta: 'searchScoreDetails'}}"));
+ "l: {$meta: 'searchScoreDetails'}}, "
+ "m: {$meta: 'vectorSearchScore'}"));
DepsTracker deps;
exclusion->addDependencies(&deps);
ASSERT_EQ(deps.fields.size(), 0UL);
- // 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).
+ // We do not add the dependencies for searchScore, searchHighlights, searchScoreDetails, or
+ // distance 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_FALSE(deps.metadataDeps()[DocumentMetadataFields::kVectorSearchScore]);
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 53ebf3af4a3..b31899043ca 100644
--- a/src/mongo/db/exec/inclusion_projection_executor_test.cpp
+++ b/src/mongo/db/exec/inclusion_projection_executor_test.cpp
@@ -814,18 +814,20 @@ TEST_F(InclusionProjectionExecutionTestWithFallBackToDefault,
"i: {$meta: 'recordId'}, "
"j: {$meta: 'indexKey'}, "
"k: {$meta: 'sortKey'}, "
- "l: {$meta: 'searchScoreDetails'}}"));
+ "l: {$meta: 'searchScoreDetails'}}, "
+ "m: {$meta: 'vectorSearchScore'}"));
DepsTracker deps;
inclusion->addDependencies(&deps);
ASSERT_EQ(deps.fields.size(), 2UL);
- // 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).
+ // We do not add the dependencies for searchScore, searchHighlights, searchScoreDetails, or
+ // distance 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_FALSE(deps.metadataDeps()[DocumentMetadataFields::kVectorSearchScore]);
ASSERT_TRUE(deps.metadataDeps()[DocumentMetadataFields::kTextScore]);
ASSERT_TRUE(deps.metadataDeps()[DocumentMetadataFields::kRandVal]);
@@ -847,7 +849,8 @@ TEST_F(InclusionProjectionExecutionTestWithFallBackToDefault, ShouldEvaluateMeta
"i: {$meta: 'recordId'}, "
"j: {$meta: 'indexKey'}, "
"k: {$meta: 'sortKey'}, "
- "l: {$meta: 'searchScoreDetails'}}"));
+ "l: {$meta: 'searchScoreDetails'}, "
+ "m: {$meta: 'vectorSearchScore'}}"));
MutableDocument inputDocBuilder(Document{{"a", 1}});
inputDocBuilder.metadata().setTextScore(0.0);
@@ -861,6 +864,7 @@ TEST_F(InclusionProjectionExecutionTestWithFallBackToDefault, ShouldEvaluateMeta
inputDocBuilder.metadata().setSortKey(Value{Document{{"bar", 8}}}, true);
inputDocBuilder.metadata().setSearchScoreDetails(BSON("scoreDetails"
<< "foo"));
+ inputDocBuilder.metadata().setVectorSearchScore(9.0);
Document inputDoc = inputDocBuilder.freeze();
auto result = inclusion->applyTransformation(inputDoc);
@@ -868,7 +872,7 @@ TEST_F(InclusionProjectionExecutionTestWithFallBackToDefault, ShouldEvaluateMeta
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}], "
- "l: {scoreDetails: 'foo'}}")});
+ "l: {scoreDetails: 'foo'}, m: 9.0}")});
}
//
diff --git a/src/mongo/db/pipeline/expression.cpp b/src/mongo/db/pipeline/expression.cpp
index 21542dd0e48..ecb1f2382de 100644
--- a/src/mongo/db/pipeline/expression.cpp
+++ b/src/mongo/db/pipeline/expression.cpp
@@ -3092,9 +3092,11 @@ const std::string sortKeyName = "sortKey";
const std::string searchScoreDetailsName = "searchScoreDetails";
const std::string timeseriesBucketMinTimeName = "timeseriesBucketMinTime";
const std::string timeseriesBucketMaxTimeName = "timeseriesBucketMaxTime";
+const std::string vectorSearchScoreName = "vectorSearchScore";
using MetaType = DocumentMetadataFields::MetaType;
const StringMap<DocumentMetadataFields::MetaType> kMetaNameToMetaType = {
+ {vectorSearchScoreName, MetaType::kVectorSearchScore},
{geoNearDistanceName, MetaType::kGeoNearDist},
{geoNearPointName, MetaType::kGeoNearPoint},
{indexKeyName, MetaType::kIndexKey},
@@ -3110,6 +3112,7 @@ const StringMap<DocumentMetadataFields::MetaType> kMetaNameToMetaType = {
};
const stdx::unordered_map<DocumentMetadataFields::MetaType, StringData> kMetaTypeToMetaName = {
+ {MetaType::kVectorSearchScore, vectorSearchScoreName},
{MetaType::kGeoNearDist, geoNearDistanceName},
{MetaType::kGeoNearPoint, geoNearPointName},
{MetaType::kIndexKey, indexKeyName},
@@ -3153,6 +3156,9 @@ Value ExpressionMeta::serialize(bool explain) const {
Value ExpressionMeta::evaluate(const Document& root, Variables* variables) const {
const auto& metadata = root.metadata();
switch (_metaType) {
+ case MetaType::kVectorSearchScore:
+ return metadata.hasVectorSearchScore() ? Value(metadata.getVectorSearchScore())
+ : Value();
case MetaType::kTextScore:
return metadata.hasTextScore() ? Value(metadata.getTextScore()) : Value();
case MetaType::kRandVal:
diff --git a/src/mongo/db/pipeline/expression_test.cpp b/src/mongo/db/pipeline/expression_test.cpp
index b8c4cf6266a..03fc4d3b663 100644
--- a/src/mongo/db/pipeline/expression_test.cpp
+++ b/src/mongo/db/pipeline/expression_test.cpp
@@ -3161,6 +3161,18 @@ TEST(ExpressionMetaTest, ExpressionMetaSearchScoreDetails) {
Value val = expressionMeta->evaluate(doc.freeze(), &expCtx.variables);
ASSERT_DOCUMENT_EQ(val.getDocument(), Document(details));
}
+
+TEST(ExpressionMetaTest, ExpressionMetaVectorSearchScore) {
+ auto expCtx = ExpressionContextForTest{};
+ BSONObj expr = fromjson("{$meta: \"vectorSearchScore\"}");
+ auto expressionMeta =
+ ExpressionMeta::parse(&expCtx, expr.firstElement(), expCtx.variablesParseState);
+
+ MutableDocument doc;
+ doc.metadata().setVectorSearchScore(1.23);
+ Value val = expressionMeta->evaluate(doc.freeze(), &expCtx.variables);
+ ASSERT_EQ(val.getDouble(), 1.23);
+}
} // namespace expression_meta_test
namespace ExpressionRegexTest {
diff --git a/src/mongo/db/query/query_feature_flags.idl b/src/mongo/db/query/query_feature_flags.idl
index 18cb4dfedc9..81f89c97997 100644
--- a/src/mongo/db/query/query_feature_flags.idl
+++ b/src/mongo/db/query/query_feature_flags.idl
@@ -157,3 +157,8 @@ feature_flags:
cpp_varname: gFeatureFlagSearchBatchSizeLimit
default: true
version: 6.0
+
+ featureFlagVectorSearchPublicPreview:
+ description: "Feature flag to enable vector search for public preview."
+ cpp_varname: gFeatureFlagVectorSearchPublicPreview
+ default: false