假设我在名为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应用程序中实现此逻辑,以防万一。
答案 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可以更好地支持更复杂的数据库逻辑和事务。