【问题标题】:Get next birthday in MongoDB在 MongoDB 中获得下一个生日
【发布时间】:2017-09-25 14:46:25
【问题描述】:

我有一个 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。

【问题讨论】:

  • 我想你想换行吧?因此,在您的情况下,如果今天是 12 月 12 日,您希望返回名称为“J”的条目,因为它是下个月?
  • @MichaelPlatt 是的。

标签: mongodb mongodb-query


【解决方案1】:

好吧,我可以告诉您基本上需要两个查询才能完成这项工作。我不是 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.

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

【讨论】:

    【解决方案2】:

    终于在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. 如果 birthday 是闰年,则在 pLeapYear 字段中减去 1 到 dayofYear 并计算每个生日与今天之间的差异。
    3. pPast 中,将 365 添加到过去的生日以拥有 positiveDiff 字段。
    4. pSort 中按positiveDiff 字段降序排列结果,这样我们就可以得到下一个生日的列表
    5. pFirst 中只得到第一个结果。

    查询看起来像:

    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 查询该字段。可以在任何日期更改todayDayOfYearfield 的特定日期的下一个生日。

    我愿意接受任何更改,尤其是可读性和效率改进。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-11-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-11-09
      相关资源
      最近更新 更多