3

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; }
    }
SJFJ
  • 657
  • 6
  • 18
  • I answered a very similar question at https://stackoverflow.com/a/45887800/346272 after encountering the exact same problem -- my groupBy(z=>z.key).select(z=>z.first()) was returning an array of null items. – scaryman Aug 25 '17 at 20:12

1 Answers1

5

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.

Community
  • 1
  • 1
Turnerj
  • 4,258
  • 5
  • 35
  • 52