2013-07-05 2 views
12

Я использую структуру агрегации MongoDB и делать некоторые вычисления, как показано нижеОкругление до 2 знаков после запятой, используя структуру агрегации 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]} 
    } 

} 

); 

Я пытаюсь выяснить, что это лучший и простой способ округлить свои результаты до 2 знаков после запятой. Ниже мой результат

{ 
    "result" : [ 
      { 
        "_id" : { 
          "Region" : "East", 
          "Mth" : 201301 
        }, 
        "ActSls" : 72, 
        "PlnSls" : 102, 
        "ActToPln" : 70.58823529411765 
      } 
    ], 
    "ok" : 1 

}

Я хочу, чтобы "ActToPln", чтобы показать 70.59 вместо "ActToPln": 70,58823529411765, в результатах самого aggegation рамок. Я хочу, чтобы избежать округления в моей заявке

Помогите пожалуйста с этим.

Ниже приведен набор данных, которые я использовал.

{ 
    "_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 
} 

Заранее спасибо. Nandu

+0

вы должны привести несколько примеров или данных и упростить вопрос –

+0

я обновил вопрос, а также при условии, что набор данных я использовал. Спасибо amezhenin. – user2552537

+4

Почему бы не сделать это на клиенте? В плане агрегации нет естественного способа сделать это. Для выполнения функции округления вам необходимо использовать отображение карты. – WiredPrairie

ответ

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 
} 

но, как вы видите, это решение не работает должным образом из-за точных проблем. Самый простой способ в этом случае - следовать совету @wiredprairie и сделать round s в вашем приложении.

+0

Спасибо за ваш ответ @amezhenin point отметил. – user2552537

13

Оператор $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 } 

Учитывая существующий трубопровод в задаче заменить:

{$multiply:[{$divide: ['$ActSls', '$PlnSls']},100]} 

с

{$divide:[ 
    {$subtract:[ 
      {$multiply:[ 
      {$divide: ['$ActSls','$PlnSls']}, 
      10000 
      ]}, 
      {$mod:[ 
      {$multiply:[{$divide: ['$ActSls','$PlnSls']}, 10000 ]}, 
      1]} 
      ]}, 
    100 
]} 

С вашими точками данных выборки это результат:

{ "ActSls" : 31, "PlnSls" : 51, "ActToPln" : 60.78 } 
{ "ActSls" : 41, "PlnSls" : 51, "ActToPln" : 80.39 } 
{ "ActSls" : 72, "PlnSls" : 102, "ActToPln" : 70.58 } 
+2

Обратите внимание, что это технически * усекает * результат до двух знаков после запятой. Если вы хотите на самом деле * round *, вы должны добавить 49 до окончательного деления на 100. –

+0

Asya, можете ли вы указать, какая переменная/константа в вашем первом блоке кода db.a.save ({x: 1.23456789}). ... фактически округляет число. Я говорю, как в C#, например. Math.Round (123.234, 2). В том, что 2 - число знаков после запятой, число будет округлено до. Также можете ли вы изменить приведенный выше код в соответствии с указанным выше замечанием (добавьте 49 до окончательного разрыва на 100). –

+0

В шагах: если задано x, которое является числом, я умножаю x на 100 и вычитаю из этого мода x * 100,1 (который является дробной частью после умножения).Это оставляет меня с целым числом, которое составляет 100x от того, что я хочу сохранить, поэтому я делю его на 100. 1.5478 * 100 => 154.78 Мод, разделенный на 1 .78, поэтому после вычитания у меня 154, который я делю на 100. 1.54 осталось , Для разного количества десятичных знаков после. используйте несколько кратных 10, а не 100. –

1

mongo-round работает хорошо. Самый чистый способ, который я нашел.

Произнесите номер 3.3333333

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

db.myCollection.aggregate([ 
    { $project: { 
     roundAmount: round('$amount', 2) // it will become 3.33 
    } } 
]); 
0
{$divide:[ 
      {$cond: { if: { $gte: [ {$mod:[{$multiply:['$dollarAmount',100]}, 1]}, 0.5 ] }, then: {$add: [{$subtract:[ 
        {$multiply:['$dollarAmount',100]}, 
        {$mod:[{$multiply:['$dollarAmount',100]}, 1]} 
      ]} 
       ,1]}, else: {$subtract:[ 
        {$multiply:['$dollarAmount',100]}, 
        {$mod:[{$multiply:['$dollarAmount',100]}, 1]} 
      ]} }} 
      , 
      100]} 

надеюсь, это один может помочь в округлении.

1

Это решение правильно округляет вверх или вниз 2DP:

"rounded" : { 
    $subtract:[ 
    {$add:['$absolute',0.0049999999999999999]}, 
    {$mod:[{$add:['$absolute',0.0049999999999999999]}, 0.01]} 
    ] 
} 

Например, он округляет 1,2499 вверх до 1,25, а 1,2501 вниз до 1.25.

Примечание:

  1. Это решение основано на примерах, приведенных в http://www.kamsky.org/stupid-tricks-with-mongodb/rounding-numbers-in-aggregation-framework
  2. Это решает проблему в ответе Ася Камского, что это только усекает и не round up/down correctly; даже после внесения изменений в комментарии.
  3. Количество задних 9s в коэффициенте добавочного значения велико, для размещения высокоточных номеров ввода. В зависимости от точности номеров, которые нужно округлить, возможно, необходимо добавить дополнительный коэффициент, который будет более точным.
0

Я не знаю почему, но все ответы (на этой странице) дают мне 12.34 для 12.345. Поэтому я написал собственный этап проекта:

x = 12.345 

{'$project': { 
    y: {'$divide': [{'$trunc': {'$add': [{'$multiply': ['$x', 100]}, 0.5]}}, 100]}, 
}}, 

Это дает 12.35.

Вот простая арифметика, никаких фокусов:

  1. 12,345 * 100 = 1234,5 # Этот шаг заставляет нас округление положение: 100 = 10^2 (два знака после запятой). Шаг будет сбалансирован обратно за шагом 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]} 
    ]}, 
}} 

Здесь я получаю знак для номера, завернуть исходное число на абсе, а затем умножить знак округления выхода.

0
rounded:{'$multiply': [{ "$cond": [{ "$gte": [ "$x", 0 ] }, 1,-1 ]},{'$divide': [{'$trunc': {'$add': [{'$multiply': [{'$abs': '$x'}, {$pow:[10,2]}]}, 0.5]}}, {$pow:[10,2]}]}]} 

Решение egvo является прохладным, но дает деление на ноль, если оно равно нулю. Для того, чтобы избежать $ конд может быть использован для обнаружения Подписать

(Заменить х с field_name и номером 2 с желаемым числом десятичных)

Смежные вопросы