我开始在MongoDB中学习聚合。我有一个简单的文档,如下所示,其中有两个字段,name
和examScores
,examScores
是一个包含乘数文档的array
:
{ _id: ObjectId("633199db009be219a43ae426"),
name: 'Max',
examScores:
[ { difficulty: 4, score: 57.9 },
{ difficulty: 6, score: 62.1 },
{ difficulty: 3, score: 88.5 } ] }
{ _id: ObjectId("633199db009be219a43ae427"),
name: 'Manu',
examScores:
[ { difficulty: 7, score: 52.1 },
{ difficulty: 2, score: 74.3 },
{ difficulty: 5, score: 53.1 } ] }
现在,我使用和$group/$max
查询每个人的最大分数,如下所示:
db.test.aggregate([
{$unwind: "$examScores"},
{$group: {_id: {name: "$name"}, maxScore: {$max: "$examScores.score"}}}
])
{ _id: { name: 'Max' }, maxScore: 88.5 }
{ _id: { name: 'Manu' }, maxScore: 74.3 }
但是,我希望结果还包含与name
和examScores.score
对应的name
字段,如下所示:
{ _id: { name: 'Max' }, difficulty: 3, maxScore: 88.5 }
{ _id: { name: 'Manu' }, difficulty: 2, maxScore: 74.3 }
我知道我可以使用$sort
+ $group
和$first
来实现这个目标。但是我想使用$getField
或any other methods
从ROOT Doc
获取数据。
我的想法是使用$project
和$getField
从ROOT doc
(或$unwind version of ROOT doc
)获得difficulty field
,条件是ROOT.name = Aggregate.name
和Root.examScores.score = Aggregate.maxScore
。
看起来会是这样的:
{$project:
{name: 1,
maxScore: 1,
difficulty:
{$getField: {
field: "$examScores.difficulty"
input: "$$ROOT.$unwind() with condition/filter"}
}
}
}
我想知道这在MongoDB中是否可行?
发布于 2022-09-26 13:29:46
解决方案1
$unwind
$group
-集团由name
.您需要$push
将$$ROOT
文档添加到data
数组中。$project
-通过将difficulty
与maxScore
匹配,从过滤后的data
数组的第一项中获取examScores.difficulty
的值,从而设置examScores.score
字段。db.collection.aggregate([
{
$unwind: "$examScores"
},
{
$group: {
_id: {
name: "$name"
},
maxScore: {
$max: "$examScores.score"
},
data: {
$push: "$$ROOT"
}
}
},
{
$project: {
_id: 0,
name: "$_id.name",
maxScore: 1,
difficulty: {
$getField: {
field: "difficulty",
input: {
$getField: {
field: "examScores",
input: {
$first: {
$filter: {
input: "$data",
cond: {
$eq: [
"$$this.examScores.score",
"$maxScore"
]
}
}
}
}
}
}
}
}
}
}
])
$rank
解决方案2:
$unwind
$rank
-按分区name
和examScores.score
降序排序。$match
-使用{ rank: 1 }
过滤文档。$unset
-删除rank
字段。db.collection.aggregate([
{
$unwind: "$examScores"
},
{
$setWindowFields: {
partitionBy: "$name",
sortBy: {
"examScores.score": -1
},
output: {
rank: {
$rank: {}
}
}
}
},
{
$match: {
rank: 1
}
},
{
$unset: "rank"
}
])
意见:我要说的是:
$sort
由examScores.score
降序$group
按名称,取第一个文档就容易多了。
发布于 2022-09-27 00:03:12
不需要使用$unwind
,然后再通过$group
重新构建文档来实现您想要的结果。我建议完全避免那样做。
相反,考虑使用数组表达式运算符内联处理数组。根据您正在寻找的版本和确切结果,这里有两个可能值得考虑的起点。特别是,$maxN算子和$sortArray算子可能对这一特定问题感兴趣。
您可以通过运行$addFields
聚合来查看它们的输出操场在这里来了解这两个操作符所做的事情。
以这些作为起点,真正取决于您如何使管道输出达到所需的结果。下面是一个与您在问题(游乐场)中描述的输出相当匹配的示例:
db.collection.aggregate([
{
"$addFields": {
"relevantEntry": {
$first: {
$sortArray: {
input: "$examScores",
sortBy: {
"score": -1
}
}
}
}
},
},
{
"$project": {
_id: 0,
name: 1,
difficulty: "$relevantEntry.difficulty",
maxScore: "$relevantEntry.score"
}
}
])
产生的结果:
[
{
"difficulty": 3,
"maxScore": 88.5,
"name": "Max"
},
{
"difficulty": 2,
"maxScore": 74.3,
"name": "Manu"
}
]
同样值得注意的是,如果存在重复,这种特定的方法不会做任何特别的事情。如果在这方面还需要更多的东西,您可以考虑使用$filter
。
https://stackoverflow.com/questions/73854490
复制相似问题