Skip to content

Commit 31e670c

Browse files
committed
Cosmos Full Text Search support
- Adding model building API to configure property as full-text search enabled, as well as setup the index for it, - Adding model validation (e.g. FTS index not matching FTS property), - Adding EF.Functions stubs and translations for FullTextContains, FullTextContainsAll, FullTextContainsAny, FullTextScore and RRF (for hybrid), - Adding logic in SelectExpression to produce ORDER BY RANK when necessary, - Adding validation when attempting to mix with ORDER BY RANK with regular ORDER BY, - Rewrite OFFSET/LIMIT from parameter to constant when ORDER BY RANK is present. - Adding model building support for default language on the container level Also fixed / added support for vector search on owned types (since it shares logic with FTS) and added some tests. outstanding work: - support for FTS Container building using Azure.ResourceManager.CosmosDb (currently blocked on updated package being released) Fixes #35476 Fixes #35853 Fixes #35867 Fixes #35852
1 parent 2b564f7 commit 31e670c

35 files changed

+3077
-119
lines changed

Diff for: Directory.Packages.props

+1-1
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@
3737
<PackageVersion Include="Microsoft.DotNet.Build.Tasks.Templating" Version="$(MicrosoftDotNetBuildTasksTemplatingVersion)" />
3838

3939
<!-- Azure SDK for .NET dependencies -->
40-
<PackageVersion Include="Microsoft.Azure.Cosmos" Version="3.46.0" />
40+
<PackageVersion Include="Microsoft.Azure.Cosmos" Version="3.49.0-preview.0" />
4141

4242
<!-- SQL Server dependencies -->
4343
<PackageVersion Include="Microsoft.Data.SqlClient" Version="6.0.1" />

Diff for: src/EFCore.Cosmos/EFCore.Cosmos.csproj

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
<Project Sdk="Microsoft.NET.Sdk">
1+
<Project Sdk="Microsoft.NET.Sdk">
22

33
<PropertyGroup>
44
<Description>Azure Cosmos provider for Entity Framework Core.</Description>
@@ -12,6 +12,7 @@
1212
<NoWarn>$(NoWarn);EF9101</NoWarn> <!-- Metrics is experimental -->
1313
<NoWarn>$(NoWarn);EF9102</NoWarn> <!-- Paging is experimental -->
1414
<NoWarn>$(NoWarn);EF9103</NoWarn> <!-- Vector search is experimental -->
15+
<NoWarn>$(NoWarn);EF9104</NoWarn> <!-- Full-text search is experimental -->
1516
</PropertyGroup>
1617

1718
<ItemGroup>

Diff for: src/EFCore.Cosmos/Extensions/CosmosDbFunctionsExtensions.cs

+54
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,60 @@ public static T CoalesceUndefined<T>(
5252
T expression2)
5353
=> throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(CoalesceUndefined)));
5454

55+
/// <summary>
56+
/// Checks if the specified property contains the given keyword using full-text search.
57+
/// </summary>
58+
/// <param name="_">The <see cref="DbFunctions" /> instance.</param>
59+
/// <param name="property">The property to search.</param>
60+
/// <param name="keyword">The keyword to search for.</param>
61+
/// <returns><see langword="true" /> if the property contains the keyword; otherwise, <see langword="false" />.</returns>
62+
[Experimental(EFDiagnostics.CosmosFullTextSearchExperimental)]
63+
public static bool FullTextContains(this DbFunctions _, string property, string keyword)
64+
=> throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(FullTextContains)));
65+
66+
/// <summary>
67+
/// Checks if the specified property contains all the given keywords using full-text search.
68+
/// </summary>
69+
/// <param name="_">The <see cref="DbFunctions" /> instance.</param>
70+
/// <param name="property">The property to search.</param>
71+
/// <param name="keywords">The keywords to search for.</param>
72+
/// <returns><see langword="true" /> if the property contains all the keywords; otherwise, <see langword="false" />.</returns>
73+
[Experimental(EFDiagnostics.CosmosFullTextSearchExperimental)]
74+
public static bool FullTextContainsAll(this DbFunctions _, string property, params string[] keywords)
75+
=> throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(FullTextContainsAll)));
76+
77+
/// <summary>
78+
/// Checks if the specified property contains any of the given keywords using full-text search.
79+
/// </summary>
80+
/// <param name="_">The <see cref="DbFunctions" /> instance.</param>
81+
/// <param name="property">The property to search.</param>
82+
/// <param name="keywords">The keywords to search for.</param>
83+
/// <returns><see langword="true" /> if the property contains any of the keywords; otherwise, <see langword="false" />.</returns>
84+
[Experimental(EFDiagnostics.CosmosFullTextSearchExperimental)]
85+
public static bool FullTextContainsAny(this DbFunctions _, string property, params string[] keywords)
86+
=> throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(FullTextContainsAny)));
87+
88+
/// <summary>
89+
/// Returns the full-text search score for the specified property and keywords.
90+
/// </summary>
91+
/// <param name="_">The <see cref="DbFunctions" /> instance.</param>
92+
/// <param name="property">The property to score.</param>
93+
/// <param name="keywords">The keywords to score by.</param>
94+
/// <returns>The full-text search score.</returns>
95+
[Experimental(EFDiagnostics.CosmosFullTextSearchExperimental)]
96+
public static double FullTextScore(this DbFunctions _, string property, params string[] keywords)
97+
=> throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(FullTextScore)));
98+
99+
/// <summary>
100+
/// Combines scores provided by two or more specified functions.
101+
/// </summary>
102+
/// <param name="_">The <see cref="DbFunctions" /> instance.</param>
103+
/// <param name="functions">The functions to compute the score for.</param>
104+
/// <returns>The combined score.</returns>
105+
[Experimental(EFDiagnostics.CosmosFullTextSearchExperimental)]
106+
public static double Rrf(this DbFunctions _, params double[] functions)
107+
=> throw new InvalidOperationException(CoreStrings.FunctionOnClient(nameof(Rrf)));
108+
55109
/// <summary>
56110
/// Returns the distance between two vectors, using the distance function and data type defined using
57111
/// <see

Diff for: src/EFCore.Cosmos/Extensions/CosmosEntityTypeBuilderExtensions.cs

+87
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4+
using System.Diagnostics.CodeAnalysis;
45
using Microsoft.EntityFrameworkCore.Cosmos.Metadata.Internal;
56

67
// ReSharper disable once CheckNamespace
@@ -883,6 +884,92 @@ public static bool CanSetDefaultTimeToLive(
883884
bool fromDataAnnotation = false)
884885
=> entityTypeBuilder.CanSetAnnotation(CosmosAnnotationNames.DefaultTimeToLive, seconds, fromDataAnnotation);
885886

887+
/// <summary>
888+
/// Configures a default language to use for full-text search at container scope.
889+
/// </summary>
890+
/// <remarks>
891+
/// See <see href="https://aka.ms/efcore-docs-modeling">Modeling entity types and relationships</see>, and
892+
/// <see href="https://aka.ms/efcore-docs-cosmos">Accessing Azure Cosmos DB with EF Core</see> for more information and examples.
893+
/// </remarks>
894+
/// <param name="entityTypeBuilder">The builder for the entity type being configured.</param>
895+
/// <param name="language">The default language.</param>
896+
/// <returns>The same builder instance so that multiple calls can be chained.</returns>
897+
[Experimental(EFDiagnostics.CosmosFullTextSearchExperimental)]
898+
public static EntityTypeBuilder HasDefaultFullTextLanguage(
899+
this EntityTypeBuilder entityTypeBuilder,
900+
string? language)
901+
{
902+
entityTypeBuilder.Metadata.SetDefaultFullTextSearchLanguage(language);
903+
904+
return entityTypeBuilder;
905+
}
906+
907+
/// <summary>
908+
/// Configures a default language to use for full-text search at container scope.
909+
/// </summary>
910+
/// <remarks>
911+
/// See <see href="https://aka.ms/efcore-docs-modeling">Modeling entity types and relationships</see>, and
912+
/// <see href="https://aka.ms/efcore-docs-cosmos">Accessing Azure Cosmos DB with EF Core</see> for more information and examples.
913+
/// </remarks>
914+
/// <param name="entityTypeBuilder">The builder for the entity type being configured.</param>
915+
/// <param name="language">The default language.</param>
916+
/// <returns>The same builder instance so that multiple calls can be chained.</returns>
917+
[Experimental(EFDiagnostics.CosmosFullTextSearchExperimental)]
918+
public static EntityTypeBuilder<TEntity> HasDefaultFullTextLanguage<TEntity>(
919+
this EntityTypeBuilder<TEntity> entityTypeBuilder,
920+
string? language)
921+
where TEntity : class
922+
=> (EntityTypeBuilder<TEntity>)HasDefaultFullTextLanguage((EntityTypeBuilder)entityTypeBuilder, language);
923+
924+
/// <summary>
925+
/// Configures a default language to use for full-text search at container scope.
926+
/// </summary>
927+
/// <remarks>
928+
/// See <see href="https://aka.ms/efcore-docs-modeling">Modeling entity types and relationships</see>, and
929+
/// <see href="https://aka.ms/efcore-docs-cosmos">Accessing Azure Cosmos DB with EF Core</see> for more information and examples.
930+
/// </remarks>
931+
/// <param name="entityTypeBuilder">The builder for the entity type being configured.</param>
932+
/// <param name="language">The default language.</param>
933+
/// <param name="fromDataAnnotation">Indicates whether the configuration was specified using a data annotation.</param>
934+
/// <returns>
935+
/// The same builder instance if the configuration was applied,
936+
/// <see langword="null" /> otherwise.
937+
/// </returns>
938+
[Experimental(EFDiagnostics.CosmosFullTextSearchExperimental)]
939+
public static IConventionEntityTypeBuilder? HasDefaultFullTextLanguage(
940+
this IConventionEntityTypeBuilder entityTypeBuilder,
941+
string? language,
942+
bool fromDataAnnotation = false)
943+
{
944+
if (!entityTypeBuilder.CanSetDefaultFullTextLanguage(language, fromDataAnnotation))
945+
{
946+
return null;
947+
}
948+
949+
entityTypeBuilder.Metadata.SetDefaultFullTextSearchLanguage(language, fromDataAnnotation);
950+
951+
return entityTypeBuilder;
952+
}
953+
954+
/// <summary>
955+
/// Returns a value indicating whether the default full-text language can be set
956+
/// from the current configuration source
957+
/// </summary>
958+
/// <remarks>
959+
/// See <see href="https://aka.ms/efcore-docs-modeling">Modeling entity types and relationships</see>, and
960+
/// <see href="https://aka.ms/efcore-docs-cosmos">Accessing Azure Cosmos DB with EF Core</see> for more information and examples.
961+
/// </remarks>
962+
/// <param name="entityTypeBuilder">The builder for the entity type being configured.</param>
963+
/// <param name="language">The default language.</param>
964+
/// <param name="fromDataAnnotation">Indicates whether the configuration was specified using a data annotation.</param>
965+
/// <returns><see langword="true" /> if the configuration can be applied.</returns>
966+
[Experimental(EFDiagnostics.CosmosFullTextSearchExperimental)]
967+
public static bool CanSetDefaultFullTextLanguage(
968+
this IConventionEntityTypeBuilder entityTypeBuilder,
969+
string? language,
970+
bool fromDataAnnotation = false)
971+
=> entityTypeBuilder.CanSetAnnotation(CosmosAnnotationNames.DefaultFullTextSearchLanguage, language, fromDataAnnotation);
972+
886973
/// <summary>
887974
/// Configures the manual provisioned throughput offering.
888975
/// </summary>

Diff for: src/EFCore.Cosmos/Extensions/CosmosEntityTypeExtensions.cs

+49
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Licensed to the .NET Foundation under one or more agreements.
22
// The .NET Foundation licenses this file to you under the MIT license.
33

4+
using System.Diagnostics.CodeAnalysis;
45
using Microsoft.EntityFrameworkCore.Cosmos.Metadata.Internal;
56

67
// ReSharper disable once CheckNamespace
@@ -586,4 +587,52 @@ public static void SetThroughput(this IMutableEntityType entityType, int? throug
586587
public static ConfigurationSource? GetThroughputConfigurationSource(this IConventionEntityType entityType)
587588
=> entityType.FindAnnotation(CosmosAnnotationNames.Throughput)
588589
?.GetConfigurationSource();
590+
591+
/// <summary>
592+
/// Returns the default language for the full-text search at container scope.
593+
/// </summary>
594+
/// <param name="entityType">The entity type.</param>
595+
/// <returns>The default language for the full-text search.</returns>
596+
[Experimental(EFDiagnostics.CosmosFullTextSearchExperimental)]
597+
public static string? GetDefaultFullTextSearchLanguage(this IReadOnlyEntityType entityType)
598+
=> entityType.BaseType != null
599+
? entityType.GetRootType().GetDefaultFullTextSearchLanguage()
600+
: (string?)entityType[CosmosAnnotationNames.DefaultFullTextSearchLanguage];
601+
602+
/// <summary>
603+
/// Sets the default language for the full-text search at container scope.
604+
/// </summary>
605+
/// <param name="entityType">The entity type.</param>
606+
/// <param name="language">The default language for the full-text search.</param>
607+
[Experimental(EFDiagnostics.CosmosFullTextSearchExperimental)]
608+
public static void SetDefaultFullTextSearchLanguage(this IMutableEntityType entityType, string? language)
609+
=> entityType.SetOrRemoveAnnotation(
610+
CosmosAnnotationNames.DefaultFullTextSearchLanguage,
611+
language);
612+
613+
/// <summary>
614+
/// Sets the default language for the full-text search at container scope.
615+
/// </summary>
616+
/// <param name="entityType">The entity type.</param>
617+
/// <param name="language">The default language for the full-text search.</param>
618+
/// <param name="fromDataAnnotation">Indicates whether the configuration was specified using a data annotation.</param>
619+
[Experimental(EFDiagnostics.CosmosFullTextSearchExperimental)]
620+
public static string? SetDefaultFullTextSearchLanguage(
621+
this IConventionEntityType entityType,
622+
string? language,
623+
bool fromDataAnnotation = false)
624+
=> (string?)entityType.SetOrRemoveAnnotation(
625+
CosmosAnnotationNames.DefaultFullTextSearchLanguage,
626+
language,
627+
fromDataAnnotation)?.Value;
628+
629+
/// <summary>
630+
/// Gets the <see cref="ConfigurationSource" /> for the default full-text search language at container scope.
631+
/// </summary>
632+
/// <param name="entityType">The entity type to find configuration source for.</param>
633+
/// <returns>The <see cref="ConfigurationSource" /> for the default full-text search language.</returns>
634+
[Experimental(EFDiagnostics.CosmosFullTextSearchExperimental)]
635+
public static ConfigurationSource? GetDefaultFullTextSearchLanguageConfigurationSource(this IConventionEntityType entityType)
636+
=> entityType.FindAnnotation(CosmosAnnotationNames.DefaultFullTextSearchLanguage)
637+
?.GetConfigurationSource();
589638
}

0 commit comments

Comments
 (0)