简体   繁体   English

在子文档上使用Mongodb findAndModify

[英]Using the Mongodb findAndModify on subdocuments

I have a collection who's documents contain an array of sub-documents called "labels". 我有一个集合,该集合的文档包含称为“标签”的子文档数组。 I'd like to be able to findAndModify on the label level. 我希望能够在标签级别上找到findAndModify The purpose is to lock each label so the user interface will not allow more than one user to view each label at a time without locking the entire document. 目的是锁定每个标签,以便在不锁定整个文档的情况下,用户界面一次不允许多个用户查看每个标签。 At the moment I'm simply attempting to add a field called "lock" to each label as it is hit by my findAndModify query. 此刻,我只是试图为每个标签添加一个名为“ lock”的字段,因为它被我的findAndModify查询击中了。

The query is currently partially working. 该查询当前部分起作用。 It will find a document that has at least one label that does not contain a "lock" field. 它将找到一个文档,该文档至少具有一个不包含“锁定”字段的标签。 However, it only adds the lock to the first label, even if the first label is already locked. 但是,即使第一个标签已被锁定,它也只会将锁添加到第一个标签。 Here is my current query: 这是我当前的查询:

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

The first part of the $and is simply to make sure I test on the same document each time. $and的第一部分只是确保我每次都在同一文档上进行测试。 I then use $elemMatch to check each label for a lock. 然后,我使用$elemMatch检查每个标签是否有锁。

I tried various sorts in an attempt to fix my problem but I believe it is just sorting at the main document level, not the labels themselves. 我尝试了各种尝试来解决问题,但我相信它只是在主文档级别进行排序,而不是标签本身。

You can see I am using the positional operator to add the lock to the matched label. 您可以看到我正在使用位置运算符将锁添加到匹配的标签。 This is what really confuses me, the positional operator should only be hitting labels that match the query, but it modifies the first label in the array every time, whether it has a lock or not(confirmed by changing the text for the $set ). 这确实让我感到困惑,位置运算符应该只击中与查询匹配的标签,但是无论是否有锁,它每次都会修改数组中的第一个标签(通过更改$set的文本来确认) 。

EDIT: To prevent any other incorrect assumptions; 编辑:防止任何其他不正确的假设; These label objects are served to the user with no input. 这些标签对象无需输入即可提供给用户。 The user only interacts with a label after it has been chosen by the node.js server, so the only requirements are that it is a label, and that it is not already being examined by another user(locked). 用户仅在由node.js服务器选择标签后才与标签进行交互,因此唯一的要求是标签是标签,并且尚未被其他用户检查(锁定)。 The server has no way of knowing anything about a label before the findAndModify is run, so searching for ids and other unique info is not possible. 在运行findAndModify之前,服务器无法知道有关标签的任何信息,因此无法搜索ID和其他唯一信息。

Indeed you seem to have a miscomprehension of how the "sort" option is applied with .findAndModify() along with some other usage problems. 确实,您似乎对如何将.findAndModify()以及其他一些使用问题与“ sort”选项一起使用产生了.findAndModify()

The purpose of "sort" is to be applied when your "query" part of the statement matches more than one document. 当语句的“查询”部分与多个文档匹配时,将应用“排序”的目的。 In this case, as .findAndModify() acts on only one document at a time, the "sort" is used to determine "which document" will be processed with the "update" or other operation such as "remove". 在这种情况下,由于.findAndModify() 一次仅作用于一个文档,因此“排序”用于确定将使用“更新”或其他操作(例如“删除”)来处理“哪个文档”。 This does not sort arrays, as indeed none of the standard operations do, with the exception of the $sort modifier for updates and operations that the aggregation framework can do in queries. 它不会对数组进行排序,实际上没有标准操作能对数组进行排序,只是$sort修饰符可用于聚合框架可在查询中进行的更新和操作。

You main problem with matching a specific "index" to your array is that you need to compare "multiple" attributes of the array element in order to determine the positional $ match. 将特定的“索引”匹配到数组的主要问题是,需要比较数组元素的“多个”属性以确定位置$匹配。 The $elemMatch operator does this by "querying" each element for the provided conditions. $elemMatch运算符通过“查询”所提供条件的每个元素来执行此操作。

For the sort of thing you are describing, what you want is a "unique identifier" on the array elements you are intending to alter or edit. 对于您正在描述的事情,您想要的是打算更改或编辑的数组元素上的“唯一标识符”。 I'll give a sample here using ObjectId() values for each array element: 我将在此处使用每个数组元素的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" the data for the current modifier, issue your .findAndModify() with the assertion that both the element "_id" must match and that there is no locked attribute present for that element using $exists . 要“获取”当前修饰符的数据,请向您的.findAndModify()发出断言,即元素“ _id”必须匹配并且使用$exists对该元素不存在锁定属性。 In the "update" portion you are going to $set the locked attribute: 在“更新”部分,您将$set被锁定的属性:

db.sample.findAndModify({
    "query": {
        "_id": ObjectId("53cf1b1c71c5e451279fce3b"),
        "labels": {
            "$elemMatch": {           
                "_id": ObjectId("53cf1b1c71c5e451279fce38"),
                "locked": { "$exists": false }
            }
        }
    },
    "update": {
        "$set": { "labels.$.locked": true },
    },
    "new": true
});

You could optionally project the matched field using the "fields" option, this may not be part of the actual driver implementation in some languages though and matching the array element to the arguments in your query is trivial. 您可以选择使用“ fields”选项来投影匹配的字段,尽管在某些语言中这可能不是实际驱动程序实现的一部分,并且将数组元素与查询中的参数进行匹配很简单。 But generally, the document now looks like this: 但是通常,文档现在看起来像这样:

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

As you would see, any subsequent query with the same parameters will not actually match any document as the selected array element actually has a "locked" attribute. 如您所见,具有相同参数的任何后续查询实际上都不会与任何文档匹配,因为所选数组元素实际上具有“锁定”属性。 This stops other processes from accessing the same "label" for edit at the same time. 这将阻止其他进程同时访问相同的“标签”进行编辑。

Any other array element can be returned however as well as obtaining the "lock" that is required. 但是,可以返回任何其他数组元素以及获得所需的“锁”。 The $elemMatch here makes sure that both conditions are met and selects the correct element, where of course only one will ever match the query given. 此处的$elemMatch确保同时满足两个条件并选择正确的元素,当然,只有一个元素会匹配给定的查询。 So the following would allow another process to "lock" the last element in the array for it's edit: 因此,下面的代码将允许另一个进程“锁定”数组中的最后一个元素进行编辑:

db.sample.findAndModify({
    "query": {
        "_id": ObjectId("53cf1b1c71c5e451279fce3b"),
        "labels": {
            "$elemMatch": {           
                "_id": ObjectId("53cf1b1c71c5e451279fce3a"),
                "locked": { "$exists": false }
            }
        }
    },
    "update": {
        "$set": { "labels.$.locked": true },
    },
    "new": true
});

Now in the database and to the returned document "two" of the elements are currently "locked": 现在在数据库中并到返回的文档中,“两个”元素当前已“锁定”:

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

And when you want to modify the document, issue that "locked" is true for the element and then $unset the locked attribute as well as issuing your changed value: 并且,当您要修改文档时,请发出“ 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
});

So this now modifies array element in the document that this process had the "lock" on and does not affect other locked elements. 因此,这现在修改了文档中的数组元素,表明此过程具有“锁定”功能,并且不会影响其他锁定元素。 Which leaves the other process to issue it's own update in order to change that element: 剩下的另一个过程是发布自己的更新以更改该元素:

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

That is the general way to get the sort of atomic updates you want while making sure that no other update statement could modify the same element while your edit is in progress. 这是获取所需原子更新的一般方法,同时确保在进行编辑时,没有其他更新语句可以修改同一元素。

For bonus points, add a timestamp attribute as well or simply set the locked attribute to the current time. 对于奖励积分,还可以添加一个timestamp属性或简单地将locked属性设置为当前时间。 This allows you to periodically check and "unlock" any edits that may have "expired" or otherwise did not complete and unlock the element by itself. 这样,您就可以定期检查和“解锁”可能已经“过期”或以其他方式没有完成的任何编辑,并单独对其进行解锁。

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

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