MongoDb对日期范围的唯一约束

MongoDb unique constraints on a Date range

本文关键字:唯一 约束 范围 日期 MongoDb      更新时间:2023-09-26

Im使用MongoDb和Mongoskin。在集合中,我正在保存事件。在其他字段中,这些事件有一个开始和一个结束,在Mongodb中保存为Dates

events { 
  start: "Date1",
  end: "Date2",
  ...
}

在这个集合中插入新文档时,我需要一个约束,禁止插入开始日期和结束日期与已经创建的事件重叠的文档。简而言之,我不希望任何事件都有相同的时间跨度。

问题:有没有一种方法可以通过具有某种唯一索引的MongoDb来处理这种约束?我想不是,但如果我错了,请纠正我!

如果不是:

问题在插入新事件之前,我是否必须通过代码检查可能的重叠?我是否需要设置某种写锁,这样在我检查重叠和插入自己的事件之间,其他用户就不能挤进事件了?这是如何在MongoDb中完成的?

编辑

这是我迄今为止想出的最好的方法,实际上看起来效果很好。

var input = getPostInput();
var query = {$and: [
  {start: {$lte: input.end}},
  {end: {$gte: input.start}}
]};
db.events.findAndModify(query, {}, {$setOnInsert: input}, {new: true, upsert: true}, callback)

它使用findAndModify作为一种类型的"findOrCreate"运算符。只有当findAndModify找不到文档时,$setOnInsert才会添加POST输入属性,而upsert: true表示,如果找不到,它应该创建一个文档。这两个选项的组合似乎创建了一个findOrCreate运算符。

编辑

更新(PUT)事件时会出现问题。我不能重用上面的代码,因为它依赖于upsert$setOnInsert

编辑

@wdberkeley:

我仍在努力解决这个主要问题:确保范围内的唯一性。我想得越多,似乎"时间片数组"可能是最没有问题的解决方案。例如,假设选择5分钟作为最小时间段,平均预订时间为45分钟。这将需要我保存9个数字(可能是date s):timespan = [0,5,10,15,20,25,30,35,40],而不是两个:start=0, end=45。这是平均预订保存数据的四倍多。我无意苛刻,但你不认为这是个问题吗?或者,当保存的数据是10倍大还是100倍大时,它首先会成为问题?我确实意识到,这也是相对于实际预订的总数量。。。

在MongoDB中没有一种非常简单的方法可以做到这一点。我想出了一个对你有用的替代方案。如果你的日期是离散的,比如这是一个预订应用程序,用户按天或小时预订对象,那么你可以使用唯一索引和多关键字索引的组合。例如,假设预订是按天进行的。John Q保留10月11日至10月14日(含10月11至10月)。这有点像一年中的第281天到第284天-让我们假设这正是哪几天。将保留字段保存为保留的天数数组

> db.reservations.insert({ "span" : [ 281, 282, 283, 284 ] })

span字段上设置一个唯一索引。

> db.reservations.ensureIndex({ "span" : 1}, { "unique" : 1 })

现在你不能插入一个在其跨度内有任何一天的文档:

> db.reservations.insert({ "span" : [ 279, 280, 281, 282 ] })
// unique key error

考虑到年份,这可能会对您进行一些额外的调整,或者它可能是复合唯一索引的一部分,以使时间跨度具有唯一性,例如酒店预订的room_id

另一种方法是协调客户端的检查。如果你有多个客户端根本不相互交谈,我想最好的方法是在数据库中共享一个"锁":findAndModifylock集合中的一个文档,用于检查和获取锁。一旦客户端通过更改文档上的字段获得锁定,它就可以检查与查询的重叠,然后在一切正常的情况下插入,然后通过再次更改锁定文档上的标志来释放锁定。