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"
    },
  ]
}, ... ]
1
  • Try this $map's in: { version: "$$each.version" , title: "$$each.name" } for the $set and remove the $unset block. Commented Feb 18, 2021 at 15:53

1 Answer 1

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

Sign up to request clarification or add additional context in comments.

11 Comments

Is there another way of iterating over an array if $map is not available?
can you explain for what reason $map is not available? is there any version issue?
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
$map is the second thing, first it will not support update with aggregation pipeline, because it is starting from moongodb 4.2 version,
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 definitely they will provide quick and straight suggestion.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.