SJFJ
SJFJ

Reputation: 667

LINQ Select on GroupBy in MongoDB C# driver 2.1

I'm having problem the following LINQ expression on Mongo using the 2.1 C# driver running Mongo 3.0. Selecting the Id works fine but not selecting the A.

The following simple test demonstrates the error I'm getting.

Specified method is not supported. at MongoDB.Driver.Linq.Processors.AccumulatorBinder.GetAccumulatorArgument(Expression node)

If it is not supported, any suggestions how to work around it without having to unwind the queryable first? I know that I could use the mongo aggregate framework but that is not desired since we are not exposed to that here and I do not want mongo specific syntax at this level.

[Test]
    public void TestLinqSelectOnGroupBy()
    {
        MongoClient mongoClient = new MongoClient();
        var repo = mongoClient.GetDatabase("GroupSelect");

        var a = new A() { Id = "1", Group = "A" };
        var col = repo.GetCollection<A>("A");
        col.InsertOneAsync(a);
        var allA = col.AsQueryable(); // adding .ToArray(); will obviously make it work but that is not very efficient

        var works =  allA.GroupBy(x => x.Group).Select(x => x.First().Id).ToArray();
        var fails =  allA.GroupBy(x => x.Group).Select(x => x.First()).ToArray();
    }

    private class A 
    {
        public string Id { get; set; }
        public string Group { get; set; }
    }

Upvotes: 3

Views: 5393

Answers (1)

Turnerj
Turnerj

Reputation: 4278

I stumbled across an answer from another Stack Overflow question. The issue is the First() call itself.

Quoting octavioccl from that answer:

 var statusesCollection = database.GetCollection<Status>("statuses");
 var result= statusesCollection.AsQueryable()
                               .OrderByDescending(e=>e.date)
                               .GroupBy(e=>e.payment)
                               .Select(g=>new Status{_id =g.First()._id,
                                                     payment = g.Key,
                                                     code=g.First().code,
                                                     date=g.First().date
                                                    }
                                       )
                               .ToList();

Now you may wondering why I had to project the result to a new instance of Status class if I could get the same result calling First extension method from each group? Unfortunately that is not supported yet. One of the reason is because the Linq provider is using [$first][1] operation when it build the aggregation pipeline, and that is how $first operation works.

So for your case, you should just be able to do:

allA.GroupBy(x => x.Group).Select(x => new A 
{ 
    Id = x.First().Id, 
    Group = x.First().Group 
}).ToArray();

I only found your question while still seeing if this particular thing was still an issue and it still seems to be which is a little unfortunate.

Upvotes: 5

Related Questions