Skip to content

Commit 15b516c

Browse files
Merge branch 'users/kirankk/enable_exception_less' of https://github.com/Azure/azure-cosmos-dotnet-v3 into users/kirankk/enable_exception_less
2 parents 875c1e8 + 8ded201 commit 15b516c

10 files changed

Lines changed: 1110 additions & 16 deletions

File tree

Microsoft.Azure.Cosmos/src/Linq/BuiltinFunctions/BuiltinFunctionVisitor.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ public static SqlScalarExpression VisitBuiltinFunctionCall(MethodCallExpression
6464
case nameof(CosmosLinqExtensions.DocumentId):
6565
case nameof(CosmosLinqExtensions.RRF):
6666
case nameof(CosmosLinqExtensions.FullTextScore):
67+
case nameof(CosmosLinqExtensions.VectorDistance):
6768
return OtherBuiltinSystemFunctions.Visit(methodCallExpression, context);
6869
default:
6970
return TypeCheckFunctions.Visit(methodCallExpression, context);

Microsoft.Azure.Cosmos/src/Linq/BuiltinFunctions/OtherBuiltinSystemFunctions.cs

Lines changed: 61 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ namespace Microsoft.Azure.Cosmos.Linq
1010
using System.Collections.ObjectModel;
1111
using System.Globalization;
1212
using System.Linq.Expressions;
13+
using System.Text.Json;
14+
using System.Text.Json.Serialization;
15+
using Microsoft.Azure.Cosmos.CosmosElements;
1316
using Microsoft.Azure.Cosmos.SqlObjects;
1417

1518
internal static class OtherBuiltinSystemFunctions
@@ -41,19 +44,22 @@ protected override SqlScalarExpression VisitImplicit(MethodCallExpression method
4144
throw new ArgumentException(
4245
string.Format(
4346
CultureInfo.CurrentCulture,
44-
"Expressions of type {0} is not supported as an argument to CosmosLinqExtensions.RRF. Supported expressions are method calls to {1}.",
47+
"Expressions of type {0} is not supported as an argument to CosmosLinqExtensions.RRF. Supported expressions are method calls to {1}, {2}.",
4548
argument.Type,
46-
nameof(CosmosLinqExtensions.FullTextScore)));
49+
nameof(CosmosLinqExtensions.FullTextScore),
50+
nameof(CosmosLinqExtensions.VectorDistance)));
4751
}
4852

49-
if (functionCallExpression.Method.Name != nameof(CosmosLinqExtensions.FullTextScore))
53+
if (functionCallExpression.Method.Name != nameof(CosmosLinqExtensions.FullTextScore) &&
54+
functionCallExpression.Method.Name != nameof(CosmosLinqExtensions.VectorDistance))
5055
{
5156
throw new ArgumentException(
5257
string.Format(
5358
CultureInfo.CurrentCulture,
54-
"Method {0} is not supported as an argument to CosmosLinqExtensions.RRF. Supported methods are {1}.",
59+
"Method {0} is not supported as an argument to CosmosLinqExtensions.RRF. Supported methods are {1}, {2}.",
5560
functionCallExpression.Method.Name,
56-
nameof(CosmosLinqExtensions.FullTextScore)));
61+
nameof(CosmosLinqExtensions.FullTextScore),
62+
nameof(CosmosLinqExtensions.VectorDistance)));
5763
}
5864

5965
arguments.Add(ExpressionToSql.VisitNonSubqueryScalarExpression(argument, context));
@@ -108,6 +114,55 @@ protected override SqlScalarExpression VisitExplicit(MethodCallExpression method
108114
}
109115
}
110116

117+
private class VectorDistanceVisit : SqlBuiltinFunctionVisitor
118+
{
119+
public VectorDistanceVisit()
120+
: base("VectorDistance",
121+
true,
122+
new List<Type[]>()
123+
{
124+
new Type[]{typeof(float[]), typeof(float[]), typeof(bool), typeof(CosmosLinqExtensions.VectorDistanceOptions)},
125+
new Type[]{typeof(sbyte[]), typeof(sbyte[]), typeof(bool), typeof(CosmosLinqExtensions.VectorDistanceOptions)},
126+
new Type[]{typeof(byte[]), typeof(byte[]), typeof(bool), typeof(CosmosLinqExtensions.VectorDistanceOptions)},
127+
})
128+
{
129+
}
130+
131+
protected override SqlScalarExpression VisitImplicit(MethodCallExpression methodCallExpression, TranslationContext context)
132+
{
133+
if (methodCallExpression.Arguments.Count != 4) throw new ArgumentException();
134+
135+
List<SqlScalarExpression> arguments = new List<SqlScalarExpression>
136+
{
137+
ExpressionToSql.VisitNonSubqueryScalarExpression(methodCallExpression.Arguments[0], context),
138+
ExpressionToSql.VisitNonSubqueryScalarExpression(methodCallExpression.Arguments[1], context),
139+
ExpressionToSql.VisitNonSubqueryScalarExpression(methodCallExpression.Arguments[2], context)
140+
};
141+
142+
if (methodCallExpression.Arguments[3] is ConstantExpression optionExpression && optionExpression.Value != null)
143+
{
144+
JsonSerializerOptions options = new JsonSerializerOptions
145+
{
146+
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
147+
};
148+
options.Converters.Add(new JsonStringEnumConverter(JsonNamingPolicy.CamelCase));
149+
150+
string serializedConstant = JsonSerializer.Serialize(
151+
optionExpression.Value,
152+
options);
153+
154+
arguments.Add(CosmosElement.Parse(serializedConstant).Accept(CosmosElementToSqlScalarExpressionVisitor.Singleton));
155+
}
156+
157+
return SqlFunctionCallScalarExpression.CreateBuiltin(SqlFunctionCallScalarExpression.Names.VectorDistance, arguments.ToImmutableArray());
158+
}
159+
160+
protected override SqlScalarExpression VisitExplicit(MethodCallExpression methodCallExpression, TranslationContext context)
161+
{
162+
return null;
163+
}
164+
}
165+
111166
private static Dictionary<string, BuiltinFunctionVisitor> FunctionsDefinitions { get; set; }
112167

113168
static OtherBuiltinSystemFunctions()
@@ -123,6 +178,7 @@ static OtherBuiltinSystemFunctions()
123178
}),
124179
[nameof(CosmosLinqExtensions.RRF)] = new RRFVisit(),
125180
[nameof(CosmosLinqExtensions.FullTextScore)] = new FullTextScoreVisit(),
181+
[nameof(CosmosLinqExtensions.VectorDistance)] = new VectorDistanceVisit(),
126182
};
127183
}
128184

Microsoft.Azure.Cosmos/src/Linq/CosmosLinqExtensions.cs

Lines changed: 98 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,12 @@
55
namespace Microsoft.Azure.Cosmos.Linq
66
{
77
using System;
8-
using System.Collections;
98
using System.Collections.Generic;
109
using System.Diagnostics;
1110
using System.Linq;
1211
using System.Linq.Expressions;
1312
using System.Reflection;
14-
using System.Runtime.CompilerServices;
13+
using System.Text.Json.Serialization;
1514
using System.Threading;
1615
using System.Threading.Tasks;
1716
using Microsoft.Azure.Cosmos.Diagnostics;
@@ -22,6 +21,33 @@ namespace Microsoft.Azure.Cosmos.Linq
2221
/// </summary>
2322
public static class CosmosLinqExtensions
2423
{
24+
/// <summary>
25+
/// Object representing the options for vector distance calculation. All field are optional. if a field is not specified, the default value will be used.
26+
/// For more information, see https://learn.microsoft.com/en-us/azure/cosmos-db/nosql/query/vectordistance.
27+
/// </summary>
28+
public sealed class VectorDistanceOptions
29+
{
30+
/// <summary>
31+
/// The metric used to compute distance/similarity. Valid values are "cosine", "dotproduct", "euclidean".
32+
/// If not specified, the default value is what is defined in the container policy
33+
/// </summary>
34+
[JsonPropertyName("distanceFunction")]
35+
public DistanceFunction? DistanceFunction { get; set; }
36+
37+
/// <summary>
38+
/// The data type of the vectors. float32, int8, uint8 values. Default value is float32.
39+
/// </summary>
40+
[JsonPropertyName("dataType")]
41+
public VectorDataType? DataType { get; set; }
42+
43+
/// <summary>
44+
/// An integer specifying the size of the search list when conducting a vector search on the DiskANN index.
45+
/// Increasing this may improve accuracy at the expense of RU cost and latency. Min=1, Default=10, Max=100.
46+
/// </summary>
47+
[JsonPropertyName("searchListSizeMultiplier")]
48+
public int? SearchListSizeMultiplier { get; set; }
49+
}
50+
2551
/// <summary>
2652
/// Returns the integer identifier corresponding to a specific item within a physical partition.
2753
/// This method is to be used in LINQ expressions only and will be evaluated on server.
@@ -239,6 +265,75 @@ public static bool RegexMatch(this object obj, string regularExpression, string
239265
throw new NotImplementedException(ClientResources.ExtensionMethodNotImplemented);
240266
}
241267

268+
/// <summary>
269+
/// Returns the similarity score between two specified vectors.
270+
/// For more information, see https://learn.microsoft.com/en-us/azure/cosmos-db/nosql/query/vectordistance.
271+
/// This method is to be used in LINQ expressions only and will be evaluated on server.
272+
/// There's no implementation provided in the client library.
273+
/// </summary>
274+
/// <param name="vector1">The first vector.</param>
275+
/// <param name="vector2">The second vector.</param>
276+
/// <param name="isBruteForce">A boolean specifying how the computed value is used in an ORDER BY expression. If true, then brute force is used. A value of false uses any index defined on the vector property, if it exists. </param>
277+
/// <param name="options">An JSON formatted object literal used to specify options for the vector distance calculation. </param>
278+
/// <returns>Returns the similarity score between two specified vectors.</returns>
279+
/// <example>
280+
/// <code>
281+
/// <![CDATA[
282+
/// var matched = documents.Select(document => document.vector1.VectorDistance(<vector2>, true, new VectorDistanceOptions() { DistanceFunction = DistanceFunction.Cosine, DataType = VectorDataType.Float32}));
283+
/// ]]>
284+
/// </code>
285+
/// </example>
286+
public static double VectorDistance(this float[] vector1, float[] vector2, bool isBruteForce, VectorDistanceOptions options)
287+
{
288+
throw new NotImplementedException(ClientResources.ExtensionMethodNotImplemented);
289+
}
290+
291+
/// <summary>
292+
/// Returns the similarity score between two specified vectors.
293+
/// For more information, see https://learn.microsoft.com/en-us/azure/cosmos-db/nosql/query/vectordistance.
294+
/// This method is to be used in LINQ expressions only and will be evaluated on server.
295+
/// There's no implementation provided in the client library.
296+
/// </summary>
297+
/// <param name="vector1">The first vector.</param>
298+
/// <param name="vector2">The second vector.</param>
299+
/// <param name="isBruteForce">A boolean specifying how the computed value is used in an ORDER BY expression. If true, then brute force is used. A value of false uses any index defined on the vector property, if it exists. </param>
300+
/// <param name="options">An JSON formatted object literal used to specify options for the vector distance calculation. </param>
301+
/// <returns>Returns the similarity score between two specified vectors.</returns>
302+
/// <example>
303+
/// <code>
304+
/// <![CDATA[
305+
/// var matched = documents.Select(document => document.vector1.VectorDistance(<vector2>, true, new VectorDistanceOptions() { DistanceFunction = DistanceFunction.Cosine, DataType = VectorDataType.Int8}));
306+
/// ]]>
307+
/// </code>
308+
/// </example>
309+
public static double VectorDistance(this byte[] vector1, byte[] vector2, bool isBruteForce, VectorDistanceOptions options)
310+
{
311+
throw new NotImplementedException(ClientResources.ExtensionMethodNotImplemented);
312+
}
313+
314+
/// <summary>
315+
/// Returns the similarity score between two specified vectors.
316+
/// For more information, see https://learn.microsoft.com/en-us/azure/cosmos-db/nosql/query/vectordistance.
317+
/// This method is to be used in LINQ expressions only and will be evaluated on server.
318+
/// There's no implementation provided in the client library.
319+
/// </summary>
320+
/// <param name="vector1">The first vector.</param>
321+
/// <param name="vector2">The second vector.</param>
322+
/// <param name="isBruteForce">A boolean specifying how the computed value is used in an ORDER BY expression. If true, then brute force is used. A value of false uses any index defined on the vector property, if it exists. </param>
323+
/// <param name="options">An JSON formatted object literal used to specify options for the vector distance calculation. </param>
324+
/// <returns>Returns the similarity score between two specified vectors.</returns>
325+
/// <example>
326+
/// <code>
327+
/// <![CDATA[
328+
/// var matched = documents.Select(document => document.vector1.VectorDistance(<vector2>, true, new VectorDistanceOptions() { DistanceFunction = DistanceFunction.Cosine, DataType = VectorDataType.Uint8}));
329+
/// ]]>
330+
/// </code>
331+
/// </example>
332+
public static double VectorDistance(this sbyte[] vector1, sbyte[] vector2, bool isBruteForce, VectorDistanceOptions options)
333+
{
334+
throw new NotImplementedException(ClientResources.ExtensionMethodNotImplemented);
335+
}
336+
242337
/// <summary>
243338
/// Returns a boolean indicating whether the keyword string expression is contained in a specified property path.
244339
/// For more information, see https://learn.microsoft.com/en-us/azure/cosmos-db/nosql/query/fulltextcontains.
@@ -310,7 +405,7 @@ public static bool FullTextContainsAny(this object obj, params string[] searches
310405
/// </summary>
311406
/// <param name="obj"></param>
312407
/// <param name="terms">A nonempty array of string literals.</param>
313-
/// <returns>Returns true BM25 score value that can only be used in an ORDER BY RANK clause.</returns>
408+
/// <returns>Returns a BM25 score value that can only be used in an ORDER BY RANK clause.</returns>
314409
/// <example>
315410
/// <code>
316411
/// <![CDATA[

Microsoft.Azure.Cosmos/src/Linq/ExpressionToSQL.cs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2050,7 +2050,11 @@ private static SqlOrderByClause VisitOrderBy(ReadOnlyCollection<Expression> argu
20502050

20512051
LambdaExpression lambda = Utilities.GetLambda(arguments[1]);
20522052
SqlScalarExpression sqlfunc = ExpressionToSql.VisitScalarExpression(lambda, context);
2053-
SqlOrderByItem orderByItem = SqlOrderByItem.Create(sqlfunc, isDescending);
2053+
2054+
// Order By VectorDistance is a special case, since there is no ordering required.
2055+
bool isVectorDistance = (sqlfunc is SqlFunctionCallScalarExpression functionCall) && (functionCall.Name.Value == SqlFunctionCallScalarExpression.Names.VectorDistance);
2056+
2057+
SqlOrderByItem orderByItem = SqlOrderByItem.Create(sqlfunc, isVectorDistance ? null : isDescending);
20542058
SqlOrderByClause orderby = SqlOrderByClause.Create(new SqlOrderByItem[] { orderByItem });
20552059
return orderby;
20562060
}

Microsoft.Azure.Cosmos/src/SqlObjects/SqlFunctionCallScalarExpression.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -376,7 +376,8 @@ public static class Names
376376
#pragma warning restore CS0108 // Member hides inherited member; missing new keyword
377377
public const string Trim = "TRIM";
378378
public const string Trunc = "TRUNC";
379-
public const string Upper = "UPPER";
379+
public const string Upper = "UPPER";
380+
public const string VectorDistance = "VectorDistance";
380381
}
381382

382383
public static class Identifiers

Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/BaselineTest/TestBaseline/LinqTranslationBaselineTests.TestRRFOrderByRankFunction.xml

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ WHERE (RRF(FullTextScore(root["StringField"], "test1"), FullTextScore(root["Stri
7777
</Input>
7878
<Output>
7979
<SqlQuery><![CDATA[]]></SqlQuery>
80-
<ErrorMessage><![CDATA[Expressions of type System.Double is not supported as an argument to CosmosLinqExtensions.RRF. Supported expressions are method calls to FullTextScore.]]></ErrorMessage>
80+
<ErrorMessage><![CDATA[Expressions of type System.Double is not supported as an argument to CosmosLinqExtensions.RRF. Supported expressions are method calls to FullTextScore, VectorDistance.]]></ErrorMessage>
8181
</Output>
8282
</Result>
8383
<Result>
@@ -87,7 +87,7 @@ WHERE (RRF(FullTextScore(root["StringField"], "test1"), FullTextScore(root["Stri
8787
</Input>
8888
<Output>
8989
<SqlQuery><![CDATA[]]></SqlQuery>
90-
<ErrorMessage><![CDATA[Expressions of type System.Double is not supported as an argument to CosmosLinqExtensions.RRF. Supported expressions are method calls to FullTextScore.]]></ErrorMessage>
90+
<ErrorMessage><![CDATA[Expressions of type System.Double is not supported as an argument to CosmosLinqExtensions.RRF. Supported expressions are method calls to FullTextScore, VectorDistance.]]></ErrorMessage>
9191
</Output>
9292
</Result>
9393
<Result>
@@ -97,7 +97,7 @@ WHERE (RRF(FullTextScore(root["StringField"], "test1"), FullTextScore(root["Stri
9797
</Input>
9898
<Output>
9999
<SqlQuery><![CDATA[]]></SqlQuery>
100-
<ErrorMessage><![CDATA[Expressions of type System.Double is not supported as an argument to CosmosLinqExtensions.RRF. Supported expressions are method calls to FullTextScore.]]></ErrorMessage>
100+
<ErrorMessage><![CDATA[Expressions of type System.Double is not supported as an argument to CosmosLinqExtensions.RRF. Supported expressions are method calls to FullTextScore, VectorDistance.]]></ErrorMessage>
101101
</Output>
102102
</Result>
103103
<Result>
@@ -107,7 +107,7 @@ WHERE (RRF(FullTextScore(root["StringField"], "test1"), FullTextScore(root["Stri
107107
</Input>
108108
<Output>
109109
<SqlQuery><![CDATA[]]></SqlQuery>
110-
<ErrorMessage><![CDATA[Expressions of type System.Double is not supported as an argument to CosmosLinqExtensions.RRF. Supported expressions are method calls to FullTextScore.]]></ErrorMessage>
110+
<ErrorMessage><![CDATA[Expressions of type System.Double is not supported as an argument to CosmosLinqExtensions.RRF. Supported expressions are method calls to FullTextScore, VectorDistance.]]></ErrorMessage>
111111
</Output>
112112
</Result>
113113
<Result>
@@ -117,7 +117,7 @@ WHERE (RRF(FullTextScore(root["StringField"], "test1"), FullTextScore(root["Stri
117117
</Input>
118118
<Output>
119119
<SqlQuery><![CDATA[]]></SqlQuery>
120-
<ErrorMessage><![CDATA[Method RRF is not supported as an argument to CosmosLinqExtensions.RRF. Supported methods are FullTextScore.]]></ErrorMessage>
120+
<ErrorMessage><![CDATA[Method RRF is not supported as an argument to CosmosLinqExtensions.RRF. Supported methods are FullTextScore, VectorDistance.]]></ErrorMessage>
121121
</Output>
122122
</Result>
123123
</Results>

0 commit comments

Comments
 (0)