Skip to content

Commit 0a6777a

Browse files
committed
CSHARP-1409: added support for bitwise operators.
1 parent f768769 commit 0a6777a

File tree

6 files changed

+320
-15
lines changed

6 files changed

+320
-15
lines changed

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

Lines changed: 38 additions & 0 deletions
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.Tests/FilterDefinitionBuilderTests.cs

Lines changed: 64 additions & 0 deletions
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/Translators/PredicateTranslatorTests.cs

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,51 @@ public void Any_with_local_contains_on_a_scalar_array()
165165
"{\"C.E.I\": { $in: [\"itchy\" ] } }");
166166
}
167167

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

src/MongoDB.Driver/FilterDefinitionBuilder.cs

Lines changed: 88 additions & 0 deletions
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

Lines changed: 23 additions & 1 deletion
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
}

0 commit comments

Comments
 (0)