Well, you can just use the existing date aggregation operator , rather than use the math to convert to "days" like you do now:
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 ] } }} ])
This is a “kind of” approximation to the “days” and “months” without the need for “certain” leap years, but it will bring you a result that should be “close enough” for most purposes.
You can even do this without $unwind if your version of MongoDB is 3.2 or higher:
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 ] } } } } } } }} ])
This is because from MongoDB 3.4 you can use $sum directly with an array or any list of expressions in stages like $addFields or $project , and $map can use the same expressions of the “date aggregation” operator for each element of the array instead of $unwind .
Thus, the basic mathematics can really be done in one part of the “decrease” of the array, and then each amount can be adjusted using the common “dividers” over the years, and “modulo” or “remainder” from any overruns in months and days.
Essentially returns:
{ "_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 } }
Considering November 11, 2017