Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
<PackageVersion Include="Microsoft.DotNet.Build.Tasks.Templating" Version="$(MicrosoftDotNetBuildTasksTemplatingVersion)" />

<!-- Azure SDK for .NET dependencies -->
<PackageVersion Include="Microsoft.Azure.Cosmos" Version="3.49.0-preview.0" />
<PackageVersion Include="Microsoft.Azure.Cosmos" Version="3.50.0-preview.0" />

<!-- SQL Server dependencies -->
<PackageVersion Include="Microsoft.Data.SqlClient" Version="6.0.1" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,48 +80,29 @@ protected override Expression VisitExtension(Expression expression)
}

// Inlines array parameter of full-text functions, transforming FullTextContainsAll(x, @keywordsArray) to FullTextContainsAll(x, keyword1, keyword2))
// we do this for FullTextContainsAll, FullTextContainsAny and FullTextScore
case SqlFunctionExpression
{
Name: "FullTextContainsAny" or "FullTextContainsAll",
Name: string name,
IsScoringFunction: bool scoringFunction,
Arguments: [var property, SqlParameterExpression { TypeMapping: { ElementTypeMapping: var elementTypeMapping }, Type: Type type } keywords]
} fullTextContainsAllAnyFunction
when type == typeof(string[]):
when (name is "FullTextContainsAny" or "FullTextContainsAll" or "FullTextScore") && type == typeof(string[]):
{
var keywordValues = new List<SqlExpression>();
foreach (var value in (IEnumerable)parametersValues[keywords.Name])
{
keywordValues.Add(sqlExpressionFactory.Constant(value, typeof(string), elementTypeMapping));
}

return sqlExpressionFactory.Function(
return new SqlFunctionExpression(
fullTextContainsAllAnyFunction.Name,
scoringFunction,
[property, .. keywordValues],
fullTextContainsAllAnyFunction.Type,
fullTextContainsAllAnyFunction.TypeMapping);
}

// Inlines array parameter of full-text score, transforming FullTextScore(x, @keywordsArray) to FullTextScore(x, [keyword1, keyword2]))
case SqlFunctionExpression
{
Name: "FullTextScore",
IsScoringFunction: true,
Arguments: [var property, SqlParameterExpression { TypeMapping: { ElementTypeMapping: not null } typeMapping } keywords]
} fullTextScoreFunction:
{
var keywordValues = new List<string>();
foreach (var value in (IEnumerable)parametersValues[keywords.Name])
{
keywordValues.Add((string)value);
}

return new SqlFunctionExpression(
fullTextScoreFunction.Name,
scoringFunction: true,
[property, sqlExpressionFactory.Constant(keywordValues, typeMapping)],
fullTextScoreFunction.Type,
fullTextScoreFunction.TypeMapping);
}

default:
return expression;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,25 @@ public class CosmosFullTextSearchTranslator(ISqlExpressionFactory sqlExpressionF
typeMappingSource.FindMapping(typeof(bool))),

nameof(CosmosDbFunctionsExtensions.FullTextScore)
when arguments is [_, var property, var keywords] => sqlExpressionFactory.ScoringFunction(
when arguments is [_, SqlExpression property, SqlConstantExpression { Type: var keywordClrType, Value: string[] values } keywords]
&& keywordClrType == typeof(string[]) => sqlExpressionFactory.ScoringFunction(
"FullTextScore",
[property, .. values.Select(x => sqlExpressionFactory.Constant(x))],
typeof(double),
typeMappingSource.FindMapping(typeof(double))),

nameof(CosmosDbFunctionsExtensions.FullTextScore)
when arguments is [_, SqlExpression property, SqlParameterExpression { Type: var keywordClrType } keywords]
&& keywordClrType == typeof(string[]) => sqlExpressionFactory.ScoringFunction(
"FullTextScore",
[property, keywords],
typeof(double),
typeMappingSource.FindMapping(typeof(double))),

nameof(CosmosDbFunctionsExtensions.FullTextScore)
when arguments is [_, SqlExpression property, ArrayConstantExpression keywords] => sqlExpressionFactory.ScoringFunction(
"FullTextScore",
[
property,
keywords,
],
[property, .. keywords.Items],
typeof(double),
typeMappingSource.FindMapping(typeof(double))),

Expand Down
65 changes: 43 additions & 22 deletions test/EFCore.Cosmos.FunctionalTests/FullTextSearchCosmosTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,7 @@ public virtual async Task OrderByRank_FullTextScore_constant()
"""
SELECT VALUE c
FROM root c
ORDER BY RANK FullTextScore(c["Description"], ["otter"])
ORDER BY RANK FullTextScore(c["Description"], "otter")
""");
}

Expand All @@ -309,7 +309,7 @@ public virtual async Task OrderByRank_FullTextScore_constants()
"""
SELECT VALUE c
FROM root c
ORDER BY RANK FullTextScore(c["Description"], ["otter","beaver"])
ORDER BY RANK FullTextScore(c["Description"], "otter", "beaver")
""");
}

Expand All @@ -326,7 +326,7 @@ public virtual async Task OrderByRank_FullTextScore_constant_array()
"""
SELECT VALUE c
FROM root c
ORDER BY RANK FullTextScore(c["Description"], ["otter","beaver"])
ORDER BY RANK FullTextScore(c["Description"], "otter", "beaver")
""");
}

Expand All @@ -343,7 +343,7 @@ public virtual async Task OrderByRank_FullTextScore_constant_array_with_one_elem
"""
SELECT VALUE c
FROM root c
ORDER BY RANK FullTextScore(c["Description"], ["otter"])
ORDER BY RANK FullTextScore(c["Description"], "otter")
""");
}

Expand All @@ -361,7 +361,7 @@ public virtual async Task OrderByRank_FullTextScore_parameter_array()
"""
SELECT VALUE c
FROM root c
ORDER BY RANK FullTextScore(c["Description"], ["otter","beaver"])
ORDER BY RANK FullTextScore(c["Description"], "otter", "beaver")
""");
}

Expand All @@ -384,7 +384,7 @@ public virtual async Task OrderByRank_FullTextScore_using_parameters()

SELECT VALUE c
FROM root c
ORDER BY RANK FullTextScore(c["Description"], [@otter, @beaver])
ORDER BY RANK FullTextScore(c["Description"], @otter, @beaver)
""");
}

Expand All @@ -405,7 +405,7 @@ public virtual async Task OrderByRank_FullTextScore_using_one_parameter()

SELECT VALUE c
FROM root c
ORDER BY RANK FullTextScore(c["Description"], [@otter])
ORDER BY RANK FullTextScore(c["Description"], @otter)
""");
}

Expand All @@ -426,7 +426,28 @@ public virtual async Task OrderByRank_FullTextScore_using_parameters_constant_mi

SELECT VALUE c
FROM root c
ORDER BY RANK FullTextScore(c["Description"], ["otter", @beaver])
ORDER BY RANK FullTextScore(c["Description"], "otter", @beaver)
""");
}

[ConditionalFact]
public virtual async Task OrderByRank_FullTextScore_using_parameters_constant_mix_inline()
{
await using var context = CreateContext();

var beaver = "beaver";

var result = await context.Set<FullTextSearchAnimals>()
.OrderBy(x => EF.Functions.FullTextScore(x.Description, "otter", beaver))
.ToListAsync();

AssertSql(
"""
@beaver='beaver'

SELECT VALUE c
FROM root c
ORDER BY RANK FullTextScore(c["Description"], "otter", @beaver)
""");
}

Expand All @@ -447,7 +468,7 @@ public virtual async Task OrderByRank_FullTextScore_using_parameter()

SELECT VALUE c
FROM root c
ORDER BY RANK FullTextScore(c["Description"], [@otter])
ORDER BY RANK FullTextScore(c["Description"], @otter)
""");
}

Expand All @@ -464,7 +485,7 @@ public virtual async Task OrderByRank_FullTextScore_using_complex_expression()
.ToListAsync())).Message;

Assert.Contains(
"The second argument of the FullTextScore function must be a non-empty array of string literals.",
"The second through last arguments of the FullTextScore function must be string literals.",
message);
}

Expand Down Expand Up @@ -495,7 +516,7 @@ public virtual async Task OrderByRank_FullTextScore_on_non_FTS_property()
"""
SELECT VALUE c
FROM root c
ORDER BY RANK FullTextScore(c["PartitionKey"], ["taxonomy"])
ORDER BY RANK FullTextScore(c["PartitionKey"], "taxonomy")
""");
}

Expand All @@ -514,7 +535,7 @@ public virtual async Task OrderByRank_with_RRF_using_two_FullTextScore_functions
"""
SELECT VALUE c
FROM root c
ORDER BY RANK RRF(FullTextScore(c["Description"], ["beaver"]), FullTextScore(c["Description"], ["otter","bat"]))
ORDER BY RANK RRF(FullTextScore(c["Description"], "beaver"), FullTextScore(c["Description"], "otter", "bat"))
""");
}

Expand Down Expand Up @@ -584,7 +605,7 @@ public virtual async Task OrderByRank_Take()
"""
SELECT VALUE c
FROM root c
ORDER BY RANK FullTextScore(c["Description"], ["beaver"])
ORDER BY RANK FullTextScore(c["Description"], "beaver")
OFFSET 0 LIMIT 10
""");
}
Expand All @@ -603,7 +624,7 @@ public virtual async Task OrderByRank_Skip_Take()
"""
SELECT VALUE c
FROM root c
ORDER BY RANK FullTextScore(c["Description"], ["beaver","dolphin"])
ORDER BY RANK FullTextScore(c["Description"], "beaver", "dolphin")
OFFSET 1 LIMIT 20
""");
}
Expand Down Expand Up @@ -633,7 +654,7 @@ public virtual async Task OrderBy_scoring_function_overridden_by_another()
"""
SELECT VALUE c
FROM root c
ORDER BY RANK FullTextScore(c["Description"], ["beaver","dolphin","second"])
ORDER BY RANK FullTextScore(c["Description"], "beaver", "dolphin", "second")
""");
}

Expand Down Expand Up @@ -667,7 +688,7 @@ public virtual async Task Regular_OrderBy_overridden_by_OrderBy_using_scoring_fu
"""
SELECT VALUE c
FROM root c
ORDER BY RANK FullTextScore(c["Description"], ["beaver","dolphin"])
ORDER BY RANK FullTextScore(c["Description"], "beaver", "dolphin")
""");
}

Expand Down Expand Up @@ -725,7 +746,7 @@ public virtual async Task OrderByRank_Where()
SELECT VALUE c
FROM root c
WHERE ((c["PartitionKey"] || "Foo") = "habitatFoo")
ORDER BY RANK FullTextScore(c["Description"], ["beaver","dolphin"])
ORDER BY RANK FullTextScore(c["Description"], "beaver", "dolphin")
""");
}

Expand Down Expand Up @@ -779,7 +800,7 @@ public virtual async Task OrderByRank_with_FullTextScore_on_nested_owned_type()
"""
SELECT VALUE c
FROM root c
ORDER BY RANK FullTextScore(c["Owned"]["NestedReference"]["AnotherDescription"], ["beaver","dolphin"])
ORDER BY RANK FullTextScore(c["Owned"]["NestedReference"]["AnotherDescription"], "beaver", "dolphin")
""");
}

Expand Down Expand Up @@ -816,7 +837,7 @@ public virtual async Task OrderByRank_with_FullTextScore_on_nested_owned_collect
"""
SELECT VALUE c
FROM root c
ORDER BY RANK FullTextScore(c["Owned"]["NestedCollection"][0]["AnotherDescription"], ["beaver","dolphin"])
ORDER BY RANK FullTextScore(c["Owned"]["NestedCollection"][0]["AnotherDescription"], "beaver", "dolphin")
""");
}

Expand All @@ -832,7 +853,7 @@ public virtual async Task OrderBy_scoring_function_on_property_with_modified_jso
"""
SELECT VALUE c
FROM root c
ORDER BY RANK FullTextScore(c["CustomDecription"], ["beaver","dolphin"])
ORDER BY RANK FullTextScore(c["CustomDecription"], "beaver", "dolphin")
""");
}

Expand All @@ -849,7 +870,7 @@ public virtual async Task OrderByRank_with_FullTextScore_on_nested_owned_type_wi
"""
SELECT VALUE c
FROM root c
ORDER BY RANK FullTextScore(c["Owned"]["CustomNestedReference"]["AnotherDescription"], ["beaver","dolphin"])
ORDER BY RANK FullTextScore(c["Owned"]["CustomNestedReference"]["AnotherDescription"], "beaver", "dolphin")
""");
}

Expand All @@ -866,7 +887,7 @@ public virtual async Task OrderByRank_with_FullTextScore_on_property_without_ind
"""
SELECT VALUE c
FROM root c
ORDER BY RANK FullTextScore(c["DescriptionNoIndex"], ["beaver","dolphin"])
ORDER BY RANK FullTextScore(c["DescriptionNoIndex"], "beaver", "dolphin")
""");
}

Expand Down
8 changes: 4 additions & 4 deletions test/EFCore.Cosmos.FunctionalTests/HybridSearchCosmosTest.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ public virtual async Task Hybrid_search_vector_distance_and_FullTextScore_in_Ord

SELECT VALUE c
FROM root c
ORDER BY RANK RRF(FullTextScore(c["Description"], ["beaver","otter"]), VectorDistance(c["SBytes"], @inputVector, false, {'distanceFunction':'dotproduct', 'dataType':'int8'}))
ORDER BY RANK RRF(FullTextScore(c["Description"], "beaver", "otter"), VectorDistance(c["SBytes"], @inputVector, false, {'distanceFunction':'dotproduct', 'dataType':'int8'}))
""");
}

Expand All @@ -62,7 +62,7 @@ public virtual async Task Hybrid_search_vector_distance_and_FullTextScore_with_s

SELECT VALUE c
FROM root c
ORDER BY RANK RRF(FullTextScore(c["Description"], ["beaver"]), VectorDistance(c["SBytes"], @inputVector, false, {'distanceFunction':'dotproduct', 'dataType':'int8'}))
ORDER BY RANK RRF(FullTextScore(c["Description"], "beaver"), VectorDistance(c["SBytes"], @inputVector, false, {'distanceFunction':'dotproduct', 'dataType':'int8'}))
""");
}

Expand All @@ -84,7 +84,7 @@ public virtual async Task Hybrid_search_vector_distance_and_FullTextScore_in_Ord

SELECT VALUE c
FROM root c
ORDER BY RANK RRF(FullTextScore(c["Owned"]["AnotherDescription"], ["beaver"]), VectorDistance(c["Owned"]["Singles"], @inputVector, false, {'distanceFunction':'cosine', 'dataType':'float32'}))
ORDER BY RANK RRF(FullTextScore(c["Owned"]["AnotherDescription"], "beaver"), VectorDistance(c["Owned"]["Singles"], @inputVector, false, {'distanceFunction':'cosine', 'dataType':'float32'}))
""");
}

Expand All @@ -107,7 +107,7 @@ public virtual async Task Hybrid_search_vector_distance_and_FullTextScore_in_Ord

SELECT VALUE c
FROM root c
ORDER BY RANK RRF(VectorDistance(c["Owned"]["Singles"], @inputVector, false, {'distanceFunction':'cosine', 'dataType':'float32'}), FullTextScore(c["Owned"]["AnotherDescription"], ["beaver","otter"]))
ORDER BY RANK RRF(VectorDistance(c["Owned"]["Singles"], @inputVector, false, {'distanceFunction':'cosine', 'dataType':'float32'}), FullTextScore(c["Owned"]["AnotherDescription"], "beaver", "otter"))
""");
}

Expand Down