Definición
Sintaxis
{ $topN: { n: <expression>, sortBy: { <field1>: <sort order>, <field2>: <sort order> ... }, output: <expression> } }
nlimita el número de resultados por grupo y debe ser una expresión entera positiva que sea constante o que dependa del valor de_idpara$group.sortBy especifica el orden de los resultados, con una sintaxis similar a
$sort.outputrepresenta la salida de cada elemento en el grupo y puede ser cualquier expresión.
Comportamiento
Valores nulos y faltantes
$topNno filtra los valores nulos.$topNconvierte los valores faltantes a nulos, que se conservan en el resultado.
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", playerId: { $topN: { output: [ "$playerId", "$score" ], sortBy: { "score": 1 }, n: 3 } } } } ] )
En este ejemplo:
$documentscrea los documentos literales que contienen las puntuaciones de los jugadores.$groupagrupa los documentos porgameId. Este ejemplo tiene solo ungameId,G1.PlayerDtiene una puntuación faltante yPlayerEtiene unscorenulo. Estos valores se consideran ambos nulos.Los campos
playerIdyscorese especifican comooutput : ["$playerId"," $score"]y se devuelven como valores de un arreglo.Debido al
sortBy: { "score" : 1 }, los valores nulos se ordenan al principio del arreglo deplayerIddevueltos.
[ { _id: 'G1', playerId: [ [ 'PlayerD', null ], [ 'PlayerE', null ], [ 'PlayerA', 1 ] ] } ]
Orden de clasificación del tipo de datos BSON
Al ordenar diferentes tipos, se utiliza el orden de los tipos de datos BSON para determinar el orden. Como ejemplo, consideremos una colección cuyos valores consisten en cadenas y números.
En una ordenación ascendente, los valores de string se ordenan después de los valores numéricos.
En una ordenación descendente, los valores de string se ordenan antes que los valores numéricos.
db.aggregate( [ { $documents: [ { playerId: "PlayerA", gameId: "G1", score: 1 }, { playerId: "PlayerB", gameId: "G1", score: "2" }, { playerId: "PlayerC", gameId: "G1", score: "" } ] }, { $group: { _id: "$gameId", playerId: { $topN: { output: ["$playerId","$score"], sortBy: {"score": -1}, n: 3 } } } } ] )
En este ejemplo:
PlayerAtiene una puntuación entera.PlayerBtiene una puntuación de string"2".PlayerCtiene un puntaje de string vacía.
Debido a que el ordenamiento es descendente { "score" : -1 }, los valores de literales de string se ordenan antes de la puntuación numérica de PlayerA:
[ { _id: "G1", playerId: [ [ "PlayerB", "2" ], [ "PlayerC", "" ], [ "PlayerA", 1 ] ] } ]
Restricciones
Compatibilidad con funciones de ventana y expresiones de agregación
$topN no es compatible como expresión de agregación.
$topN es compatible como un window operator.
Consideraciones sobre el límite de memoria
Los grupos dentro del pipeline de agregación $topN están sujetos al límite de 100 MB. Si se supera este límite para un grupo individual, la agregación falla con un error.
Ejemplos
Considera una colección gamescores con los siguientes documentos:
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 } ])
Encuentra los tres más altos Scores
Puede usar el acumulador $topN para encontrar a los jugadores con la puntuación más alta en un solo juego.
db.gamescores.aggregate( [ { $match : { gameId : "G1" } }, { $group: { _id: "$gameId", playerId: { $topN: { output: ["$playerId", "$score"], sortBy: { "score": -1 }, n:3 } } } } ] )
La pipeline de ejemplo:
Utiliza
$matchpara filtrar los resultados en un sologameId. En este caso,G1.Usa
$grouppara agrupar los resultados porgameId. En este caso,G1.Utiliza ordenación por
{ "score": -1 }para ordenar los resultados en orden descendente.Especifica los campos que se producen de
$topNconoutput : ["$playerId"," $score"].Utiliza
$topNpara devolver los tres documentos principales con elscoremás alto para el juegoG1conn : 3.
La operación devuelve los siguientes resultados:
[ { _id: 'G1', playerId: [ [ 'PlayerC', 99 ], [ 'PlayerB', 33 ], [ 'PlayerA', 31 ] ] } ]
El equivalente en SQL para esta query es:
SELECT T3.GAMEID,T3.PLAYERID,T3.SCORE FROM GAMESCORES AS GS JOIN (SELECT TOP 3 GAMEID,PLAYERID,SCORE FROM GAMESCORES WHERE GAMEID = 'G1' ORDER BY SCORE DESC) AS T3 ON GS.GAMEID = T3.GAMEID GROUP BY T3.GAMEID,T3.PLAYERID,T3.SCORE ORDER BY T3.SCORE DESC
Encontrar los tres documentos con la puntuación más alta en varios juegos
Puedes usar el acumulador $topN para encontrar a los jugadores con mayor puntuación en cada partida.
db.gamescores.aggregate( [ { $group: { _id: "$gameId", playerId: { $topN: { output: [ "$playerId","$score" ], sortBy: { "score": -1 }, n: 3 } } } } ] )
La pipeline de ejemplo:
Utiliza
$grouppara agrupar los resultados porgameId.Especifica los campos que se producen de
$topNconoutput : ["$playerId", "$score"].Utiliza ordenación por
{ "score": -1 }para ordenar los resultados en orden descendente.Utiliza
$topNpara devolver los tres documentos principales con el más altoscorepara cada juego conn: 3.
La operación devuelve los siguientes resultados:
[ { _id: 'G1', playerId: [ [ 'PlayerC', 99 ], [ 'PlayerB', 33 ], [ 'PlayerA', 31 ] ] }, { _id: 'G2', playerId: [ [ 'PlayerD', 80 ], [ 'PlayerC', 66 ], [ 'PlayerB', 14 ] ] } ]
El equivalente en SQL para esta query es:
SELECT PLAYERID,GAMEID,SCORE FROM( SELECT ROW_NUMBER() OVER (PARTITION BY GAMEID ORDER BY SCORE DESC) AS GAMERANK, GAMEID,PLAYERID,SCORE FROM GAMESCORES ) AS T WHERE GAMERANK <= 3 ORDER BY GAMEID
Cálculo de n en función de la clave de grupo para $group
También puede asignar dinámicamente el valor de n. En este ejemplo, se utiliza la expresión $cond en el campo gameId.
db.gamescores.aggregate([ { $group: { _id: {"gameId": "$gameId"}, gamescores: { $topN: { output: "$score", n: { $cond: { if: {$eq: ["$gameId","G2"] }, then: 1, else: 3 } }, sortBy: { "score": -1 } } } } } ] )
La pipeline de ejemplo:
Utiliza
$grouppara agrupar los resultados porgameId.Especifica los campos que se producen de
$topNconoutput : "$score".Si el
gameIdesG2entoncesnes 1, de lo contrariones 3.Utiliza ordenación por
{ "score": -1 }para ordenar los resultados en orden descendente.
La operación devuelve los siguientes resultados:
[ { _id: { gameId: 'G1' }, gamescores: [ 99, 33, 31 ] }, { _id: { gameId: 'G2' }, gamescores: [ 80 ] } ]