【问题标题】:How to query for a nested date range in Mongoid如何在 Mongoid 中查询嵌套的日期范围
【发布时间】:2016-12-01 13:13:36
【问题描述】:

好的,这是我的数据:

"stats" : [
    {
        "campaign_id" : "some_id",
        "log_id" : "some_id",
        "agent" : "some_id",
        "office" : "some_id",
        "hq" : "some_name",
        "seller" : "some_name",
        "status" : "live",
        "phases" : [
            {
                "phase" : "main_phase",
                "banners" : [
                    {
                        "banner_id" : "some_id_same_as_below",
                        "split_var" : "light",
                        "reports" : [
                            {
                                "date" : "2016-11-25",
                                "banner" : "some_id_same_as_above",
                                "cost" : "0.231",
                                "impressions" : 14,
                                "clicks" : 0
                            },
          ...

我需要查询数据库中的所有报告:

"reports" : [
{
    "date" : "2016-11-25",
    "banner" : "some_id_same_as_above",
    "cost" : "0.231",
    "impressions" : 14,
    "clicks" : 0
},

对于某个日期范围内的"date" : "2016-11-25"。对于日期范围,我有这个:

start_month = DateTime.current.beginning_of_month - 1.month
end_month = DateTime.current.end_of_month - 1.month

这给了我上个月的开始和结束,这是正确的。如何搜索所有包含此范围内的报告(统计信息、阶段等中的嵌套值)的文档?

有什么想法吗?

编辑

有人建议更改数据插入数据库的方式,但不幸的是我无法控制数据的插入方式(由第三方服务/API 完成)。

【问题讨论】:

    标签: mongodb mongodb-query mongoid


    【解决方案1】:

    您可以将日期存储为标准 Date 对象,而不是格式化字符串,MongoDB 以 ISODate 格式存储,例如:

    db.collection.insert({date: new Date()});
    

    会有一个像这样的字段:

    { "date" : ISODate("2016-11-15T15:50:15.167Z") }
    

    然后可以按日期范围查询(如果需要在两个范围之间查询,请使用 $and 运算符,否则第二条语句可能会覆盖第一条)

    如:

    // Return all documents in collection with a date between 11-1-2016 and 12-1-2016
    db.collection.find({
        $and: [
            { date: { $gte: ISODate("2016-11-01T00:00:00.000Z") } }, 
            { date: { $lt: ISODate("2016-12-01T00:00:00.000Z") } }
        ]
    })
    

    编辑:如果您无法修改您的收藏,您可以进行正则表达式样式搜索...

    例如:

    db.collection.find({
        "stats.phases.banners.reports.date": /2016-11/
    });
    

    将返回 2016 年 11 月的所有文档,因为它匹配包含“2016-11”的所有字符串

    再次编辑:

    这是一个使用聚合框架以您上面提到的格式返回文档的解决方案,即

    {
        "reports" : { 
            "date" : "2016-11-23", 
            "banner" : ObjectId("58404a9450b5412e92ebbb97"), 
            "cost" : "0.231", 
            "impressions" : 14, 
            "clicks" : 0 
        } 
    },
    { 
        "reports" : { 
            "date" : "2016-11-25", 
            "banner" : ObjectId("58404a9450b5412e92ebbb97"), 
            "cost" : "0.231", 
            "impressions" : 14, 
            "clicks" : 0 
        } 
    }
    

    请注意,由于您的数组结构嵌套严重...

    db.collection.aggregate([
        { $unwind: "$stats" },
        { $unwind: "$stats.phases" },
        { $unwind: "$stats.phases.banners" },
        { $unwind: "$stats.phases.banners.reports" },
        { $match: { "stats.phases.banners.reports.date": /2016-11/ } },
        { $project: { _id: 0, reports: "$stats.phases.banners.reports" } }
    ])
    

    【讨论】:

      【解决方案2】:

      您需要将 date 字段存储为 ISODate 对象,然后您可以使用比较运算符,如 $lt$lte$gt$gte 等。 以下是插入数据的方法:

      db.test.insert({
        "stats": [
          {
            "campaign_id": "some_id",
            "log_id": "some_id",
            "agent": "some_id",
            "office": "some_id",
            "hq": "some_name",
            "seller": "some_name",
            "status": "live",
            "phases": [
              {
                "phase": "main_phase",
                "banners": [
                  {
                    "banner_id": "some_id_same_as_below",
                    "split_var": "light",
                    "reports": [
                      {
                        "date": ISODate("2016-11-25T00:00:00.0Z"),
                        "banner": "some_id_same_as_above",
                        "cost": "0.231",
                        "impressions": 14,
                        "clicks": 0
                      }
                    ]
                  }
                ]
              }
            ]
          }
        ]
      })
      

      以下是查找 stats.phases.banners.reports.date 在 2016 年 11 月 25 日至 2016 年 12 月 15 日之间的文档的查询。

      db.test.find({"stats.phases.banners.reports.date": {$lt: ISODate("2016-11-25T00:00:00.0Z"), $gt: ISODate("2016-12-25T00:00:00.0Z")}})
      

      【讨论】:

      • 你确定这是一个有效的例子吗?请解释一下这个答案是如何工作的?
      猜你喜欢
      • 2011-08-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-07-13
      • 1970-01-01
      • 1970-01-01
      • 2020-09-28
      相关资源
      最近更新 更多