猫鼬生日查询

时间:2014-12-29 17:47:14

标签: node.js mongodb mongoose mongodb-query aggregation-framework

我将出生日期(dob)存储在用户模型中,我想检查它是否是今天的用户生日。我该如何查询?

我已经开始尝试了,但显然失败了

// 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);
  });
};

2 个答案:

答案 0 :(得分:2)

假设您在文档中将生日存储为日期对象,那么它们可能看起来像这样:

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

所以它不仅仅是“日”,而是整个出生日的全年。这似乎是存储出生日期的合理方式,对于许多目的而言,它是一个有用的日期对象。但是如何查询呢?从当前年度派生的任何日期都不会与某个范围内的该值匹配,并且同一天的其他用户数据可能会在不同年份发生。

为了解决这个问题,您可以执行一些JavaScript日期操作,以及聚合框架中的MongoDB的一些功能(或者在mapReduce中使用JavaScript),以获取有用的日期之外的数据匹配。

首先,您可以查看$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
    }
)

现在一切都很好,或者似乎。但当然,有一个闰年会发生什么? 2月28日之后的所有日子都向前移动一个。您可以通过其他方式解释这一点,但是如何使用“day”和“month”来代替匹配:

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
}

然后很容易对此进行查询,因为“day”和“month”字段也可以编入索引以进一步提高性能并避免扫描整个集合以进行匹配:

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)的操作,要么经常使用这样的查询和/或在集合中有许多项,然后在文档模式中添加可用作数据点的其他字段直接在查询本身。

答案 1 :(得分:0)

我通过以下方式解决了这个问题:

在用户模型中添加了birthday和brithmonth字段,并添加了一个预保存挂钩,以便在生日的任何更改中保持更新:

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

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

      next();
  });

感谢Neil的灵感!