MongoDB C# Get latest document from group

后端 未结 3 1710
星月不相逢
星月不相逢 2021-01-13 00:29

I have a group of statuses of pretend payments, each with a payment ID.

I want to get the latest status for each payment ID. The test I have creates some dummy data

3条回答
  •  执笔经年
    2021-01-13 00:56

    Let's start with the easy way to get what you're trying to achieve. In the C# Driver 2.X of MongoDB you can find AsQueryable extension method that let's you create LINQ queries from your collections. This Linq provider was built over the Aggregation framework of MongoDB, so at the end your link query is going to be translated to an aggregation pipeline. So, if you have a class like this:

    public class Status
    {
      public ObjectId _id { get; set; }
      public ObjectId payment { get; set; }
      public string code { get; set; }
      public DateTime date { get; set; }
    }
    

    You can create a query like the following:

     var statusesCollection = database.GetCollection("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 operation when it build the aggregation pipeline, and that is how $first operation works. Also, as you can see in the link a shared earlier,when you use $first in a $group stage, the $group stage should follow a $sort stage to have the input documents in a defined order.


    Now, supposing you don't want to use Linq and you want to work creating the aggregation pipeline by yourself, you could do the following:

     var groupByPayments = new BsonDocument
                          {
                              { "_id", "$payment" },
                              { "statusId", new BsonDocument { { "$first", "$_id" } } },
                              { "code", new BsonDocument { { "$first", "$code" } } },
                              { "date", new BsonDocument { { "$first", "$date" } } }
                          };
    
    var sort = Builders.Sort.Descending(document => document["date"]);
    
    ProjectionDefinition projection = new BsonDocument
            {
                {"payment", "$_id"},
                {"id", "$statusId"},
                {"code", "$code"},
                {"date", "$date"},
            }; 
    var statuses = statusesCollection.Aggregate().Sort(sort).Group(groupByPayments).Project(projection).ToList();
    

    The advantage of this solution is that you get the data in one round trip, and the disadvantage is you have to project all the fields that you need.My conclusion would be if the document doesn't have many fields or you don't need all the fields from your document I would use this variant.

提交回复
热议问题