Skip to content

Commit ade7e34

Browse files
onionhammerneildshleminh98
authored
Query: Added remaining Cosmos Type checking functions to CosmosLinqExtensions (#3724)
* Added the remaining Cosmos Type checking functions to the CosmosLinqExtensions * Added comments requested * Updated comment * Updated baseline * Improve readability of dictionary initialization * Aligned with code style guide * Revert change to baseline * Executed update baseline script --------- Co-authored-by: neildsh <[email protected]> Co-authored-by: leminh98 <[email protected]>
1 parent 0c15865 commit ade7e34

File tree

5 files changed

+311
-19
lines changed

5 files changed

+311
-19
lines changed

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

Lines changed: 44 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -18,33 +18,62 @@ static TypeCheckFunctions()
1818
{
1919
TypeCheckFunctionsDefinitions = new Dictionary<string, BuiltinFunctionVisitor>
2020
{
21-
{
22-
"IsDefined",
23-
new SqlBuiltinFunctionVisitor("IS_DEFINED",
21+
[nameof(CosmosLinqExtensions.IsArray)] = new SqlBuiltinFunctionVisitor(
22+
"IS_ARRAY",
2423
true,
2524
new List<Type[]>()
2625
{
2726
new Type[]{typeof(object)},
28-
})
29-
},
30-
{
31-
"IsNull",
32-
new SqlBuiltinFunctionVisitor("IS_NULL",
27+
}),
28+
[nameof(CosmosLinqExtensions.IsBool)] = new SqlBuiltinFunctionVisitor(
29+
"IS_BOOL",
3330
true,
3431
new List<Type[]>()
3532
{
3633
new Type[]{typeof(object)},
37-
})
38-
},
39-
{
40-
"IsPrimitive",
41-
new SqlBuiltinFunctionVisitor("IS_PRIMITIVE",
34+
}),
35+
[nameof(CosmosLinqExtensions.IsDefined)] = new SqlBuiltinFunctionVisitor(
36+
"IS_DEFINED",
4237
true,
4338
new List<Type[]>()
4439
{
4540
new Type[]{typeof(object)},
46-
})
47-
}
41+
}),
42+
[nameof(CosmosLinqExtensions.IsNull)] = new SqlBuiltinFunctionVisitor(
43+
"IS_NULL",
44+
true,
45+
new List<Type[]>()
46+
{
47+
new Type[]{typeof(object)},
48+
}),
49+
[nameof(CosmosLinqExtensions.IsNumber)] = new SqlBuiltinFunctionVisitor(
50+
"IS_NUMBER",
51+
true,
52+
new List<Type[]>()
53+
{
54+
new Type[]{typeof(object)},
55+
}),
56+
[nameof(CosmosLinqExtensions.IsObject)] = new SqlBuiltinFunctionVisitor(
57+
"IS_OBJECT",
58+
true,
59+
new List<Type[]>()
60+
{
61+
new Type[]{typeof(object)},
62+
}),
63+
[nameof(CosmosLinqExtensions.IsPrimitive)] = new SqlBuiltinFunctionVisitor(
64+
"IS_PRIMITIVE",
65+
true,
66+
new List<Type[]>()
67+
{
68+
new Type[]{typeof(object)},
69+
}),
70+
[nameof(CosmosLinqExtensions.IsString)] = new SqlBuiltinFunctionVisitor(
71+
"IS_STRING",
72+
true,
73+
new List<Type[]>()
74+
{
75+
new Type[]{typeof(object)},
76+
}),
4877
};
4978
}
5079

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

Lines changed: 97 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,44 @@ namespace Microsoft.Azure.Cosmos.Linq
2020
/// </summary>
2121
public static class CosmosLinqExtensions
2222
{
23+
/// <summary>
24+
/// Returns a Boolean value indicating if the type of the specified expression is an array.
25+
/// This method is to be used in LINQ expressions only and will be evaluated on server.
26+
/// There's no implementation provided in the client library.
27+
/// </summary>
28+
/// <param name="obj"></param>
29+
/// <returns>Returns true if the type of the specified expression is an array; otherwise, false.</returns>
30+
/// <example>
31+
/// <code>
32+
/// <![CDATA[
33+
/// var isArrayQuery = documents.Where(document => document.Names.IsArray());
34+
/// ]]>
35+
/// </code>
36+
/// </example>
37+
public static bool IsArray(this object obj)
38+
{
39+
throw new NotImplementedException(ClientResources.TypeCheckExtensionFunctionsNotImplemented);
40+
}
41+
42+
/// <summary>
43+
/// Returns a Boolean value indicating if the type of the specified expression is a boolean.
44+
/// This method is to be used in LINQ expressions only and will be evaluated on server.
45+
/// There's no implementation provided in the client library.
46+
/// </summary>
47+
/// <param name="obj"></param>
48+
/// <returns>Returns true if the type of the specified expression is a boolean; otherwise, false.</returns>
49+
/// <example>
50+
/// <code>
51+
/// <![CDATA[
52+
/// var isBoolQuery = documents.Where(document => document.IsRegistered.IsBool());
53+
/// ]]>
54+
/// </code>
55+
/// </example>
56+
public static bool IsBool(this object obj)
57+
{
58+
throw new NotImplementedException(ClientResources.TypeCheckExtensionFunctionsNotImplemented);
59+
}
60+
2361
/// <summary>
2462
/// Determines if a certain property is defined or not.
2563
/// This method is to be used in LINQ expressions only and will be evaluated on server.
@@ -52,12 +90,50 @@ public static bool IsDefined(this object obj)
5290
/// var isNullQuery = documents.Where(document => document.Name.IsNull());
5391
/// ]]>
5492
/// </code>
55-
/// </example>s>
93+
/// </example>
5694
public static bool IsNull(this object obj)
5795
{
5896
throw new NotImplementedException(ClientResources.TypeCheckExtensionFunctionsNotImplemented);
5997
}
6098

99+
/// <summary>
100+
/// Returns a Boolean value indicating if the type of the specified expression is a number.
101+
/// This method is to be used in LINQ expressions only and will be evaluated on server.
102+
/// There's no implementation provided in the client library.
103+
/// </summary>
104+
/// <param name="obj"></param>
105+
/// <returns>Returns true if the type of the specified expression is a number; otherwise, false.</returns>
106+
/// <example>
107+
/// <code>
108+
/// <![CDATA[
109+
/// var isNumberQuery = documents.Where(document => document.Age.IsNumber());
110+
/// ]]>
111+
/// </code>
112+
/// </example>
113+
public static bool IsNumber(this object obj)
114+
{
115+
throw new NotImplementedException(ClientResources.TypeCheckExtensionFunctionsNotImplemented);
116+
}
117+
118+
/// <summary>
119+
/// Returns a Boolean value indicating if the type of the specified expression is an object.
120+
/// This method is to be used in LINQ expressions only and will be evaluated on server.
121+
/// There's no implementation provided in the client library.
122+
/// </summary>
123+
/// <param name="obj"></param>
124+
/// <returns>Returns true if the type of the specified expression is an object; otherwise, false.</returns>
125+
/// <example>
126+
/// <code>
127+
/// <![CDATA[
128+
/// var isObjectQuery = documents.Where(document => document.Address.IsObject());
129+
/// ]]>
130+
/// </code>
131+
/// </example>
132+
public static bool IsObject(this object obj)
133+
{
134+
throw new NotImplementedException(ClientResources.TypeCheckExtensionFunctionsNotImplemented);
135+
}
136+
61137
/// <summary>
62138
/// Determines if a certain property is of primitive JSON type.
63139
/// This method is to be used in LINQ expressions only and will be evaluated on server.
@@ -74,12 +150,31 @@ public static bool IsNull(this object obj)
74150
/// var isPrimitiveQuery = documents.Where(document => document.Name.IsPrimitive());
75151
/// ]]>
76152
/// </code>
77-
/// </example>s>
153+
/// </example>
78154
public static bool IsPrimitive(this object obj)
79155
{
80156
throw new NotImplementedException(ClientResources.TypeCheckExtensionFunctionsNotImplemented);
81157
}
82158

159+
/// <summary>
160+
/// Returns a Boolean value indicating if the type of the specified expression is a string.
161+
/// This method is to be used in LINQ expressions only and will be evaluated on server.
162+
/// There's no implementation provided in the client library.
163+
/// </summary>
164+
/// <param name="obj"></param>
165+
/// <returns>Returns true if the type of the specified expression is a string; otherwise, false.</returns>
166+
/// <example>
167+
/// <code>
168+
/// <![CDATA[
169+
/// var isStringQuery = documents.Where(document => document.Name.IsString());
170+
/// ]]>
171+
/// </code>
172+
/// </example>
173+
public static bool IsString(this object obj)
174+
{
175+
throw new NotImplementedException(ClientResources.TypeCheckExtensionFunctionsNotImplemented);
176+
}
177+
83178
/// <summary>
84179
/// This method generate query definition from LINQ query.
85180
/// </summary>

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

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,52 @@
11
<Results>
2+
<Result>
3+
<Input>
4+
<Description><![CDATA[IsArray array]]></Description>
5+
<Expression><![CDATA[query.Where(doc => doc.ArrayField.IsArray())]]></Expression>
6+
</Input>
7+
<Output>
8+
<SqlQuery><![CDATA[
9+
SELECT VALUE root
10+
FROM root
11+
WHERE IS_ARRAY(root["ArrayField"])]]></SqlQuery>
12+
</Output>
13+
</Result>
14+
<Result>
15+
<Input>
16+
<Description><![CDATA[IsArray string]]></Description>
17+
<Expression><![CDATA[query.Where(doc => doc.StringField.IsArray())]]></Expression>
18+
</Input>
19+
<Output>
20+
<SqlQuery><![CDATA[
21+
SELECT VALUE root
22+
FROM root
23+
WHERE IS_ARRAY(root["StringField"])]]></SqlQuery>
24+
</Output>
25+
</Result>
26+
<Result>
27+
<Input>
28+
<Description><![CDATA[IsBool bool]]></Description>
29+
<Expression><![CDATA[query.Where(doc => Convert(doc.BooleanField, Object).IsBool())]]></Expression>
30+
</Input>
31+
<Output>
32+
<SqlQuery><![CDATA[
33+
SELECT VALUE root
34+
FROM root
35+
WHERE IS_BOOL(root["BooleanField"])]]></SqlQuery>
36+
</Output>
37+
</Result>
38+
<Result>
39+
<Input>
40+
<Description><![CDATA[IsBool string]]></Description>
41+
<Expression><![CDATA[query.Where(doc => doc.StringField.IsBool())]]></Expression>
42+
</Input>
43+
<Output>
44+
<SqlQuery><![CDATA[
45+
SELECT VALUE root
46+
FROM root
47+
WHERE IS_BOOL(root["StringField"])]]></SqlQuery>
48+
</Output>
49+
</Result>
250
<Result>
351
<Input>
452
<Description><![CDATA[IsDefined array]]></Description>
@@ -45,6 +93,52 @@ FROM root
4593
WHERE IS_NULL(root["StringField"])]]></SqlQuery>
4694
</Output>
4795
</Result>
96+
<Result>
97+
<Input>
98+
<Description><![CDATA[IsNumber number]]></Description>
99+
<Expression><![CDATA[query.Select(doc => Convert(doc.NumericField, Object).IsNumber())]]></Expression>
100+
</Input>
101+
<Output>
102+
<SqlQuery><![CDATA[
103+
SELECT VALUE IS_NUMBER(root["NumericField"])
104+
FROM root]]></SqlQuery>
105+
</Output>
106+
</Result>
107+
<Result>
108+
<Input>
109+
<Description><![CDATA[IsNumber string]]></Description>
110+
<Expression><![CDATA[query.Where(doc => doc.StringField.IsNumber())]]></Expression>
111+
</Input>
112+
<Output>
113+
<SqlQuery><![CDATA[
114+
SELECT VALUE root
115+
FROM root
116+
WHERE IS_NUMBER(root["StringField"])]]></SqlQuery>
117+
</Output>
118+
</Result>
119+
<Result>
120+
<Input>
121+
<Description><![CDATA[IsObject object]]></Description>
122+
<Expression><![CDATA[query.Select(doc => doc.ObjectField.IsObject())]]></Expression>
123+
</Input>
124+
<Output>
125+
<SqlQuery><![CDATA[
126+
SELECT VALUE IS_OBJECT(root["ObjectField"])
127+
FROM root]]></SqlQuery>
128+
</Output>
129+
</Result>
130+
<Result>
131+
<Input>
132+
<Description><![CDATA[IsObject string]]></Description>
133+
<Expression><![CDATA[query.Where(doc => doc.StringField.IsObject())]]></Expression>
134+
</Input>
135+
<Output>
136+
<SqlQuery><![CDATA[
137+
SELECT VALUE root
138+
FROM root
139+
WHERE IS_OBJECT(root["StringField"])]]></SqlQuery>
140+
</Output>
141+
</Result>
48142
<Result>
49143
<Input>
50144
<Description><![CDATA[IsPrimitive array]]></Description>
@@ -68,4 +162,27 @@ FROM root
68162
WHERE IS_PRIMITIVE(root["StringField"])]]></SqlQuery>
69163
</Output>
70164
</Result>
165+
<Result>
166+
<Input>
167+
<Description><![CDATA[IsString string]]></Description>
168+
<Expression><![CDATA[query.Where(doc => doc.StringField.IsString())]]></Expression>
169+
</Input>
170+
<Output>
171+
<SqlQuery><![CDATA[
172+
SELECT VALUE root
173+
FROM root
174+
WHERE IS_STRING(root["StringField"])]]></SqlQuery>
175+
</Output>
176+
</Result>
177+
<Result>
178+
<Input>
179+
<Description><![CDATA[IsString number]]></Description>
180+
<Expression><![CDATA[query.Select(doc => Convert(doc.NumericField, Object).IsString())]]></Expression>
181+
</Input>
182+
<Output>
183+
<SqlQuery><![CDATA[
184+
SELECT VALUE IS_STRING(root["NumericField"])
185+
FROM root]]></SqlQuery>
186+
</Output>
187+
</Result>
71188
</Results>

Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/LinqTranslationBaselineTests.cs

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ internal class DataObject : LinqTestObject
116116
public int? NullableField;
117117
#pragma warning disable CS0649 // Field is never assigned to, and will always have its default value false
118118
public bool BooleanField;
119+
public SimpleObject ObjectField = new SimpleObject();
119120
public Guid GuidField;
120121
#pragma warning restore // Field is never assigned to, and will always have its default value false
121122

@@ -155,6 +156,11 @@ internal class DataObject : LinqTestObject
155156
public string Pk;
156157
}
157158

159+
internal class SimpleObject
160+
{
161+
public string Field { get; set; }
162+
}
163+
158164
class DateJsonConverter : IsoDateTimeConverter
159165
{
160166
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
@@ -257,7 +263,7 @@ public void TestLiteralSerialization()
257263
[TestMethod]
258264
public void TestTypeCheckFunctions()
259265
{
260-
// IsDefined, IsNull, and IsPrimitive are not supported on the client side.
266+
// IsArray, IsBool, IsDefined, IsNull, IsNumber, IsObject, IsPrimitive, and IsString are not supported on the client side.
261267
// Partly because IsPrimitive is not trivial to implement.
262268
// Therefore these methods are verified with baseline only.
263269
List<DataObject> data = new List<DataObject>();
@@ -266,12 +272,22 @@ public void TestTypeCheckFunctions()
266272

267273
List<LinqTestInput> inputs = new List<LinqTestInput>
268274
{
275+
new LinqTestInput("IsArray array", b => getQuery(b).Where(doc => doc.ArrayField.IsArray())),
276+
new LinqTestInput("IsArray string", b => getQuery(b).Where(doc => doc.StringField.IsArray())),
277+
new LinqTestInput("IsBool bool", b => getQuery(b).Where(doc => doc.BooleanField.IsBool())),
278+
new LinqTestInput("IsBool string", b => getQuery(b).Where(doc => doc.StringField.IsBool())),
269279
new LinqTestInput("IsDefined array", b => getQuery(b).Select(doc => doc.ArrayField.IsDefined())),
270280
new LinqTestInput("IsDefined string", b => getQuery(b).Where(doc => doc.StringField.IsDefined())),
271281
new LinqTestInput("IsNull array", b => getQuery(b).Select(doc => doc.ArrayField.IsNull())),
272282
new LinqTestInput("IsNull string", b => getQuery(b).Where(doc => doc.StringField.IsNull())),
283+
new LinqTestInput("IsNumber number", b => getQuery(b).Select(doc => doc.NumericField.IsNumber())),
284+
new LinqTestInput("IsNumber string", b => getQuery(b).Where(doc => doc.StringField.IsNumber())),
285+
new LinqTestInput("IsObject object", b => getQuery(b).Select(doc => doc.ObjectField.IsObject())),
286+
new LinqTestInput("IsObject string", b => getQuery(b).Where(doc => doc.StringField.IsObject())),
273287
new LinqTestInput("IsPrimitive array", b => getQuery(b).Select(doc => doc.ArrayField.IsPrimitive())),
274-
new LinqTestInput("IsPrimitive string", b => getQuery(b).Where(doc => doc.StringField.IsPrimitive()))
288+
new LinqTestInput("IsPrimitive string", b => getQuery(b).Where(doc => doc.StringField.IsPrimitive())),
289+
new LinqTestInput("IsString string", b => getQuery(b).Where(doc => doc.StringField.IsString())),
290+
new LinqTestInput("IsString number", b => getQuery(b).Select(doc => doc.NumericField.IsString())),
275291
};
276292
this.ExecuteTestSuite(inputs);
277293
}

0 commit comments

Comments
 (0)