Arrondir à la deuxième décimale en utilisant le cadre D'agrégation MongoDB

j'utilise le cadre d'agrégation de mongodb et je fais quelques calculs comme indiqué ci-dessous

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

}

); 

j'essaie de comprendre ce qui est le meilleur et le plus facile pour arrondir mes résultats à 2 décimales. Voici mon résultat

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

}

je veux "ActToPln" pour montrer 70.59 au lieu de "ActToPln": 70.58823529411765, dans les résultats du cadre d'aggégation lui-même. Je veux éviter de faire l'arrondi dans mon application

Pouvez-vous s'il vous plaît aider avec le même.

Voici l'ensemble de données que j'ai utilisé.

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

Merci d'avance. Nandu

16
demandé sur Neil Lunn 2013-07-05 10:41:00

8 réponses

Il n'y a pas de $round opérateur mais vous pouvez le faire dans le cadre d'agrégation - le faire dans un ordre spécifique évitera généralement les problèmes de précision à virgule flottante.

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

compte tenu du pipeline existant dans le problème, remplacer:

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

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

avec vos points de données sample ceci est le résultat:

{ "ActSls" : 31, "PlnSls" : 51, "ActToPln" : 60.78 }
{ "ActSls" : 41, "PlnSls" : 51, "ActToPln" : 80.39 }
{ "ActSls" : 72, "PlnSls" : 102, "ActToPln" : 70.58 }
15
répondu Asya Kamsky 2014-03-22 12:33:51

mongo-ronde œuvres de nice. Le plus propre que j'ai trouvé.

Dire le nombre est 3.3333333

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

db.myCollection.aggregate([
    { $project: {
        roundAmount: round('$amount', 2)  // it will become 3.33
    } }
]);
3
répondu Hongbo Miao 2016-08-25 05:44:03

il n'y a pas d'opérateur rond dans la version actuelle du cadre D'agrégation. Vous pouvez essayer ce morceau de code:

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

mais comme vous le voyez, cette solution ne fonctionne pas bien à cause de problèmes de précision. La façon la plus simple dans ce cas est de suivre @wiredprairie's des conseils et faire rounds dans votre application.

2
répondu Artem Mezhenin 2013-07-05 20:55:15

cette solution arrondit correctement vers le haut ou vers le bas à 2dp:

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

par exemple il tourne 1.2499 vers le haut à 1.25, mais 1.2501 vers le bas à 1.25.

Notes:

  1. Cette solution est basée sur les exemples donnés au http://www.kamsky.org/stupid-tricks-with-mongodb/rounding-numbers-in-aggregation-framework
  2. il résout le problème dans la réponse D'Asya Kamsky, qu'il ne tronque et ne fait pas arrondir Haut/Bas correctement; même après le changement suggéré dans les commentaires.
  3. le nombre de 9 de queue dans le facteur d'addition est grand, pour accommoder des nombres d'entrée de haute précision. En fonction de la précision des chiffres à arrondir, le facteur d'addition peut devoir être encore plus précis que celui-ci.
2
répondu Vince Bowdren 2017-04-05 11:48:10
{$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]}

espérons que celui-ci pourrait aider à compléter.

0
répondu Enrico Domingo 2017-03-23 02:15:13

je ne sais pas pourquoi, mais toutes les réponses (à cette page) donnez-moi 12.3412.345. J'ai donc écrit ma propre étape de projet:

x = 12.345

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

donne 12.35.

Voici simple arithmétique, pas de trucs:

  1. 12.345 * 100 = 1234.5 # cette étape nous amène à la position d'arrondi: 100 = 10^2 (deux signes après le point). Le pas sera équilibré par l'étape 4.
  2. 1234.5 + 0.5 = 1235.0 # Ici, je reçois mon round half up
  3. tronqué (1235.0) = 1235 # Simplement gouttes partie fractionnaire
  4. 1235 / 100 = 12.35

cependant, cela ne fonctionne pas correctement pour les négatifs (c'était suffisant pour mon agrégation). Pour les deux cas (positif et négatif), vous devez l'utiliser avec abs:

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

ici je reçois le signe du nombre, envelopper le nombre original par abs et ensuite multiplier le signe en arrondissant la sortie.

0
répondu egvo 2017-06-27 12:43:11
rounded:{'$multiply': [{ "$cond": [{ "$gte": [ "$x", 0 ] }, 1,-1 ]},{'$divide': [{'$trunc': {'$add': [{'$multiply': [{'$abs': '$x'}, {$pow:[10,2]}]}, 0.5]}}, {$pow:[10,2]}]}]}

la solution d'egvo est cool mais donne la division par zéro si elle est zéro. Pour éviter $cond peut être utilisé pour détecter le signe

(remplacez x par field_name et le numéro 2 par le nombre décimal désiré)

0
répondu Brcn 2017-12-13 03:19:57

laissez-moi vous dire qu'il est dommage que MongoDB manque cette fonction. Je suis hopping ils l'ajouteront bientôt.

cependant, je suis arrivé avec un long pipeline d'agrégation. En admettant, il peut ne pas être efficace, mais il honore les règles de l'arrondissement.

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

supposons que j'ai les documents suivants dans ma collection nommés t

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

Après les requêtes ci-dessus, j'ai:

{ "number" : 2.342 }
{ "number" : 2.001 }
{ "number" : 2.001 }
0
répondu Saleem 2018-03-21 17:24:27