MongoDB在集合中的文档中实施复杂约束

时间:2017-04-21 21:48:42

标签: node.js mongodb atomicity

假设我在名为Users的集合中有文档,如下所示:

{
  name: 'vic', <-- unique index
  ownedItems: ['paperclip-1252', 'stapler-1337']
}

ownedItems是一些其他Items集合中唯一索引标识符的数组。现在,假设我想向ownItems数组添加一个元素,但是我想强制执行以下规则:一个人不能以导致包含同一个项目的多个User文档的方式更新用户的ownedItems数组其ownedItems数组中的标识符。

也就是说,我想执行:

db.Users.updateOne(
  { name: 'vic'}, 
  { $set: { 
      ownedItems: [
        'paperclip-1252', 
        'stapler-1337', 
        'notebook-42'
      ]
    } 
  })

...只有在其他用户文档没有包含'notebook-42'(或'paperclip-1252'或'stapler-1337')的ownedItems数组时才允许成功。

我可以进行查询检查,除了'vic'之外的任何用户都有建议的ownedItems数组中的任何项目,如下所示:

db.Users.find({
  $and: [
    { 
      ownedItems: { 
        $in: ['paperclip-1252', 'stapler-1337','notebook-42'] 
      } 
    },
    {
      name: { $ne: 'vic' }
    } 
  ]  
})

...如果没有返回结果,则只进行更新。但是,在我得到答案之间的时间和我执行更新时,答案没有改变(例如因为有人将'notebook-42'插入其ownItems中)?

如何以原子方式执行该查询,然后根据结果更新文档?虽然我已经使用MongoDB CLI语法对此进行了描述,但我将使用mongodb包在NodeJS应用程序中实现此逻辑,以防万一。

1 个答案:

答案 0 :(得分:1)

我认为唯一的索引是可以在Mongodb中应用跨文档约束的唯一方法,在这种情况下恰好可以使用。

ownedItems上创建唯一索引。 Mongo将索引每个设置值而不是集合本身。

要允许重复的空数组(ab),请在填充数组之前使用null和空记录的排序顺序。

db.Users.createIndex({ ownedItems: 1 }, { 
  unique:1, 
  partialFilterExpression: { 
    ownedItems: { $gt: []  }
  }
})

测试

db.Users.insert({name: 'vic', ownedItems: ['one','two']}) // Works

db.Users.insert({name: 'ted', ownedItems: ['one']}) // E11000 duplicate key
db.Users.insert({name: 'ted', ownedItems: ['two']}) // E11000 duplicate key
db.Users.insert({name: 'ted', ownedItems: ['one', 'three']}) // E11000 duplicate key

db.Users.insert({name: 'ted', ownedItems: ['three']}) // Works

db.Users.insert({name: 'ann', ownedItems: ['four','four']})  // E11000 duplicate key

db.Users.insert({name: 'jim', ownedItems: []}) 
db.Users.insert({name: 'pen', ownedItems: []}) // Works

如果您需要更复杂的东西,您需要在您的应用中实施逻辑,包括交易系统(如果需要)。像Maria / Postgres这样的传统RDBMS可以更好地支持更复杂的数据库逻辑和事务。