MongoDB - 在聚合中选择组而不指定字段

时间:2017-05-26 12:00:15

标签: mongodb aggregation-framework

长篇大论道歉!

我有一个带有以下文件的Mongo集合:

{
    "_id" : ObjectId("592811e3fab9f74b07139d73"),
    "Name" : "John",
    "Value" : 1,
    "AnotherValue": "12345"
},
{
    "_id" : ObjectId("592811f8fab9f74b07139d78"),
    "Name" : "John",
    "Value" : 5,
    "AnotherValue": "55555"
},
{
    "_id" : ObjectId("59281206fab9f74b07139d7e"),
    "Name" : "John",
    "Value" : 12,
    "AnotherValue": "654321"

},
{
    "_id" : ObjectId("59281217fab9f74b07139d81"),
    "Name" : "Chris",
    "Value" : 3,
    "AnotherValue": "11111"
},
{
    "_id" : ObjectId("59281223fab9f74b07139d85"),
    "Name" : "Steve",
    "Value" : 2,
    "AnotherValue": "22222"
},
{
    "_id" : ObjectId("5928122ffab9f74b07139d87"),
    "Name" : "Steve",
    "Value" : 4,
    "AnotherValue": "33333"
}

我想查询这些文档并返回每个具有最高值的名称的条目,因此我想要的结果集(顺序无关紧要)是:

{
    "_id" : ObjectId("59281206fab9f74b07139d7e"),
    "Name" : "John",
    "Value" : 12,
    "AnotherValue": "654321"
},
{
    "_id" : ObjectId("59281217fab9f74b07139d81"),
    "Name" : "Chris",
    "Value" : 3,
    "AnotherValue": "11111"
},
{
    "_id" : ObjectId("5928122ffab9f74b07139d87"),
    "Name" : "Steve",
    "Value" : 4,
    "AnotherValue": "33333"
}

如果我想在C#中做同样的事情,我会使用:

var result = 
    from item in collection
    orderby item.Value descending
    group item by item.Name into itemGroup
    select itemGroup.First();

使用聚合管道我已经达到:

db.getCollection('test').aggregate(
[
    { "$sort" : { "Value" : -1 } }, //sort descendingly by the Value field
    { "$group" : { "_id" : "$Name", "highest" : { "$first" : "$$ROOT" }  } }, //group by name and select the first document in the group (as they are sorted descendingly, this will be the document with the highest value)
])

这给了我以下结果集:

{
    "_id" : "Steve",
    "highest" : {
        "_id" : ObjectId("5928122ffab9f74b07139d87"),
        "Name" : "Steve",
        "Value" : 4,
        "AnotherValue": "33333"
    }
},
{
    "_id" : "Chris",
    "highest" : {
        "_id" : ObjectId("59281217fab9f74b07139d81"),
        "Name" : "Chris",
        "Value" : 3,
        "AnotherValue": "11111"
    }
},
{
    "_id" : "John",
   "highest" : {
        "_id" : ObjectId("59281206fab9f74b07139d7e"),
        "Name" : "John",
        "Value" : 12,
        "AnotherValue": "654321"
    }
}

正如您所看到的,我有一个文档数组,每个文档都包含一个“_id”字段,这个字段是名称,而“最高”字段是实际文档。

这将在C#中表示为:

var result = 
    from item in collection
    orderby item.Value descending
    group item by item.Name into itemGroup
    select new { id = itemGroup.Key, highest = itemGroup.First() };

我想知道的是,是否可以在我的管道中添加另一个步骤以确保我只选择实际的人员文档,而不是包含人员文档的组文档,我可以在不指定字段的情况下执行此操作吗?我希望编写一个C#类,它能够将此查询用于各种不同类型的对象,因此可能不知道这些字段(假设我可能想要使用此查询的每个集合都有名称和值字段,它们将会有一些共同的属性。)

如果我以完全错误的方式来到这里,那么我会接受全新的建议。只要我在最后获得所需的结果集,我就会很高兴。

提前感谢您的帮助。

1 个答案:

答案 0 :(得分:0)

非常感谢Neil Lunn在评论中回答了我的问题。

https://docs.mongodb.com/manual/reference/operator/aggregation/replaceRoot/

MongoDB 3.4有一个$ replaceRoot管道选项,可以完全满足我的需求:

db.getCollection('test').aggregate(
[
    { "$sort" : { "Value" : -1 } }, //sort descendingly by the Value field
    { "$group" : { "_id" : "$Name", "highest" : { "$first" : "$$ROOT" }  } }, //group by name and select the first document in the group (as they are sorted descendingly, this will be the document with the highest value)
    { "$replaceRoot": { newRoot: "$highest" } }
])

结果集:

{
    "_id" : ObjectId("5928122ffab9f74b07139d87"),
    "Name" : "Steve",
    "Value" : 4
},
{
    "_id" : ObjectId("59281217fab9f74b07139d81"),
    "Name" : "Chris",
    "Value" : 3
},
{
     "_id" : ObjectId("59281206fab9f74b07139d7e"),
    "Name" : "John",
    "Value" : 12
}