文章目录
- 语法
- 使用
- 空值或缺失值的处理
- f i r s t N 与 firstN与 firstN与topN对比
- 关于窗口功能和聚合表达式的支持
- 内存限制
- 举例
- 查找单场比赛的前三名运动员得分
- 查找多场比赛中前三名运动员的得分
- s o r t 与 sort与 sort与firstN一起使用
- 根据group的键计算n
- 在聚合表达式中使用$firstN
$firstN
聚合运算符返回分组中前n个元素的聚合,只有在分组元素是有序的情况下才有意义,如果分组中的元素个数小于n,将返回分组中所有的元素。
语法
{$firstN:{input: <expression>,n: <expression>}
}
input
指定要从文档中获取的字段,可以是任何表达式n
n 必须是正整数表达式,可以是常量,也可以是$group
的_id
值。
使用
空值或缺失值的处理
$firstN
不会过滤掉空值- 缺失的值会被
$firstN
转换为null
下面的聚合返回每个分组的前5个文档:
db.aggregate( [{$documents: [{ playerId: "PlayerA", gameId: "G1", score: 1 },{ playerId: "PlayerB", gameId: "G1", score: 2 },{ playerId: "PlayerC", gameId: "G1", score: 3 },{ playerId: "PlayerD", gameId: "G1"},{ playerId: "PlayerE", gameId: "G1", score: null }]},{$group:{_id: "$gameId",firstFiveScores:{$firstN:{input: "$score",n: 5}}}}
] )
在这个示例中:
documents
创建了一些包含运动员得分的字面量文档$group
对文档按照gameId
对文档进行分组,本例中只有一个gameId
:G1
PlayerD
的得分得分不存在,PlayerE
的得分为null,这两者的值都为都会被认为是null
input: "$score"
指定给firstFiveScores
字段并返回一个数组- 由于没有排序标准,因此返回前 5 个分数字段
[{_id: 'G1',firstFiveScores: [ 1, 2, 3, null, null ]}
]
f i r s t N 与 firstN与 firstN与topN对比
$firstN
和$topN
运算符返回的结果类似,但是:
- 如果文档来自于
$group
且已经排序,应该使用$firstN
- 如果要排序并且返回前n个元素,使用
$topN
一个运算就可以搞定 $firstN
可以用于聚合表达式,但是$topN
不可以
关于窗口功能和聚合表达式的支持
$firstN
支持作为聚合表达式$firstN
同时支持作为窗口操作符
内存限制
调用$firstN
的聚合管道受100MB的限制,如果单个组超出此限制,聚合将失败并返回错误。
举例
使用下面的脚本创建gamescores
集合:
db.gamescores.insertMany([{ playerId: "PlayerA", gameId: "G1", score: 31 },{ playerId: "PlayerB", gameId: "G1", score: 33 },{ playerId: "PlayerC", gameId: "G1", score: 99 },{ playerId: "PlayerD", gameId: "G1", score: 1 },{ playerId: "PlayerA", gameId: "G2", score: 10 },{ playerId: "PlayerB", gameId: "G2", score: 14 },{ playerId: "PlayerC", gameId: "G2", score: 66 },{ playerId: "PlayerD", gameId: "G2", score: 80 }
])
查找单场比赛的前三名运动员得分
使用$firstN
单场比赛的前三名得分:
db.gamescores.aggregate( [{$match : { gameId : "G1" }},{$group:{_id: "$gameId",firstThreeScores:{$firstN:{input: ["$playerId", "$score"],n:3}}}}
] )
在本例中:
- 使用
match
过滤出gameId
为G1
的文档 - 使用
$group
按照gameId
进行分组 - 使用
input : ["$playerId"," $score"].
为$firstN
指定输入字段 - 使用
$fristN
指定n:3
返回G1
游戏前三个文档
操作返回的结果如下:
[{_id: 'G1',firstThreeScores: [ [ 'PlayerA', 31 ], [ 'PlayerB', 33 ], [ 'PlayerC', 99 ] ]}
]
查找多场比赛中前三名运动员的得分
使用$firstN
查找每个比赛中前n
个输入字段:
db.gamescores.aggregate( [{$group:{_id: "$gameId", playerId:{$firstN:{input: [ "$playerId","$score" ],n: 3}}}}
] )
在本例聚合管道中:
- 使用
$group
根据gameId
进行分组 - 使用
$firstN
和n:3
返回比赛前三个文档 - 为
firstN
指定输入字段input : ["$playerId", "$score"]
操作返回下面的结果:
[{_id: 'G1',playerId: [ [ 'PlayerA', 31 ], [ 'PlayerB', 33 ], [ 'PlayerC', 99 ] ]},{_id: 'G2',playerId: [ [ 'PlayerA', 10 ], [ 'PlayerB', 14 ], [ 'PlayerC', 66 ] ]}
]
s o r t 与 sort与 sort与firstN一起使用
在管道中较早使用$sort
阶段可能会影响$firstN
db.gamescores.aggregate( [{ $sort : { score : -1 } },{$group:{ _id: "$gameId", playerId:{$firstN:{input: [ "$playerId","$score" ],n: 3}}}}] )
在这个例子中:
{$sort : { score : -1 } }
将得分最高的排在分组最后firstN
返回分组中前三个得分最高的
db.gamescores.aggregate( [{ $sort : { score : -1 } },{$group:{ _id: "$gameId", playerId:{$firstN:{input: [ "$playerId","$score" ],n: 3}}}}] )
操作返回下面的结果:
[{_id: 'G2',playerId: [ [ 'PlayerD', 80 ], [ 'PlayerC', 66 ], [ 'PlayerB', 14 ] ]},{_id: 'G1',playerId: [ [ 'PlayerC', 99 ], [ 'PlayerB', 33 ], [ 'PlayerA', 31 ] ]}
]
根据group的键计算n
可以动态的指定n
的值,在下面的例子中$cond
表达式被用于gameId
字段
db.gamescores.aggregate([{$group:{_id: {"gameId": "$gameId"},gamescores:{$firstN:{input: "$score",n: { $cond: { if: {$eq: ["$gameId","G2"] }, then: 1, else: 3 } }}}}}
] )
在本例的管道中:
- 使用
$group
根据gameId
进行分组 - 使用
input : "$score"
指定$firstN
输入字段 - 如果
gameId
是G2
则n
为1,否则n
为3
操作返回下面的结果:
[{ _id: { gameId: 'G1' }, gamescores: [ 31, 33, 99 ] },{ _id: { gameId: 'G2' }, gamescores: [ 10 ] }
]
在聚合表达式中使用$firstN
请看下面的聚合:
db.aggregate( [{$documents: [{ array: [10, 20, 30, 40] } ]},{ $project: {firstThreeElements:{$firstN:{input: "$array",n: 3}}}}
] )
$documents
创建了一个包含一个数组的字面量文档$project
用于返回$firstN
的输出_id
被输出省略_id:0
$firstN
使用输入数组[10, 20, 30, 40]
- 返回输入文档数组的前三个元素
操作返回下面的结果:
[{ firstThreeElements: [ 10, 20, 30 ] }
]