【问题标题】:MongoDB Enforcing Complex constraints across documents in a collectionMongoDB 在集合中的文档之间强制执行复杂的约束
【发布时间】:2017-09-19 00:49:33
【问题描述】:

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

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

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

也就是说,我要执行:

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 应用程序中实现此逻辑,以防万一。

【问题讨论】:

    标签: node.js mongodb atomic


    【解决方案1】:

    唯一索引是我能想到的唯一方法,可以在 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 对更复杂的数据库逻辑和事务有更好的支持。

    【讨论】:

    • 啊,太好了,我错过了数组字段上的唯一索引强制跨文档数组元素的唯一性这一微妙之处;从 mongo 文档中不是很清楚
    • ...但是有一个问题,我刚刚发现,由于文档中“唯一索引和缺失字段”下的描述,两个或多个用户不能拥有 ownItems: [] 否则我会重复key: { : undefined } ... 有办法解决吗?
    • ...我将尝试使用“具有唯一约束的部分索引”的指导
    • 好吧,我只需要为 ownItemsLength 添加一个字段并维护它,以便我可以添加一个 partialFilterExpression: {ownedItemsLength: { $gte: 1 } } } 到唯一约束;感谢您为我指明正确的方向!
    • partialFilterExpression: { ownedItems: { $gt: [] }} 应该允许您删除跟踪。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-02-10
    • 1970-01-01
    • 2020-08-18
    • 2020-12-23
    • 2014-12-25
    • 2011-09-07
    相关资源
    最近更新 更多