MongoDB - Update data not using $switch or $cond

56 Views Asked by At

I'm using a lower version of MongoDB so $cond or $switch are giving me the error:

MongoServerError: The dollar ($) prefixed field '$switch' in 'elems.1.val.$switch' is not valid for storage.

I have a collection as below:

{
  "attribute": [
    { "type": "ethnicity", "val": "White" }, 
    { "type": "ethnicity", "val": "Asian" }, 
    { "type": "Language", "val": "English" }
  ]
}

I want to update ethnicities to something specific. Example:

Asian --> East Asian,

White --> caucasian

How do I write a query without getting this error?

db.ethnicity.updateMany(
   { "attributes.type": "ethnicity", "attributes.val": { $in: ["asian", "Caucasian"] } },
   [
      {
         $set: {
            "attributes.$[elem].val": {
               $switch: {
                  branches: [
                     { case: { $eq: [ "asian", "$attributes.$[elem].val" ] }, then: "non-asian" },
                     { case: { $eq: [ "Caucasian", "$attributes.$[elem].val" ] }, then: "White" }
                  ],
                  default: "$attributes.$[elem].val"
               }
            }
         }
      }
   ],
   { arrayFilters: [ { "elem.type": "ethnicity", "elem.val": { $in: ["asian", "Caucasian"] } } ] }
);
1

There are 1 best solutions below

0
Yong Shun On

Note that according to your sample data and query, the update query won't work as attributes field doesn't exist in your attached data. I assume it is a typo error, attributes, instead of attribute.

[
  {
    attributes: [
      {
        type: "ethnicity",
        val: "White"
      },
      {
        type: "ethnicity",
        val: "Asian"
      },
      {
        type: "Language",
        val: "English"
      }
    ]
  }
]

Meanwhile, the aggregation pipeline can't work together with arrayFilters.

You should use the $map operator to iterate each element in the attributes array and update each object with the val field is only updated if the conditions matched.

db.collection.update({
  "attributes.type": "ethnicity",
  "attributes.val": {
    $in: [
      "Asian",
      "White"
    ]
  }
},
[
  {
    $set: {
      attributes: {
        $map: {
          input: "$attributes",
          in: {
            $mergeObjects: [
              "$$this",
              {
                val: {
                  $switch: {
                    branches: [
                      {
                        case: {
                          $and: [
                            {
                              $eq: [
                                "ethnicity",
                                "$$this.type"
                              ]
                            },
                            {
                              $eq: [
                                "Asian",
                                "$$this.val"
                              ]
                            }
                          ]
                        },
                        then: "East Asian"
                      },
                      {
                        case: {
                          $and: [
                            {
                              $eq: [
                                "ethnicity",
                                "$$this.type"
                              ]
                            },
                            {
                              $eq: [
                                "Caucasian",
                                "$$this.val"
                              ]
                            }
                          ]
                        },
                        then: "White"
                      }
                    ],
                    default: "$$this.val"
                  }
                }
              }
            ]
          }
        }
      }
    }
  }
])

Demo @ Mongo Playground