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