我有一个集合,其中包含一系列名为"标签"的子文档。我希望能够在标签级别上findAndModify
。目的是锁定每个标签,以便用户界面不允许多个用户一次查看每个标签而不锁定整个文档。目前我只是试图添加一个名为" lock"我的findAndModify
查询点击每个标签。
该查询目前正在部分运行。它会找到一个至少有一个标签不含"锁定的文件。领域。但是,它只会将锁定添加到第一个标签,即使第一个标签已被锁定。这是我目前的查询:
db.manual_ncsc_test.findAndModify({query: {$and: [{"labels.x": 201}, {"labels": {$elemMatch: {$exists: {"lock": false}}}}]}, sort:{"labels.lock": -1}, update: {$set: {"labels.$.lock": "TEST!!!!"}}, upsert: false})
$and
的第一部分只是为了确保每次都对同一文档进行测试。然后我使用$elemMatch
检查每个标签是否有锁。
我尝试了各种各样的尝试来解决我的问题,但我相信它只是在主文档级别排序,而不是标签本身。
您可以看到我正在使用位置运算符将锁添加到匹配的标签。这真让我困惑的是,位置操作符应该只是命中与查询匹配的标签,但它每次都会修改数组中的第一个标签,无论它是否有锁(通过更改{{1的文本来确认) }})。
编辑: 防止任何其他不正确的假设; 这些标签对象在没有输入的情况下提供给用户。用户只有在node.js服务器选择后才与标签进行交互,因此唯一的要求是它是一个标签,并且它尚未被另一个用户检查(已锁定)。在运行findAndModify之前,服务器无法知道有关标签的任何信息,因此无法搜索ID和其他唯一信息。
答案 0 :(得分:2)
事实上,你似乎对"排序"选项适用于.findAndModify()
以及其他一些使用问题。
"排序"的目的将在您的"查询"声明的一部分匹配多个文档。在这种情况下,由于.findAndModify()
一次只对一个文档起作用,因此"排序"用于确定"哪个文件"将使用"更新"进行处理或其他操作,例如"删除"。这不会对数组进行排序,实际上没有任何标准操作,除了聚合框架可以在查询中执行的更新和操作的$sort
修饰符之外。
匹配特定"索引"的主要问题对你的阵列是你需要比较"多个"数组元素的属性,以确定positional $
匹配。 $elemMatch
运算符通过"查询"提供条件的每个元素。
对于你所描述的那种东西,你想要的是一个"唯一标识符"在您要更改或编辑的数组元素上。我将使用每个数组元素的ObjectId()
值给出示例:
{
"_id" : ObjectId("53cf1b1c71c5e451279fce3b"),
"labels" : [
{
"_id" : ObjectId("53cf1b1c71c5e451279fce37"),
"name" : "A"
},
{
"_id" : ObjectId("53cf1b1c71c5e451279fce38"),
"name" : "B"
},
{
"_id" : ObjectId("53cf1b1c71c5e451279fce39"),
"name" : "C"
},
{
"_id" : ObjectId("53cf1b1c71c5e451279fce3a"),
"name" : "D"
}
]
}
To" fetch"当前修饰符的数据,向.findAndModify()
发出断言,即元素" _id"必须匹配,并且使用$exists
表示该元素没有锁定属性。在"更新"部分您将$set
锁定属性:
db.sample.findAndModify({
"query": {
"_id": ObjectId("53cf1b1c71c5e451279fce3b"),
"labels": {
"$elemMatch": {
"_id": ObjectId("53cf1b1c71c5e451279fce38"),
"locked": { "$exists": false }
}
}
},
"update": {
"$set": { "labels.$.locked": true },
},
"new": true
});
您可以使用"字段"选择性地投影匹配的字段。选项,这可能不是某些语言中实际驱动程序实现的一部分,但是将数组元素与查询中的参数匹配是微不足道的。但一般来说,该文件现在看起来像这样:
{
"_id" : ObjectId("53cf1b1c71c5e451279fce3b"),
"labels" : [
{
"_id" : ObjectId("53cf1b1c71c5e451279fce37"),
"name" : "A"
},
{
"_id" : ObjectId("53cf1b1c71c5e451279fce38"),
"name" : "B",
"locked": true
},
{
"_id" : ObjectId("53cf1b1c71c5e451279fce39"),
"name" : "C"
},
{
"_id" : ObjectId("53cf1b1c71c5e451279fce3a"),
"name" : "D"
}
]
}
正如您所看到的,具有相同参数的任何后续查询都不会实际匹配任何文档,因为所选数组元素实际上具有"锁定"属性。这会阻止其他进程访问相同的"标签"同时进行编辑。
除了获取" lock"这是必需的。这里的$elemMatch
确保满足两个条件并选择正确的元素,当然只有一个元素与给定的查询匹配。因此,以下内容将允许另一个进程锁定"数组中最后一个元素编辑:
db.sample.findAndModify({
"query": {
"_id": ObjectId("53cf1b1c71c5e451279fce3b"),
"labels": {
"$elemMatch": {
"_id": ObjectId("53cf1b1c71c5e451279fce3a"),
"locked": { "$exists": false }
}
}
},
"update": {
"$set": { "labels.$.locked": true },
},
"new": true
});
现在在数据库和返回的文件中#2;两个"这些元素目前已被锁定":
{
"_id" : ObjectId("53cf1b1c71c5e451279fce3b"),
"labels" : [
{
"_id" : ObjectId("53cf1b1c71c5e451279fce37"),
"name" : "A"
},
{
"_id" : ObjectId("53cf1b1c71c5e451279fce38"),
"name" : "B",
"locked": true
},
{
"_id" : ObjectId("53cf1b1c71c5e451279fce39"),
"name" : "C"
},
{
"_id" : ObjectId("53cf1b1c71c5e451279fce3a"),
"name" : "D",
"locked": true
}
]
}
当您想要修改文档时,请发出" locked"对于元素是真的,然后是$unset
锁定的属性以及发出更改的值:
db.sample.findAndModify({
"query": {
"_id": ObjectId("53cf1b1c71c5e451279fce3b"),
"labels": {
"$elemMatch": {
"_id": ObjectId("53cf1b1c71c5e451279fce38"),
"locked": { "$exists": true }
}
}
},
"update": {
"$unset": { "labels.$.locked": true },
"$set": { "labels.$.name": "C" }
},
"new": true
});
所以这现在修改文档中的数组元素,该进程具有" lock" on并且不影响其他锁定元素。这使得另一个进程发布它自己的更新以便更改该元素:
{
"_id" : ObjectId("53cf1b1c71c5e451279fce3b"),
"labels" : [
{
"_id" : ObjectId("53cf1b1c71c5e451279fce37"),
"name" : "A"
},
{
"_id" : ObjectId("53cf1b1c71c5e451279fce38"),
"name" : "C"
},
{
"_id" : ObjectId("53cf1b1c71c5e451279fce39"),
"name" : "C"
},
{
"_id" : ObjectId("53cf1b1c71c5e451279fce3a"),
"name" : "D",
"locked": true
}
]
}
这是获取所需原子更新的一般方法,同时确保在编辑过程中没有其他更新语句可以修改相同的元素。
对于奖励积分,还要添加时间戳属性,或者只是将锁定属性设置为当前时间。这允许您定期检查并解锁"任何可能有"过期的编辑"或以其他方式完成并解锁元素。