繁体   English   中英

在子文档上使用Mongodb findAndModify

[英]Using the Mongodb findAndModify on subdocuments

我有一个集合,该集合的文档包含称为“标签”的子文档数组。 我希望能够在标签级别上找到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检查每个标签是否有锁。

我尝试了各种尝试来解决问题,但我相信它只是在主文档级别进行排序,而不是标签本身。

您可以看到我正在使用位置运算符将锁添加到匹配的标签。 这确实让我感到困惑,位置运算符应该只击中与查询匹配的标签,但是无论是否有锁,它每次都会修改数组中的第一个标签(通过更改$set的文本来确认) 。

编辑:防止任何其他不正确的假设; 这些标签对象无需输入即可提供给用户。 用户仅在由node.js服务器选择标签后才与标签进行交互,因此唯一的要求是标签是标签,并且尚未被其他用户检查(锁定)。 在运行findAndModify之前,服务器无法知道有关标签的任何信息,因此无法搜索ID和其他唯一信息。

确实,您似乎对如何将.findAndModify()以及其他一些使用问题与“ sort”选项一起使用产生了.findAndModify()

当语句的“查询”部分与多个文档匹配时,将应用“排序”的目的。 在这种情况下,由于.findAndModify() 一次仅作用于一个文档,因此“排序”用于确定将使用“更新”或其他操作(例如“删除”)来处理“哪个文档”。 它不会对数组进行排序,实际上没有标准操作能对数组进行排序,只是$sort修饰符可用于聚合框架可在查询中进行的更新和操作。

将特定的“索引”匹配到数组的主要问题是,需要比较数组元素的“多个”属性以确定位置$匹配。 $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"
            }
    ]
}

要“获取”当前修饰符的数据,请向您的.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
});

您可以选择使用“ fields”选项来投影匹配的字段,尽管在某些语言中这可能不是实际驱动程序实现的一部分,并且将数组元素与查询中的参数进行匹配很简单。 但是通常,文档现在看起来像这样:

{
    "_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"
            }
    ]
}

如您所见,具有相同参数的任何后续查询实际上都不会与任何文档匹配,因为所选数组元素实际上具有“锁定”属性。 这将阻止其他进程同时访问相同的“标签”进行编辑。

但是,可以返回任何其他数组元素以及获得所需的“锁”。 此处的$elemMatch确保同时满足两个条件并选择正确的元素,当然,只有一个元素会匹配给定的查询。 因此,下面的代码将允许另一个进程“锁定”数组中的最后一个元素进行编辑:

db.sample.findAndModify({
    "query": {
        "_id": ObjectId("53cf1b1c71c5e451279fce3b"),
        "labels": {
            "$elemMatch": {           
                "_id": ObjectId("53cf1b1c71c5e451279fce3a"),
                "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",
                    "locked": true
            }
    ]
}

并且,当您要修改文档时,请发出“ locked”对于元素为true的信息,然后$unset the locked属性以及发出更改的值:

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
});

因此,这现在修改了文档中的数组元素,表明此过程具有“锁定”功能,并且不会影响其他锁定元素。 剩下的另一个过程是发布自己的更新以更改该元素:

{
    "_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
            }
    ]
}

这是获取所需原子更新的一般方法,同时确保在进行编辑时,没有其他更新语句可以修改同一元素。

对于奖励积分,还可以添加一个timestamp属性或简单地将locked属性设置为当前时间。 这样,您就可以定期检查和“解锁”可能已经“过期”或以其他方式没有完成的任何编辑,并单独对其进行解锁。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM