SERVER-82020: BACKPORT-25616 [v8.2]: Enable hybrid search feature flag and upgrade/downgrade testing (#39468)

Co-authored-by: Finley Lau <finley.lau@mongodb.com>
Co-authored-by: Mariano Shaar <mariano.shaar@mongodb.com>
GitOrigin-RevId: 6e5c812d221048df48014948cc841e034a4873fc
This commit is contained in:
joe-mongodb 2025-07-31 15:36:31 -05:00 committed by MongoDB Bot
parent 8387f4eb6a
commit 675d0b61d2
16 changed files with 303 additions and 138 deletions

View File

@ -9,7 +9,8 @@
* @tags: [
* featureFlagRankFusionFull,
* featureFlagSearchHybridScoringFull,
* do_not_wrap_aggregations_in_facets
* do_not_wrap_aggregations_in_facets,
* requires_fcv_82
* ]
*/

View File

@ -3,7 +3,7 @@
* $scoreFusion and $score exists inside of jstests/with_mongot/e2e/hybridSearch. This test exists
* in order to get picked up by the fuzzer/query shape hash stability tests (tests must exist inside
* of the jstests/aggregation directory).
* @tags: [ featureFlagSearchHybridScoringFull, do_not_wrap_aggregations_in_facets, requires_fcv_81
* @tags: [ featureFlagSearchHybridScoringFull, do_not_wrap_aggregations_in_facets, requires_fcv_82
* ]
*/

View File

@ -1,6 +1,6 @@
/**
* Test that $minMaxScaler window function output values are as expected.
* @tags: [featureFlagSearchHybridScoringFull, requires_fcv_81]
* @tags: [featureFlagSearchHybridScoringFull, requires_fcv_82]
*/
const coll = db[jsTestName()];

View File

@ -1,6 +1,6 @@
/**
* Test that $minMaxScaler window function expression parsing works.
* @tags: [featureFlagSearchHybridScoringFull, requires_fcv_81]
* @tags: [featureFlagSearchHybridScoringFull, requires_fcv_82]
*/
const coll = db[jsTestName()];

View File

@ -18,6 +18,8 @@ const coll = testDb[collName];
coll.drop();
coll.insert({a: 1});
const kUnrecognizedPipelineStageErrorCode = 40324;
const unstablePipelines = [
[{$collStats: {count: {}, latencyStats: {}}}],
[{$currentOp: {}}],
@ -42,71 +44,47 @@ if (is81orAbove(db)) {
unstablePipelines.push([{$listClusterCatalog: {}}]);
}
// TODO SERVER-85426 $rankFusion can always be included when the feature flag is removed.
// TODO SERVER-98591 Change RankFusionFull to RankFusionBasic.
if (FeatureFlagUtil.isPresentAndEnabled(testDb.getMongo(), 'RankFusionFull')) {
unstablePipelines.push([{$rankFusion: {input: {pipelines: {field1: [{$sort: {foo: 1}}]}}}}]);
}
unstablePipelines.push([{$rankFusion: {input: {pipelines: {field1: [{$sort: {foo: 1}}]}}}}]);
// TODO SERVER-82020: $scoreFusion/$score/$minMaxScaler can always be included when the feature flag
// is enabled by default.
if (FeatureFlagUtil.isPresentAndEnabled(testDb.getMongo(), 'SearchHybridScoringFull')) {
let hybridSearchPipelines = [
// $score is not included in the strict api.
[{$score: {score: 10}}],
// $minMaxScaler is not included in the strict api.
[{
$setWindowFields: {
sortBy: {_id: 1},
output: {
"relativeXValue": {
$minMaxScaler: {
input: "$x",
},
window: {range: ["unbounded", "unbounded"]}
let hybridSearchPipelines = [
// $score is not included in the strict api.
[{$score: {score: 10}}],
// $minMaxScaler is not included in the strict api.
[{
$setWindowFields: {
sortBy: {_id: 1},
output: {
"relativeXValue": {
$minMaxScaler: {
input: "$x",
},
}
}
}],
// $scoreFusion is not included in the strict api.
[{
$scoreFusion: {
input: {
pipelines: {
score2: [
{
$search: {
index: "search_index",
text: {query: "mystery", path: "genres"}
}
},
{$match: {author: "dave"}}
]
},
normalization: "none"
window: {range: ["unbounded", "unbounded"]}
},
combination: {weights: {score2: 5}}
}
}]
];
}
}],
// $scoreFusion is not included in the strict api.
[{
$scoreFusion: {
input: {
pipelines: {
score2: [
{
$search:
{index: "search_index", text: {query: "mystery", path: "genres"}}
},
{$match: {author: "dave"}}
]
},
normalization: "none"
},
combination: {weights: {score2: 5}}
}
}]
];
for (var hybridSearchPipeline of hybridSearchPipelines) {
// Hybrid search stages require a minimum FCV of 8.1. In some tests, namely
// fcv_upgrade_downgrade_replica_sets_jscore_passthrough and its burn-in tests, the FCV is
// changed in the background as the test is running. This results in a flaky test, unless we
// accept both the strict API error code and the queryFeatureNotAllowed error code for
// hybrid search pipelines.
assert.commandFailedWithCode(
testDb.runCommand({
aggregate: collName,
pipeline: hybridSearchPipeline,
cursor: {},
apiStrict: true,
apiVersion: "1"
}),
[ErrorCodes.APIStrictError, ErrorCodes.QueryFeatureNotAllowed]);
}
}
unstablePipelines.concat(hybridSearchPipelines);
function assertAggregateFailsWithAPIStrict(pipeline) {
assert.commandFailedWithCode(testDb.runCommand({
@ -116,7 +94,7 @@ function assertAggregateFailsWithAPIStrict(pipeline) {
apiStrict: true,
apiVersion: "1"
}),
ErrorCodes.APIStrictError);
[ErrorCodes.APIStrictError, kUnrecognizedPipelineStageErrorCode]);
}
for (let pipeline of unstablePipelines) {
@ -124,6 +102,8 @@ for (let pipeline of unstablePipelines) {
assertAggregateFailsWithAPIStrict(pipeline);
// Assert error thrown when creating a view on a pipeline with stages not in API Version 1.
// The error code may also be an unrecognized pipeline stage, if the test is running in a
// multiversioned scenario.
assert.commandFailedWithCode(testDb.runCommand({
create: 'api_version_pipeline_stages_should_fail',
viewOn: collName,
@ -131,7 +111,7 @@ for (let pipeline of unstablePipelines) {
apiStrict: true,
apiVersion: "1"
}),
ErrorCodes.APIStrictError);
[ErrorCodes.APIStrictError, kUnrecognizedPipelineStageErrorCode]);
}
// Test that $collStats is allowed in APIVersion 1, even with 'apiStrict: true', so long as the only

View File

@ -0,0 +1,10 @@
load("//bazel:mongo_js_rules.bzl", "mongo_js_library")
package(default_visibility = ["//visibility:public"])
mongo_js_library(
name = "all_javascript_files",
srcs = glob([
"*.js",
]),
)

View File

@ -0,0 +1,5 @@
version: 2.0.0
filters:
- "*":
approvers:
- 10gen/query-integration-search

View File

@ -11,8 +11,14 @@ import {
const collName = jsTestName();
const getDB = (primaryConnection) => primaryConnection.getDB(jsTestName());
const docs = [{_id: 0, foo: "xyz"}, {_id: 1, foo: "bar"}, {_id: 2, foo: "mongodb"}];
const viewName = "rank_fusion_view";
// This is a simple view pipeline that we will attempt to run $rankFusion queries on.
const viewPipeline = [{$match: {_id: {$gt: 0}}}];
// TODO SERVER-108470 Add tests for $rankFusion with multiple input pipelines.
const rankFusionPipeline = [{$rankFusion: {input: {pipelines: {field: [{$sort: {foo: 1}}]}}}}];
const rankFusionPipelineWithScoreDetails = [
{$rankFusion: {input: {pipelines: {field: [{$sort: {foo: 1}}]}}, scoreDetails: true}},
@ -20,7 +26,6 @@ const rankFusionPipelineWithScoreDetails = [
];
const kUnrecognizedPipelineStageErrorCode = 40324;
function setupCollection(primaryConn, shardingTest = null) {
const coll = assertDropAndRecreateCollection(getDB(primaryConn), collName);
@ -28,8 +33,7 @@ function setupCollection(primaryConn, shardingTest = null) {
shardingTest.shardColl(coll, {_id: 1});
}
assert.commandWorked(
coll.insertMany([{_id: 0, foo: "xyz"}, {_id: 1, foo: "bar"}, {_id: 2, foo: "mongodb"}]));
assert.commandWorked(coll.insertMany(docs));
}
function assertRankFusionCompletelyRejected(primaryConn) {
@ -47,7 +51,6 @@ function assertRankFusionCompletelyRejected(primaryConn) {
{aggregate: collName, pipeline: rankFusionPipelineWithScoreDetails, cursor: {}}),
[kUnrecognizedPipelineStageErrorCode, ErrorCodes.QueryFeatureNotAllowed]);
// TODO SERVER-101721 Remove "OptionNotSupportedOnView" once $rankFusion in view is enabled.
// View creation is rejected when view pipeline has $rankFusion.
assert.commandFailedWithCode(db.createView(viewName, collName, rankFusionPipeline), [
kUnrecognizedPipelineStageErrorCode,
@ -60,6 +63,12 @@ function assertRankFusionCompletelyRejected(primaryConn) {
ErrorCodes.QueryFeatureNotAllowed,
ErrorCodes.OptionNotSupportedOnView
]);
// Running $rankFusion against a view is rejected.
assert.commandWorked(db.createView(viewName, collName, viewPipeline));
assert.commandFailedWithCode(
db.runCommand({aggregate: viewName, pipeline: rankFusionPipeline, cursor: {}}),
[kUnrecognizedPipelineStageErrorCode, ErrorCodes.OptionNotSupportedOnView]);
}
function assertRankFusionCompletelyAccepted(primaryConn) {
@ -74,31 +83,7 @@ function assertRankFusionCompletelyAccepted(primaryConn) {
assert.commandWorked(db.runCommand(
{aggregate: collName, pipeline: rankFusionPipelineWithScoreDetails, cursor: {}}));
// TODO SERVER-101721 Enable $rankFusion to be run in a view definition.
assert.commandFailedWithCode(db.createView(viewName, collName, rankFusionPipeline),
[ErrorCodes.OptionNotSupportedOnView]);
assert.commandFailedWithCode(
db.createView(viewName, collName, rankFusionPipelineWithScoreDetails),
[ErrorCodes.OptionNotSupportedOnView]);
/**
assert.commandWorked(db.createView(viewName, collName, rankFusionPipeline));
assert.commandWorked(
db.runCommand({aggregate: viewName, pipeline: [{$match: {_id: {$gt: 0}}}], cursor: {}}));
*/
}
function assertRankFusionAcceptedButNotInView(primaryConn) {
const db = getDB(primaryConn);
db[viewName].drop();
// $rankFusion succeeds in an aggregation command, but view creation is rejection with
// $rankFusion in the view pipeline.
assert.commandWorked(
db.runCommand({aggregate: collName, pipeline: rankFusionPipeline, cursor: {}}));
assert.commandWorked(db.runCommand(
{aggregate: collName, pipeline: rankFusionPipelineWithScoreDetails, cursor: {}}));
// TODO SERVER-101721 Remove "OptionNotSupportedOnView" once $rankFusion in view is enabled.
// View creation is rejected when view pipeline has $rankFusion.
assert.commandFailedWithCode(db.createView(viewName, collName, rankFusionPipeline), [
kUnrecognizedPipelineStageErrorCode,
ErrorCodes.QueryFeatureNotAllowed,
@ -110,6 +95,13 @@ function assertRankFusionAcceptedButNotInView(primaryConn) {
ErrorCodes.QueryFeatureNotAllowed,
ErrorCodes.OptionNotSupportedOnView
]);
// Running $rankFusion against a view succeeds.
assert.commandWorked(db.createView(viewName, collName, viewPipeline));
assert.commandWorked(
db.runCommand({aggregate: viewName, pipeline: rankFusionPipeline, cursor: {}}));
assert.commandWorked(db.runCommand(
{aggregate: viewName, pipeline: rankFusionPipelineWithScoreDetails, cursor: {}}));
}
testPerformUpgradeDowngradeReplSet({
@ -126,6 +118,9 @@ testPerformUpgradeDowngradeSharded({
whenOnlyConfigIsLatestBinary: assertRankFusionCompletelyRejected,
whenSecondariesAndConfigAreLatestBinary: assertRankFusionCompletelyRejected,
whenMongosBinaryIsLastLTS: assertRankFusionCompletelyRejected,
whenBinariesAreLatestAndFCVIsLastLTS: assertRankFusionAcceptedButNotInView,
// TODO SERVER-108470 This should completely reject $rankFusion, however because mongos is not
// FCV-aware, it is non-trivial to detect that $rankFusion should be rejected here on the
// shards.
whenBinariesAreLatestAndFCVIsLastLTS: assertRankFusionCompletelyAccepted,
whenFullyUpgraded: assertRankFusionCompletelyAccepted,
});

View File

@ -0,0 +1,205 @@
/**
* Verifies that $scoreFusion behaves correctly in FCV upgrade/downgrade scenarios.
*/
import {assertDropAndRecreateCollection} from "jstests/libs/collection_drop_recreate.js";
import {
testPerformUpgradeDowngradeReplSet
} from "jstests/multiVersion/libs/mixed_version_fixture_test.js";
import {
testPerformUpgradeDowngradeSharded
} from "jstests/multiVersion/libs/mixed_version_sharded_fixture_test.js";
const collName = jsTestName();
const getDB = (primaryConnection) => primaryConnection.getDB(jsTestName());
const viewName = collName + "_view";
const scoreFusionPipeline = [{
$scoreFusion: {
input: {
pipelines:
{otherField: [{$score: {score: "$bar"}}], field: [{$score: {score: "$foo"}}]},
normalization: "minMaxScaler"
}
}
}];
const scoreFusionPipelineWithScoreDetails = [{
$scoreFusion: {
input: {
pipelines:
{otherField: [{$score: {score: "$bar"}}], field: [{$score: {score: "$foo"}}]},
normalization: "none",
},
scoreDetails: true
}
}];
// This is a simple view pipeline that we will attempt to run $scoreFusion queries on.
const viewPipeline = [{$match: {_id: {$gt: 0}}}];
const scorePipeline = [{$score: {score: "$foo"}}];
const scorePipelineWithScoreDetails = [{$score: {score: "$foo", scoreDetails: true}}];
const projectStage = {
$project: {scoreDetails: {$meta: "scoreDetails"}, score: {$meta: "score"}}
};
const scoreFusionPipelineWithProject = [...scoreFusionPipelineWithScoreDetails, projectStage];
const scorePipelineWithProject = [...scorePipelineWithScoreDetails, projectStage];
const kUnrecognizedPipelineStageErrorCode = 40324;
function setupCollection(primaryConn, shardingTest = null) {
const coll = assertDropAndRecreateCollection(getDB(primaryConn), collName);
if (shardingTest) {
shardingTest.shardColl(coll, {_id: 1});
}
assert.commandWorked(coll.insertMany(
[{_id: 0, foo: 5, bar: 10}, {_id: 1, foo: 6, bar: 20}, {_id: 2, foo: 7, bar: 30}]));
}
function assertScoreFusionCompletelyRejected(primaryConn) {
const db = getDB(primaryConn);
db[viewName].drop();
// Creating a view with $scoreFusion/$score is rejected.
assert.commandFailedWithCode(
db.createView("bad_score_fusion_view", collName, scoreFusionPipeline),
[kUnrecognizedPipelineStageErrorCode, ErrorCodes.OptionNotSupportedOnView]);
assert.commandFailedWithCode(
db.createView("bad_score_view", collName, scorePipeline),
[kUnrecognizedPipelineStageErrorCode, ErrorCodes.OptionNotSupportedOnView]);
// $scoreFusion/$score on a view is rejected.
assert.commandWorked(db.createView(viewName, collName, viewPipeline));
assert.commandFailedWithCode(
db.runCommand({aggregate: viewName, pipeline: scoreFusionPipeline, cursor: {}}), [
kUnrecognizedPipelineStageErrorCode,
ErrorCodes.QueryFeatureNotAllowed,
ErrorCodes.OptionNotSupportedOnView,
ErrorCodes.FailedToParse
]);
assert.commandFailedWithCode(
db.runCommand({aggregate: viewName, pipeline: scorePipeline, cursor: {}}), [
kUnrecognizedPipelineStageErrorCode,
ErrorCodes.QueryFeatureNotAllowed,
ErrorCodes.OptionNotSupportedOnView,
ErrorCodes.FailedToParse
]);
// $projects referencing score and scoreDetails metadata are rejected in aggregation
// commands.
assert.commandFailedWithCode(db.runCommand({
aggregate: collName,
pipeline: [...scorePipelineWithScoreDetails, projectStage],
cursor: {}
}),
[
ErrorCodes.FailedToParse,
ErrorCodes.QueryFeatureNotAllowed,
kUnrecognizedPipelineStageErrorCode,
17308
]);
assert.commandFailedWithCode(db.runCommand({
aggregate: collName,
pipeline: [...scorePipelineWithScoreDetails, projectStage],
cursor: {}
}),
[
ErrorCodes.FailedToParse,
ErrorCodes.QueryFeatureNotAllowed,
kUnrecognizedPipelineStageErrorCode,
17308
]);
// $scoreFusion is rejected in a plain aggregation command.
assert.commandFailedWithCode(
db.runCommand({aggregate: collName, pipeline: scoreFusionPipeline, cursor: {}}), [
kUnrecognizedPipelineStageErrorCode,
ErrorCodes.FailedToParse,
ErrorCodes.QueryFeatureNotAllowed,
]);
// $scoreFusion with scoreDetails is still rejected.
assert.commandFailedWithCode(
db.runCommand(
{aggregate: collName, pipeline: scoreFusionPipelineWithScoreDetails, cursor: {}}),
[
kUnrecognizedPipelineStageErrorCode,
ErrorCodes.FailedToParse,
ErrorCodes.QueryFeatureNotAllowed,
]);
// $score is rejected in a plain aggregation command.
assert.commandFailedWithCode(
db.runCommand({aggregate: collName, pipeline: scorePipeline, cursor: {}}), [
kUnrecognizedPipelineStageErrorCode,
ErrorCodes.FailedToParse,
ErrorCodes.QueryFeatureNotAllowed,
]);
// $score with scoreDetails is still rejected.
assert.commandFailedWithCode(
db.runCommand({aggregate: collName, pipeline: scorePipelineWithScoreDetails, cursor: {}}), [
kUnrecognizedPipelineStageErrorCode,
ErrorCodes.FailedToParse,
ErrorCodes.QueryFeatureNotAllowed,
]);
}
function assertScoreFusionCompletelyAccepted(primaryConn) {
const db = getDB(primaryConn);
db[viewName].drop();
// Creating a view with $scoreFusion/$score is rejected.
assert.commandFailedWithCode(
db.createView("bad_score_fusion_view", collName, scoreFusionPipeline),
[ErrorCodes.OptionNotSupportedOnView]);
assert.commandFailedWithCode(db.createView("bad_score_view", collName, scorePipeline),
[ErrorCodes.OptionNotSupportedOnView]);
// $scoreFusion/$score on a view works.
assert.commandWorked(db.createView(viewName, collName, viewPipeline));
assert.commandWorked(
db.runCommand({aggregate: viewName, pipeline: scoreFusionPipeline, cursor: {}}));
assert.commandWorked(db.runCommand({aggregate: viewName, pipeline: scorePipeline, cursor: {}}));
// $projects referencing score and scoreDetails metadata succeed in aggregation commands.
assert.commandWorked(
db.runCommand({aggregate: collName, pipeline: scoreFusionPipelineWithProject, cursor: {}}));
assert.commandWorked(
db.runCommand({aggregate: collName, pipeline: scorePipelineWithProject, cursor: {}}));
// $scoreFusion succeeds in an aggregation command.
assert.commandWorked(
db.runCommand({aggregate: collName, pipeline: scoreFusionPipeline, cursor: {}}));
// $scoreFusion with scoreDetails succeeds in an aggregation command.
assert.commandWorked(db.runCommand(
{aggregate: collName, pipeline: scoreFusionPipelineWithScoreDetails, cursor: {}}));
// $score succeeds in an aggregation command.
assert.commandWorked(db.runCommand({aggregate: collName, pipeline: scorePipeline, cursor: {}}));
// $score with scoreDetails succeeds in an aggregation command.
assert.commandWorked(
db.runCommand({aggregate: collName, pipeline: scorePipelineWithScoreDetails, cursor: {}}));
}
testPerformUpgradeDowngradeReplSet({
setupFn: setupCollection,
whenFullyDowngraded: assertScoreFusionCompletelyRejected,
whenSecondariesAreLatestBinary: assertScoreFusionCompletelyRejected,
whenBinariesAreLatestAndFCVIsLastLTS: assertScoreFusionCompletelyRejected,
whenFullyUpgraded: assertScoreFusionCompletelyAccepted,
});
testPerformUpgradeDowngradeSharded({
setupFn: setupCollection,
whenFullyDowngraded: assertScoreFusionCompletelyRejected,
whenOnlyConfigIsLatestBinary: assertScoreFusionCompletelyRejected,
whenSecondariesAndConfigAreLatestBinary: assertScoreFusionCompletelyRejected,
whenMongosBinaryIsLastLTS: assertScoreFusionCompletelyRejected,
whenBinariesAreLatestAndFCVIsLastLTS: assertScoreFusionCompletelyRejected,
whenFullyUpgraded: assertScoreFusionCompletelyAccepted,
});

View File

@ -8,7 +8,7 @@
* featureFlagRankFusionFull,
* # Needed for the nested $scoreFusion.
* featureFlagSearchHybridScoringFull,
* requires_fcv_81
* requires_fcv_82
* ]
*/

View File

@ -2,7 +2,7 @@
/**
* Tests that the $sample stage works reasonably within $rankFusion.
*
* @tags: [featureFlagSearchHybridScoringFull, requires_fcv_81]
* @tags: [featureFlagSearchHybridScoringFull, requires_fcv_82]
*/
import {assertErrorCode} from "jstests/aggregation/extras/utils.js";

View File

@ -20,6 +20,7 @@
* featureFlagRankFusionBasic,
* featureFlagRankFusionFull,
* featureFlagSearchHybridScoringFull,
* requires_fcv_82
* ]
*/

View File

@ -4,7 +4,7 @@
* analysis in various pipeline structures and does not verify correctness of the scoreDetails field
* contents itself.
*
* @tags: [ featureFlagRankFusionFull, featureFlagSearchHybridScoringFull, requires_fcv_81 ]
* @tags: [ featureFlagRankFusionFull, featureFlagSearchHybridScoringFull, requires_fcv_82 ]
*/
import {assertErrCodeAndErrMsgContains} from "jstests/aggregation/extras/utils.js";

View File

@ -63,24 +63,7 @@ public:
scopedGlobalServiceContextForTest = nullptr)
: AggregationContextFixture(
NamespaceString::createNamespaceString_forTest(boost::none, "test", "pipeline_test"),
std::move(scopedGlobalServiceContextForTest)) {
// TODO SERVER-82020: Delete this once the feature flag defaults to true.
// $minMaxScaler is gated behind a feature flag and does
// not get put into the map as the flag is off by default. Changing the value of the feature
// flag with RAIIServerParameterControllerForTest() does not solve the issue because the
// registration logic is not re-hit.
try {
window_function::Expression::registerParser(
"$minMaxScaler",
window_function::ExpressionMinMaxScaler::parse,
nullptr,
AllowedWithApiStrict::kNeverInVersion1);
} catch (const DBException& e) {
// Allow this exception, to allow multiple instances
// to be created in this process.
ASSERT_EQ(e.reason(), "Duplicate parsers ($minMaxScaler) registered.");
}
}
std::move(scopedGlobalServiceContextForTest)) {}
explicit AggregationContextFixture(NamespaceString nss,
std::unique_ptr<ScopedGlobalServiceContextForTest>

View File

@ -46,24 +46,7 @@ namespace {
class DocumentSourceScoreFusionTest : service_context_test::WithSetupTransportLayer,
public AggregationContextFixture {
public:
DocumentSourceScoreFusionTest() {
// TODO SERVER-82020: Delete this once the feature flag defaults to true.
// $minMaxScaler is gated behind a feature flag and does
// not get put into the map as the flag is off by default. Changing the value of the feature
// flag with RAIIServerParameterControllerForTest() does not solve the issue because the
// registration logic is not re-hit.
try {
window_function::Expression::registerParser(
"$minMaxScaler",
window_function::ExpressionMinMaxScaler::parse,
nullptr,
AllowedWithApiStrict::kNeverInVersion1);
} catch (const DBException& e) {
// Allow this exception, to allow multiple instances
// to be created in this process.
ASSERT(e.reason() == "Duplicate parsers ($minMaxScaler) registered.");
}
}
DocumentSourceScoreFusionTest() {}
private:
RAIIServerParameterControllerForTest scoreFusionFlag{"featureFlagSearchHybridScoringFull",

View File

@ -187,8 +187,9 @@ feature_flags:
featureFlagRankFusionFull:
description:
"Feature flag to finish up $rankFusion features as well as introduce $score and
$scoreDetails stages. (Second milestone of Vector Search Hybrid Scoring)"
"Feature flag to finish up $rankFusion features as well as introduce score and
scoreDetails metadata fields. $rankFusion will also begin to produce these metadata fields.
(Second milestone of Vector Search Hybrid Scoring)"
cpp_varname: gFeatureFlagRankFusionFull
default: true
version: 8.1
@ -197,10 +198,11 @@ feature_flags:
featureFlagSearchHybridScoringFull:
description:
"Feature flag to introduce $scoreFusion, the $minMaxScaler helper and finish up
"Feature flag to introduce $scoreFusion, $score, and the $minMaxScaler helper and finish up
hybrid scoring features. (Final milestone of Vector Search Hybrid Scoring)"
cpp_varname: gFeatureFlagSearchHybridScoringFull
default: false
default: true
version: 8.2
fcv_gated: true
featureFlagQueryMemoryTracking: