Christian Lau
Christian Lau

Reputation: 41

Updating a multilevel embedded document in MongoDB

I am trying to update a multilevel embedded document in MongoDB using dot notation and the $ operator. Below, I have a collection composed of one document:

{
"_id" : ObjectId("55da48520549875d8480707c"),
"queried" : [],
"field" : "materials science",
"subfields" : [ 
    {
        "subfield_name" : "electronic materials",
        "queried" : [],
        "subfields_2" : [ 
            {
                "subfield_2_name" : "electronics",
                "queried" : [],
                "keywords" : [ 
                    {
                        "queried" : [],
                        "name" : "silicon"
                    }, 
                    {
                        "queried" : [],
                        "name" : "graphene"
                    }, 
                    {
                        "queried" : [],
                        "name" : "carbon nanotube"
                    }, 
                    {
                        "queried" : [],
                        "name" : "black phosphorus"
                    }, 
                    {
                        "queried" : [],
                        "name" : "phophorene"
                    }, 
                    {
                        "queried" : [],
                        "name" : "molybdenum disulphide"
                    }
                ],
            }, 
            {
                "subfield_2_name" : "dielectrics",             
                "queried" : [],
                "keywords" : [ 
                    {
                        "queried" : [],
                        "name" : "silicon oxide"
                    }, 
                    {
                        "queried" : [],
                        "name" : "aluminum oxide"
                    }, 
                    {
                        "queried" : [],
                        "name" : "hafnium dioxide"
                    }, 
                    {
                        "queried" : [],
                        "name" : "hexagonal boron nitride"
                    }, 
                    {
                        "queried" : [],
                        "name" : "Zirconium dioxide"
                    }
                ],
            },
        ],
    }
]
}

I would like to update this collection by:
1) querying the document to match a "name" key in the keywords array (in the 3rd level embedded document) with the value "carbon nanotube"

2) I would then like to append a timestamp (time()) to the "queried":[] key array within the same embedded document that has the key-value pair "name":"carbon nanotube"

{
"_id" : ObjectId("55da48520549875d8480707c"),
"queried" : [],
"field" : "materials science",
"subfields" : [ 
    {
        "subfield_name" : "electronic materials",
        "queried" : [],
        "subfields_2" : [ 
            {
                "subfield_2_name" : "electronics",
                "queried" : [],
                "keywords" : [ 
                    {
                        "queried" : [],
                        "name" : "silicon"
                    }, 
                    {
                        "queried" : [],
                        "name" : "graphene"
                    }, 
                    {
                        "queried" : [1359147763.02],
                        "name" : "carbon nanotube"
                    }, 
                    {
                        "queried" : [],
                        "name" : "black phosphorus"
                    }, 
                    {
                        "queried" : [],
                        "name" : "phophorene"
                    }, 
                    {
                        "queried" : [],
                        "name" : "molybdenum disulphide"
                    }
                ],
            }, 
            {
                "subfield_2_name" : "dielectrics",             
                "queried" : [],
                "keywords" : [ 
                    {
                        "queried" : [],
                        "name" : "silicon oxide"
                    }, 
                    {
                        "queried" : [],
                        "name" : "aluminum oxide"
                    }, 
                    {
                        "queried" : [],
                        "name" : "hafnium dioxide"
                    }, 
                    {
                        "queried" : [],
                        "name" : "hexagonal boron nitride"
                    }, 
                    {
                        "queried" : [],
                        "name" : "Zirconium dioxide"
                    }
                ],
            },
        ],
    }
]
}

I know that this can all be done in the update() command:

topics.collection.update({"subfields.subfields_2.keywords.name":"carbon nanotube"}, {$push: {"subfields.subfields_2.keywords.$.queried":time()}})

But I think there is an error with my dot notation because I receive an error SyntaxError: invalid syntax. Do I need to change my schema in order to carry out this update() or is there an alternative way to update this embedded document

Upvotes: 0

Views: 1473

Answers (1)

sheilak
sheilak

Reputation: 5873

This isn't possible with the current schema. In order to perform updates in an array without explicitly specifying the position of the element, you need to use the positional $ operator. Because there are 2 nested queries your update would need to be (note I've added another $):

{$push: {"subfields.subfields_2.$.keywords.$.queried":time()}}

But this won't work because the $ operator cannot be used for nested arrays - you can only have one positional $ operator in the update.

You will need to modify your schema to support this.

For example you could store all subfields in their own individual documents, and link to their parent documents to allow navigation of the tree structure (an index on the parent field might be needed)

{
    "_id" : ObjectId("55da48520549875d84800000"),
    "queried" : [],
    "field" : "materials science"
}

{
    "_id" : ObjectId("55da48520549875d84801111"),
    "queried" : [],
    "field" : "electronic materials",
    "parent" : ObjectId("55da48520549875d84800000")
}

{
    "_id" : ObjectId("55da48520549875d84802222"),
    "queried" : [],
    "field" : "electronics",
    "parent" : ObjectId("55da48520549875d84801111"),
    "keywords" : [{
            "queried" : [],
            "name" : "silicon"
        }, {
            "queried" : [],
            "name" : "graphene"
        }, {
            "queried" : [],
            "name" : "carbon nanotube"
        }]    
}

If you'd like to look at more options to fit your requirements, the Mongo documentation has a section on how to Model Tree Structures. The example above would be Model Tree Structures with Parent References but there are other pattern thats that might be better depending on how else you query the collection.

Upvotes: 0

Related Questions