Redondeo a 2 decimales utilizando el marco de agregación MongoDB

Estoy usando el marco de agregación mongodb y haciendo algunos cálculos como se muestra a continuación

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

Estoy tratando de averiguar cuál es la mejor y más fácil forma de redondear mis resultados a 2 decimales. Lo siguiente es mi resultado

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

}

Quiero que “ActToPln” muestre 70.59 en lugar de “ActToPln”: 70.58823529411765 , en los resultados del propio marco de aggegation. Quiero evitar hacer el redondeo en mi aplicación

¿Puedes ayudarme con lo mismo?

A continuación está el conjunto de datos que utilicé.

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

Gracias por adelantado. Nandu

No hay $round operador $round , pero puede hacerlo en el marco de agregación; hacerlo en un orden específico normalmente evitará problemas de precisión de coma flotante.

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

Dada la tubería existente en el problema, reemplace:

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

con

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

Con sus puntos de datos de muestra, este es el resultado:

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

mongo-round funciona bien. La forma más limpia que he encontrado.

Diga que el número es 3.3333333

 var round = require('mongo-round'); db.myCollection.aggregate([ { $project: { roundAmount: round('$amount', 2) // it will become 3.33 } } ]); 

No hay operador de ronda en la versión actual del Marco de Agregación. Puedes probar este fragmento:

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

pero como ve, esta solución no funciona bien debido a problemas de precisión. La forma más fácil en este caso es seguir los consejos de @wiredprairie y hacer s en su aplicación.

Esta solución redondea correctamente hacia arriba o hacia abajo a 2dp:

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

Por ejemplo, redondea 1.2499 hacia arriba a 1.25, pero 1.2501 hacia abajo a 1.25.

Notas:

  1. Esta solución se basa en los ejemplos dados en http://www.kamsky.org/stupid-tricks-with-mongodb/rounding-numbers-in-aggregation-framework
  2. Resuelve el problema en la respuesta de Asya Kamsky, que solo trunca y no redondea hacia arriba / hacia abajo correctamente ; incluso después del cambio sugerido en los comentarios.
  3. El número de 9s finales en el factor de sum es grande, para acomodar los números de entrada de alta precisión. Dependiendo de la precisión de los números que se redondearán, es posible que el factor de sum deba ser aún más preciso que esto.
 {$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]} 

con suerte, estos podrían ayudar a redondear.

No sé por qué, pero todas las respuestas (en esta página) me dan 12.34 para 12.345 . Entonces escribí mi propia etapa de proyecto:

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

Da 12.35 .

Aquí hay aritmética simple, sin trucos:

  1. 12.345 * 100 = 1234.5 # Este paso nos lleva a la posición de redondeo: 100 = 10 ^ 2 (dos signos después del punto). El paso se equilibrará en el paso 4.
  2. 1234.5 + 0.5 = 1235.0 # Aquí tengo la round half up mi round half up
  3. truncado (1235.0) = 1235 # Simplemente cae la parte fraccionaria
  4. 1235/100 = 12.35

Sin embargo, no funciona correctamente para negativos (eso fue suficiente para mi agregación). Para ambos casos (positivo y negativo) debe usarlo con abs :

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

Aquí obtengo el signo del número, ajuste el número original por abs y luego multiplico el signo redondeando el resultado.

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

La solución de egvo es genial pero da división por cero si es cero. Para evitar $ cond se puede utilizar para detectar el signo

(Reemplace x con field_name y number 2 con el número decimal deseado)

Permítanme decir que es una pena que a MongoDB le falte esta función. Estoy saltando, lo agregarán pronto.

Sin embargo, se me ocurrió una larga tubería de agregación. Admitir, puede no ser eficiente pero respeta las reglas de redondeo.

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

Supongamos que tengo los siguientes documentos en mi colección llamados t

 { number" : 2.341567 } { number" : 2.0012 } { number" : 2.0012223 } 

Después de ejecutar las consultas anteriores, obtuve:

 { "number" : 2.342 } { "number" : 2.001 } { "number" : 2.001 }