0

Я сохраняю дату рождения (dob) в пользовательской модели, и я хотел бы проверить, является ли это днем ​​рождения пользователей. Как мне это сделать?Mongoose Birthday question

Я начал пробовать, но, очевидно, не удалось

// User Model 
var UserSchema = new Schema({ 
     name: String, 
     email: { type: String, lowercase: true }, 
     role: { 
     type: String, 
     default: 'user' 
     }, 
     hashedPassword: String, 
     dob: { type: Date, default: new Date() }, 
     joinedOn: { type: Date, default: new Date() }, 
     leftOn: { type: Date }, 
     position: { type: String }, 
     provider: String, 
     salt: String, 
     google: {}, 
     github: {} 
    }); 

// Birtdhays 
exports.birthdays = function(req, res, next) { 
    var todayStart = moment().startOf('day'); 
    var todayEnd = moment().endOf('day'); 

    User 
    .find() 
    .where('dob').gt(todayStart).lt(todayEnd) 
    .limit(3) 
    .sort('dob') 
    .select('name dob') 
    .exec(function(err, users){ 
    if(err) return res.send(500, err); 
    res.json(200, users); 
    }); 
}; 
+0

Добавлена ​​модель пользователя – Tino

ответ

1

Предположив, что у вас есть дни рождения, хранящиеся в качестве объекта даты в документах, то они, вероятно, искать что-то вроде этого:

{ 
    "name": "Neil", 
    "dob": ISODate("1971-09-22T00:00:00Z") 
} 

Так что это не только «день», но и весь год, начиная с первоначально выбранного дня рождения. Вероятно, это похоже на логический способ хранения даты рождения, и это полезный объект даты для многих целей. Но как запросить об этом? Любая дата, полученная в текущем году, не будет соответствовать этому значению в пределах диапазона, а другие данные пользователей в тот же день могут возникать в разные годы.

Существует некоторая манипуляция датой JavaScript, которую вы можете сделать, чтобы справиться с этим, но также и некоторые функции MongoDB в структуре агрегации (или, альтернативно, с использованием JavaScript в mapReduce), чтобы получить данные с полезной даты для сопоставления.

Во-первых, вы можете посмотреть на $dayOfyear оператора и код, чтобы получить, что от текущей даты, чтобы использовать в качестве матча:

var now = new Date(); 
var start = new Date(now.getFullYear() + "-01-01"); 
var diff = now - start; 
var oneDay = 1000 * 60 * 60 * 24; 
var dayOfYear = Math.floor(diff/oneDay);  


User.aggregate(
    [ 
     { "$project": { 
      "name": 1, 
      "dob": 1, 
      "dayOfYear": { "$dayOfYear": "$dob" } 
     }}, 
     { "$match": { "dayOfYear": dayOfYear } 
    ], 
    function(err,users) { 
     // work here 
    } 
) 

Теперь, когда все хорошо, или это кажется. Но, конечно, что происходит, когда есть високосный год? Все дни после 28 февраля продвигаются вперед. Вы могли бы объяснить это другими способами, но как насчет просто с помощью «день» и «месяц», чтобы сделать матч на вместо:

var now = new Date(); 
var day = now.getDate();  // funny method name but that's what it is 
var month = now.getMonth() + 1; // numbered 0-11 


User.aggregate(
    [ 
     { "$project": { 
      "name": 1, 
      "dob": 1, 
      "day": "$dayOfMonth", 
      "month": "$month" 
     }}, 
     { "$match": { 
      "day": day, 
      "month": month 
     }} 
    ], 
    function(err,users) { 
     // work here 
    } 
) 

И агрегации операторов для $dayOfMonth и $month помощь там.

Итак, это все лучше, и вы можете запросить «день рождения» сейчас, но есть что-то еще не совсем прав. Пока запрос будет работать, он явно неэффективен. Поскольку то, что вы здесь делаете, выполняет все результаты в коллекции и манипулирует существующими датами, чтобы извлечь части для выполнения соответствия.

В идеале вы не хотите этого делать, и сам запрос запрашивает текущие «дни рождения» простым ходом, чтобы избежать выполнения этой манипуляции, чтобы получить соответствие. Это где моделирование приходит, и где вы должны рассмотреть вопрос о добавлении большего количества данных в документ, где запросы, как это обычны:

{ 
    "name": "Neil", 
    "dob": ISODate("1971-09-22T00:00:00Z"), 
    "day": 22, 
    "month": 9 
} 

Тогда легко запросить по этому вопросу и как «день» и «месяц» поля может также следует индексировать, чтобы еще больше повысить производительность и избежать сканирования всей коллекции для совпадений:

var now = new Date(); 
var day = now.getDate();  // funny method name but that's what it is 
var month = now.getMonth() + 1; // numbered 0-11 

User.find({ "day": day, "month": month },function(err,users) { 
    // work here 
}) 

Так что это соображения. Либо принять манипуляции с каркасом агрегации (или, возможно, mapReduce), либо там, где вы собираетесь часто использовать такой запрос и/или иметь много элементов в коллекции, а затем добавить дополнительные поля в схему документа, которые могут использоваться в качестве точки данных непосредственно в самом запросе.

+0

Благодарим вас за этот долгий и впечатляющий ответ! – Tino

0

я решил ее следующим образом:

Добавлен поле рождения и brithmonth к пользовательской модели и добавлен предварительно сохранить крюк, чтобы держать их в курсе любых изменений в день рождения:

UserSchema 
    .pre('save', function(next) { 

    this.birthmonth = this.dob.getMonth() + 1; 
    this.birthday = this.dob.getDate() 

     next(); 
    }); 

Спасибо Нилу за вдохновение!