1

I need to update the name of field in a collection. The problem is that the field in question is within an array. So I'm trying to determine the correct way do this. I tried this to accomplish renaming the field that exists within the "plans" array: :

db.customers.updateMany( {}, { $rename: { "plans" : { "subscriptionType": "membershipType" } } } );

But this won't work. What's the correct way to handle this kind of transformation of a field within an array?

The data looks like this:

{
  _id: 123,
  prop1: value,
  prop2: value,
  prop3: value,
  plans: [
    subscriptionType: value,
    otherProp: value,
    otherProp: value
  ]
}
Muirik
  • 6,049
  • 7
  • 58
  • 116
  • 3
    possible duplicate https://stackoverflow.com/questions/9122966/mongodb-rename-database-field-within-array – Helping hand Aug 21 '18 at 17:48
  • Could you post sample document from customers collection ? – mickl Aug 21 '18 at 17:49
  • The answers in the other question are very old. Wondering if there is new functionality for Mongo to handle this kind of transformation now? – Muirik Aug 21 '18 at 17:54

1 Answers1

5

You can use Aggregation Framework's $addFields to override plans field and $map operator to rename field inside an array. Then you can use $out to override existing collection:

db.customers.aggregate([
    {
        $addFields: {
            plans: {
                $map:{
                    input: "$plans",
                    as: "plan",
                    in: {
                        membershipType: "$$plan.subscriptionType",
                        otherField: "$$plan.otherField",
                        otherField2: "$$plan.otherField2"
                    }
                }
            }
        }
    },
    {
        $out: "customers"
    }
])

Alternatively you can do that dynamically. In this solution you don't have to explicitly specify other field names:

db.customers.aggregate([
    {
        $addFields: {
            plans: {
                $map:{
                    input: "$plans",
                    as: "plan",
                    in: {
                        $mergeObjects: [
                            { membershipType: "$$plan.subscriptionType" },
                            { 
                                $arrayToObject: {
                                    $filter: {
                                        input: { $objectToArray: "$$plan" },
                                        as: "plan",
                                        cond: { $ne: [ "$$plan.k", "subscriptionType" ] }
                                    }
                                } 
                            }
                        ]
                    }
                }
            }
        }
    },
    {
        $out: "customers"
    }
])

Using $objectToArray to $filter out old key-value pair and the using $mergeObjects to combine that filtered object with new renamed field.

mickl
  • 48,568
  • 9
  • 60
  • 89
  • I added more detail to my data structure. Is there a way to do this without overriding other fields within the plans array? Sorry, should have been more specific to begin with. – Muirik Aug 21 '18 at 18:01
  • @Muirik that's why I wanted to see the structure. So the simplest way would be to specify them as well like in my edited answer. Let met know if that's okay for you. Otherwise we can try some tricks with `$mergeObjects` :) – mickl Aug 21 '18 at 18:07
  • 2
    Anyway, I've added the "dynamic" approach – mickl Aug 21 '18 at 18:14
  • 2
    @mickl Works amazingly! You saved me potential hours of work :D thank you!!! – Patrik Šimunič Feb 13 '21 at 16:15
  • Sorry if I am late @mickl, but why do we need `$$plan.k` in `$ne`? is that some field name or what? trying to implement your second solution. Thanks in advance! – Alikhan Oitan Jul 11 '21 at 09:57
  • @AlikhanOitan the objective is to rename a field so we're using `$mergeObjects` where new field is the first argument and the old ones will be merge with it but since we're renaming we want to skip old field by name `$$plan.k` will be equal to `subscriptionType` (old field name) – mickl Jul 11 '21 at 19:20