3

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

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”运算符的一种类型。$setOnInsert仅在findAndModify未找到文档时添加 POST 输入属性,并upsert: true表示如果未找到则应创建文档。这两个选项组合起来似乎创建了一个 findOrCreate 运算符。

编辑

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

编辑

@wdberkeley:

我仍在努力解决这个主要问题:确保 range 的唯一性。我想得越多,似乎“时间片数组”可能是最没有问题的解决方案。例如,假设选择 5 分钟作为最短时间段,平均预订时间为 45 分钟。这将需要我保存 9 个数字(可能是dates)timespan = [0,5,10,15,20,25,30,35,40]:,而不是两个:start=0, end=45。这是平均预订数据的四倍多。我并不是要严厉,但你不认为这是一个问题吗?还是当保存的数据大10 倍100 倍时首先成为问题?我确实意识到这也与实际预订的总数量有关......

4

1 回答 1

3

在 MongoDB 中没有一个简单的方法可以做到这一点。我设计了一种可能对您有用的替代方案。如果您的日期以离散的步骤出现,例如如果这是用于用户按天或按小时预订对象的预订应用程序,那么您可以使用唯一索引和多键索引的组合。例如,假设预订是按天进行的。John Q 保留 10 月 11 日至 10 月 14 日(含)。这就像一年中的第 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酒店预订。

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

于 2014-09-26T15:22:36.067 回答