集約フレームワークは間違いなく 正しいアプローチ-サーバー上で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は、興味深いエッジケースを捉えました。2月28日以降のうるう年に生まれた人は、計算が1つ減ります。以下は、それを正しく調整する集計です。
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 ]);
「今日」を任意の日付(うるう年かどうか)に設定すると、計算が常に正しく、はるかに簡単になります。