• MongoDB聚合运算符:$bottomN


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

    语法

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

    用法

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

    关于null和缺失值的处理

    • $bottom不会过滤掉空值
    • $bottom会将缺失值转换为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:
                   {
                      $bottomN:
                         {
                            output: [ "$playerId", "$score" ],
                            sortBy: { "score": -1 },
                            n: 3
                         }
                   }
          }
       }
    ] )
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26

    在这个例子中:

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

    如下:

    [
       {
          _id: "G1",
          playerId: [ [ "PlayerA", 1 ], [ "PlayerD", null ], [ "PlayerE", null ] ]
       }
    ]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    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: {
                   $bottomN:
                   {
                      output: ["$playerId","$score"],
                      sortBy: {"score": -1},
                      n: 3
                   }
                }
             }
       }
    ] )
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23

    在这个例子中:

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

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

    [
       {
          _id: "G1",
          playerId: [ [ "PlayerB", "2" ], [ "PlayerC", "" ], [ "PlayerA", 1 ] ]
       }
    ]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    举例

    使用下面的命令创建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 }
    ])
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    查找三个得分最低的

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

    db.gamescores.aggregate( [
       {
          $match : { gameId : "G1" }
       },
       {
          $group:
             {
                _id: "$gameId",
                playerId:
                   {
                      $bottomN:
                      {
                         output: ["$playerId", "$score"],
                         sortBy: { "score": -1 },
                         n:3
                      }
                   }
             }
       }
    ] )
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    本例中:

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

    结果如下:

    [
       {
          _id: "G1",
          playerId: [ [ "PlayerB", 33 ], [ "PlayerA", 31 ], [ "PlayerD", 1 ] ]
       }
    ]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    与下面的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) AS T3
                ON GS.GAMEID = T3.GAMEID
    GROUP BY T3.GAMEID,T3.PLAYERID,T3.SCORE
       ORDER BY T3.SCORE DESC
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

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

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

    db.gamescores.aggregate( [
          {
             $group:
             { _id: "$gameId", playerId:
                {
                   $bottomN:
                      {
                         output: [ "$playerId","$score" ],
                         sortBy: { "score": -1 },
                         n: 3
                      }
                }
             }
          }
    ] )
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    在本例中:

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

    结果如下:

    [
       {
          _id: "G1",
          playerId: [ [ "PlayerB", 33 ], [ "PlayerA", 31 ], [ "PlayerD", 1 ] ]
       },
       {
          _id: "G2",
          playerId: [ [ "PlayerC", 66 ], [ "PlayerB", 14 ], [ "PlayerA", 10 ] ]
       }
    ]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    这个操作与下面的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 >= 2
    ORDER BY GAMEID
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    基于分组key来计算参数n

    可以动态指定n的值,在本例中$cond表达式用在gameId字段:

    db.gamescores.aggregate([
       {
          $group:
          {
             _id: {"gameId": "$gameId"},
             gamescores:
                {
                   $bottomN:
                      {
                         output: "$score",
                         n: { $cond: { if: {$eq: ["$gameId","G2"] }, then: 1, else: 3 } },
                         sortBy: { "score": -1 }
                      }
                }
          }
       }
    ] )
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    在本例中:

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

    操作结果如下:

    [
       { _id: { gameId: "G2" }, gamescores: [ 10 ] },
       { _id: { gameId: "G1" }, gamescores: [ 33, 31, 1 ] }
    ]
    
    • 1
    • 2
    • 3
    • 4
  • 相关阅读:
    I350网卡烧录oprom,通过UEFI PXE引导方案
    Ardupilot Rpanion iperf网络性能测试
    Rust中FnOnce如何传递给一个约束Fn的回调
    Python少儿编程小课堂(五)入门篇(5)
    js基础算法
    多线程---锁策略与CAS
    【SpringCloud】SpringCloud简介
    JAVA计算机毕业设计在线玩具租赁系统Mybatis+源码+数据库+lw文档+系统+调试部署
    Matlab save colormap
    40. 到达目的地的最短距离(第四期模拟笔试)
  • 原文地址:https://blog.csdn.net/superatom01/article/details/136289767