Mongo update of subdocs

2019-07-15 09:51发布

问题:

I seem to have run into a roadblock while using MongoDB, and would like to know if there is anyway around it without having to modify my database structure. Right now my database structure is as follows:

Company= 
{
   _id:1,
   properties:[
       {
           property_id: 1
           tags : [
             {
                 tag_id: 1
                 tag_value: 1000
                 channels: [
                       {
                          channel_id:1
                          channel_name:"test1"
                       },
                       {
                          channel_id:2
                          channel_name:"test2"
                       }]
             },
             {
                 tag_id:2
                 tag_value: 2500
                 channels: [
                       {
                          channel_id:2
                          channel_name:"test2"
                       },
                       {
                          channel_id:3
                          channel_name:"test3"
                       }]

             }]
       },
       {
           property_id: 2
           tags : [
             {
                 tag_id: 3
                 tag_value: 500
                 channels: [
                       {
                          channel_id:1
                          channel_name:"test1"
                       },
                       {
                          channel_id:3
                          channel_name:"test3"
                       }]
             },
             {
                 tag_id: 4
                 tag_value: 5000
                 channels: [
                       {
                          channel_id:1
                          channel_name:"test1"
                       }]                    
             }]
       }]       
}

I am running into a problem where I cannot figure out a way to update specific tag_ids in a property. For example, I want to change the value of tag_id 4 in property 2 to 100. Or I want to add a channel to the channel array in tag_id 3.

What I would like to do would be something similar to this:

db.company.update({_id:1, "properties.property_id":2, "tags.tag_id":4}, {"properties.$.tags.$.tag_value":100});

However I know this isn't supported in the current version of MongoDB, and it is already in JIRA. So my question is, is there anyway to update a value of a document where you need to specify two different conditions in order to access it. I know that I could do properties.1.tags.0, however I will not know the order of my arrays, and my queries will be run programatically.

Any help would be great, even a conformation that I must restructure my table.

Thank you

回答1:

Flatten an array could lead to rather big documents, because for every inner array element all the data of its outer element has to be repeated (and this for all levels).

So, if flatten is not an option, here is a workaround while waiting for the enhanced functionality in the mentioned Jira (SERVER-831):

  • read the document into a variable
  • manipulate the array
  • update the document, rewriting the entire array

Given your examples, this would look like this:

doc = db.xx.findOne( {_id:1} );
doc.properties.forEach( function(p) {
    if ( p.property_id == 2 ) { 
        p.tags.forEach( function(t) {
           if ( t.tag_id == 3 ) {
               t.tag_value = 100;
           }
           else if ( t.tag_id == 4 ) {
               newChannel = {};
               newChannel.channel_id = 5;
               newChannel.channel_name = "test5";
               t.channels.push(newChannel);
           }
        })
    }
});
db.xx.update({_id:1},{$set:{properties:doc.properties}});

The result is:

doc = db.xx.findOne({_id:1})
{
    "_id" : 1,
    "properties" : [
        {
            "property_id" : 1,
            "tags" : [
                {
                    "tag_id" : 1,
                    "tag_value" : 1000,
                    "channels" : [
                        {
                            "channel_id" : 1,
                            "channel_name" : "test1"
                        },
                        {
                            "channel_id" : 2,
                            "channel_name" : "test2"
                        }
                    ]
                },
                {
                    "tag_id" : 2,
                    "tag_value" : 2500,
                    "channels" : [
                        {
                            "channel_id" : 2,
                            "channel_name" : "test2"
                        },
                        {
                            "channel_id" : 3,
                            "channel_name" : "test3"
                        }
                    ]
                }
            ]
        },
        {
            "property_id" : 2,
            "tags" : [
                {
                    "tag_id" : 3,
                    "tag_value" : 100,
                    "channels" : [
                        {
                            "channel_id" : 1,
                            "channel_name" : "test1"
                        },
                        {
                            "channel_id" : 3,
                            "channel_name" : "test3"
                        }
                    ]
                },
                {
                    "tag_id" : 4,
                    "tag_value" : 5000,
                    "channels" : [
                        {
                            "channel_id" : 1,
                            "channel_name" : "test1"
                        },
                        {
                            "channel_id" : 5,
                            "channel_name" : "test5"
                        }
                    ]
                }
            ]
        }
    ]
}