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
Original file line number Diff line number Diff line change
Expand Up @@ -187,9 +187,42 @@ private void TranslateMethodCall(MethodCallExpression methodCall)
this.TranslateContains(source, item);
return;

// C# 14 made changes to overload resolution to prefer Span-based overloads when those exist ("first-class spans");
// this makes MemoryExtensions.Contains() be resolved rather than Enumerable.Contains() (see above).
// MemoryExtensions.Contains() also accepts a Span argument for the source, adding an implicit cast we need to remove.
// See https://github.com/dotnet/runtime/issues/109757 for more context.
// Note that MemoryExtensions.Contains has an optional 3rd ComparisonType parameter; we only match when
// it's null.
case { Method.Name: nameof(MemoryExtensions.Contains), Arguments: [var spanArg, var item, ..] } contains
when contains.Method.DeclaringType == typeof(MemoryExtensions)
&& (contains.Arguments.Count is 2
|| (contains.Arguments.Count is 3 && contains.Arguments[2] is ConstantExpression { Value: null }))
&& TryUnwrapSpanImplicitCast(spanArg, out var source):
this.TranslateContains(source, item);
return;

default:
throw new NotSupportedException($"Unsupported method call: {methodCall.Method.DeclaringType?.Name}.{methodCall.Method.Name}");
}

static bool TryUnwrapSpanImplicitCast(Expression expression, [NotNullWhen(true)] out Expression? result)
{
if (expression is UnaryExpression
{
NodeType: ExpressionType.Convert,
Method: { Name: "op_Implicit", DeclaringType: { IsGenericType: true } implicitCastDeclaringType },
Operand: var unwrapped
}
&& implicitCastDeclaringType.GetGenericTypeDefinition() is var genericTypeDefinition
&& (genericTypeDefinition == typeof(Span<>) || genericTypeDefinition == typeof(ReadOnlySpan<>)))
{
result = unwrapped;
return true;
}

result = null;
return false;
}
}

private void TranslateContains(Expression source, Expression item)
Expand Down
33 changes: 33 additions & 0 deletions dotnet/src/VectorData/Common/SqlFilterTranslator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -239,9 +239,42 @@ private void TranslateMethodCall(MethodCallExpression methodCall, bool isSearchC
this.TranslateContains(source, item);
return;

// C# 14 made changes to overload resolution to prefer Span-based overloads when those exist ("first-class spans");
// this makes MemoryExtensions.Contains() be resolved rather than Enumerable.Contains() (see above).
// MemoryExtensions.Contains() also accepts a Span argument for the source, adding an implicit cast we need to remove.
// See https://github.com/dotnet/runtime/issues/109757 for more context.
// Note that MemoryExtensions.Contains has an optional 3rd ComparisonType parameter; we only match when
// it's null.
case { Method.Name: nameof(MemoryExtensions.Contains), Arguments: [var spanArg, var item, ..] } contains
when contains.Method.DeclaringType == typeof(MemoryExtensions)
&& (contains.Arguments.Count is 2
|| (contains.Arguments.Count is 3 && contains.Arguments[2] is ConstantExpression { Value: null }))
&& TryUnwrapSpanImplicitCast(spanArg, out var source):
this.TranslateContains(source, item);
return;

default:
throw new NotSupportedException($"Unsupported method call: {methodCall.Method.DeclaringType?.Name}.{methodCall.Method.Name}");
}

static bool TryUnwrapSpanImplicitCast(Expression expression, [NotNullWhen(true)] out Expression? result)
{
if (expression is UnaryExpression
{
NodeType: ExpressionType.Convert,
Method: { Name: "op_Implicit", DeclaringType: { IsGenericType: true } implicitCastDeclaringType },
Operand: var unwrapped
}
&& implicitCastDeclaringType.GetGenericTypeDefinition() is var genericTypeDefinition
&& (genericTypeDefinition == typeof(Span<>) || genericTypeDefinition == typeof(ReadOnlySpan<>)))
{
result = unwrapped;
return true;
}

result = null;
return false;
}
}

private void TranslateContains(Expression source, Expression item)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,8 @@ private BsonDocument TranslateNot(UnaryExpression not)
}

private BsonDocument TranslateMethodCall(MethodCallExpression methodCall)
=> methodCall switch
{
return methodCall switch
{
// Enumerable.Contains()
{ Method.Name: nameof(Enumerable.Contains), Arguments: [var source, var item] } contains
Expand All @@ -171,11 +172,45 @@ private BsonDocument TranslateMethodCall(MethodCallExpression methodCall)
},
Object: Expression source,
Arguments: [var item]
} when declaringType.GetGenericTypeDefinition() == typeof(List<>) => this.TranslateContains(source, item),
} when declaringType.GetGenericTypeDefinition() == typeof(List<>)
=> this.TranslateContains(source, item),

// C# 14 made changes to overload resolution to prefer Span-based overloads when those exist ("first-class spans");
// this makes MemoryExtensions.Contains() be resolved rather than Enumerable.Contains() (see above).
// MemoryExtensions.Contains() also accepts a Span argument for the source, adding an implicit cast we need to remove.
// See https://github.com/dotnet/runtime/issues/109757 for more context.
// Note that MemoryExtensions.Contains has an optional 3rd ComparisonType parameter; we only match when
// it's null.
{ Method.Name: nameof(MemoryExtensions.Contains), Arguments: [var spanArg, var item, ..] } contains
when contains.Method.DeclaringType == typeof(MemoryExtensions)
&& (contains.Arguments.Count is 2
|| (contains.Arguments.Count is 3 && contains.Arguments[2] is ConstantExpression { Value: null }))
&& TryUnwrapSpanImplicitCast(spanArg, out var source)
=> this.TranslateContains(source, item),

_ => throw new NotSupportedException($"Unsupported method call: {methodCall.Method.DeclaringType?.Name}.{methodCall.Method.Name}")
};

static bool TryUnwrapSpanImplicitCast(Expression expression, [NotNullWhen(true)] out Expression? result)
{
if (expression is UnaryExpression
{
NodeType: ExpressionType.Convert,
Method: { Name: "op_Implicit", DeclaringType: { IsGenericType: true } implicitCastDeclaringType },
Operand: var unwrapped
}
&& implicitCastDeclaringType.GetGenericTypeDefinition() is var genericTypeDefinition
&& (genericTypeDefinition == typeof(Span<>) || genericTypeDefinition == typeof(ReadOnlySpan<>)))
{
result = unwrapped;
return true;
}

result = null;
return false;
}
}

private BsonDocument TranslateContains(Expression source, Expression item)
{
switch (source)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -230,9 +230,42 @@ private void TranslateMethodCall(MethodCallExpression methodCall)
this.TranslateContains(source, item);
return;

// C# 14 made changes to overload resolution to prefer Span-based overloads when those exist ("first-class spans");
// this makes MemoryExtensions.Contains() be resolved rather than Enumerable.Contains() (see above).
// MemoryExtensions.Contains() also accepts a Span argument for the source, adding an implicit cast we need to remove.
// See https://github.com/dotnet/runtime/issues/109757 for more context.
// Note that MemoryExtensions.Contains has an optional 3rd ComparisonType parameter; we only match when
// it's null.
case { Method.Name: nameof(MemoryExtensions.Contains), Arguments: [var spanArg, var item, ..] } contains
when contains.Method.DeclaringType == typeof(MemoryExtensions)
&& (contains.Arguments.Count is 2
|| (contains.Arguments.Count is 3 && contains.Arguments[2] is ConstantExpression { Value: null }))
&& TryUnwrapSpanImplicitCast(spanArg, out var source):
this.TranslateContains(source, item);
return;

default:
throw new NotSupportedException($"Unsupported method call: {methodCall.Method.DeclaringType?.Name}.{methodCall.Method.Name}");
}

static bool TryUnwrapSpanImplicitCast(Expression expression, [NotNullWhen(true)] out Expression? result)
{
if (expression is UnaryExpression
{
NodeType: ExpressionType.Convert,
Method: { Name: "op_Implicit", DeclaringType: { IsGenericType: true } implicitCastDeclaringType },
Operand: var unwrapped
}
&& implicitCastDeclaringType.GetGenericTypeDefinition() is var genericTypeDefinition
&& (genericTypeDefinition == typeof(Span<>) || genericTypeDefinition == typeof(ReadOnlySpan<>)))
{
result = unwrapped;
return true;
}

result = null;
return false;
}
}

private void TranslateContains(Expression source, Expression item)
Expand Down
1 change: 1 addition & 0 deletions dotnet/src/VectorData/Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
<Import Project="$([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)../'))" />

<PropertyGroup>
<LangVersion>latest</LangVersion>
<NoWarn>$(NoWarn);MEVD9000,MEVD9001</NoWarn> <!-- Experimental MEVD connector-facing APIs -->
</PropertyGroup>

Expand Down
36 changes: 35 additions & 1 deletion dotnet/src/VectorData/MongoDB/MongoFilterTranslator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,8 @@ private BsonDocument TranslateNot(UnaryExpression not)
}

private BsonDocument TranslateMethodCall(MethodCallExpression methodCall)
=> methodCall switch
{
return methodCall switch
{
// Enumerable.Contains()
{ Method.Name: nameof(Enumerable.Contains), Arguments: [var source, var item] } contains
Expand All @@ -179,9 +180,42 @@ private BsonDocument TranslateMethodCall(MethodCallExpression methodCall)
Arguments: [var item]
} when declaringType.GetGenericTypeDefinition() == typeof(List<>) => this.TranslateContains(source, item),

// C# 14 made changes to overload resolution to prefer Span-based overloads when those exist ("first-class spans");
// this makes MemoryExtensions.Contains() be resolved rather than Enumerable.Contains() (see above).
// MemoryExtensions.Contains() also accepts a Span argument for the source, adding an implicit cast we need to remove.
// See https://github.com/dotnet/runtime/issues/109757 for more context.
// Note that MemoryExtensions.Contains has an optional 3rd ComparisonType parameter; we only match when
// it's null.
{ Method.Name: nameof(MemoryExtensions.Contains), Arguments: [var spanArg, var item, ..] } contains
when contains.Method.DeclaringType == typeof(MemoryExtensions)
&& (contains.Arguments.Count is 2
|| (contains.Arguments.Count is 3 && contains.Arguments[2] is ConstantExpression { Value: null }))
&& TryUnwrapSpanImplicitCast(spanArg, out var source)
=> this.TranslateContains(source, item),

_ => throw new NotSupportedException($"Unsupported method call: {methodCall.Method.DeclaringType?.Name}.{methodCall.Method.Name}")
};

static bool TryUnwrapSpanImplicitCast(Expression expression, [NotNullWhen(true)] out Expression? result)
{
if (expression is UnaryExpression
{
NodeType: ExpressionType.Convert,
Method: { Name: "op_Implicit", DeclaringType: { IsGenericType: true } implicitCastDeclaringType },
Operand: var unwrapped
}
&& implicitCastDeclaringType.GetGenericTypeDefinition() is var genericTypeDefinition
&& (genericTypeDefinition == typeof(Span<>) || genericTypeDefinition == typeof(ReadOnlySpan<>)))
{
result = unwrapped;
return true;
}

result = null;
return false;
}
}

private BsonDocument TranslateContains(Expression source, Expression item)
{
switch (source)
Expand Down
36 changes: 35 additions & 1 deletion dotnet/src/VectorData/Pinecone/PineconeFilterTranslator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,8 @@ private Metadata TranslateNot(UnaryExpression not)
}

private Metadata TranslateMethodCall(MethodCallExpression methodCall)
=> methodCall switch
{
return methodCall switch
{
// Enumerable.Contains()
{ Method.Name: nameof(Enumerable.Contains), Arguments: [var source, var item] } contains
Expand All @@ -176,9 +177,42 @@ private Metadata TranslateMethodCall(MethodCallExpression methodCall)
Arguments: [var item]
} when declaringType.GetGenericTypeDefinition() == typeof(List<>) => this.TranslateContains(source, item),

// C# 14 made changes to overload resolution to prefer Span-based overloads when those exist ("first-class spans");
// this makes MemoryExtensions.Contains() be resolved rather than Enumerable.Contains() (see above).
// MemoryExtensions.Contains() also accepts a Span argument for the source, adding an implicit cast we need to remove.
// See https://github.com/dotnet/runtime/issues/109757 for more context.
// Note that MemoryExtensions.Contains has an optional 3rd ComparisonType parameter; we only match when
// it's null.
{ Method.Name: nameof(MemoryExtensions.Contains), Arguments: [var spanArg, var item, ..] } contains
when contains.Method.DeclaringType == typeof(MemoryExtensions)
&& (contains.Arguments.Count is 2
|| (contains.Arguments.Count is 3 && contains.Arguments[2] is ConstantExpression { Value: null }))
&& TryUnwrapSpanImplicitCast(spanArg, out var source)
=> this.TranslateContains(source, item),

_ => throw new NotSupportedException($"Unsupported method call: {methodCall.Method.DeclaringType?.Name}.{methodCall.Method.Name}")
};

static bool TryUnwrapSpanImplicitCast(Expression expression, [NotNullWhen(true)] out Expression? result)
{
if (expression is UnaryExpression
{
NodeType: ExpressionType.Convert,
Method: { Name: "op_Implicit", DeclaringType: { IsGenericType: true } implicitCastDeclaringType },
Operand: var unwrapped
}
&& implicitCastDeclaringType.GetGenericTypeDefinition() is var genericTypeDefinition
&& (genericTypeDefinition == typeof(Span<>) || genericTypeDefinition == typeof(ReadOnlySpan<>)))
{
result = unwrapped;
return true;
}

result = null;
return false;
}
}

private Metadata TranslateContains(Expression source, Expression item)
{
switch (source)
Expand Down
36 changes: 35 additions & 1 deletion dotnet/src/VectorData/Qdrant/QdrantFilterTranslator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -267,7 +267,8 @@ private Filter TranslateNot(Expression expression)
#endregion Logical operators

private Filter TranslateMethodCall(MethodCallExpression methodCall)
=> methodCall switch
{
return methodCall switch
{
// Enumerable.Contains()
{ Method.Name: nameof(Enumerable.Contains), Arguments: [var source, var item] } contains
Expand All @@ -286,9 +287,42 @@ private Filter TranslateMethodCall(MethodCallExpression methodCall)
} when declaringType.GetGenericTypeDefinition() == typeof(List<>)
=> this.TranslateContains(source, item),

// C# 14 made changes to overload resolution to prefer Span-based overloads when those exist ("first-class spans");
// this makes MemoryExtensions.Contains() be resolved rather than Enumerable.Contains() (see above).
// MemoryExtensions.Contains() also accepts a Span argument for the source, adding an implicit cast we need to remove.
// See https://github.com/dotnet/runtime/issues/109757 for more context.
// Note that MemoryExtensions.Contains has an optional 3rd ComparisonType parameter; we only match when
// it's null.
{ Method.Name: nameof(MemoryExtensions.Contains), Arguments: [var spanArg, var item, ..] } contains
when contains.Method.DeclaringType == typeof(MemoryExtensions)
&& (contains.Arguments.Count is 2
|| (contains.Arguments.Count is 3 && contains.Arguments[2] is ConstantExpression { Value: null }))
&& TryUnwrapSpanImplicitCast(spanArg, out var source)
=> this.TranslateContains(source, item),

_ => throw new NotSupportedException($"Unsupported method call: {methodCall.Method.DeclaringType?.Name}.{methodCall.Method.Name}")
};

static bool TryUnwrapSpanImplicitCast(Expression expression, [NotNullWhen(true)] out Expression? result)
{
if (expression is UnaryExpression
{
NodeType: ExpressionType.Convert,
Method: { Name: "op_Implicit", DeclaringType: { IsGenericType: true } implicitCastDeclaringType },
Operand: var unwrapped
}
&& implicitCastDeclaringType.GetGenericTypeDefinition() is var genericTypeDefinition
&& (genericTypeDefinition == typeof(Span<>) || genericTypeDefinition == typeof(ReadOnlySpan<>)))
{
result = unwrapped;
return true;
}

result = null;
return false;
}
}

private Filter TranslateContains(Expression source, Expression item)
{
switch (source)
Expand Down
Loading
Loading