MongoDB 4.0.12 update nested array with concat

Viewed 1475

Mongo DB 4.0

I want to update all elements of an array of an document by concatenate fields I have following document:

{
  "id": 123455,
  "articles": [
    {
      "country": "DE",
      "articleNumber": "123456789"
    },
    {
      "country": "AT",
      "articleNumber": "987654321"
    }
  ]
}

and I need extends article with a new field which is concatenate by to other fields

{
  "id": 123455,
  "articles": [
    {
      "country": "DE",
      "articleNumber": "123456789",
      "countryArticleNumber": "DE123456789"
    },
    {
      "country": "AT",
      "articleNumber": "987654321",
      "countryArticleNumber": "AT987654321"
    }
  ]
}

I tried several solutions I found here in Stackoverflow like:

db.articleDocument.update (
  {_id: ObjectId('5f15414ff3812f4838391edd')},
  {
    $push:
      {'articles.countryArticleNumber': 'dasd'}
  }
  )

but even with a simple update I got

MongoWriteException: Cannot create field 'countryArticleNumber' in element {articles: [ {

I also tried with db.articleDocument.updateOne, $set instead of $push, positional operator

I was expecting something like this should work

db.articleDocument.updateOne(
  {_id: ObjectId('5f15414ff3812f4838391edd')},
  {
    $set:
      {
        "articles.$.countryArticleNumber": {
          $concat: ["articles.$.country", "articles.$.articleNumber"]
        }
      }
  }
  )

but

The positional operator did not find the match needed from the query.

Thx in advanced

1 Answers

For v4.0, might be one solution you have to perform two queries,

  • use aggregation with $map and $project or $addFields
  • update returned aggregation result using updateOne()
db.articleDocument.aggregate([
  {
    $match: {
      _id: ObjectId("5f15414ff3812f4838391edd")
    }
  },
  {
    $project: {
      _id: 1,
      articles: {
        $map: {
          input: "$articles",
          as: "article",
          in: {
            country: "$$article.country",
            articleNumber: "$$article.articleNumber",
            countryArticleNumber: {
              $concat: [
                "$$article.country",
                "$$article.articleNumber"
              ]
            }
          }
        }
      }
    }
  }
])
.forEach(function(doc) { 
  db.articleDocument.updateOne( 
    { _id: doc._id }, 
    { $set: { articles: doc.articles } } 
  )
})

Alternatively can use $addFields instead of $project,

Aggregation Working Playground


For v4.2, There are new feature update with aggregation pipeline,

db.articleDocument.updateMany(
    {
        "_id" : ObjectId("5f15414ff3812f4838391edd")
    },
    [
        {
            $addFields: {
                articles: {
                    $map: {
                        input: "$articles",
                        as: "article",
                        in: {
                            country: "$$article.country",
                            articleNumber: "$$article.articleNumber",
                            countryArticleNumber: {
                                $concat: [
                                    "$$article.country",
                                    "$$article.articleNumber"
                                ]
                            }
                        }
                    }
                }
            }
        }
    ]
)

Note: Make sure mongo shell version should be >= 4.2

Related