MongoDB C#从集团获取最新文档

MongoDB C#从集团获取最新文档,c#,mongodb,mongodb-query,aggregation-framework,mongodb-.net-driver,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

我有一组假装付款的状态,每个状态都有一个付款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);

}
[测试]
public void GetPaymentLatestStatuses()
{
var client=newtestmongoclient();
var数据库=client.GetDatabase(“付款”);
var paymentrequestscolection=database.GetCollection(“paymentRequests”);
var statusesCollection=database.GetCollection(“status”);
var payment=新的BsonDocument{{“金额”,随机。下一个(10)};
paymentRequestsCollection.InsertOne(付款);
var paymentId=付款[“_id”];
var receivedStatus=新的B文件
{
{“付款”,paymentId},
{“代码”,“收到”},
{“date”,DateTime.UtcNow}
};
var acceptedStatus=新的BsonDocument
{
{“付款”,paymentId},
{“代码”,“已接受”},
{“date”,DateTime.UtcNow.AddSeconds(-1)}
};
var completedStatus=新的BsonDocument
{
{“付款”,paymentId},
{“代码”,“已完成”},
{“date”,DateTime.UtcNow.AddSeconds(-2)}
};
statusesCollection.InsertMany(新[]{receivedStatus,acceptedStatus,completedStatus});
var groupByPayments=新的BsonDocument{{{u id',“$payment};
var status=statuseCollection.Aggregate().Group(groupByPayments);
}
但现在我在一堵砖墙前

任何朝正确方向的戳都会有帮助。我不确定我没有朝望远镜的另一端看

更新 下面给出了正确文档的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();
var groupByPayments=新的BsonDocument
{
{u id',“$payment”},
{“id”,新的BsonDocument{{“$first”,“$\u id”}}
};
var sort=Builders.sort.Descending(document=>document[“date”]);
var statuses=statuseCollection.Aggregate().Sort(排序).Group(groupByPayments.ToList();

但是,我可以通过一次查询获得完整的文档,还是现在必须重新发出命令才能获得该列表中的所有文档?

我就是这样实现的。但必须有更好的办法

[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" },
                              { "id", new BsonDocument { { "$first", "$_id" } } }
                          };

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

    var statuses = statusesCollection.Aggregate().Sort(sort).Group(groupByPayments).ToList();

    var statusIds = statuses.Select(x => x["id"]);

    var completedStatusDocumentsFilter =
        Builders<BsonDocument>.Filter.Where(document => statusIds.Contains(document["_id"]));

    var statusDocuments = statusesCollection.Find(completedStatusDocumentsFilter).ToList();

    foreach (var status in statusDocuments)
    {
        Assert.That(status["code"].AsString, Is.EqualTo("completed"));
    }
}
[测试]
public void GetPaymentLatestStatuses()
{
var client=newtestmongoclient();
var数据库=client.GetDatabase(“付款”);
var paymentrequestscolection=database.GetCollection(“paymentRequests”);
var statusesCollection=database.GetCollection(“status”);
var payment=新的BsonDocument{{“金额”,随机。下一个(10)};
paymentRequestsCollection.InsertOne(付款);
var paymentId=付款[“_id”];
var receivedStatus=新的B文件
{
{“付款”,paymentId},
{“代码”,“收到”},
{“date”,DateTime.UtcNow}
};
var acceptedStatus=新的BsonDocument
{
{“付款”,paymentId},
{“代码”,“已接受”},
{“date”,DateTime.UtcNow.AddSeconds(+1)}
};
var completedStatus=新的BsonDocument
{
{“付款”,paymentId},
{“代码”,“已完成”},
{“date”,DateTime.UtcNow.AddSeconds(+2)}
};
statusesCollection.InsertMany(新[]{receivedStatus,acceptedStatus,completedStatus});
var groupByPayments=新的BsonDocument
{
{u id',“$payment”},
{“id”,新的BsonDocument{{“$first”,“$\u id”}}
};
var sort=Builders.sort.Descending(document=>document[“date”]);
var statuses=statuseCollection.Aggregate().Sort(排序).Group(groupByPayments.ToList();
var statusIds=状态。选择(x=>x[“id”]);
变量completedStatusDocumentsFilter=
Builders.Filter.Where(document=>statusIds.Contains(document[“_id]”));
var statusDocuments=statuseCollection.Find(completedStatusDocumentsFilter.ToList();
foreach(statusDocuments中的var状态)
{
Assert.That(status[“code”].AsString,Is.EqualTo(“completed”));
}
}

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

public class Status
{
  public ObjectId _id { get; set; }
  public ObjectId payment { get; set; }
  public string code { get; set; }
  public DateTime date { get; set; }
}
您可以创建如下所示的查询:

 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();
 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>();
此解决方案的优点是您可以在一次往返中获得数据,缺点是您必须投影所有需要的字段。我的结论是,如果文档没有很多字段,或者您不需要文档中的所有字段,我将使用此变体

但必须有更好的办法

[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" },
                              { "id", new BsonDocument { { "$first", "$_id" } } }
                          };

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

    var statuses = statusesCollection.Aggregate().Sort(sort).Group(groupByPayments).ToList();

    var statusIds = statuses.Select(x => x["id"]);

    var completedStatusDocumentsFilter =
        Builders<BsonDocument>.Filter.Where(document => statusIds.Contains(document["_id"]));

    var statusDocuments = statusesCollection.Find(completedStatusDocumentsFilter).ToList();

    foreach (var status in statusDocuments)
    {
        Assert.That(status["code"].AsString, Is.EqualTo("completed"));
    }
}
从2.5.3开始,您可以访问聚合中的当前组。这使我们能够构建一个通用访问器,该访问器将通过本机mongo查询从分组中检索第一个元素

首先,一个用于反序列化的助手类
KeyValuePair
是密封的,所以我们自己滚动

    /// <summary>
    /// Mongo-ified version of <see cref="KeyValuePair{TKey, TValue}"/>
    /// </summary>
    class InternalKeyValuePair<T, TKey>
    {
        [BsonId]
        public TKey Key { get; set; } 
        public T Value { get; set; }
    }
    //you may not need this method to be completely generic, 
    //but have the sortkey be the same helps
    interface IDateModified
    {
        DateTime DateAdded { get; set; }
    }
    private List<T> GroupFromMongo<T,TKey>(string KeyName) where T : IDateModified
    {
        //mongo linq driver doesn't support this syntax, so we make our own bsondocument. With blackjack. And Hookers. 
        BsonDocument groupDoc = MongoDB.Bson.BsonDocument.Parse(@"
         {
                _id: '$" + KeyName + @"',
                Value: { '$first': '$$CURRENT' }
        }");
        //you could use the same bsondocument parsing trick to get a generic 
        //sorting key as well as a generic grouping key, or you could use
        //expressions and lambdas and make it...perfect.
        SortDefinition<T> sort = Builders<T>.Sort.Descending(document => document.DateAdded);
        List<BsonDocument> intermediateResult = getCol<T>().Aggregate().Sort(sort).Group(groupDoc).ToList();
        InternalResult<T, TKey>[] list = intermediateResult.Select(r => MongoDB.Bson.Serialization.BsonSerializer.Deserialize<InternalResult<T, TKey>>(r)).ToArray();
        return list.Select(z => z.Value).ToList();
    }
//
///蒙哥版
/// 
类内部KeyValuePair
{
[BsonId]
    class MongoKeyValuePair<T, TKey>
    {
        [BsonId]
        public TKey Key { get; set; }
        public T Value { get; set; }
        public long Count { get; set; }
    }

        BsonDocument groupDoc = MongoDB.Bson.BsonDocument.Parse(@"
         {
                _id: '$" + GetPropertyName(KeySelector) + @"',
                Value: { '$first': '$$CURRENT' },
                Count: { $sum: 1 }
        }");