Округление до 2 десятичных знаков с использованием агрегационной структуры MongoDB
Я использую структуру агрегации 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]}
}
}
);
Я пытаюсь выяснить, что является лучшим и простым способом округлить мои результаты до двух знаков после запятой. Ниже приведен мой результат
{
"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
Ответы
Ответ 1
Нет оператора $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
mongo-round работает хорошо. Самый чистый способ, который я нашел.
Скажите, что номер 3.3333333
var round = require('mongo-round');
db.myCollection.aggregate([
{ $project: {
roundAmount: round('$amount', 2) // it will become 3.33
} }
]);
Ответ 3
В текущей версии Aggregation Framework нет круглого оператора. Вы можете попробовать этот фрагмент:
> 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
в вашем приложении.
Ответ 4
Это решение правильно округляет или уменьшает до 2dp:
"rounded" : {
$subtract:[
{$add:['$absolute',0.0049999999999999999]},
{$mod:[{$add:['$absolute',0.0049999999999999999]}, 0.01]}
]
}
Например, он округляет 1.2499 вверх до 1.25, но 1.2501 вниз до 1.25.
Примечания:
Ответ 5
{$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]}
надеюсь, что это может помочь в округлении.
Ответ 6
Я не знаю, почему, но все ответы (на этой странице) дают мне 12.34
для 12.345
. Поэтому я написал свой собственный проект:
x = 12.345
{'$project': {
y: {'$divide': [{'$trunc': {'$add': [{'$multiply': ['$x', 100]}, 0.5]}}, 100]},
}},
Он дает 12.35
.
Вот простая арифметика, без трюков:
- 12.345 * 100 = 1234.5 # Этот шаг позволяет нам округлить положение: 100 = 10 ^ 2 (два знака после точки). Шаг будет сбалансирован на шаг 4.
- 1234.5 + 0.5 = 1235.0 # Здесь я получаю свой
round half up
- truncate (1235.0) = 1235 # Просто падает дробная часть
- 1235/100 = 12,35
Однако для негативов он не работает корректно (этого было достаточно для моей агрегации). Для обоих (положительных и отрицательных) случаев вы должны использовать его с abs
:
{'$project': {
z: {'$multiply': [
{'$divide': ['$x', {'$abs': '$x'}]},
{'$divide': [{'$trunc': {'$add': [{'$multiply': [{'$abs': '$x'}, 100]}, 0.5]}}, 100]}
]},
}}
Здесь я получаю знак номера, обертываю исходное число абс, а затем умножаю знак, округляя вывод.
Ответ 7
rounded:{'$multiply': [{ "$cond": [{ "$gte": [ "$x", 0 ] }, 1,-1 ]},{'$divide': [{'$trunc': {'$add': [{'$multiply': [{'$abs': '$x'}, {$pow:[10,2]}]}, 0.5]}}, {$pow:[10,2]}]}]}
Решение egvo является прохладным, но дает деление на ноль, если оно равно нулю. Чтобы избежать $cond, можно использовать для обнаружения знака
(Замените x с именем поля и номером 2 с нужным десятичным числом)
Ответ 8
Позвольте мне сказать, что стыдно, что MongoDB пропускает эту функцию. Я скачу, что они скоро это добавят.
Однако я придумал длинный агрегатный трубопровод. Признавая, он может быть неэффективным, но он соблюдает правила округления.
db.t.aggregate([{
$project: {
_id: 0,
number: {
$let: {
vars: {
factor: {
$pow: [10, 3]
},
},
in: {
$let: {
vars: {
num: {$multiply: ["$$factor", "$number"]},
},
in: {
$switch: {
branches: [
{case: {$gte: ["$$num", {$add: [{$floor: "$$num"}, 0.5]}]}, then: {$divide:[ {$add: [{$floor: "$$num"}, 1.0]},"$$factor"]}},
{case: {$lt: ["$$num", {$add: [{$floor: "$$num"}, 0.5]}]}, then: {$divide:[{$floor: "$$num"}, "$$factor"]}}
]
}
}
}
}
}
}
}
}])
Предположим, у меня есть следующие документы в моей коллекции с именем t
{ number" : 2.341567 }
{ number" : 2.0012 }
{ number" : 2.0012223 }
После выполнения запросов я получил:
{ "number" : 2.342 }
{ "number" : 2.001 }
{ "number" : 2.001 }