How to use $unset when the key being removed contains a dot in its name?

How to use $unset when a key contains dots in its name?

const key = "test"
const result = await db.collection(collection).updateOne(
    { [key]: { $exists: true } },
    { $unset: { [pathToKey]: 1 } }
);

given this document:

{
  "test": {
    "a": "b",         // <- test.a successfully erases the 'a' key
    "v0.0.1": "ok"    // <- test.v0.0.1 fails
  }
}

when pathToKey is 'test.a' it successfully erases the a key

when it 'test.v0.0.1' it fails, probably because the key contains a . in its name

I’m using the latest Mongo version

Example: Mongo playground

I also tried using $unsetField: Mongo playground

Complicated schema calls for complicated update.

My approach is to use $objectToArray and then $filter to remove the field. The dotted field names then become a value which is easier to manipulate. You will find this approach on Mongo playground.

Note, that I do everything in multiple stage as this is easier to develop and understand. You may of course put everything in an unreadable stage. I use temporary values in a _tmp object as to see what each steps perform. I left out a clean up state that $unset this _tmp object for readability of the playground.

I dont understand what you did but the output is a thing completely different from the input which i would like to just remove the v0.0.1 key

Like I wrote

Like I wrote I left out the clean up stage:

which I added in this updated Mongo playground.

Dear @michel_guedes, I have spent time working on your issue.

I would appreciate a followup. Perhaps a closure by marking one of my post as the solution.

Thanks

That’s a really nice approach, and just to simplify all your stages what can be done directly is -

db.orders.update({
  "test": {
    $exists: true
  }
},
[
  {
    $set: {
      test: {
        $arrayToObject: {
          $filter: {
            input: {
              $objectToArray: "$test"
            },
            cond: {
              $ne: [
                "$$this.k",
                "v0.0.1"
              ]
            }// Condition to exclude the key
          }
        }
      }
    }
  }
])
1 Like

I would need to retract

because it is quite readable once done in a single stage.

Final followup and closure from @michel_guedes would still be appreciated.

As @steevej suggested, you can use aggregation pipeline to express update. You can also use aggregation expressions in filter portion of update by wrapping it in $expr.

If you’re on MongoDB version 5.0+, check out aggregation expressions $getFiled, $setField, $unsetField for working with field names containing dots or leading $.

filter = {$expr: {$getField: {field: 'v0.0.1',input: '$test'}}};
pipeline = [ { $replaceWith: { $unsetField: { field: "v0.0.1", input: "$test" } } }];
db.collection.updateOne(filter, pipeline)

The pipeline looks incomplete since other fields are dropped as seen in this playground.

Thanks!
Correcting
pipeline =

 [ {
    $replaceWith: {
      $mergeObjects: [
        "$$ROOT",
        {
          test: {
            $unsetField: {
              field: "v0.0.1",
              input: "$test"
            }
          }
        }
      ]
    }
  }
]
1 Like