1

Disclaimer: Even though similar, not the same as MongoDB rename database field within array.

I want to change the key of a field from name to title with database commands and NOT the shell. I found out that using $rename is not possible inside an array. So I turned to $set and $unset and created this:

db.runCommand({
    update: 'apps',
    updates: [
        {
            q: {versions: {$elemMatch: {title: {$exists: false}}}},
            u: {
                $set: {'versions.$.title': '$name'}
            },
            multi: true
        },
        {
            q: {versions: {$elemMatch: {name: {$exists: true}}}},
            u: {
                $unset: {'versions.$.name': ''}
            },
            multi: true
        }
    ]
})

This works somewhat. It does all I want but it changes every title/name to the string "$name" instead of the value of the field name.

I also tried this:

db.runCommand({
    update: 'apps',
    updates: [
        {
            q: {versions: {$elemMatch: {title: {$exists: false}}}},
            u: {
                $set: {
                    versions: {
                        $map: {
                            input: "versions",
                            as: 'each',
                            in: {
                                "title": "$$each.name"
                            }
                        }
                    }
                }
            },
            multi: true
        },
        {
            q: {versions: {$elemMatch: {name: {$exists: true}}}},
            u: {
                $unset: {'versions.$.name': ''}
            },
            multi: true
        }
    ]
})

But that just results in [The dollar ($) prefixed field '$map' in 'versions.$map' is not valid for storage.]. How would I go about renaming a field inside an array?

Below is a minified version of my apps collection for reference. I literally only want to change every key called name into title.

[{
  identifier: "x",
  versions: [
    {
      name: "test_name"
      version: "x.x.x"
    },
    {
      name: "test_name"
      version: "x.x.x"
    },
  ]
},
{
  identifier: "y",
  versions: [
    {
      name: "test_name2"
      version: "x.x.x"
    },
    {
      name: "test_name2"
      version: "x.x.x"
    },
  ]
}, ... ]
leonheess
  • 16,068
  • 14
  • 77
  • 112
  • Try this `$map`'s `in: { version: "$$each.version" , title: "$$each.name" }` for the `$set` _and_ remove the `$unset` block. – prasad_ Feb 18 '21 at 15:53

1 Answers1

3

But that just results in The dollar ($) prefixed field '$map' in 'versions.$map' is not valid for storage..

  • The error says $map's input accepts reference field using $ sign $version,
  • enclose the u object in array bracket for update with an aggregation pipeline
  • just put both field title and version in $map
  • $unset is not required because $map will replace old data with new fields in in
db.runCommand({
    update: 'apps',
    updates: [
        {
            q: { "versions.name": { $exists: true } },
            u: [{
                $set: {
                    versions: {
                        $map: {
                            input: "$versions",
                            in: {
                                "title": "$$this.name",
                                "version": "$$this.version"
                            }
                        }
                    }
                }
            }],
            multi: true
        }
    ]
})

Playground


Second way, For more dynamic approach

  • $mergeObjects inside $map, to prevent manual list of key-values pair
  • $unset stage to remove name field from version array
db.runCommand({
    update: 'apps',
    updates: [
        {
            q: { "versions.name": { $exists: true } },
            u: [
              {
                $set: {
                    versions: {
                        $map: {
                            input: "$versions",
                            in: {
                                $mergeObjects: [
                                    "$$this",
                                    { "title": "$$this.name" }
                                ]
                            }
                        }
                    }
                }
              },
              { $unset: "versions.name" }
            ],
            multi: true
        }
    ]
})

Playground

turivishal
  • 34,368
  • 7
  • 36
  • 59
  • Is there another way of iterating over an array if `$map` is not available? – leonheess Feb 23 '21 at 19:16
  • can you explain for what reason $map is not available? is there any version issue? – turivishal Feb 24 '21 at 05:14
  • Yeah, unfortunately, we are bound to using Azure CosmosDB for MongoDB (3.6 version) and it doesn't support `$map` as you can see [here](https://learn.microsoft.com/de-de/azure/cosmos-db/mongodb-feature-support-36) – leonheess Feb 24 '21 at 08:11
  • $map is the second thing, first it will not support update with aggregation pipeline, because it is starting from moongodb 4.2 version, – turivishal Feb 24 '21 at 09:06
  • Oh dear, any idea on how this could be achieved then? – leonheess Feb 24 '21 at 09:18
  • it is not possible with single query, you need to do 2 queries first query for find and second for update. – turivishal Feb 24 '21 at 09:28
  • I created a new question here: https://stackoverflow.com/questions/66348675/how-to-rename-a-field-inside-an-array-with-database-commands-of-azure-cosmos-db – leonheess Feb 24 '21 at 10:09
  • Is it possible with these limitations? – leonheess Mar 02 '21 at 23:26
  • I don't think it is possible in single query, you have to do do 2 queries 1) find query 2) do your required changes in client side language 3) update query – turivishal Mar 03 '21 at 05:18
  • Can you maybe check out the questions I linked above? – leonheess Mar 03 '21 at 09:15
  • 1
    yes i checked it is almost similar to this question, i understand your problem but i can't, i would suggest to ask your question in [mongodb forum](https://developer.mongodb.com/community/forums/) definitely they will provide quick and straight suggestion. – turivishal Mar 03 '21 at 09:54