2

I am very new to MongoDB and I need to do a somewhat complex Update operation on my collection.

I have this kind of collection:

[
  {
    "Id": 1,
    "extension": [
      {
        "keyName": "Name",
        "value": "Bob"
      },
      {
        "keyAge": "Age",
        "value": 20
      }
    ]
  },
  {
    "Id": 2,
    "extension": [
      {
        "keyName": "Name",
        "value": "Sam"
      },
      {
        "key": "Name",
        "value": "Sam"
      }
    ]
  },
  {
    "Id": 3,
    "extension": [
      {
        "keyName": "Age",
        "value": 25
      },
      {
        "key": "Age",
        "value": 25
      }
    ]
  },
  {
    "Id": 4
  }
]

I would like to update any items in the extension array of all documents

so that when an item is found with a key property, to rename it keyAge.

Here is the expected result:

[
  {
    "Id": 1,
    "extension": [
      {
        "keyName": "Name",
        "value": "Bob"
      },
      {
        "keyAge": "Age",
        "value": 20
      }
    ]
  },
  {
    "Id": 2,
    "extension": [
      {
        "keyName": "Name",
        "value": "Sam"
      },
      {
        "keyAge": "Name",
        "value": "Sam"
      }
    ]
  },
  {
    "Id": 3,
    "extension": [
      {
        "keyName": "Age",
        "value": 25
      },
      {
        "keyAge": "Age",
        "value": 25
      }
    ]
  },
  {
    "Id": 4
  }
]

I tried to use $rename in a similar way to this question: MongoDB rename database field within array but I get the same error $rename source may not be dynamic array

I think this solution might also apply to me, I tried using it but it's not updating anything on my side, so I guess I cannot understand how to apply that answer to me... https://stackoverflow.com/a/49193743/215553

Thanks for the help!

Bruno
  • 4,685
  • 7
  • 54
  • 105

3 Answers3

2

I tried to use $rename in a similar way to this question: MongoDB rename database field within array but I get the same error $rename source may not be dynamic array

There is a note in $rename:

$rename does not work if these fields are in array elements.

You can try update with aggregation pipeline starting from MongoDB 4.2,

  • check condition id key field is exists
  • $map to iterate loop of extension array
  • $map to iterate loop of array that is converted from extension object to array in key-value format
  • $cond check condition if k is key then return keyAge otherwise return current
  • $arrayToObject back to convert key-value array return from above map to object original format
db.collection.update(
  { "extension.key": { $exists: true } },
  [{
    $set: {
      extension: {
        $map: {
          input: "$extension",
          in: {
            $arrayToObject: {
              $map: {
                input: { $objectToArray: "$$this" },
                in: {
                  k: {
                    $cond: [
                      { $eq: ["$$this.k", "key"] }, // check "key" field name
                      "keyAge", // update new name "keyAge"
                      "$$this.k"
                    ]
                  },
                  v: "$$this.v"
                }
              }
            }
          }
        }
      }
    }
  }],
  { multi: true }
)

Playground

turivishal
  • 34,368
  • 7
  • 36
  • 59
  • that is weird, it's working in the playground but not when I execute the query in MongoDBCompass on my data...`{ acknowledged: true, insertedId: null, matchedCount: 0, modifiedCount: 0, upsertedCount: 0 }` – Bruno Apr 28 '21 at 15:40
  • can you show screenshot how you are executing this query in monbodb compass. i would suggest do this kind of operation in mongo shell, because mongodb compass is still in beta version. – turivishal Apr 28 '21 at 15:44
  • Like that: https://i.imgur.com/C8OmiYn.png I will try to understand...I think your answer is the right one :) Thanks! – Bruno Apr 28 '21 at 16:00
  • 1
    ahh first command `use ` second use your collection name `db..update(..` – turivishal Apr 28 '21 at 16:05
0

Question : How to Rename or add new key for existing array object key?

Answer : Inside projection of mongodb query we have map property which will resolve this.

Solution Example :

{
  parents: {
    $map: {
      input: "$parents",
      as: "parent",
      in: {
        caaUserId: "$$parent._id",
        email: "$$parent.email",
        countryCode: "$$parent.countryCode",
        mobile: "$$parent.mobile",
        reportType : "single"
      }
    }
  }
}

In this example if we want to rename $parent._id as caaUserId in parents array for each Element. Then we can use map and define caaUserId like $$parent._id. This whole code will work in mongoose projection of Query.

It should return following :

{
    "parents" : [
        {
            "caaUserId" : "62d17fa164057000149e283f", 
            "email" : "john.doe@hotmail.com", 
            "countryCode" : 91,
            "mobile": 9876543210,
            "reportType":"single",
        },
        {
            "caaUserId" : "6195d50f15ae2b001293c486", 
            "email" : "akka.ash@hotmail.com", 
            "countryCode" : 91,
            "mobile": 9876543211,
            "reportType":"multi",
        },
    ]
}
-1

This is something that works in your case. Might not be the most readable though.

import json

data = json.loads(strdata)
for entry in data:
    if 'extension' in entry:
        for x in entry['extension']:
            for k, v in x.items():
                if k == 'key':
                    x['keyAge'] = x.pop(k)
  • Thanks for your answer! Can this be run from MongoDB Compass..? I am used to doing db.collection.updateMany...etc. – Bruno Apr 28 '21 at 14:46
  • Also I think I need to use Mongo Aggregation pipelines for performance...I have many hundreds of thousands of documents to update this way – Bruno Apr 28 '21 at 14:49