Рамката за агрегиране е определено правилният подход – всичко, което изисква JS на сървъра, е проблем с производителността, докато всички агрегати се изпълняват в сървъра в собствен код.
Въпреки че е възможно да трансформирате рождения ден в дати на предстоящи рождени дни и след това да направите заявка за диапазон, аз предпочитам да го направя по малко по-различен начин.
Единственото "необходимо условие е да се изчисли днешният ден от годината". Има начини да направите това на различни езици, така че това може да се направи в слоя на приложението, преди да се извика агрегирането, като му се предаде този номер. Щях да се обадя на моя todayDayOfYear
но разбрах, че можете да оставите рамката за агрегиране да го разбере въз основа на днешния ден, така че единствената променлива ще бъде днешната дата.
var today=new Date();
Предполагам, че документ, който включва име и рожден ден, коригирайте подходящо за варианти
var p1 = { "$project" : {
"_id" : 0,
"name" : 1,
"birthday" : 1,
"todayDayOfYear" : { "$dayOfYear" : today },
"dayOfYear" : { "$dayOfYear" : "$birthday"}
} };
Сега прогнозирайте колко дни от днес до следващия им рожден ден:
var p2 = { "$project" : {
"name" : 1,
"birthday" : 1,
"daysTillBirthday" : { "$subtract" : [
{ "$add" : [
"$dayOfYear",
{ "$cond" : [{"$lt":["$dayOfYear","$todayDayOfYear"]},365,0 ] }
] },
"$todayDayOfYear"
] }
} };
Изключете всички освен тези в желания диапазон:
var m = { "$match" : { "daysTillBirthday" : { "$lt" : 31 } } };
Сега стартирайте агрегирането с:
db.collection.aggregate( p1, p2, m );
за да получите обратно списък с имена, рождени дни и дни до рождения ден за всички щастливци, чийто рожден ден е в рамките на 30 дни.
РЕДАКТИРАНЕ
@Sean999 улови интересен крайен случай - хората, които са родени през високосна година след 28 февруари, ще имат изчисленията с единица. Следното е агрегиране, което се настройва правилно за това:
var p1 = { "$project" : {
"_id" : 0,
"name" : 1,
"birthday" : 1,
"todayDayOfYear" : { "$dayOfYear" : ISODate("2014-03-09T12:30:51.515Z") },
"leap" : { "$or" : [
{ "$eq" : [ 0, { "$mod" : [ { "$year" : "$birthday" }, 400 ] } ] },
{ "$and" : [
{ "$eq" : [ 0, { "$mod" : [ { "$year" : "$birthday" }, 4 ] } ] },
{ "$ne" : [ 0, { "$mod" : [ { "$year" : "$birthday" }, 100 ] } ] } ] } ] },
"dayOfYear" : { "$dayOfYear" : "$birthday" } } };
var p1p = { "$project" : {
"name" : 1,
"birthday" : 1,
"todayDayOfYear" : 1,
"dayOfYear" : { "$subtract" : [
"$dayOfYear",
{ "$cond" : [ { "$and" : [ "$leap", { "$gt" : [ "$dayOfYear", 59 ] } ] }, 1, 0 ] } ] }
}
}
p2
и m
останете същите като по-горе.
Тестов вход:
db.birthdays.find({},{name:1,birthday:1,_id:0})
{ "name" : "Ally", "birthday" : ISODate("1975-06-12T00:00:00Z") }
{ "name" : "Ben", "birthday" : ISODate("1968-04-03T00:00:00Z") }
{ "name" : "Mark", "birthday" : ISODate("1949-12-23T00:00:00Z") }
{ "name" : "Paul", "birthday" : ISODate("2014-03-04T15:59:05.374Z") }
{ "name" : "Paul", "birthday" : ISODate("2011-02-07T00:00:00Z") }
{ "name" : "Sean", "birthday" : ISODate("2004-01-31T00:00:00Z") }
{ "name" : "Tim", "birthday" : ISODate("2008-02-28T00:00:00Z") }
{ "name" : "Sandy", "birthday" : ISODate("2005-01-31T00:00:00Z") }
{ "name" : "Toni", "birthday" : ISODate("2009-02-28T00:00:00Z") }
{ "name" : "Sam", "birthday" : ISODate("2005-03-31T00:00:00Z") }
{ "name" : "Max", "birthday" : ISODate("2004-03-31T00:00:00Z") }
{ "name" : "Jen", "birthday" : ISODate("1971-04-03T00:00:00Z") }
{ "name" : "Ellen", "birthday" : ISODate("1996-02-28T00:00:00Z") }
{ "name" : "Fanny", "birthday" : ISODate("1996-02-29T00:00:00Z") }
{ "name" : "Gene", "birthday" : ISODate("1996-03-01T00:00:00Z") }
{ "name" : "Edgar", "birthday" : ISODate("1997-02-28T00:00:00Z") }
{ "name" : "George", "birthday" : ISODate("1997-03-01T00:00:00Z") }
Изход:
db.birthdays.aggregate( p1, p1p, p2, {$sort:{daysTillBirthday:1}});
{ "name" : "Sam", "birthday" : ISODate("2005-03-31T00:00:00Z"), "daysTillBirthday" : 22 }
{ "name" : "Max", "birthday" : ISODate("2004-03-31T00:00:00Z"), "daysTillBirthday" : 22 }
{ "name" : "Ben", "birthday" : ISODate("1968-04-03T00:00:00Z"), "daysTillBirthday" : 25 }
{ "name" : "Jen", "birthday" : ISODate("1971-04-03T00:00:00Z"), "daysTillBirthday" : 25 }
{ "name" : "Ally", "birthday" : ISODate("1975-06-12T00:00:00Z"), "daysTillBirthday" : 95 }
{ "name" : "Mark", "birthday" : ISODate("1949-12-23T00:00:00Z"), "daysTillBirthday" : 289 }
{ "name" : "Sean", "birthday" : ISODate("2004-01-31T00:00:00Z"), "daysTillBirthday" : 328 }
{ "name" : "Sandy", "birthday" : ISODate("2005-01-31T00:00:00Z"), "daysTillBirthday" : 328 }
{ "name" : "Paul", "birthday" : ISODate("2011-02-07T00:00:00Z"), "daysTillBirthday" : 335 }
{ "name" : "Tim", "birthday" : ISODate("2008-02-28T00:00:00Z"), "daysTillBirthday" : 356 }
{ "name" : "Toni", "birthday" : ISODate("2009-02-28T00:00:00Z"), "daysTillBirthday" : 356 }
{ "name" : "Ellen", "birthday" : ISODate("1996-02-28T00:00:00Z"), "daysTillBirthday" : 356 }
{ "name" : "Fanny", "birthday" : ISODate("1996-02-29T00:00:00Z"), "daysTillBirthday" : 356 }
{ "name" : "Edgar", "birthday" : ISODate("1997-02-28T00:00:00Z"), "daysTillBirthday" : 356 }
{ "name" : "Gene", "birthday" : ISODate("1996-03-01T00:00:00Z"), "daysTillBirthday" : 357 }
{ "name" : "George", "birthday" : ISODate("1997-03-01T00:00:00Z"), "daysTillBirthday" : 357 }
{ "name" : "Paul", "birthday" : ISODate("2014-03-04T15:59:05.374Z"), "daysTillBirthday" : 360 }
Можете да видите, че хората със същия рожден ден вече имат същия брой дни до рождения ден, независимо дали са родени през високосна година или не. Сега може да се изпълни стъпката за съвпадение за проектираното прекъсване.
РЕДАКТИРАНЕ
От версия 3.5.11 има няколко израза за манипулиране на дата в конвейера за агрегиране, които правят това значително по-лесно за писане. По-специално, изразът $dateFromParts позволява конструиране на дата от различни части, позволявайки това агрегиране:
var today = new Date();
var a1 = {$addFields:{
today:{$dateFromParts:{year:{$year:today},month:{$month:today},day:{$dayOfMonth:today}}},
birthdayThisYear:{$dateFromParts:{year:{$year:today}, month:{$month:"$birthday"}, day:{$dayOfMonth:"$birthday"}}},
birthdayNextYear:{$dateFromParts:{year:{$add:[1,{$year:today}]}, month:{$month:"$birthday"}, day:{$dayOfMonth:"$birthday"}}}
}};
var a2 = {$addFields:{
nextBirthday:{$cond:[ {$gte:[ "$birthdayThisYear", "$today"]}, "$birthdayThisYear", "$birthdayNextYear"]}
}};
var p1 = {$project:{
name:1,
birthday:1,
daysTillNextBirthday:{$divide:[
{$subtract:["$nextBirthday", "$today"]},
24*60*60*1000 /* milliseconds in a day */
]},
_id:0
}};
var s1 = {$sort:{daysTillNextBirthday:1}};
db.birthdays.aggregate([ a1, a2, p1, s1 ]);
Можете да зададете "днес" на всяка дата (високосна година или не) и да видите, че изчислението вече винаги е правилно и много по-лесно.