我有以下文件:
{
"_id": "538584aad48c6cdc3f07a2b3",
"startTime": "2014-06-12T21:30:00.000Z",
"endTime": "2014-06-12T22:00:00.000Z",
},
{
"_id": "538584b1d48c6cdc3f07a2b4",
"startTime": "2014-06-12T22:30:00.000Z",
"endTime": "2014-06-12T23:00:00.000Z",
}
所有这些都有startTime和endTime值。我需要保持一致性,即集合中没有两个日期跨度重叠。
如果我在以下日期添加以下文档,请说:
db.collection.insert({
"startTime": "2014-06-12T19:30:00.000Z",
"endTime": "2014-06-12T21:00:00.000Z"
});
此日期范围插入应失败,因为它与现有间隔重叠。
我的问题是:
编辑:为防止重复我问那里并开始赏金。我需要使用单个查询进行更新操作,如下所述:How to query and update document by using single query?
答案 0 :(得分:4)
查询并不像它最初看起来那么复杂 - 查询所有文件"重叠"给出的范围是:
db.test.find( { "startTime" : { "$lt" : new_end_time },
"endTime" : { "$gt": new_start_time }
}
)
这将匹配任何文档的开始日期早于我们的结束日期和结束日期大于我们的开始时间。如果您将范围可视化为一条线上的点:
-----|*********|----------|****|-----------|******||********|--- s1 e1 s2 e2 s3 e3s4 e4
sX-eX对代表现有范围。如果您使用新的s5-e5,您可以看到,如果我们消除在结束日期后开始的对(它们不能重叠我们),那么我们将消除在开始之前结束的所有对日期,如果我们什么都没有,那么我们很乐意插入。
这个条件是所有文件的联合,结束日期$lte
我们的开始和那些开始日期$gte
我们的文件包括已经收集的所有文件。我们的查询翻转了这一点,以确保没有文件满足这种情况的相反。
在表演方面,不幸的是您只将日期存储为字符串。如果您将它们存储为时间戳(或任何数字,实际上),您可以使此查询更好地利用索引。事实上,对于性能,您希望在{ "startTime":1, "endTime":1 }
上建立索引。
很容易找到您要插入的范围是否与任何现有范围重叠,但第二个问题是:
如何使用单个查询检查和插入?
由于它们不接受查询(即它们不是有条件的),因此无法使用插入方式进行正确的方法。
但是,您可以使用具有upsert条件的更新。如果条件不匹配任何内容,它可以插入,但如果匹配,它将尝试更新匹配的文档!
因此,您将使用的技巧是使更新成为noop,并仅在upsert时设置所需的字段。从2.4开始,有一个$setOnInsert
运算符要更新。完整的东西看起来像这样:
db.test.update(
{ startTime: { "$lt" : new_end_time }, "endTime" : { "$gt": new_start_time } },
{ $setOnInsert:{ startTime:new_start_time, endTime: new_end_time}},
{upsert:1}
)
WriteResult({
"nMatched" : 0,
"nUpserted" : 1,
"nModified" : 0,
"_id" : ObjectId("538e0f6e7110dddea4383938")
})
db.test.update(
{ startTime:{ "$lt" : new_end_time }, "endTime" : { "$gt": new_start_time } },
{ $setOnInsert:{ startTime:new_start_time, endTime: new_end_time}},
{upsert:1}
)
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 0 })
我做了同样的事情"更新"两次 - 第一次,没有重叠文档,因此更新执行了#34; upsert"你可以在它返回的WriteResult
中看到。
当我第二次运行它时,它会重叠(当然,本身),所以它试图更新匹配的文档,但是注意到没有工作要做。您可以看到返回的nMatched为1但未插入或修改任何内容。
答案 1 :(得分:1)
此查询应返回与新的开始/结束时间值重叠的所有文档。
db.test.find({"$or":[
{"$and":[{"startTime":{"$lte":"new_start_time"}, "endTime":{"$gte":"new_start_time"}}, //new time has an old startTime in the middle
{"startTime":{"$lte":"new_end_time"}, "endTime":{"$lte":"new_end_time"}}]},
{"$and":[{"startTime":{"$gte":"new_start_time"}, "endTime":{"$gte":"new_start_time"}}, //new time sorounds and old time
{"startTime":{"$lte":"new_end_time"}, "endTime":{"$lte":"new_end_time"}}]},
{"$and":[{"startTime":{"$gte":"new_start_time"}, "endTime":{"$gte":"new_start_time"}}, //an old time has the new endTime in the middle
{"startTime":{"$lte":"new_end_time"}, "endTime":{"$gte":"new_end_time"}}]},
{"$and":[{"startTime":{"$lte":"new_start_time"}, "endTime":{"$gte":"new_start_time"}}, //new time is within an old time
{"startTime":{"$lte":"new_end_time"}, "endTime":{"$gte":"new_end_time"}}]}
]})
答案 2 :(得分:0)
您希望同时运行两个查询。这意味着您希望代码中的Synchronous访问此问题可能对您的答案有所帮助