在MongoDB中获得下一个生日

时间:2017-09-25 14:46:26

标签: mongodb mongodb-query

我有一个MongoDB集合,其中包含与日期相关的事件。像这样:

{
    "year"  : 1985,
    "month" : 4,
    "day"   : 16,
    "name"  : "J"
},
{
    "year"  : 1985,
    "month" : 9,
    "day"   : 16,
    "name"  : "E"
},
{
    "year"  : 1950,
    "month" : 11,
    "day"   : 11,
    "name"  : "M"
},
{
    "year"  : 1947,
    "month" : 5,
    "day"   : 6,
    "name"  : "D"
}

我想要一个从今天开始下一个生日的MongoDB查询。例如,如果今天是9月25日,我的收藏中的下一个生日将是11月11日,对应于name等于"M"的条目。

我会接受添加Date类型的字段,移除我当前的yearmonthday字段,或其他任何内容,以便有效地实现我的目标目标

我正在使用MongoDB v3.4。

2 个答案:

答案 0 :(得分:0)

好的,我可以告诉你基本上需要两个查询来完成这项工作。我不是Mongo专家,但是这将为您提供您现在所需要的/为您提供正确的研究方向。这两个查询如下:

db.YOUR_COLLECTION.find({ $where: "this.year = input_year & this.month > input_month & this.day > input_day" }).sort({ month: 1, day: 1 }) 
// Gets you back all the entries that have a month greater than the input month.

db.YOUR_COLLECTION.find({ $where: "this.year > input_year }).sort({ year: 1, month: 1, day: 1 }) 
// Gets you back all the entries that start at next year and go forward.  This could probably be optimized a bit at some point.

请注意,在第一种情况下,我先按月份订购,然后按升序订购,按年份订购第二份作为额外参数。我也要归还所有的行。在实践中,您可能希望对某个结果进行限制。无论哪种方式,您都需要运行第一个查询来处理下一个日期未包装到下一年的情况。如果没有返回结果,那么您将需要第二个查询将在下一年开始并向前搜索。如果这不能完全回答你的问题,请告诉我。

答案 1 :(得分:0)

最后我在this answer之后得到了答案。它使用the Aggregation Pipeline

首先,我决定使用Date / ISODate类型来存储生日,而不是单独的年/月/日字段。所以我的test集合会有这些条目:

{
    "birthday" : ISODate("1985-04-16T10:00:00.000Z"),
    "name" : "J"
}

{
    "birthday" : ISODate("1985-09-16T11:00:00.000Z"),
    "name" : "E"
}

{
    "birthday" : ISODate("1950-11-11T11:00:00.000Z"),
    "name" : "M"
}

{
    "birthday" : ISODate("1947-05-06T10:00:00.000Z"),
    "name" : "D"
}

然后,我构建了一些在聚合管道中使用的结构:

  1. pDayOfYear生日的连续年份和今天,使用$dayOfYear operator
  2. 如果pLeapYear处于闰年,则在dayofYear减去1到birthday字段,并计算每个生日与今天之间的差异。
  3. pPast中将365添加到过去的生日以获得positiveDiff字段。
  4. pSortpositiveDiff字段降序排序,因此我们可以列出下一个生日
  5. pFirst只获取第一个结果。
  6. 查询看起来像:

    pDayOfYear = {
        "$project": {
            "birthday": 1,
            "todayDayOfYear": {"$dayOfYear": new ISODate()},
            "leap": {"$or": [
                {"$eq": [0, {"$mod": [{"$year": "$birthday"}, 400]}]},
                {"$and": [
                    {"$eq": [0, {"$mod": [{"$year": "$birthday"}, 4]}]},
                    {"$ne": [0, {"$mod": [{"$year": "$birthday"}, 100]}]}
                ]}
            ]},
            "dayOfYear": {"$dayOfYear": "$birthday"}
        }
    }
    
    pLeapYear = {
        "$project": {
            "birthday": 1,
            "todayDayOfYear": 1,
            "dayOfYear": {
                "$subtract": [
                    "$dayOfYear",
                    {
                        "$cond": [
                            {"$and":
                                 ["$leap",
                                  {"$gt": ["$dayOfYear", 59]}
                                  ]},
                            1,
                            0]
                    }
                ]},
             "diff": { "$subtract": [ "$dayOfYear", "$todayDayOfYear"] }
        }
    }
    
    
    pPast = {
        "$project": {
            "diff": 1,
            "birthday": 1,
            "positiveDiff": {
                "$cond": { 
                    "if": { "$lt": ["$diff", 0 ]},
                        "then": { "$add": ["$diff", 365] },
                    "else":  "$diff"
                    },
                }
        }
    }
    
    pSort = {
            "$sort": {
                "positiveDiff": 1
            }
    }
    
    
    pFirst = {
        "$group": {
            "_id": "first_birthday",
            "first": {
                "$first": "$$ROOT"
            }
        }
    }
    
    db.getCollection('tests').aggregate(pDayOfYear, pLeapYear, pPast, pSort, pFirst);
    

    所以我会得到下一个生日ID,我可以通过ID查询该字段。可以在任何日期更改todayDayOfYear字段的特定日期的下一个生日。

    我愿意接受任何改变,特别是可读性和效率改进。