文章目录
- 语法
- 使用
- 空值或缺失值的处理
- 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指定要从文档中获取的字段,可以是任何表达式nn 必须是正整数表达式,可以是常量,也可以是$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:G1PlayerD的得分得分不存在,PlayerE的得分为null,这两者的值都为都会被认为是nullinput: "$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 ] }
]