leonheess
leonheess

Reputation: 21381

How to rename a field inside an array with database commands?

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"
    },
  ]
}, ... ]

Upvotes: 1

Views: 575

Answers (1)

turivishal
turivishal

Reputation: 36104

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

Upvotes: 3

Related Questions