的$filter
运营商是不是一个独立的流水线阶段。它只是返回一个数组作为属性。所以最好在这种情况下,直接用于$group
作为参数传递给$avg
:
db.restaurants.aggregate([
{ '$group': {
'_id': '$name',
'avg': {
'$avg': {
'$avg': {
'$map': {
'input': {
'$filter': {
'input': '$grades',
'as': 'grade',
'cond': { '$eq': [ '$$grade.grade', 'A' ] }
}
},
'as': 'grade',
'in': '$$grade.score'
}
}
}
}
}},
{ '$sort': { 'avg': 1 } }
])
也适用$map
只提取"score"
值和养活他们$avg
,从而出现“两次”,是一次以创建“ avg“值,其次是分组密钥的”平均累加器“。
对于这个问题显示的数据,您可以:
{
"_id" : "Morris Park Bake Shop",
"avg" : 6.75
}
这是从只标有“A”级的条目平均分。
有趣的是能正常工作的一份文件中,但如果从https://raw.githubusercontent.com/mongodb/docs-assets/primer-dataset/primer-dataset.json给整个数据集,这是获得实际未累积超过一个文件的任何$avg
生产null
值。
简单地增加从过滤的阵列到文档的平均值正常工作:
db.restaurants.aggregate([
{ "$addFields": {
"average": {
"$avg": {
"$map": {
"input": {
"$filter": {
"input": "$grades",
"as": "g",
"cond": { "$eq": [ "$$g.grade", "A" ] }
}
},
"as": "g",
"in": "$$g.score"
}
}
}
}}
])
作为不积累在一个以上的文件。即对"cuisine"
:
db.restaurants.aggregate([
{ '$group': {
'_id': '$cuisine',
'avg': {
'$avg': {
'$avg': {
'$map': {
'input': {
'$filter': {
'input': '$grades',
'as': 'g',
'cond': { '$eq': [ '$$g.grade', 'A' ] }
}
},
'as': 'g',
'in': '$$g.score'
}
}
}
}
}},
{ '$sort': { 'avg': 1 } }
])
这意味着原先规定的工作且令人信服确实当值被“划分为”实际发生在一个以上的文件。
遗憾的是,无论有多少文件被分组,所以可应用的唯一可靠方法仍然是应用$unwind
。这真是现代的版本不应该是必要的:
虽然这将与一致的结果工作:
db.restaurants.aggregate([
{ "$match": { "grades.grade": "A" } },
{ "$unwind": "$grades" },
{ "$match": { "grades.grade": "A" } },
{ "$group": {
"_id": "$name",
"score": { "$avg": "$grades.score" }
}},
{ "$sort": { "score": -1 } }
])
最优化的事情,实际上比较“与苹果苹果”是预过滤器的任何“文档”为只有那些都不可能匹配与$match
在初始阶段的标准一个数组元素:
db.restaurants.aggregate([
{ '$match': { 'grades.grade': 'A' } },
{ '$group': {
'_id': '$name',
'value': {
'$avg': {
'$avg': {
'$map': {
'input': {
'$filter': {
'input': '$grades',
'as': 'g',
'cond': { '$eq': [ '$$g.grade', 'A' ] }
}
},
'as': 'g',
'in': '$$g.score'
}
}
}
}
}},
{ "$sort": { "value": -1 } }
])
另外,$sort
将典型地在所施加的“负”或“降序”顺序,首先表示最大值。或者至少当你检查数据并且总体掌握聚合时最有意义。
所以看起来更短或可能“不太令人困惑”并不意味着“更好”在这里。我们在$group
流水线中使用$filter
和$map
操作编写此操作的原因是因为使用$unwind
进行处理的代价非常高。
使用$unwind
会为每个数组成员创建一个整个文档的副本,这通常意味着要处理的文档数量大量增加,这当然会增加处理时间。
因此,“更短更好”的实际情况实际上是在“少管线阶段”的使用中,并且通过完全去除所有使用的$unwind
来不增加要处理的文档的数量。
的原因$group
之前添加的$match
是因为在其他的例子,当你与$unwind
处理任何空数组会从文件删除处理,然后其他后续$match
会有过滤掉任何地方没有"A"
等级和文档确实有分数,但没有匹配"A"
的文档也将在那里被删除。
因此,使用$map
和$filter
,这些文件将返回null
,因为这是从参数一个空数组$avg
返回值。但是,当然如果最初的条件是“文档”必须包含匹配条件,那么最初为空或“过滤空”数组将不会被考虑,因为它们从一开始就从处理中移除。
作为使得包括一定程度的“过滤”任何聚合操作的金科玉律是总是$match
作为第一个流水线级,以便只将适用于以后的任何条件的文件是唯一选择。这也加速了它自己的事情。
NOTE: This caused me some considerable panic in a tired state because of the null
values returned. It should be noted that the "usual" application of any $group
operation is typically to accumulate and "reduce" results considerably.
The "$name"
field chosen in the question is pretty much unique for each document in the sample dataset obtained from the MongoDB documentation samples. A more realistic "grouping" sample would be to use "$cuisine"
from the data, which actually accumulates "across documents" for which you typically use
好,我觉得我得到了理论,但在执行这个,当我得到这个错误:异常:无效运算符“$平均” – user3174311
@ user3174311如果你得到了,我认为你做那么数据都需要做的是复制和粘贴。注意到数据源,其实我只是在做一些调查,因为我注意到了一些奇怪的行为。在关于这个 –
的回答中添加了注释,实际上在你的最后一个例子中,语法更加清晰,谢谢。 – user3174311