MongoDB
 sql >> Datenbank >  >> NoSQL >> MongoDB

Finden Sie heraus, ob jemand in den nächsten 30 Tagen mit mongo Geburtstag hat

Das Aggregations-Framework ist definitiv der richtige Ansatz - alles, was JS auf dem Server erfordert, ist ein Leistungsproblem, während Aggregationen alle im nativen Code auf dem Server ausgeführt werden.

Obwohl es möglich ist, den Geburtstag in Daten bevorstehender Geburtstage umzuwandeln und dann eine Bereichsabfrage durchzuführen, mache ich es selbst lieber etwas anders.

Die einzige "Voraussetzung ist, den heutigen Tag des Jahres zu berechnen". Es gibt Möglichkeiten, dies in verschiedenen Sprachen zu tun, so dass dies in der Anwendungsschicht erfolgen könnte, bevor die Aggregation aufgerufen und diese Nummer an sie übergeben wird. Ich wollte meinen todayDayOfYear nennen aber mir wurde klar, dass Sie das Aggregations-Framework basierend auf dem heutigen Tag berechnen lassen können, sodass die einzige Variable das heutige Datum sein wird.

var today=new Date();

Ich gehe davon aus, dass ein Dokument, das Name und Geburtstag enthält, entsprechend für Abweichungen angepasst wird

var p1 = { "$project" : {
            "_id" : 0,
            "name" : 1,
            "birthday" : 1,
            "todayDayOfYear" : { "$dayOfYear" : today }, 
            "dayOfYear" : { "$dayOfYear" : "$birthday"}
} };

Rechnen Sie jetzt hoch, wie viele Tage von heute bis zu ihrem nächsten Geburtstag:

var p2 = { "$project" : {
        "name" : 1,
        "birthday" : 1,
        "daysTillBirthday" : { "$subtract" : [
             { "$add" : [ 
                     "$dayOfYear",
             { "$cond" : [{"$lt":["$dayOfYear","$todayDayOfYear"]},365,0 ] }
             ] },
             "$todayDayOfYear"
        ] }
} };

Alle außer denen innerhalb des gewünschten Bereichs ausschließen:

var m = { "$match" : { "daysTillBirthday" : { "$lt" : 31 } } };

Führen Sie nun die Aggregation mit:

aus
db.collection.aggregate( p1, p2, m );

um eine Liste mit Namen, Geburtstagen und Tagen bis zum Geburtstag für alle Glücklichen zurückzuerhalten, deren Geburtstag innerhalb von 30 Tagen liegt.

BEARBEITEN

@ Sean999 hat einen interessanten Randfall erwischt - Menschen, die in einem Schaltjahr nach dem 28. Februar geboren wurden, werden ihre Berechnung um eins verfälschen. Das Folgende ist eine Aggregation, die dies korrekt anpasst:

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 und m bleiben wie oben.

Testeingabe:

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") }

Ausgabe:

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 }

Sie können sehen, dass Personen mit demselben Geburtstag jetzt dieselbe Anzahl von Tagen bis zum Geburtstag haben, unabhängig davon, ob sie in einem Schaltjahr geboren wurden oder nicht. Match Step kann jetzt für den entworfenen Cut-off durchgeführt werden.

BEARBEITEN

Ab Version 3.5.11 gibt es mehrere Datumsmanipulationsausdrücke in der Aggregationspipeline, die das Schreiben erheblich vereinfachen. Insbesondere der Ausdruck $dateFromParts ermöglicht die Konstruktion eines Datums aus verschiedenen Teilen, wodurch diese Aggregation ermöglicht wird:

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 ]);

Sie können "heute" auf ein beliebiges Datum (Schaltjahr oder nicht) einstellen und sehen, dass die Berechnung jetzt immer korrekt und viel einfacher ist.