J'utilise le cadre d'agrégation mongodb et effectue certains 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 déterminer le meilleur et le plus simple moyen d'arrondir mes résultats à deux décimales. Voici mon résultat
{
"result" : [
{
"_id" : {
"Region" : "East",
"Mth" : 201301
},
"ActSls" : 72,
"PlnSls" : 102,
"ActToPln" : 70.58823529411765
}
],
"ok" : 1
}
Je veux que "ActToPln" affiche 70.59 au lieu de "ActToPln": 70.58823529411765 , dans les résultats du cadre de coordination lui-même. Je veux éviter de faire l'arrondi dans ma demande
Pouvez-vous s'il vous plaît aider avec les mêmes.
Voici le jeu 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
Il n'y a pas d'opérateur $round
mais vous pouvez le faire dans le cadre d'agrégation - le faire dans un ordre spécifique évite généralement les problèmes de précision en 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 }
Étant donné le pipeline existant dans le problème, remplacez:
{$multiply:[{$divide: ['$ActSls', '$PlnSls']},100]}
avec
{$divide:[
{$subtract:[
{$multiply:[
{$divide: ['$ActSls','$PlnSls']},
10000
]},
{$mod:[
{$multiply:[{$divide: ['$ActSls','$PlnSls']}, 10000 ]},
1]}
]},
100
]}
Avec vos exemples de points de données, voici le résultat:
{ "ActSls" : 31, "PlnSls" : 51, "ActToPln" : 60.78 }
{ "ActSls" : 41, "PlnSls" : 51, "ActToPln" : 80.39 }
{ "ActSls" : 72, "PlnSls" : 102, "ActToPln" : 70.58 }
mongo-round fonctionne Nice. Le moyen le plus propre que j'ai trouvé.
Dites que le numéro est 3.3333333
var round = require('mongo-round');
db.myCollection.aggregate([
{ $project: {
roundAmount: round('$amount', 2) // it will become 3.33
} }
]);
Je ne sais pas pourquoi, mais toutes les réponses (sur cette page) me donnent 12.34
pour 12.345
. J'ai donc écrit ma propre phase de projet:
x = 12.345
{'$project': {
y: {'$divide': [{'$trunc': {'$add': [{'$multiply': ['$x', 100]}, 0.5]}}, 100]},
}},
Cela donne 12.35
.
Voici une arithmétique simple, sans astuce:
round half up
Cependant, cela ne fonctionne pas correctement pour les négatifs (cela suffisait 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, j’obtiens le signe du nombre, encapsule le nombre original par des abs puis multiplie le signe en arrondissant la sortie.
Cette solution arrondit correctement à 2dp:
"rounded" : {
$subtract:[
{$add:['$absolute',0.0049999999999999999]},
{$mod:[{$add:['$absolute',0.0049999999999999999]}, 0.01]}
]
}
Par exemple, il arrondit 1,2499 à 1,25, mais 1,2501 à 1,25.
Remarques:
Il n'y a pas d'opérateur round dans la version actuelle de Aggregation Framework. Vous pouvez essayer cet extrait:
> 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 en raison de problèmes de précision. Le moyen le plus simple dans ce cas est de suivre les conseils de @wiredprairie
et de créer des variables round
dans votre application.
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 une division par zéro si elle est nulle. Pour éviter, $ cond peut être utilisé pour détecter un signe
(Remplacez x par nom_zone et numéro 2 par le nombre décimal souhaité)
Laissez-moi vous dire que c'est dommage que MongoDB manque cette fonction. J'espère qu'ils l'ajouteront bientôt.
Cependant, j'ai mis au point un long pipeline d'agrégation. En admettant, cela n’est peut-être pas efficace, mais cela respecte les règles d'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 dans ma collection les documents suivants nommés t
{ number" : 2.341567 }
{ number" : 2.0012 }
{ number" : 2.0012223 }
Après avoir exécuté les requêtes ci-dessus, j'ai obtenu:
{ "number" : 2.342 }
{ "number" : 2.001 }
{ "number" : 2.001 }
{$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 ceux-ci pourraient aider à arrondir.