简体   繁体   中英

Using MongoDB, how do I update a sub-document of a sub-array when element position is unknown?

I'm trying to update a sub-document of a sub-array, but I can't count on the element's position in the array to always be the same, so how do I update it.

For example, here's my document, I just want to push another tag object (key/val) onto the tags array of real_pagenum : 1 of the pdf with the _id of ObjectId("50634e26dc7c22c64d00000a")

{
    "_id" : ObjectId("503b83dfad79cc8d26000004"),
    "pdfs" : [
        {
            "_id" : ObjectId("50634e26dc7c22c64d00000a"),
            "pages" : [
                {
                    "real_pagenum" : 1,
                    "_id" : ObjectId("50634e74dc7c22c64d00002b"),
                    "tags" : [
                        {
                            "key" : "Item",
                            "val" : "foo"
                        },
                        {
                            "key" : "Item",
                            "val" : "bar"
                        }

                    ]

                },
                {
                    "real_pagenum" : 2,
                    "_id" : ObjectId("50634e74dc7c22c64d00002b")
                }
            ],
            "title" : "PDF3",
            "version" : 3
        }
    ],
}

To reiterate, the goal is to first target the right pdf by _id , then the right page by real_pagenum and push into that pdf's page's tags array.

I had tried:

db.projects.update({'pdfs.pdf_id':ObjectId("50634e25dc7c22c64d000007")},
    {$push:{'pages.$.tags':{'key':'foo','val':'bar'}}},false,false);

but that doesn't get to the level I need. I've read that I can use a combination of the positional operator with actual element position , but again, I can't guarantee or know the element's position.

This is currently not possible. You can only address the highest level array in your document tree with the positional operator. Any deeper arrays can only be accessed if you know the exact element position.

This is currently an open issue : https://jira.mongodb.org/browse/SERVER-831

It is a currently filed bug in mongodb that the positional operator cannot be used for nested arrays:

https://jira.mongodb.org/browse/SERVER-831

The positional operator also cannot be used multiple times in one query, for the time being.

The best way to handle this in your case may be to change your schema. In general, having arrays within a document that will grow to an arbitrary size is not a good idea, since BSON documents have a maximum size of 16mb.

Especially since in this case it seems like your document is basically just an array of pdfs. Have you considered creating a separate collection for pdfs?

The other answers are correct as far as doing an atomic update of the document.

I highly agree that this looks like less than optimal schema - a document per pdf might make more sense then you only have an array of pages within each document which you can update atomically with positional operator.

If you cannot modify the schema, you can still make the update you want, it may not be thread safe though. You can read the entire document into your application, modify appropriate fields in code and then save the item. If you wanted to make sure that multiple threads wouldn't stomp over each other, you would have to keep a version in each document and increment it on each "update" which would be conditional on the version not having been changed in the mean time. It's more complex and it still requires an additional field in the current schema, but at least it allows you to do what you state you need to do.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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