Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Query: Adds DOCUMENTID extension method for LINQ #4489

Merged
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,16 @@ public static SqlScalarExpression VisitBuiltinFunctionCall(MethodCallExpression

if (methodCallExpression.Method.DeclaringType.GeUnderlyingSystemType() == typeof(CosmosLinqExtensions))
{
// CosmosLinq Extensions are either RegexMatch or Type check functions (IsString, IsBool, etc.)
// CosmosLinq Extensions can be RegexMatch, DocumentId or Type check functions (IsString, IsBool, etc.)
if (methodCallExpression.Method.Name == nameof(CosmosLinqExtensions.RegexMatch))
{
return StringBuiltinFunctions.Visit(methodCallExpression, context);
}
}

if (methodCallExpression.Method.Name == nameof(CosmosLinqExtensions.DocumentId))
{
return OtherBuiltinSystemFunctions.Visit(methodCallExpression, context);
}

return TypeCheckFunctions.Visit(methodCallExpression, context);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
//------------------------------------------------------------
// Copyright (c) Microsoft Corporation. All rights reserved.
//------------------------------------------------------------

namespace Microsoft.Azure.Cosmos.Linq
{
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq.Expressions;
using Microsoft.Azure.Cosmos.SqlObjects;

internal static class OtherBuiltinSystemFunctions
{
private static Dictionary<string, BuiltinFunctionVisitor> FunctionsDefinitions { get; set; }

static OtherBuiltinSystemFunctions()
{
FunctionsDefinitions = new Dictionary<string, BuiltinFunctionVisitor>
{
[nameof(CosmosLinqExtensions.DocumentId)] = new SqlBuiltinFunctionVisitor(
sqlName: "DOCUMENTID",
isStatic: true,
argumentLists: new List<Type[]>()
{
new Type[]{typeof(object)},
})
};
}

public static SqlScalarExpression Visit(MethodCallExpression methodCallExpression, TranslationContext context)
{
return FunctionsDefinitions.TryGetValue(methodCallExpression.Method.Name, out BuiltinFunctionVisitor visitor)
? visitor.Visit(methodCallExpression, context)
: throw new DocumentQueryException(string.Format(CultureInfo.CurrentCulture, ClientResources.MethodNotSupported, methodCallExpression.Method.Name));
}
}
}
21 changes: 20 additions & 1 deletion Microsoft.Azure.Cosmos/src/Linq/CosmosLinqExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,26 @@ namespace Microsoft.Azure.Cosmos.Linq
/// This class provides extension methods for cosmos LINQ code.
/// </summary>
public static class CosmosLinqExtensions
{
{
/// <summary>
/// Returns the integer identifier corresponding to a specific item within a physical partition.
/// This method is to be used in LINQ expressions only and will be evaluated on server.
/// There's no implementation provided in the client library.
/// </summary>
/// <param name="obj">The root object</param>
/// <returns>Returns the integer identifier corresponding to a specific item within a physical partition.</returns>
/// <example>
/// <code>
/// <![CDATA[
/// var documentIdQuery = documents.Where(root => root.DocumentId());
/// ]]>
/// </code>
/// </example>
public static int DocumentId(this object obj)
{
throw new NotImplementedException(ClientResources.TypeCheckExtensionFunctionsNotImplemented);
}

/// <summary>
/// Returns a Boolean value indicating if the type of the specified expression is an array.
/// This method is to be used in LINQ expressions only and will be evaluated on server.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
<Results>
<Result>
<Input>
<Description><![CDATA[In Select clause]]></Description>
<Expression><![CDATA[query.Select(doc => doc.DocumentId())]]></Expression>
</Input>
<Output>
<SqlQuery><![CDATA[
SELECT VALUE DOCUMENTID(root)
FROM root]]></SqlQuery>
</Output>
</Result>
<Result>
<Input>
<Description><![CDATA[In Filter clause]]></Description>
<Expression><![CDATA[query.Where(doc => (doc.DocumentId() > 123))]]></Expression>
</Input>
<Output>
<SqlQuery><![CDATA[
SELECT VALUE root
FROM root
WHERE (DOCUMENTID(root) > 123)]]></SqlQuery>
</Output>
</Result>
<Result>
<Input>
<Description><![CDATA[With non root term]]></Description>
<Expression><![CDATA[query.Where(doc => (Convert(doc.BooleanField, Object).DocumentId() > 123))]]></Expression>
</Input>
<Output>
<SqlQuery><![CDATA[
SELECT VALUE root
FROM root
WHERE (DOCUMENTID(root["BooleanField"]) > 123)]]></SqlQuery>
</Output>
</Result>
leminh98 marked this conversation as resolved.
Show resolved Hide resolved
<Result>
<Input>
<Description><![CDATA[In Order by clause]]></Description>
<Expression><![CDATA[query.OrderBy(doc => doc.DocumentId())]]></Expression>
</Input>
<Output>
<SqlQuery><![CDATA[
SELECT VALUE root
FROM root
ORDER BY DOCUMENTID(root) ASC]]></SqlQuery>
<ErrorMessage><![CDATA[Status Code: BadRequest,{"errors":[{"severity":"Error","code":2206,"message":"Unsupported ORDER BY clause. ORDER BY item expression could not be mapped to a document path."}]},0x800A0B00]]></ErrorMessage>
</Output>
</Result>
</Results>
Original file line number Diff line number Diff line change
Expand Up @@ -290,6 +290,28 @@ public void TestTypeCheckFunctions()
};
this.ExecuteTestSuite(inputs);
}

[TestMethod]
public void TestDocumentIdBuiltinFunction()
{
List<DataObject> data = new List<DataObject>();
IOrderedQueryable<DataObject> query = testContainer.GetItemLinqQueryable<DataObject>(allowSynchronousQueryExecution: true);
Func<bool, IQueryable<DataObject>> getQuery = useQuery => useQuery ? query : data.AsQueryable();

List<LinqTestInput> inputs = new List<LinqTestInput>
{
new LinqTestInput("In Select clause", b => getQuery(b).Select(doc => doc.DocumentId())),
new LinqTestInput("In Filter clause", b => getQuery(b).Where(doc => doc.DocumentId() > 123)),
new LinqTestInput("With non root term", b => getQuery(b).Where(doc => doc.BooleanField.DocumentId() > 123)),
new LinqTestInput("With JOIN", b => getQuery(b).SelectMany(doc => doc.EnumerableField
.Where(number => doc.DocumentId() > 0)
.Select(number => number))),
// Negative case
new LinqTestInput("In Order by clause", b => getQuery(b).OrderBy(doc => doc.DocumentId())),
};

this.ExecuteTestSuite(inputs);
}

[TestMethod]
public void TestRegexMatchFunction()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5650,6 +5650,13 @@
],
"MethodInfo": "Boolean RegexMatch(System.Object, System.String);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;"
},
"Int32 DocumentId(System.Object)[System.Runtime.CompilerServices.ExtensionAttribute()]": {
"Type": "Method",
"Attributes": [
"ExtensionAttribute"
],
"MethodInfo": "Int32 DocumentId(System.Object);IsAbstract:False;IsStatic:True;IsVirtual:False;IsGenericMethod:False;IsConstructor:False;IsFinal:False;"
},
"Microsoft.Azure.Cosmos.FeedIterator ToStreamIterator[T](System.Linq.IQueryable`1[T])[System.Runtime.CompilerServices.ExtensionAttribute()]": {
"Type": "Method",
"Attributes": [
Expand Down
Loading