MongoDB C#从组中获取最新文档

Ban*_*San 4 c# mongodb mongodb-query aggregation-framework mongodb-.net-driver

我有一组假装付款的状态,每个都有付款ID.

我想获取每个付款ID的最新状态.测试我创建了一些虚拟数据然后尝试查询它.我到目前为止:

[Test]
public void GetPaymentLatestStatuses()
{
    var client = new TestMongoClient();

    var database = client.GetDatabase("payments");

    var paymentRequestsCollection = database.GetCollection<BsonDocument>("paymentRequests");

    var statusesCollection = database.GetCollection<BsonDocument>("statuses");

    var payment = new BsonDocument { { "amount", RANDOM.Next(10) } };

    paymentRequestsCollection.InsertOne(payment);

    var paymentId = payment["_id"];

    var receivedStatus = new BsonDocument
                         {
                             { "payment", paymentId },
                             { "code", "received" },
                             { "date", DateTime.UtcNow }
                         };
    var acceptedStatus = new BsonDocument
                         {
                             { "payment", paymentId },
                             { "code", "accepted" },
                             { "date", DateTime.UtcNow.AddSeconds(-1) }
                         };
    var completedStatus = new BsonDocument
                          {
                              { "payment", paymentId },
                              { "code", "completed" },
                              { "date", DateTime.UtcNow.AddSeconds(-2) }
                          };

    statusesCollection.InsertMany(new [] { receivedStatus, acceptedStatus, completedStatus });

    var groupByPayments = new BsonDocument { {"_id", "$payment"} };

    var statuses = statusesCollection.Aggregate().Group(groupByPayments);

}
Run Code Online (Sandbox Code Playgroud)

但现在我在一堵砖墙.

任何向正确方向戳戳都会有所帮助.我不确定我是不是在俯视望远镜的错误端.

更新

以下为我提供了正确文件的ID.

var groupByPayments = new BsonDocument
                      {
                          { "_id", "$payment" },
                          { "id", new BsonDocument { { "$first", "$_id" } } }
                      };

var sort = Builders<BsonDocument>.Sort.Descending(document => document["date"]);

var statuses = statusesCollection.Aggregate().Sort(sort).Group(groupByPayments).ToList();
Run Code Online (Sandbox Code Playgroud)

我是否可以使用单个查询获取完整文档,或者我是否必须重新发出命令以获取该列表中的所有文档?

oct*_*ccl 11

让我们从简单的方法开始,以获得您想要实现的目标.在MongoDB的C#Driver 2.X中,您可以找到AsQueryable扩展方法,让您从集合中创建LINQ查询.这个Linq提供程序是在MongoDB的Aggregation框架上构建的,所以最后你的链接查询将被转换为聚合管道.所以,如果你有这样一个类:

public class Status
{
  public ObjectId _id { get; set; }
  public ObjectId payment { get; set; }
  public string code { get; set; }
  public DateTime date { get; set; }
}
Run Code Online (Sandbox Code Playgroud)

您可以创建如下查询:

 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();
Run Code Online (Sandbox Code Playgroud)

现在您可能想知道为什么我必须将结果投影到一个新的Status类实例,如果我可以First从每个组获得相同的结果调用扩展方法?不幸的是,这还不支持.其中一个原因是因为Linq提供程序在构建聚合管道时使用$ first操作,这就是$first操作的工作原理.此外,正如您在前面共享的链接中所看到的,当您$first$group舞台中使用时,$group舞台应遵循一个$sort阶段,以按照定义的顺序输入文档.


现在,假设您不想使用Linq,并且您希望自己创建聚合管道,则可以执行以下操作:

 var groupByPayments = new BsonDocument
                      {
                          { "_id", "$payment" },
                          { "statusId", new BsonDocument { { "$first", "$_id" } } },
                          { "code", new BsonDocument { { "$first", "$code" } } },
                          { "date", new BsonDocument { { "$first", "$date" } } }
                      };

var sort = Builders<BsonDocument>.Sort.Descending(document => document["date"]);

ProjectionDefinition<BsonDocument> projection = new BsonDocument
        {
            {"payment", "$_id"},
            {"id", "$statusId"},
            {"code", "$code"},
            {"date", "$date"},
        }; 
var statuses = statusesCollection.Aggregate().Sort(sort).Group(groupByPayments).Project(projection).ToList<BsonDocument>();
Run Code Online (Sandbox Code Playgroud)

这个解决方案的优点是你可以在一次往返中得到数据,缺点是你必须预测你需要的所有字段.我的结论是如果文档没有很多字段或者你不需要你文档中的所有字段我都会使用这个变体.