Well you could just simply use the existing date aggregation operators as opposed to using math to convert to "days" as you presently have:
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 ] }
}}
])
It is "sort of" an approximation on the "days" and "months" without the necessary operations to be "certain" of leap years, but it would get you the result which should be "near enough" for most purposes.
You can even do this without $unwind as long as your MongoDB version is 3.2 or greater:
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 of or any list of expressions in stages like $addFields or $project, and the $map can apply those same "date aggregation operator" expressions against each array element in place of doing $unwind first.
So the main math can really be done in one part of "reducing" the array, and then each total can be adjusted by the general "divisors" for the years, and the "modulo" or "remainder" from any overruns in the 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
}
}
Given the 11th of November 2017