【问题标题】:How to get n records of each group in mongodb using C#, LINQ?如何使用 C#、LINQ 在 mongodb 中获取每个组的 n 条记录?
【发布时间】:2021-03-03 11:33:41
【问题描述】:

每组需要得到 10 件物品。按 ID 降序和按类别分组

public class Item
{
  public string Id { get; set ; }
  public string Name { get ; set ; }
  public string Category { get ; set ; }
}

我尝试过这样的事情。

    var data = Collection.AsQueryable()
              .OrderByDescending(o=> o.Id)
              .GroupBy(x => x.Category)
              .Select(g => new { GroupName = g.Key, Items = 
              g.Take(10).ToList() });

但是遇到了这样的异常

System.NotSupportedException:不支持指定的方法。在 MongoDB.Driver.Linq.Processors.AccumulatorBinder.GetAccumulatorArgument(表达式 节点)

【问题讨论】:

  • 嗯,到目前为止,您尝试了什么,遇到了什么问题?我们不是来为您编写代码的。
  • 您现在使用的查询有什么问题?它不会从组中返回 10 条记录吗?
  • System.NotSupportedException:不支持指定的方法。在 MongoDB.Driver.Linq.Processors.AccumulatorBinder.GetAccumulatorArgument(表达式节点)
  • @KhairulAlam 你为什么要这样做? GroupName = g.Key, Items = g.Take(10).ToList()你能解释一下吗

标签: c# mongodb entity-framework linq


【解决方案1】:

改用聚合框架。 MongoDB LINQ 提供程序建立在它之上。

您可以通过以下查询获得按Id降序排列的每个组的10个项目:

db.items.aggregate([
   { "$sort": { "Id": -1 } },
   { "$group": { "_id": "$Category", "Items": { "$push": "$$ROOT" } } },
   { "$project": { "_id": 0, "GroupName": "$_id", "Items": { "$slice": ["$Items", 10] } } }
])

C#代码如下:

// models
public class Item
{
    public string Id { get; set; }
    public string Name { get; set; }
    public string Category { get; set; }
}

public class ItemsGroup
{
    public string GroupName { get; set; }
    public Item[] Items { get; set; }
}

// query
var collection = db.GetCollection<Item>("Items");

IAggregateFluent<ItemsGroup> result = collection.Aggregate()
    .SortByDescending(o => o.Id)
    .Group(BsonDocument.Parse("{ _id: '$Category', Items: { '$push': '$$ROOT'}}"))
    .Project<ItemsGroup>(BsonDocument.Parse("{ _id: 0, GroupName: '$_id',  Items: { $slice: ['$Items', 10]}}"));

List<ItemsGroup> groups = result.ToList();

但是,此查询可能有问题。如果每个组有数千个项目,我猜小组赛会将它们全部保存在Items 临时数组中,而我们只需要 10 个。

如果被分组的键的数量不是很大,那么最好为每个组执行 $lookup 而不是将所有内容推入一个数组然后对其进行 $slicing。这可以通过以下查询来实现:

aggregate([
   { "$group": { "_id": "$Category" } },
   { "$lookup": { 
       "from": "Items",
       "let": { "c": "$_id" },
       "as": "Items"
       "pipeline": [
            { "$match": { "$expr": { "$eq": ["$Category", "$$c"] } } },
            { "$sort": { "Id": -1 } },
            { "$limit": 10 }
         ],        
      }
   },
   { "$project": { "_id": 0, "GroupName": "$_id", "Items": 1 } }
])

在 C# 代码中,它会是这样的:

BsonArray pipeline = new BsonArray
{
    BsonDocument.Parse("{ $match: { $expr: { $eq: ['$Category', '$$c']} } }"),
    BsonDocument.Parse("{ $sort: { Id: -1 } }"),
    BsonDocument.Parse("{ $limit: 10 }")
};

BsonDocument lookup = new BsonDocument("$lookup",
    new BsonDocument("from", "Items")
        .Add("let", new BsonDocument("c", "$_id"))
        .Add("pipeline", pipeline)
        .Add("as", "Items")
);

IAggregateFluent<ItemsGroup> result = collection.Aggregate()
    .Group(BsonDocument.Parse("{ _id: '$Category' }"))
    .AppendStage<object>(lookup)
    .Project<ItemsGroup>("{ _id: 0, GroupName: '$_id', Items: 1 }");

List<ItemsGroup> groups = result.ToList();

【讨论】:

  • 感谢@ruslan-gilmutdinov,它正在按预期工作......
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-11-26
  • 1970-01-01
  • 2016-07-15
  • 2011-05-24
相关资源
最近更新 更多