我有一个简单的“事件”mongo架构。下面有两个示例文档:
{
"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
}
]
}
]
}
{
"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是否完全是这种情况的错误用例?
答案 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
}
]
}
]
}