使用MongoDB聚合框架舍入到小数点后2位

use*_*537 22 rounding mongodb aggregation-framework

我正在使用mongodb聚合框架并进行一些计算,如下所示

db.RptAgg.aggregate( 
{ $group :
 { _id : {Region:"$RegionTxt",Mth:"$Month"},           
   ActSls:{$sum:"$ActSls"},
   PlnSls:{$sum:"$PlnSls"}
 } 
},
{ $project : 
 {
   ActSls:1,
   PlnSls:1,
   ActToPln:{$cond:[{ $ne: ["$PlnSls", 0] },{$multiply:[{$divide: ['$ActSls', '$PlnSls']},100]},0]}
  }

}

); 
Run Code Online (Sandbox Code Playgroud)

我想弄清楚将结果舍入到2位小数的最佳和最简单的方法是什么.以下是我的结果

{
    "result" : [
            {
                    "_id" : {
                            "Region" : "East",
                            "Mth" : 201301
                    },
                    "ActSls" : 72,
                    "PlnSls" : 102,
                    "ActToPln" : 70.58823529411765
            }
    ],
    "ok" : 1
Run Code Online (Sandbox Code Playgroud)

}

我希望"ActToPln"在aggegation框架本身的结果中显示70.59而不是"ActToPln":70.58823529411765.我想避免在我的应用程序中进行舍入

你能帮忙吗?

以下是我使用的数据集.

{
    "_id" : ObjectId("51d67ef69557c507cb172572"),
    "RegionTxt" : "East",
    "Month" : 201301,
    "Date" : "2013-01-01",
    "ActSls" : 31,
    "PlnSls" : 51
}
{
    "_id" : ObjectId("51d67ef69557c507cb172573"),
    "RegionTxt" : "East",
    "Month" : 201301,
    "Date" : "2013-01-02",
    "ActSls" : 41,
    "PlnSls" : 51
}
Run Code Online (Sandbox Code Playgroud)

提前致谢.南都

Asy*_*sky 17

没有$round运算符,但您可以在聚合框架中执行此操作 - 按特定顺序执行此操作通常可以避免浮点精度问题.

> db.a.save({x:1.23456789})
> db.a.save({x:9.87654321})
> db.a.aggregate([{$project:{ _id:0, 
         y:{$divide:[
              {$subtract:[
                      {$multiply:['$x',100]},
                      {$mod:[{$multiply:['$x',100]}, 1]}
              ]},
              100]}
}}])
{ "y" : 1.23 }
{ "y" : 9.87 }
Run Code Online (Sandbox Code Playgroud)

鉴于问题中的现有管道,请替换:

{$multiply:[{$divide: ['$ActSls', '$PlnSls']},100]}
Run Code Online (Sandbox Code Playgroud)

{$divide:[
     {$subtract:[ 
          {$multiply:[
             {$divide: ['$ActSls','$PlnSls']},
             10000
          ]}, 
          {$mod:[
             {$multiply:[{$divide: ['$ActSls','$PlnSls']}, 10000 ]},
             1]}
          ]}, 
     100
]}
Run Code Online (Sandbox Code Playgroud)

使用您的示例数据点,结果如下:

{ "ActSls" : 31, "PlnSls" : 51, "ActToPln" : 60.78 }
{ "ActSls" : 41, "PlnSls" : 51, "ActToPln" : 80.39 }
{ "ActSls" : 72, "PlnSls" : 102, "ActToPln" : 70.58 }
Run Code Online (Sandbox Code Playgroud)

  • 未来读者需要注意的是:从3.2(2015年底)开始,聚合框架将有三个可用于此的运算符:$ trunc,它截断为0,$ floor和$ ceil. (4认同)
  • 请注意,这在技术上*将结果截断为两位小数.如果你想实际*圆*它你应该在最后除数之前加上49. (2认同)

Xav*_*hot 10

首先Mongo 4.2,有一个新的$round聚合运算符,可用于将具有特定精度的数字舍入到指定的小数

{ $round : [ <number>, <place> ] }

可以在聚合管道中这样使用(这里我们将xs舍入到2小数位):

// db.collection.insert([{x: 1.23456}, {x: 9.87654}, {x: 0.055543}, {x: 12.345}])
db.collection.aggregate([{ $project: { "rounded_x": { $round: ["$x", 2] }}}])
// [{"rounded_x": 1.23}, {"rounded_x": 9.88}, {"rounded_x": 0.06}, {"rounded_x": 12.35}]
Run Code Online (Sandbox Code Playgroud)

请注意,该place参数是可选的,省略它会导致四舍五入为整数(即​​四舍五入到 0 位小数)。

  • 请注意,这将**舍入为偶数**。如果你想向上/向下/为零/无穷大,它不会为你工作 (2认同)

egv*_*gvo 8

我不知道为什么,但所有答案(在此页面上)都给了我12.34for 12.345. 所以我写了我自己的项目阶段:

x = 12.345

{'$project': {
    y: {'$divide': [{'$trunc': {'$add': [{'$multiply': ['$x', 100]}, 0.5]}}, 100]},
}},
Run Code Online (Sandbox Code Playgroud)

它给12.35.


这是简单的算术,没有技巧:

  1. 12.345 * 100 = 1234.5 # 这一步让我们四舍五入:100 = 10^2(点后的两个符号)。Step 4 将被平衡。
  2. 1234.5 + 0.5 = 1235.0 # 在这里我得到我的 round half up
  3. truncate(1235.0) = 1235 # 简单地去掉小数部分
  4. 1235 / 100 = 12.35

但是,对于底片它不能正常工作(这对于我的聚合来说已经足够了)。对于(正面和负面)两种情况,您都应该将其用于abs

{'$project': {
    z: {'$multiply': [
        {'$divide': ['$x', {'$abs': '$x'}]}, 
        {'$divide': [{'$trunc': {'$add': [{'$multiply': [{'$abs': '$x'}, 100]}, 0.5]}}, 100]}
    ]},
}}
Run Code Online (Sandbox Code Playgroud)

在这里,我得到数字的符号,用 abs 包裹原始数字,然后将符号乘以舍入输出。


Hon*_*iao 5

mongo-round很好用。我找到的最干净的方法。

说号码是 3.3333333

var round = require('mongo-round');

db.myCollection.aggregate([
    { $project: {
        roundAmount: round('$amount', 2)  // it will become 3.33
    } }
]);
Run Code Online (Sandbox Code Playgroud)


Art*_*nin 2

当前版本的聚合框架中没有舍入运算符。你可以尝试这个片段:

> db.a.save({x:1.23456789})
> db.a.save({x:9.87654321})
> db.a.aggregate([{$project:{y:{$subtract:['$x',{$mod:['$x', 0.01]}]}}}])
{
    "result" : [
        {
            "_id" : ObjectId("51d72eab32549f94da161448"),
            "y" : 1.23
        },
        {
            "_id" : ObjectId("51d72ebe32549f94da161449"),
            "y" : 9.870000000000001
        }
    ],
    "ok" : 1
}
Run Code Online (Sandbox Code Playgroud)

但正如您所看到的,由于精度问题,该解决方案效果不佳。在这种情况下,最简单的方法是遵循@wiredprairie的建议并round在您的应用程序中添加 s 。