MongoDB - 在聚合中选择组而不指定字段
Posted
技术标签:
【中文标题】MongoDB - 在聚合中选择组而不指定字段【英文标题】:MongoDB - Select the group in aggregation without specifying fields 【发布时间】:2017-10-27 07:40:08 【问题描述】:为长篇道歉!
我有一个包含以下文档的 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# 类,该类将能够将此查询用于各种不同类型的对象,因此可能不知道字段(假设我可能想将此查询用于 DOES 的每个集合都有名称和值字段,它们都会有一些共同的属性)。
如果我以完全错误的方式来解决这个问题,那么我愿意接受全新的建议。只要我最终得到想要的结果集,我就会很高兴。
提前感谢您的帮助。
【问题讨论】:
聚合无法扩展,因为它们不能很好地处理分片。 如果你有 MongoDB 3.4 你可以使用$replaceRoot
,否则你需要用$project
指定所有的字段。因此,如果这是必须的,请升级。但真的有那么糟糕吗?无论哪种情况,我都认为权衡是在聚合管道中再次运行结果的成本,或者只是在客户端代码中处理每个返回的结果。对于这种微不足道的用法,我只是在客户端代码中进行。
$replaceRoot 完全实现了我的目标。谢谢!
【参考方案1】:
非常感谢 Neil Lunn 在 cmets 中回答了我的问题。
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
【讨论】:
以上是关于MongoDB - 在聚合中选择组而不指定字段的主要内容,如果未能解决你的问题,请参考以下文章