Search code examples
c#linqmongodbmongodb-.net-driverbitwise-operators

Bitwise enum (flags) query using MongoDB's official C# driver


When I try to run a LINQ query of the form:

MongoCollection<MyEntity> collection;

collection.AsQueryable().Where(entity =>
    (entity.Flags & MyFlags.AFlag) != MyFlags.None);

I get an ArgumentException with the message Unsupported where clause: ((Int32)((Int32)entity.Flags & 4) != 0).

Is this a known bug/feature?

Is there any workaround?

From the documentation it seems like MongoDB has a bitwise update, but not a bitwise query.

For comparison, the same query runs smoothly above Redis using ServiceStack as a client.

I did find these two links (link1, link2) which suggest using JavaScript, however, that would make the implementation of the service layer very dependant on the DB technology.


Solution

  • My solution has two parts. I made a serializer for Enum flags that stores all the values in a list of strings. I made an extension method for Linq to "inject" the mongo query i need.

    public static IQueryable<TItem> HasFlags<TItem, TProperty>(
        this IQueryable<TItem> items,
        Expression<Func<TItem, TProperty>> itemPropertyExpression,
        params Enum[] enumFlags)
    {
        var enumFlagNames = enumFlags.Select(enumFlag => (BsonValue)enumFlag.ToString());
        return items.Where(item => Query.In(ExtendedObject.GetPropertyName(itemPropertyExpression), enumFlagNames).Inject());
    }
    

    That way, its both readable and i don't need to deserialize all the objects into memory.

    P.S: The GetPropertyName method is just a type safe way to get the property name:

    public static string GetPropertyName<TClass, TProperty>(
        Expression<Func<TClass, TProperty>> entityPropertyExpression)
    {
        return ((MemberExpression)entityPropertyExpression.Body).Member.Name;
    }