在子文档中按日期排序,然后按文档排序

时间:2014-06-24 11:53:42

标签: mongodb mongoose mongodb-query aggregation-framework

我有一个简单的“事件”mongo架构。下面有两个示例文档:

活动文件#1

{
  "event_name": "Some nice event",
  "venues": [
    {
      "venue_name": "venue #1",
      "shows": [
        {
          "show_time": "2014-06-18T07:46:02.415Z",
          "capacity": 20
        },
        {
          "show_time": "2014-06-20T07:46:02.415Z",
          "capacity": 40
        }
      ]
    },
    {
      "venue_name": "venue #2",
      "shows": [
        {
          "show_time": "2014-06-17T07:46:02.415Z",
          "capacity": 20
        },
        {
          "show_time": "2014-06-24T07:46:02.415Z",
          "capacity": 40
        }
      ]
    }
  ]
}

活动文件#2

{
  "event_name": "Another nice event",
  "venues": [
    {
      "venue_name": "venue #1",
      "shows": [
        {
          "show_time": "2014-06-19T07:46:02.415Z",
          "capacity": 20
        },
        {
          "show_time": "2014-06-16T07:46:02.415Z",
          "capacity": 40
        }
      ]
    }
  ]
}

我需要查询此事件文档集合,并使用最接近的节目获取事件,并分别与特定时间相关。

因此,例如,如果我必须在 16 Jun 之后或之后发现事件,我应该获得文档#2,然后是文档#1,场地子文档顺序为[场所# 2,场地#1]。

另一方面,如果我希望事件发生在 18 Jun 之上或之后,我应该获得文档#1,[场地#1,场地#2],然后是文档#2。

基本上,我需要能够按嵌套子文档的start_time排序。这种排序应该适用于多个场地子文档。

根据mongo的文档,这似乎不受支持,所以有没有一种方法可以使用聚合来实现这一目标?

或者有没有办法重新设置架构以支持此类查询?

或者mongoDB是否完全是这种情况的错误用例?

1 个答案:

答案 0 :(得分:0)

真的好问题。希望你的日期是真实日期,但词汇形式在这里并不重要。只要您考虑日期,就可以使用以下表格:

db.event.aggregate([
    // Match the "documents" that meet the condition
    { "$match": {
        "venues.shows.show_time": { "$gte": new Date("2014-06-16") }
    }},

    // Unwind the arrays
    { "$unwind": "$venues" },
    { "$unwind": "$venues.shows" },

    // Sort the entries just to float the nearest result
    { "$sort": { "venues.shows.show_time": 1 } },

    // Find the "earliest" for the venue while grouping
    { "$group": {
        "_id": {
            "_id": "$_id",
            "event_name": "$event_name",
            "venue_name": "$venues.venue_name"
        },
        "shows": {
            "$push": "$venues.shows"
        },
        "earliest": { 
            "$min": {
                "$cond": [
                     { "$gte": [ 
                         "$venues.shows.show_time",
                         new Date("2014-06-16")
                     ]},
                     "$venues.shows.show_time",
                     null
                 ]
             }
        }
    }},

    // Sort those because of the order you want
    { "$sort": { "earliest": 1 } },

    // Group back and with the "earliest" document
    { "$group": {
        "_id": "$_id._id",
        "event_name": { "$first": "$_id.event_name" },
        "venues": {
           "$push": {
               "venue_name": "$_id.venue_name",
               "shows": "$shows"
           }
        },
        "earliest": { 
            "$min": {
                "$cond": [
                     { "$gte": [ 
                         "$earliest",
                         new Date("2014-06-16")
                     ]},
                     "$earliest",
                     null
                 ]
             }
        }
    }},

    // Sort by the earliest document
    { "$sort": { "earliest": 1 } },

    // Project the fields
    { "$project": {
        "event_name": 1,
        "venues": 1
    }}
])

因此,如果您对聚合框架有一些经验,那么大部分内容看起来都很合理。如果没有那么就会有一些一般的解释,还有一些" funky"在我们进一步评估时发生的事情。

聚合的第一步是$match,就像任何普通查询一样,然后到$unwind要处理的数组。 "放松"有效的陈述"去标准化"数组中包含的文档本身就是标准文档。

下一个$sort最终会成为"起来"作为最早的"每个事件中的事件"设置"结果会在顶部。

因为有"两个"数组级别,您可以通过$group管道阶段分两个阶段进行分组。

第一个$group"群组" by" document"," event_name"和"场地"。所有节目都被放回原始的数组形式,但此时我们提取了#34; show_time"的$min值。

所采取的价值不仅仅是普通的"最小的"值。在这里,我们使用$cond运算符来确保返回的值必须大于或等于"您最初在查询中请求的日期。这确保了任何早期的" "排序"。

时不考虑这些值

接下来要做的就是$sort关于那个"最早的"日期,以保持"场地"为了。接下来的步骤与上面的步骤相同,但是"分组"这次回到原始文件,然后最后"排序"按顺序排列" show_time"将是最早的"。

显示为输入的日期的结果将是您对第16个所需的结果:

{
    "_id" : ObjectId("53a95263a1923f45a6c2d3dd"),
    "event_name" : "Another nice event",
    "venues" : [
        {
            "venue_name" : "venue #1",
            "shows" : [
                {
                    "show_time" : ISODate("2014-06-16T07:46:02.415Z"),
                    "capacity" : 40
                },
                {
                    "show_time" : ISODate("2014-06-19T07:46:02.415Z"),
                    "capacity" : 20
                }
            ]
        }
    ]
}
{
    "_id" : ObjectId("53a952b5a1923f45a6c2d3de"),
    "event_name" : "Some nice event",
    "venues" : [
        {
            "venue_name" : "venue #2",
            "shows" : [
                {
                    "show_time" : ISODate("2014-06-17T07:46:02.415Z"),
                    "capacity" : 20
                },
                {
                    "show_time" : ISODate("2014-06-24T07:46:02.415Z"),
                    "capacity" : 40
                }
            ]
        },
        {
            "venue_name" : "venue #1",
            "shows" : [
                {
                    "show_time" : ISODate("2014-06-18T07:46:02.415Z"),
                    "capacity" : 20
                },
                {
                    "show_time" : ISODate("2014-06-20T07:46:02.415Z"),
                    "capacity" : 40
                }
            ]
        }
    ]
}

通过将输入更改为18,您也可以获得所需的结果:

{
    "_id" : ObjectId("53a952b5a1923f45a6c2d3de"),
    "event_name" : "Some nice event",
    "venues" : [
        {
            "venue_name" : "venue #1",
            "shows" : [
                {
                    "show_time" : ISODate("2014-06-18T07:46:02.415Z"),
                    "capacity" : 20
                },
                {
                    "show_time" : ISODate("2014-06-20T07:46:02.415Z"),
                    "capacity" : 40
                }
            ]
        },
        {
            "venue_name" : "venue #2",
            "shows" : [
                {
                    "show_time" : ISODate("2014-06-17T07:46:02.415Z"),
                    "capacity" : 20
                },
                {
                    "show_time" : ISODate("2014-06-24T07:46:02.415Z"),
                    "capacity" : 40
                }
            ]
        }
    ]
}
{
    "_id" : ObjectId("53a95263a1923f45a6c2d3dd"),
    "event_name" : "Another nice event",
    "venues" : [
        {
            "venue_name" : "venue #1",
            "shows" : [
                {
                    "show_time" : ISODate("2014-06-16T07:46:02.415Z"),
                    "capacity" : 40
                },
                {
                    "show_time" : ISODate("2014-06-19T07:46:02.415Z"),
                    "capacity" : 20
                }
            ]
        }
    ]
}

此外,如果您想进一步使用此功能,只需添加一个额外的$match阶段,即可过滤掉"事件"发生在查询中请求的日期之前:

db.event.aggregate([
    { "$match": {
        "venues.shows.show_time": { "$gte": new Date("2014-06-18") }
    }},
    { "$unwind": "$venues" },
    { "$unwind": "$venues.shows" },
    { "$match": {
        "venues.shows.show_time": { "$gte": new Date("2014-06-18") }
    }},
    { "$sort": { "venues.shows.show_time": 1 } },
    { "$group": {
        "_id": {
            "_id": "$_id",
            "event_name": "$event_name",
            "venue_name": "$venues.venue_name"
        },
        "shows": {
            "$push": "$venues.shows"
        },
        "earliest": { 
            "$min": {
                "$cond": [
                     { "$gte": [ 
                         "$venues.shows.show_time",
                         new Date("2014-06-18")
                     ]},
                     "$venues.shows.show_time",
                     null
                 ]
             }
        }
    }},
    { "$sort": { "earliest": 1 } },
    { "$group": {
        "_id": "$_id._id",
        "event_name": { "$first": "$_id.event_name" },
        "venues": {
           "$push": {
               "venue_name": "$_id.venue_name",
               "shows": "$shows"
           }
        },
        "earliest": { 
            "$min": {
                "$cond": [
                     { "$gte": [ 
                         "$earliest",
                         new Date("2014-06-18")
                     ]},
                     "$earliest",
                     null
                 ]
             }
        }
    }},
    { "$sort": { "earliest": 1 } },
    { "$project": {
        "event_name": 1,
        "venues": 1
    }}
])

结果:

{
    "_id" : ObjectId("53a952b5a1923f45a6c2d3de"),
    "event_name" : "Some nice event",
    "venues" : [
        {
            "venue_name" : "venue #1",
            "shows" : [
                {
                    "show_time" : ISODate("2014-06-18T07:46:02.415Z"),
                    "capacity" : 20
                },
                {
                    "show_time" : ISODate("2014-06-20T07:46:02.415Z"),
                    "capacity" : 40
                }
            ]
        },
        {
            "venue_name" : "venue #2",
            "shows" : [
                {
                    "show_time" : ISODate("2014-06-24T07:46:02.415Z"),
                    "capacity" : 40
                }
            ]
        }
    ]
}
{
    "_id" : ObjectId("53a95263a1923f45a6c2d3dd"),
    "event_name" : "Another nice event",
    "venues" : [
        {
            "venue_name" : "venue #1",
            "shows" : [
                {
                    "show_time" : ISODate("2014-06-19T07:46:02.415Z"),
                    "capacity" : 20
                }
            ]
        }
    ]
}