Search code examples
c#mongodblinqfluent

dynamically building a Mongo Query using C# and the fluent library


I am trying to build up a mongo query, I very nearly have it working in Linq (95% there), but it looks like there is an unfortunate missing ability in the Linq provider to do the equivalent of an Intersect(coll).Any() or in Mongo parlance AnyIn()

I understand that if I use the fluent library with some builders I can build up a filter that will give me an AnyIn() - but what I am missing is how could I build up the whole query, the initial filter, the aggregation, projection and the filter at the end?

This is a very close approximation of my Linq query - this works exactly as required as long as I am not trying to compare collection membership in the filter

public List<MyResult> ListMyResults(Expression<Func<MyResult, bool>> filter, int skip, int take)
{
    // Limit to tennants on main Entity being queried
    var ents = ApplyDataRestrictions(db.GetCollection<Entity>("entities").AsQueryAble());
    var children = db.GetCollection<Child>("children").AsQueryable();
    var chickens = db.GetCollection<Chicken>("chickens").AsQueryable();

    // Join a couple collections for their counts
    var result = from ent in ents   
                join c in children on ent.Id equals c.EntityId into kids
                join ck in chickens on ent.Id equals ck.EntityId into birds
                // Project the results into a MyResult
                select new MyResult
                {
                    Id = ent.Id,
                    AProperty = ent.AProperty,
                    SomeCollection = ent.SomeCollection,
                    SomeOtherCollectionTagsMaybe = ent.SomeOtherCollectionTagsMaybe,
                    TotalKids = kids.Count(),
                    TotalChickens = birds.Count() 
                };
    if(filter != null)
    {
        // Apply the filter that was built up from criteria on the MyResults data shape
        result = result.Where(filter);
    }

    result = result.Skip(skip).Take(take);
    return result.ToList()
}

public IQueryable<Entity> ApplyDataRestrictions(IQueryable<Entity> query)
{
    ... restrict results to only those with my tennant id ...
}

Solution

  • Actually it turns out that the same behavior can be achieved without Query Builder and AnyIn. Having IQueryable interface you can try .Any() with Contains() as inner predicate

    var inMemoryList = new List<int>() { 3, 4, 5 };
    
    var q = from doc in Col.AsQueryable()
            where doc.Collection.Any(x => inMemoryList.Contains(x))
            select doc;
    

    or

    var q2 = Col.AsQueryable().Where(x => x.Collection.Any(y => inMemoryList.Contains(y)));