diff options
| author | Alyssa Wagenmaker <alyssa.wagenmaker@mongodb.com> | 2023-09-01 18:02:11 +0000 |
|---|---|---|
| committer | Evergreen Agent <no-reply@evergreen.mongodb.com> | 2023-09-01 18:43:21 +0000 |
| commit | 8e4b5670df9b9fe814e57cb5f3f8ee9407237b5a (patch) | |
| tree | 2c2f75187377c70d9772c5fac171470fc2b8f7b0 | |
| parent | 69353845c8a545dc5a763ee199d9002e7cb95e6c (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>
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 |
