J'ai la requête suivante:
db.getCollection('user').aggregate([
{$unwind: "$education"},
{$project: {
duration: {"$divide":[{$subtract: ['$education.to', '$education.from'] }, 1000 * 60 * 60 * 24 * 365]}
}},
{$group: {
_id: '$_id',
"duration": {$sum: '$duration'}
}}]
])
Le résultat de la requête ci-dessus est:
{
"_id" : ObjectId("59fabb20d7905ef056f55ac1"),
"duration" : 2.34794520547945
}
/* 2 */
{
"_id" : ObjectId("59fab630203f02f035301fc3"),
"duration" : 2.51232876712329
}
Mais ce que je veux faire, c'est obtenir sa durée au format year
+ month
+ day
, quelque chose comme: 2 y, 3 m, 20 d
. Un autre point, si un cours est donné dans le champ to
est nul, et un autre champ isGoingOn: true
, donc ici, je devrais calculer la durée en utilisant la date actuelle au lieu du champ to
. Et l'utilisateur a un tableau de sous-documents de cours
education: [
{
"courseName": "Java",
"from" : ISODate("2010-12-08T00:00:00.000Z"),
"to" : ISODate("2011-05-31T00:00:00.000Z"),
"isGoingOn": false
},
{
"courseName": "PHP",
"from" : ISODate("2013-12-08T00:00:00.000Z"),
"to" : ISODate("2015-05-31T00:00:00.000Z"),
"isGoingOn": false
},
{
"courseName": "Mysql",
"from" : ISODate("2017-02-08T00:00:00.000Z"),
"to" : null,
"isGoingOn": true
}
]
Un autre point est le suivant: cette date peut ne pas être continue dans un sous-document à l’autre sous-document. Un utilisateur peut suivre un cours pendant 1 an puis, au bout de deux ans, il commence son prochain cours pendant 1 an et 3 mois (cela signifie que cet utilisateur a au total une durée de cours de 2 ans et de 3 mois). . Ce que je veux, c’est d’obtenir la différence de date de chaque sous-document dans le tableau educations, et de les additionner. Supposons que dans mon exemple de données Java
durée du cours soit de 6 mois et 22 jours, PHP
durée du cours est de 1 an et 6 mois et 22 jours, et le dernier est du 8 février 2017 à maintenant, et ça continue, donc mon la durée de l'éducation est la somme de ces intervalles.
S'il vous plaît essayez cette agrégation pour obtenir la différence de date en jours, mois et années, ajouté le calcul de plusieurs étapes $addFields
et réduire les différences à la date, la plage de mois sans dépassement de valeur, et l'hypothèse est ici 1 mois = 30 jours
pipeline
db.edu.aggregate(
[
{
$addFields : {
trainingPeriod : {
$map : {
input : "$education",
as : "t",
in : {
year: {$subtract: [{$year : {$ifNull : ["$$t.to", new Date()]}}, {$year : "$$t.from"}]},
month: {$subtract: [{$month : {$ifNull : ["$$t.to", new Date()]}}, {$month : "$$t.from"}]},
dayOfMonth: {$subtract: [{$dayOfMonth : {$ifNull : ["$$t.to", new Date()]}}, {$dayOfMonth : "$$t.from"}]}
}
}
}
}
},
{
$addFields : {
trainingPeriod : {
$map : {
input : "$trainingPeriod",
as : "d",
in : {
year: "$$d.year",
month: {$cond : [{$lt : ["$$d.dayOfMonth", 0]}, {$subtract : ["$$d.month", 1]}, "$$d.month" ]},
day: {$cond : [{$lt : ["$$d.dayOfMonth", 0]}, {$add : [30, "$$d.dayOfMonth"]}, "$$d.dayOfMonth" ]}
}
}
}
}
},
{
$addFields : {
trainingPeriod : {
$map : {
input : "$trainingPeriod",
as : "d",
in : {
year: {$cond : [{$lt : ["$$d.month", 0]}, {$subtract : ["$$d.year", 1]}, "$$d.year" ]},
month: {$cond : [{$lt : ["$$d.month", 0]}, {$add : [12, "$$d.month"]}, "$$d.month" ]},
day: "$$d.day"
}
}
}
}
},
{
$addFields : {
total : {
$reduce : {
input : "$trainingPeriod",
initialValue : {year : 0, month : 0, day : 0},
in : {
year: {$add : ["$$this.year", "$$value.year"]},
month: {$add : ["$$this.month", "$$value.month"]},
day: {$add : ["$$this.day", "$$value.day"]}
}
}
}
}
},
{
$addFields : {
total : {
year : "$total.year",
month : {$add : ["$total.month", {$floor : {$divide : ["$total.day", 30]}}]},
day : {$mod : ["$total.day", 30]}
}
}
},
{
$addFields : {
total : {
year : {$add : ["$total.year", {$floor : {$divide : ["$total.month", 12]}}]},
month : {$mod : ["$total.month", 12]},
day : "$total.day"
}
}
}
]
).pretty()
résultat
{
"_id" : ObjectId("5a895d4721cbd77dfe857f95"),
"education" : [
{
"courseName" : "Java",
"from" : ISODate("2010-12-08T00:00:00Z"),
"to" : ISODate("2011-05-31T00:00:00Z"),
"isGoingOn" : false
},
{
"courseName" : "PHP",
"from" : ISODate("2013-12-08T00:00:00Z"),
"to" : ISODate("2015-05-31T00:00:00Z"),
"isGoingOn" : false
},
{
"courseName" : "Mysql",
"from" : ISODate("2017-02-08T00:00:00Z"),
"to" : null,
"isGoingOn" : true
}
],
"trainingPeriod" : [
{
"year" : 0,
"month" : 5,
"day" : 23
},
{
"year" : 1,
"month" : 5,
"day" : 23
},
{
"year" : 1,
"month" : 0,
"day" : 10
}
],
"total" : {
"year" : 2,
"month" : 11,
"day" : 26
}
}
>
Vous pouvez simplifier votre code en utilisant le traitement côté client avec moment js
library.
Tous les calculs de date et heure sont gérés par la bibliothèque moment js. Utilisez duration
pour calculer le temps réduit diff
Utilisez reduction pour ajouter la diff de temps sur tous les éléments du tableau, suivie de la durée du moment pour afficher le temps en années/mois/jours.
Il résout deux problèmes:
Par exemple:
var education = [
{
"courseName": "Java",
"from" : new Date("2010-12-08T00:00:00.000Z"),
"to" : new Date("2011-05-31T00:00:00.000Z"),
"isGoingOn": false
},
{
"courseName": "PHP",
"from" : new Date("2013-12-08T00:00:00.000Z"),
"to" : new Date("2015-05-31T00:00:00.000Z"),
"isGoingOn": false
},
{
"courseName": "Mysql",
"from" : new Date("2017-02-08T00:00:00.000Z"),
"to" : null,
"isGoingOn": true
}
];
var reducedDiff = education.reduce(function(prevVal, elem) {
if(elem.isGoingOn) elem.to = new Date();
var diffDuration = moment(elem.to).diff(moment(elem.from));
return prevVal + diffDuration;
}, 0);
var duration = moment.duration(reducedDiff);
alert(duration.years() +" y, " + duration.months() + " m, " + duration.days() + " d " );
var durationstr = duration.years() +" y, " + duration.months() + " m, " + duration.days() + " d ";
Intégration MongoDb:
var reducedDiff = db.getCollection('user').find({},{education:1}).reduce(function(...
Eh bien, vous pouvez simplement utiliser l'opérateur de regroupement date existant s au lieu d'utiliser les mathématiques pour convertir en "jours" comme vous l'avez actuellement:
db.getCollection('user').aggregate([
{ "$unwind": "$education" },
{ "$group": {
"_id": "$_id",
"years": {
"$sum": {
"$subtract": [
{ "$subtract": [
{ "$year": { "$ifNull": [ "$education.to", new Date() ] } },
{ "$year": "$education.from" }
]},
{ "$cond": {
"if": {
"$gt": [
{ "$month": { "$ifNull": [ "$education.to", new Date() ] } },
{ "$month": "$education.from" }
]
},
"then": 0,
"else": 1
}}
]
}
},
"months": {
"$sum": {
"$add": [
{ "$subtract": [
{ "$month": { "$ifNull": [ "$education.to", new Date() ] } },
{ "$month": "$education.from" }
]},
{ "$cond": {
"if": {
"$gt": [
{ "$month": { "$ifNull": ["$education.to", new Date() ] } },
{ "$month": "$education.from" }
]
},
"then": 0,
"else": 12
}}
]
}
},
"days": {
"$sum": {
"$add": [
{ "$subtract": [
{ "$dayOfYear": { "$ifNull": [ "$education.to", new Date() ] } },
{ "$dayOfYear": "$education.from" }
]},
{ "$cond": {
"if": {
"$gt": [
{ "$month": { "$ifNull": [ "$education.to", new Date() ] } },
{ "$month": "$education.from" }
]
},
"then": 0,
"else": 365
}}
]
}
}
}},
{ "$project": {
"years": {
"$add": [
"$years",
{ "$add": [
{ "$floor": { "$divide": [ "$months", 12 ] } },
{ "$floor": { "$divide": [ "$days", 365 ] } }
]}
]
},
"months": {
"$mod": [
{ "$add": [
"$months",
{ "$floor": {
"$multiply": [
{ "$divide": [ "$days", 365 ] },
12
]
}}
]},
12
]
},
"days": { "$mod": [ "$days", 365 ] }
}}
])
C'est "en quelque sorte" une approximation des "jours" et des "mois" sans les opérations nécessaires pour être "certaines" années bissextiles, mais vous obtiendrez un résultat qui devrait être "assez proche" pour la plupart des objectifs.
Vous pouvez même le faire sans $unwind
tant que votre version de MongoDB est 3.2 ou supérieure:
db.getCollection('user').aggregate([
{ "$addFields": {
"duration": {
"$let": {
"vars": {
"edu": {
"$map": {
"input": "$education",
"as": "e",
"in": {
"$let": {
"vars": { "toDate": { "$ifNull": ["$$e.to", new Date()] } },
"in": {
"years": {
"$subtract": [
{ "$subtract": [
{ "$year": "$$toDate" },
{ "$year": "$$e.from" }
]},
{ "$cond": {
"if": { "$gt": [{ "$month": "$$toDate" },{ "$month": "$$e.from" }] },
"then": 0,
"else": 1
}}
]
},
"months": {
"$add": [
{ "$subtract": [
{ "$ifNull": [{ "$month": "$$toDate" }, new Date() ] },
{ "$month": "$$e.from" }
]},
{ "$cond": {
"if": { "$gt": [{ "$month": "$$toDate" },{ "$month": "$$e.from" }] },
"then": 0,
"else": 12
}}
]
},
"days": {
"$add": [
{ "$subtract": [
{ "$ifNull": [{ "$dayOfYear": "$$toDate" }, new Date() ] },
{ "$dayOfYear": "$$e.from" }
]},
{ "$cond": {
"if": { "$gt": [{ "$month": "$$toDate" },{ "$month": "$$e.from" }] },
"then": 0,
"else": 365
}}
]
}
}
}
}
}
}
},
"in": {
"$let": {
"vars": {
"years": { "$sum": "$$edu.years" },
"months": { "$sum": "$$edu.months" },
"days": { "$sum": "$$edu.days" }
},
"in": {
"years": {
"$add": [
"$$years",
{ "$add": [
{ "$floor": { "$divide": [ "$$months", 12 ] } },
{ "$floor": { "$divide": [ "$$days", 365 ] } }
]}
]
},
"months": {
"$mod": [
{ "$add": [
"$$months",
{ "$floor": {
"$multiply": [
{ "$divide": [ "$$days", 365 ] },
12
]
}}
]},
12
]
},
"days": { "$mod": [ "$$days", 365 ] }
}
}
}
}
}
}}
])
En effet, à partir de MongoDB 3.4, vous pouvez utiliser $sum
directement avec un tableau d’expressions ou toute liste d’expressions par étapes comme $addFields
ou $project
, et le $map
peut appliquer ces mêmes expressions "opérateur d'agrégation de date" à chaque élément du tableau au lieu de faire $unwind
en premier.
Ainsi, les calculs principaux peuvent vraiment être faits dans une partie de la "réduction" du tableau, puis chaque total peut être ajusté par les "diviseurs" généraux pour les années, et le "modulo" ou le "reste" de tout dépassement dans les mois et jours.
Retourne essentiellement:
{
"_id" : ObjectId("5a07688e98e4471d8aa87940"),
"education" : [
{
"courseName" : "Java",
"from" : ISODate("2010-12-08T00:00:00.000Z"),
"to" : ISODate("2011-05-31T00:00:00.000Z"),
"isGoingOn" : false
},
{
"courseName" : "PHP",
"from" : ISODate("2013-12-08T00:00:00.000Z"),
"to" : ISODate("2015-05-31T00:00:00.000Z"),
"isGoingOn" : false
},
{
"courseName" : "Mysql",
"from" : ISODate("2017-02-08T00:00:00.000Z"),
"to" : null,
"isGoingOn" : true
}
],
"duration" : {
"years" : 3.0,
"months" : 3.0,
"days" : 259.0
}
}
Le 11 novembre 2017