简体   繁体   中英

Update object inside nested array in Mongodb

I have a document that looks like this. Where I need to update a specific object inside a nested array. I need to edit the text object with the name which is having a locale of en .

[
    {
        "_id": ObjectId("5e049ebc8e935c407f78c190"),
        "source": "homepage",
        "url": [
            {
                "type": "admindsg",
                "text": [
                    {
                        "locale": "en",
                        "name": "Admin DSG"
                    },
                    {
                        "locale": "nb",
                        "name": "Admin DSG"
                    },
                    {
                        "locale": "li",
                        "name": "Admin DSG"
                    },
                    {
                        "locale": "fi",
                        "name": "Admin DSG"
                    }
                ],
                "value": [
                    {
                        "locale": "en",
                        "link": "https://www.facebook.cloud"
                    },
                    {
                        "locale": "nb",
                        "link": "https://www.facebook.cloud"
                    },
                    {
                        "locale": "li",
                        "link": "https://www.facebook.cloud"
                    },
                    {
                        "locale": "fi",
                        "link": "https://www.facebook.cloud"
                    }
                ],
                "datetime": "2020-02-08 13:36:37"
            },
            {
                "type": "dataauth",
                "text": [
                    {
                        "locale": "en",
                        "name": "Data Authorities"
                    },
                    {
                        "locale": "nb",
                        "name": "Data Authorities"
                    },
                    {
                        "locale": "li",
                        "name": "Data Authorities"
                    },
                    {
                        "locale": "fi",
                        "name": "Data Authorities"
                    }
                ],
                "value": [
                    {
                        "locale": "en",
                        "link": "https://www.facebook.cloud"
                    },
                    {
                        "locale": "nb",
                        "link": "https://www.facebook.cloud"
                    },
                    {
                        "locale": "li",
                        "link": "https://www.facebook.cloud"
                    },
                    {
                        "locale": "fi",
                        "link": "https://www.facebook.cloud"
                    }
                ],
                "datetime": "2020-02-08 13:36:38"
            },
            {
                "type": "blog",
                "text": [
                    {
                        "locale": "en",
                        "name": "facebook blog"
                    },
                    {
                        "locale": "nb",
                        "name": "facebook blog"
                    },
                    {
                        "locale": "li",
                        "name": "facebook blog"
                    },
                    {
                        "locale": "fi",
                        "name": "facebook blog"
                    }
                ],
                "value": [
                    {
                        "locale": "en",
                        "link": "https://www.facebook.no"
                    },
                    {
                        "locale": "nb",
                        "link": "https://www.facebook.no"
                    },
                    {
                        "locale": "li",
                        "link": "https://www.facebook.no"
                    },
                    {
                        "locale": "fi",
                        "link": "https://www.facebook.no"
                    }
                ],
                "datetime": "2020-02-08 13:36:39"
            },
            {
                "type": "guide",
                "text": [
                    {
                        "locale": "en",
                        "name": "Guidelines for you"
                    },
                    {
                        "locale": "nb",
                        "name": "Guidelines for you"
                    },
                    {
                        "locale": "li",
                        "name": "Guidelines for you"
                    },
                    {
                        "locale": "fi",
                        "name": "Guidelines for you"
                    }
                ],
                "value": [
                    {
                        "locale": "en",
                        "link": "https://my.instagram.as/"
                    },
                    {
                        "locale": "nb",
                        "link": "https://my.instagram.as/"
                    },
                    {
                        "locale": "li",
                        "link": "https://my.instagram.as/"
                    },
                    {
                        "locale": "fi",
                        "link": "https://my.instagram.as/"
                    }
                ],
                "datetime": "2020-02-08 13:36:41"
            }
        ]
    }
]

What should be the best approach to get this done?

This the query I have tried:

db.getCollection('general').update({
    "source": "homepage",
    "url.type": "admindsg"
}, {
    "$set": {
        "url.text.$[elem].name": "YOYO"
    }
}, {
    "arrayFilters": [{
        "elem.locale": {
            "$eq": "en"
        }
    }],
    "multi": true
})

This throws an error saying:

No array filter found for identifier 'elem' in path 'url.text.$[elem].name'

在此处输入图片说明

url field is an array, so you need to use this syntax:

db.getCollection('general').update({
    "source": "homepage"
}, {
        "$set": {
            "url.$[urlId].text.$[textId].name": "YOYO"
        }
    }, {
        "arrayFilters": [
            { "urlId.type": "admindsg" },
            { "textId.locale": "en" },

        ],
        "multi": true
    })

filtered positional operator $

Problem is you have 2 array filters

  • url.type = "admindsg"
  • url.text.local = "en"

You can use an aggregation pipeline. This would be one solution:

db.getCollection('general').aggregate([
   { $match: { source: "homepage" } },
   { $unwind: "$url" },
   {
      $set: {
         "url.text": {
            $cond: {
               if: { $eq: ["$url.type", "admindsg"] },
               then: {
                  $reduce: {
                     input: "$url.text",
                     initialValue: [],
                     in: {
                        $concatArrays: [
                           "$$value",
                           [{
                              $cond: {
                                 if: { $eq: ["$$this.locale", "fi"] },
                                 then: {
                                    $mergeObjects: [
                                       { locale: "$$this.locale" },
                                       { name: "Järjestelmänvalvoja DSG" }
                                    ]
                                 },
                                 else: "$$this"
                              }
                           }]
                        ]
                     }
                  }
               },
               else: "$url.text"
            }
         }
      }
   },
   {
      $group: {
         _id: { _id: "$_id", source: "$source" },
         url: { $push: "$$ROOT.url" }
      }
   },
   { $replaceRoot: { newRoot: { $mergeObjects: ["$$ROOT", "$_id"] } } }
]).forEach(function (doc) {
   db.getCollection('general').updateOne(
      { _id: doc._id },
      { $set: { url: doc.url } }
   );
})

Instead of $reduce you can also use $map :

  {
    $set: {
      "url.text": {
        $cond: {
          if: { $eq: ["$url.type", "admindsg"] },
          then: {
            $map: {
              input: "$url.text",
              in: {
                $cond: {
                  if: { $eq: [ "$$this.locale", "fi" ] },
                  then: {
                    $mergeObjects: [
                      { locale: "$$this.locale" },
                      { name: "Järjestelmänvalvoja DSG" }
                    ]
                  },
                  else: "$$this"
                }
              }
            }
          },
          else: "$url.text"
        }
      }
    }
  },

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