Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 059ce9b

Browse files
committedOct 19, 2015
Merge remote-tracking branch 'refs/remotes/mongodb/master' into force_bsonclassmaps
2 parents b6288d2 + f1ec471 commit 059ce9b

File tree

11 files changed

+386
-21
lines changed

11 files changed

+386
-21
lines changed
 

‎Docs/reference/content/reference/driver/expressions.md

+38
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ class Person
2929
public HashSet<string> FavoriteNames { get; set; }
3030

3131
public DateTime CreatedAtUtc { get; set; }
32+
33+
public int PermissionFlags { get; set; }
3234
}
3335

3436
class Pet
@@ -139,6 +141,42 @@ Find(p => !localAges.Contains(p.Age));
139141
{ Age: { $nin: [10, 20, 30] } }
140142
```
141143

144+
#### $bitsAllClear
145+
146+
```csharp
147+
Find(p => (p.PermissionFlags & 7) == 0);
148+
```
149+
```json
150+
{ PermissionFlags: { $bitsAllClear: 7 } }
151+
```
152+
153+
#### $bitsAllSet
154+
155+
```csharp
156+
Find(p => (p.PermissionFlags & 7) == 7);
157+
```
158+
```json
159+
{ PermissionFlags: { $bitsAllSet: 7 } }
160+
```
161+
162+
#### $bitsAnyClear
163+
164+
```csharp
165+
Find(p => (p.PermissionFlags & 7) != 7);
166+
```
167+
```json
168+
{ PermissionFlags: { $bitsAnyClear: 7 } }
169+
```
170+
171+
#### $bitsAnySet
172+
173+
```csharp
174+
Find(p => (p.PermissionFlags & 7) != 0);
175+
```
176+
```json
177+
{ PermissionFlags: { $bitsAnySet: 7 } }
178+
```
179+
142180
### Logical
143181

144182
See the [MongoDB documentation]({{< docsref "reference/operator/query/#logical" >}}) for more information on each operator.

‎src/MongoDB.Driver.Legacy.Tests/Operations/CurrentOpUsingFindOperationTests.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ public void CreateOperation_should_return_expected_result()
7272
}
7373

7474
[Test]
75-
[RequiresServer(VersionLessThan = "3.0.0")]
75+
[RequiresServer(ClusterTypes = ClusterTypes.StandaloneOrReplicaSet, StorageEngines = "mmapv1")]
7676
public void Execute_should_return_expected_result()
7777
{
7878
var subject = new CurrentOpUsingFindOperation(_adminDatabaseNamespace, _messageEncoderSettings);

‎src/MongoDB.Driver.Tests/FilterDefinitionBuilderTests.cs

+64
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,70 @@ public void And_with_a_nested_and_and_clashing_keys_using_ampersand()
127127
Assert(filter, "{$and: [{a: 1}, {a: 2}, {c: 3}]}");
128128
}
129129

130+
[Test]
131+
public void BitsAllClear()
132+
{
133+
var subject = CreateSubject<BsonDocument>();
134+
135+
Assert(subject.BitsAllClear("a", 43), "{a: {$bitsAllClear: 43}}");
136+
}
137+
138+
[Test]
139+
public void BitsAllClear_Typed()
140+
{
141+
var subject = CreateSubject<Person>();
142+
143+
Assert(subject.BitsAllClear(x => x.Age, 43), "{age: {$bitsAllClear: 43}}");
144+
}
145+
146+
[Test]
147+
public void BitsAllSet()
148+
{
149+
var subject = CreateSubject<BsonDocument>();
150+
151+
Assert(subject.BitsAllSet("a", 43), "{a: {$bitsAllSet: 43}}");
152+
}
153+
154+
[Test]
155+
public void BitsAllSet_Typed()
156+
{
157+
var subject = CreateSubject<Person>();
158+
159+
Assert(subject.BitsAllSet(x => x.Age, 43), "{age: {$bitsAllSet: 43}}");
160+
}
161+
162+
[Test]
163+
public void BitsAnyClear()
164+
{
165+
var subject = CreateSubject<BsonDocument>();
166+
167+
Assert(subject.BitsAnyClear("a", 43), "{a: {$bitsAnyClear: 43}}");
168+
}
169+
170+
[Test]
171+
public void BitsAnyClear_Typed()
172+
{
173+
var subject = CreateSubject<Person>();
174+
175+
Assert(subject.BitsAnyClear(x => x.Age, 43), "{age: {$bitsAnyClear: 43}}");
176+
}
177+
178+
[Test]
179+
public void BitsAnySet()
180+
{
181+
var subject = CreateSubject<BsonDocument>();
182+
183+
Assert(subject.BitsAnySet("a", 43), "{a: {$bitsAnySet: 43}}");
184+
}
185+
186+
[Test]
187+
public void BitsAnySet_Typed()
188+
{
189+
var subject = CreateSubject<Person>();
190+
191+
Assert(subject.BitsAnySet(x => x.Age, 43), "{age: {$bitsAnySet: 43}}");
192+
}
193+
130194
[Test]
131195
public void ElemMatch()
132196
{

‎src/MongoDB.Driver.Tests/Linq/MongoQueryableTests.cs

+48
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,14 @@ public void Count_with_predicate()
123123
result.Should().Be(1);
124124
}
125125

126+
[Test]
127+
public void Count_with_no_matches()
128+
{
129+
var result = CreateQuery().Count(x => x.C.E.F == 13151235);
130+
131+
result.Should().Be(0);
132+
}
133+
126134
[Test]
127135
public async Task CountAsync()
128136
{
@@ -139,6 +147,14 @@ public async Task CountAsync_with_predicate()
139147
result.Should().Be(1);
140148
}
141149

150+
[Test]
151+
public async Task CountAsync_with_no_matches()
152+
{
153+
var result = await CreateQuery().CountAsync(x => x.C.E.F == 123412523);
154+
155+
result.Should().Be(0);
156+
}
157+
142158
[Test]
143159
[RequiresServer(MinimumVersion = "2.6.0")]
144160
public void Distinct_followed_by_where()
@@ -364,6 +380,14 @@ public void LongCount_with_predicate()
364380
result.Should().Be(1);
365381
}
366382

383+
[Test]
384+
public void LongCount_with_no_results()
385+
{
386+
var result = CreateQuery().LongCount(x => x.C.E.F == 123452135);
387+
388+
result.Should().Be(0);
389+
}
390+
367391
[Test]
368392
public async Task LongCountAsync()
369393
{
@@ -380,6 +404,14 @@ public async Task LongCountAsync_with_predicate()
380404
result.Should().Be(1);
381405
}
382406

407+
[Test]
408+
public async Task LongCountAsync_with_no_results()
409+
{
410+
var result = await CreateQuery().LongCountAsync(x => x.C.E.F == 12351235);
411+
412+
result.Should().Be(0);
413+
}
414+
383415
[Test]
384416
public void Max()
385417
{
@@ -1039,6 +1071,14 @@ public void Sum_with_selector()
10391071
result.Should().Be(122);
10401072
}
10411073

1074+
[Test]
1075+
public void Sum_with_no_results()
1076+
{
1077+
var result = CreateQuery().Where(x => x.C.E.F == 12341235).Sum(x => x.C.E.F);
1078+
1079+
result.Should().Be(0);
1080+
}
1081+
10421082
[Test]
10431083
public async Task SumAsync()
10441084
{
@@ -1055,6 +1095,14 @@ public async Task SumAsync_with_selector()
10551095
result.Should().Be(122);
10561096
}
10571097

1098+
[Test]
1099+
public async Task SumAsync_with_no_results()
1100+
{
1101+
var result = await CreateQuery().Where(x => x.C.E.F == 21341235).SumAsync(x => x.C.E.F);
1102+
1103+
result.Should().Be(0);
1104+
}
1105+
10581106
[Test]
10591107
public void Take()
10601108
{

‎src/MongoDB.Driver.Tests/Linq/Translators/PredicateTranslatorTests.cs

+50
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,56 @@ public void Any_with_local_contains_on_a_scalar_array()
165165
"{\"C.E.I\": { $in: [\"itchy\" ] } }");
166166
}
167167

168+
[Test]
169+
[RequiresServer(MinimumVersion = "3.1.9")]
170+
public void BitsAllClear_with_bitwise_operators()
171+
{
172+
Assert(
173+
x => (x.C.E.F & 20) == 0,
174+
1,
175+
"{'C.E.F': { $bitsAllClear: 20 } }");
176+
}
177+
178+
[Test]
179+
[RequiresServer(MinimumVersion = "3.1.9")]
180+
public void BitsAllSet_with_bitwise_operators()
181+
{
182+
Assert(
183+
x => (x.C.E.F & 7) == 7,
184+
1,
185+
"{'C.E.F': { $bitsAllSet: 7 } }");
186+
}
187+
188+
[Test]
189+
[RequiresServer(MinimumVersion = "3.1.9")]
190+
public void BitsAllSet_with_HasFlag()
191+
{
192+
Assert(
193+
x => x.Q.HasFlag(Q.One),
194+
1,
195+
"{Q: { $bitsAllSet: 1 } }");
196+
}
197+
198+
[Test]
199+
[RequiresServer(MinimumVersion = "3.1.9")]
200+
public void BitsAnyClear_with_bitwise_operators()
201+
{
202+
Assert(
203+
x => (x.C.E.F & 7) != 7,
204+
1,
205+
"{'C.E.F': { $bitsAnyClear: 7 } }");
206+
}
207+
208+
[Test]
209+
[RequiresServer(MinimumVersion = "3.1.9")]
210+
public void BitsAnySet_with_bitwise_operators()
211+
{
212+
Assert(
213+
x => (x.C.E.F & 20) != 0,
214+
1,
215+
"{'C.E.F': { $bitsAnySet: 20 } }");
216+
}
217+
168218
[Test]
169219
public void LocalIListContains()
170220
{

‎src/MongoDB.Driver/FilterDefinitionBuilder.cs

+88
Original file line numberDiff line numberDiff line change
@@ -290,6 +290,94 @@ public FilterDefinition<TDocument> AnyNin<TItem>(Expression<Func<TDocument, IEnu
290290
return AnyNin(new ExpressionFieldDefinition<TDocument>(field), values);
291291
}
292292

293+
/// <summary>
294+
/// Creates a bits all clear filter.
295+
/// </summary>
296+
/// <param name="field">The field.</param>
297+
/// <param name="bitmask">The bitmask.</param>
298+
/// <returns>A bits all clear filter.</returns>
299+
public FilterDefinition<TDocument> BitsAllClear(FieldDefinition<TDocument> field, long bitmask)
300+
{
301+
return new OperatorFilterDefinition<TDocument>("$bitsAllClear", field, bitmask);
302+
}
303+
304+
/// <summary>
305+
/// Creates a bits all clear filter.
306+
/// </summary>
307+
/// <param name="field">The field.</param>
308+
/// <param name="bitmask">The bitmask.</param>
309+
/// <returns>A bits all clear filter.</returns>
310+
public FilterDefinition<TDocument> BitsAllClear(Expression<Func<TDocument, object>> field, long bitmask)
311+
{
312+
return BitsAllClear(new ExpressionFieldDefinition<TDocument>(field), bitmask);
313+
}
314+
315+
/// <summary>
316+
/// Creates a bits all set filter.
317+
/// </summary>
318+
/// <param name="field">The field.</param>
319+
/// <param name="bitmask">The bitmask.</param>
320+
/// <returns>A bits all set filter.</returns>
321+
public FilterDefinition<TDocument> BitsAllSet(FieldDefinition<TDocument> field, long bitmask)
322+
{
323+
return new OperatorFilterDefinition<TDocument>("$bitsAllSet", field, bitmask);
324+
}
325+
326+
/// <summary>
327+
/// Creates a bits all set filter.
328+
/// </summary>
329+
/// <param name="field">The field.</param>
330+
/// <param name="bitmask">The bitmask.</param>
331+
/// <returns>A bits all set filter.</returns>
332+
public FilterDefinition<TDocument> BitsAllSet(Expression<Func<TDocument, object>> field, long bitmask)
333+
{
334+
return BitsAllSet(new ExpressionFieldDefinition<TDocument>(field), bitmask);
335+
}
336+
337+
/// <summary>
338+
/// Creates a bits any clear filter.
339+
/// </summary>
340+
/// <param name="field">The field.</param>
341+
/// <param name="bitmask">The bitmask.</param>
342+
/// <returns>A bits any clear filter.</returns>
343+
public FilterDefinition<TDocument> BitsAnyClear(FieldDefinition<TDocument> field, long bitmask)
344+
{
345+
return new OperatorFilterDefinition<TDocument>("$bitsAnyClear", field, bitmask);
346+
}
347+
348+
/// <summary>
349+
/// Creates a bits any clear filter.
350+
/// </summary>
351+
/// <param name="field">The field.</param>
352+
/// <param name="bitmask">The bitmask.</param>
353+
/// <returns>A bits any clear filter.</returns>
354+
public FilterDefinition<TDocument> BitsAnyClear(Expression<Func<TDocument, object>> field, long bitmask)
355+
{
356+
return BitsAnyClear(new ExpressionFieldDefinition<TDocument>(field), bitmask);
357+
}
358+
359+
/// <summary>
360+
/// Creates a bits any set filter.
361+
/// </summary>
362+
/// <param name="field">The field.</param>
363+
/// <param name="bitmask">The bitmask.</param>
364+
/// <returns>A bits any set filter.</returns>
365+
public FilterDefinition<TDocument> BitsAnySet(FieldDefinition<TDocument> field, long bitmask)
366+
{
367+
return new OperatorFilterDefinition<TDocument>("$bitsAnySet", field, bitmask);
368+
}
369+
370+
/// <summary>
371+
/// Creates a bits any set filter.
372+
/// </summary>
373+
/// <param name="field">The field.</param>
374+
/// <param name="bitmask">The bitmask.</param>
375+
/// <returns>A bits any set filter.</returns>
376+
public FilterDefinition<TDocument> BitsAnySet(Expression<Func<TDocument, object>> field, long bitmask)
377+
{
378+
return BitsAnySet(new ExpressionFieldDefinition<TDocument>(field), bitmask);
379+
}
380+
293381
/// <summary>
294382
/// Creates an element match filter for an array field.
295383
/// </summary>

‎src/MongoDB.Driver/Linq/Expressions/ISerializationExpression.cs

+23-1
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,13 @@
1313
* limitations under the License.
1414
*/
1515

16+
using System;
1617
using System.Collections;
1718
using MongoDB.Bson;
1819
using MongoDB.Bson.IO;
1920
using MongoDB.Bson.Serialization;
2021
using MongoDB.Driver.Core.Misc;
22+
using MongoDB.Driver.Support;
2123

2224
namespace MongoDB.Driver.Linq.Expressions
2325
{
@@ -44,6 +46,8 @@ public static BsonValue SerializeValue(this ISerializationExpression field, obje
4446
{
4547
Ensure.IsNotNull(field, nameof(field));
4648

49+
value = ConvertEnumIfNecessary(field.Serializer.ValueType, value);
50+
4751
var tempDocument = new BsonDocument();
4852
using (var bsonWriter = new BsonDocumentWriter(tempDocument))
4953
{
@@ -67,7 +71,7 @@ public static BsonArray SerializeValues(this ISerializationExpression field, IEn
6771
bsonWriter.WriteStartArray();
6872
foreach (var value in values)
6973
{
70-
field.Serializer.Serialize(context, value);
74+
field.Serializer.Serialize(context, ConvertEnumIfNecessary(field.Serializer.ValueType, value));
7175
}
7276
bsonWriter.WriteEndArray();
7377
bsonWriter.WriteEndDocument();
@@ -89,5 +93,23 @@ private static string CombineFieldNames(string prefix, string suffix)
8993

9094
return prefix + "." + suffix;
9195
}
96+
97+
private static object ConvertEnumIfNecessary(Type valueType, object value)
98+
{
99+
if (valueType.IsEnum || valueType.IsNullableEnum())
100+
{
101+
if (value != null)
102+
{
103+
if (valueType.IsNullableEnum())
104+
{
105+
valueType = valueType.GetNullableUnderlyingType();
106+
}
107+
108+
value = Enum.ToObject(valueType, value);
109+
}
110+
}
111+
112+
return value;
113+
}
92114
}
93115
}

‎src/MongoDB.Driver/Linq/Expressions/ResultOperators/CountResultOperator.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -40,12 +40,12 @@ public override Type Type
4040

4141
public LambdaExpression CreateAggregator(Type sourceType)
4242
{
43-
return ResultTransformerHelper.CreateAggregator("Single", sourceType);
43+
return ResultTransformerHelper.CreateAggregator("SingleOrDefault", sourceType);
4444
}
4545

4646
public LambdaExpression CreateAsyncAggregator(Type sourceType)
4747
{
48-
return ResultTransformerHelper.CreateAsyncAggregator("SingleAsync", sourceType);
48+
return ResultTransformerHelper.CreateAsyncAggregator("SingleOrDefaultAsync", sourceType);
4949
}
5050
}
5151
}

‎src/MongoDB.Driver/Linq/Expressions/ResultOperators/SumResultOperator.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -40,12 +40,12 @@ public override Type Type
4040

4141
public LambdaExpression CreateAggregator(Type sourceType)
4242
{
43-
return ResultTransformerHelper.CreateAggregator("Single", sourceType);
43+
return ResultTransformerHelper.CreateAggregator("SingleOrDefault", sourceType);
4444
}
4545

4646
public LambdaExpression CreateAsyncAggregator(Type sourceType)
4747
{
48-
return ResultTransformerHelper.CreateAsyncAggregator("SingleAsync", sourceType);
48+
return ResultTransformerHelper.CreateAsyncAggregator("SingleOrDefaultAsync", sourceType);
4949
}
5050
}
5151
}

‎src/MongoDB.Driver/Linq/MongoQueryProviderImpl.cs

+8-1
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,14 @@ public object Execute(Expression expression)
7979
Translate(expression));
8080

8181
var lambda = Expression.Lambda(executionPlan);
82-
return lambda.Compile().DynamicInvoke(null);
82+
try
83+
{
84+
return lambda.Compile().DynamicInvoke(null);
85+
}
86+
catch (TargetInvocationException tie)
87+
{
88+
throw tie.InnerException;
89+
}
8390
}
8491

8592
public Task<TResult> ExecuteAsync<TResult>(Expression expression, CancellationToken cancellationToken = default(CancellationToken))

‎src/MongoDB.Driver/Linq/Translators/PredicateTranslator.cs

+62-14
Original file line numberDiff line numberDiff line change
@@ -259,6 +259,48 @@ private FilterDefinition<BsonDocument> TranslateArrayLength(Expression variableN
259259
return null;
260260
}
261261

262+
private FilterDefinition<BsonDocument> TranslateBitwiseComparison(Expression variableExpression, ExpressionType operatorType, ConstantExpression constantExpression)
263+
{
264+
var binaryExpression = variableExpression as BinaryExpression;
265+
if (binaryExpression == null ||
266+
binaryExpression.NodeType != ExpressionType.And ||
267+
binaryExpression.Right.NodeType != ExpressionType.Constant ||
268+
(operatorType != ExpressionType.Equal && operatorType != ExpressionType.NotEqual))
269+
{
270+
return null;
271+
}
272+
273+
var field = GetFieldExpression(binaryExpression.Left);
274+
275+
var value = field.SerializeValue(((ConstantExpression)binaryExpression.Right).Value).ToInt64();
276+
var comparison = Convert.ToInt64(constantExpression.Value);
277+
278+
if (value == comparison)
279+
{
280+
if (operatorType == ExpressionType.Equal)
281+
{
282+
return __builder.BitsAllSet(field.FieldName, value);
283+
}
284+
else
285+
{
286+
return __builder.BitsAnyClear(field.FieldName, value);
287+
}
288+
}
289+
else if (comparison == 0)
290+
{
291+
if (operatorType == ExpressionType.Equal)
292+
{
293+
return __builder.BitsAllClear(field.FieldName, value);
294+
}
295+
else
296+
{
297+
return __builder.BitsAnySet(field.FieldName, value);
298+
}
299+
}
300+
301+
return null;
302+
}
303+
262304
private FilterDefinition<BsonDocument> TranslateBoolean(bool value)
263305
{
264306
if (value)
@@ -347,6 +389,12 @@ private FilterDefinition<BsonDocument> TranslateComparison(BinaryExpression bina
347389
return query;
348390
}
349391

392+
query = TranslateBitwiseComparison(variableExpression, operatorType, constantExpression);
393+
if (query != null)
394+
{
395+
return query;
396+
}
397+
350398
return TranslateComparison(variableExpression, operatorType, constantExpression);
351399
}
352400

@@ -389,20 +437,6 @@ private FilterDefinition<BsonDocument> TranslateComparison(Expression variableEx
389437
}
390438

391439
var fieldExpression = GetFieldExpression(variableExpression);
392-
var valueType = fieldExpression.Serializer.ValueType;
393-
if (valueType.IsEnum || valueType.IsNullableEnum())
394-
{
395-
if (!valueType.IsEnum && value != null)
396-
{
397-
valueType = valueType.GetNullableUnderlyingType();
398-
}
399-
400-
if (value != null)
401-
{
402-
value = Enum.ToObject(valueType, value);
403-
}
404-
}
405-
406440
var serializedValue = fieldExpression.SerializeValue(value);
407441
switch (operatorType)
408442
{
@@ -576,6 +610,19 @@ private FilterDefinition<BsonDocument> TranslateEquals(MethodCallExpression meth
576610
return null;
577611
}
578612

613+
private FilterDefinition<BsonDocument> TranslateHasFlag(MethodCallExpression methodCallExpression)
614+
{
615+
if (methodCallExpression.Object == null)
616+
{
617+
return null;
618+
}
619+
620+
var field = GetFieldExpression(methodCallExpression.Object);
621+
var value = field.SerializeValue(((ConstantExpression)methodCallExpression.Arguments[0]).Value).ToInt64();
622+
623+
return __builder.BitsAllSet(field.FieldName, value);
624+
}
625+
579626
private FilterDefinition<BsonDocument> TranslateIn(MethodCallExpression methodCallExpression)
580627
{
581628
var methodDeclaringType = methodCallExpression.Method.DeclaringType;
@@ -683,6 +730,7 @@ private FilterDefinition<BsonDocument> TranslateMethodCall(MethodCallExpression
683730
case "ContainsKey": return TranslateContainsKey(methodCallExpression);
684731
case "EndsWith": return TranslateStringQuery(methodCallExpression);
685732
case "Equals": return TranslateEquals(methodCallExpression);
733+
case "HasFlag": return TranslateHasFlag(methodCallExpression);
686734
case "In": return TranslateIn(methodCallExpression);
687735
case "IsMatch": return TranslateIsMatch(methodCallExpression);
688736
case "IsNullOrEmpty": return TranslateIsNullOrEmpty(methodCallExpression);

0 commit comments

Comments
 (0)
Please sign in to comment.