Skip to content
Draft
Show file tree
Hide file tree
Changes from 3 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
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,8 @@ public static class SqlServerQueryableExtensions
/// </param>
/// <remarks>
/// <para>
/// Compose the returned query with <c>OrderBy(r => r.Distance)</c> and <c>Take(...)</c> to limit the results as required
/// for approximate vector search.
/// Results are implicitly ordered by distance ascending. Compose the returned query with <c>Take(...)</c> to limit the
/// number of results as required for approximate vector search.
Comment thread
roji marked this conversation as resolved.
/// </para>
/// </remarks>
/// <seealso href="https://learn.microsoft.com/sql/t-sql/functions/vector-search-transact-sql">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -147,12 +147,18 @@ var metric
var valueProjectionMember = new ProjectionMember().Append(resultType.GetProperty(nameof(VectorSearchResult<>.Value))!);
var distanceProjectionMember = new ProjectionMember().Append(resultType.GetProperty(nameof(VectorSearchResult<>.Distance))!);

var distanceColumn = new ColumnExpression("Distance", vectorSearchFunction.Alias, typeof(double), _typeMappingSource.FindMapping(typeof(double)), nullable: false);

select.ReplaceProjection(new Dictionary<ProjectionMember, Expression>
{
[valueProjectionMember] = entityProjection,
[distanceProjectionMember] = new ColumnExpression("Distance", vectorSearchFunction.Alias, typeof(double), _typeMappingSource.FindMapping(typeof(double)), nullable: false)
[distanceProjectionMember] = distanceColumn
});

// VECTOR_SEARCH() results are implicitly ordered by distance ascending; add this ordering so that
// users can compose with Take() without needing an explicit OrderBy(r => r.Distance).
select.AppendOrdering(new OrderingExpression(distanceColumn, ascending: true));

Comment thread
roji marked this conversation as resolved.
Comment thread
roji marked this conversation as resolved.
var shaper = Expression.New(
resultType.GetConstructors().Single(),
arguments:
Expand Down Expand Up @@ -802,22 +808,42 @@ bool TryTranslate(
/// doing so can result in application failures when updating to a new Entity Framework Core release.
/// </summary>
protected override bool IsNaturallyOrdered(SelectExpression selectExpression)
=> selectExpression is
=> selectExpression switch
{
Tables: [SqlServerOpenJsonExpression openJsonExpression, ..],
Orderings:
[
{
Expression: SqlUnaryExpression
// OPENJSON rows are naturally ordered by their "key" column (array index), ascending.
// EF adds this ordering implicitly when expanding JSON arrays; treat it as natural to avoid spurious
// "Distinct after OrderBy without row limiting operator" warnings.
{
Tables: [SqlServerOpenJsonExpression openJsonExpression, ..],
Orderings:
[
{
OperatorType: ExpressionType.Convert,
Operand: ColumnExpression { Name: "key", TableAlias: var orderingTableAlias }
},
IsAscending: true
}
]
}
&& orderingTableAlias == openJsonExpression.Alias;
Expression: SqlUnaryExpression
{
OperatorType: ExpressionType.Convert,
Operand: ColumnExpression { Name: "key", TableAlias: var openJsonOrderingTableAlias }
},
IsAscending: true
}
]
} when openJsonOrderingTableAlias == openJsonExpression.Alias => true,

// VECTOR_SEARCH() results are naturally ordered by Distance ascending.
// EF adds this ordering implicitly during VectorSearch translation; treat it as natural to avoid spurious
// "Distinct after OrderBy without row limiting operator" warnings.
{
Tables: [TableValuedFunctionExpression { Name: "VECTOR_SEARCH" } vectorSearchFunction, ..],
Orderings:
[
{
Expression: ColumnExpression { Name: "Distance", TableAlias: var vectorSearchOrderingTableAlias },
IsAscending: true
}
]
} when vectorSearchOrderingTableAlias == vectorSearchFunction.Alias => true,

_ => false
};

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,6 @@ public async Task VectorSearch_project_entity_and_distance()

var results = await ctx.VectorEntities
.VectorSearch(e => e.Vector, similarTo: vector, "cosine")
.OrderBy(e => e.Distance)
.Take(1)
.ToListAsync();

Expand All @@ -103,7 +102,7 @@ ORDER BY [v0].[Distance]
[ConditionalFact]
[SqlServerCondition(SqlServerCondition.IsAzureSql)]
[Experimental("EF9105")]
public async Task VectorSearch_project_entity_only_with_distance_filter_and_ordering()
public async Task VectorSearch_project_entity_only_with_distance_filter()
{
using var ctx = CreateContext();

Expand All @@ -112,7 +111,6 @@ public async Task VectorSearch_project_entity_only_with_distance_filter_and_orde
var results = await ctx.VectorEntities
.VectorSearch(e => e.Vector, similarTo: vector, "cosine")
.Where(e => e.Distance < 0.01)
.OrderBy(e => e.Distance)
.Select(e => e.Value)
.Take(3)
.ToListAsync();
Comment thread
roji marked this conversation as resolved.
Expand Down Expand Up @@ -151,7 +149,6 @@ public async Task VectorSearch_in_subquery()

var results = await ctx.VectorEntities
.VectorSearch(e => e.Vector, similarTo: vector, "cosine")
.OrderBy(e => e.Distance)
.Take(3)
.Select(e => new { e.Value.Id, e.Distance })
.Where(e => e.Distance < 0.01)
Expand Down
Loading