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

MongoDB Enforcing Complex constraints across documents in a collection

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

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

ownedItems 是一些其他 Items 集合中唯一索引标识符的数组。现在,假设我想向 ownedItems 数组添加一个元素,但我想强制执行以下规则:不能以导致存在多个包含相同项目的用户文档的方式更新用户的 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' }
    } 
  ]  
})

...只有在 returns 没有结果时才继续更新。然而,谁能说在我得到答案和执行更新之间的时间里,答案没有改变(例如,因为其他人将 'notebook-42' 插入到他们的 ownedItems 中)?

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

唯一索引是我能想到的可以在 Mongodb 中应用跨文档约束的唯一方法,这恰好适用于这种情况。

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

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

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 对更复杂的数据库逻辑和事务有更好的支持。