MongoDB聚合运算符:$topN

MongoDB聚合运算符:$topN


$topN聚合运算符返回分组中指定顺序的最前面 n个元素,如果分组中的元素数量小于 n,则返回分组的全部元素。从MongoDB5.2开始支持。

语法

{
   $topN:
      {
         n: <expression>,
         sortBy: { <field1>: <sort order>, <field2>: <sort order> ... },
         output: <expression>
      }
}
  • n用于限制每组结果的数量,必须是正整数表达式,要么是常数,要么取决于$group_id
  • sortBy制定返回结果的顺序,语法类似于$sort
  • output指定分组元素输出的内容,可以是任何合法的表达式。

用法

  • $topN不支持作为聚合表达式。
  • $topN只支持作为window 操作符
  • 聚合管道调用$topN受100M的限制,如果单组超过这一限制将报错。

关于null和缺失值的处理

  • $topN不会过滤掉空值
  • $topN会将缺失值转换为null
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
                  }
            }
      }
   }
] )

在这个例子中:

  • 使用$documents阶段创建了一些字面量(常量)文档,包含了选手的得分
  • $group阶段根据gameId对文档进行了分组,显然文档中的gameId都是G1
  • PlayerD的得分缺失,PlayerE的得分为null,他们的得分都会被当做null处理
  • playerId字段和score字段被指定为输出:["$playerId"," $score"],以数组的形式返回
  • sortBy: { "score": 1 }指定了排序的方式,空值被排在最前面,返回playerId数组

如下:

[
   {
      _id: 'G1',
      playerId: [ [ 'PlayerD', null ], [ 'PlayerE', null ], [ 'PlayerA', 1 ] ]
   }
]

BSON数据类型排序

当不同类型排序是,使用BSON数据类型的顺序进行排序:

  • 当进行正序排序时(由小到大),字符串的优先级在数值之前
  • 当进行逆序排序时(由大到小),字符串的优先级在数值之前

下面的例子中包含了字符串和数值类型:

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
               }
            }
         }
   }
] )

在这个例子中:

  • PlayerA的得分是整数1
  • PlayerB的得分是字符串"2"
  • PlayerC的得分是空字符串""

因为排序指定为逆序{ "score" : -1 },字符串的字面量排在PlayerA的数值得分之前:

[
   {
      _id: "G1",
      playerId: [ [ "PlayerB", "2" ], [ "PlayerC", "" ], [ "PlayerA", 1 ] ]
   }
]

举例

使用下面的命令创建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 }
])

查找三个得分最高的

使用$topN查找单个游戏中得分最高的3个:

db.gamescores.aggregate( [
   {
      $match : { gameId : "G1" }
   },
   {
      $group:
         {
            _id: "$gameId",
            playerId:
               {
                  $topN:
                  {
                     output: ["$playerId", "$score"],
                     sortBy: { "score": -1 },
                     n:3
                  }
               }
         }
   }
] )

本例中:

  • 使用$match阶段用一个gameId对结果进行筛选,即:G1
  • 使用$group阶段依据gameId对结果进行分组,本例中只有一个分组G1
  • 使用sortBy: { "score": -1 }按照得分进行逆序排序
  • 使用output : ["$playerId"," $score"]$topN指定输出字段
  • 使用$topN返回游戏得分最高的3个选手和得分

结果如下:

[
   {
      _id: 'G1',
      playerId: [ [ 'PlayerC', 99 ], [ 'PlayerB', 33 ], [ 'PlayerA', 31 ] ]
   }
]

与下面的SQL查询等价:

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

查找全部游戏中三个最高的得分

使用$topN查找所有游戏中得分最高的三个

db.gamescores.aggregate( [
      {
         $group:
         { _id: "$gameId", playerId:
            {
               $topN:
                  {
                     output: [ "$playerId","$score" ],
                     sortBy: { "score": -1 },
                     n: 3
                  }
            }
         }
      }
] )

在本例中:

  • 使用$group按照groupId对结果排序
  • 使用output : ["$playerId", "$score"]指定bottom输出的字段
  • 使用sortBy: { "score": -1 }按照得分进行逆序排序
  • 使用$topN返回所有游戏中得分最高的三个

结果如下:

[
   {
      _id: 'G1',
      playerId: [ [ 'PlayerC', 99 ], [ 'PlayerB', 33 ], [ 'PlayerA', 31 ] ]
   },
   {
      _id: 'G2',
      playerId: [ [ 'PlayerD', 80 ], [ 'PlayerC', 66 ], [ 'PlayerB', 14 ] ]
   }
]

这个操作与下面的SQL语句等价:

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

基于分组key来计算参数n

可以动态指定n的值,在本例中$cond表达式用在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 }
                  }
            }
      }
   }
] )

在本例中:

  • 使用$group按照groupId对结果排序
  • 使用output : "$score"指定$topN输出的字段
  • 如果gameIdG2n为1,否则n为3
  • 使用sortBy: { "score": -1 }按照得分进行逆序排序

操作结果如下:

[
   { _id: { gameId: 'G1' }, gamescores: [ 99, 33, 31 ] },
   { _id: { gameId: 'G2' }, gamescores: [ 80 ] }
]

相关推荐

  1. MongoDB聚合运算符:$topN

    2024-05-13 19:16:03       12 阅读
  2. MongoDB聚合运算符:$top

    2024-05-13 19:16:03       12 阅读
  3. MongoDB聚合运算符:$add

    2024-05-13 19:16:03       34 阅读
  4. MongoDB聚合运算符:$arrayToObject

    2024-05-13 19:16:03       28 阅读
  5. MongoDB聚合运算符;$dateToParts

    2024-05-13 19:16:03       26 阅读
  6. MongoDB聚合运算符:$dayOfWeek

    2024-05-13 19:16:03       28 阅读
  7. MongoDB聚合运算符:$dayOfMonth

    2024-05-13 19:16:03       28 阅读
  8. MongoDB聚合运算符;$dateToString

    2024-05-13 19:16:03       23 阅读
  9. MongoDB聚合运算符:$dayOfYear

    2024-05-13 19:16:03       23 阅读
  10. MongoDB聚合运算符:$denseRank

    2024-05-13 19:16:03       18 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-05-13 19:16:03       18 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-05-13 19:16:03       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-05-13 19:16:03       19 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-05-13 19:16:03       20 阅读

热门阅读

  1. stylus详解与引入

    2024-05-13 19:16:03       13 阅读
  2. 深度学习学习日记(5.6)

    2024-05-13 19:16:03       11 阅读
  3. 初级银行从业资格证知识点(十)

    2024-05-13 19:16:03       11 阅读
  4. 升级WSL Ubuntu内核从5.10到5.15

    2024-05-13 19:16:03       17 阅读
  5. Flink面试整理-Flink的配置管理包含哪些?

    2024-05-13 19:16:03       14 阅读
  6. Python Pandas 数据分析快速入门

    2024-05-13 19:16:03       12 阅读
  7. el-tree

    2024-05-13 19:16:03       24 阅读
  8. QT 文字转语言插件

    2024-05-13 19:16:03       15 阅读
  9. 特殊类的设计与单例模式

    2024-05-13 19:16:03       15 阅读
  10. 网络工程师----第二十六天

    2024-05-13 19:16:03       12 阅读
  11. 计算机组成与结构 计算机基本原理 软设刷题

    2024-05-13 19:16:03       16 阅读
  12. 面试被问ThreadLocal要怎么回答?

    2024-05-13 19:16:03       12 阅读