1

Sounds messy, I know.
The document I'm modifying is structured like this:

{
   "_id":12345,
   "name":"harold",
   "basicData":{
      "devices":[
         {
            "id":7654,
            "relatedJson":{
               "make":"sony",
               "equipmentID":"asdf"
            }
         },
         {
            "id":9493
         }
      ],
      "car":"toyota"
   }
}

I can't quite get the code right. This is what I have:

db.serviceAgreement.updateMany({"basicData.devices.relatedJson.equipmentID": {$exists: true}},
    [
        {
            $set: {
                "basicData.devices": {
                    $map: {
                        input: "$basicData.devices", in: {
                            $mergeObjects: ["$$this.relatedJson",
                                {equipmentId: "$$this.relatedJson.equipmentID",}]
                        }
                    }
                }
            }
        },
        {
            $unset: "basicData.devices.relatedJson.equipmentID"
        }

    ])

So my understanding of this script is that I'm calling $set to set the field basicData.devices, then I'm setting it to that same list, but before I do that I'm merging the documents relatedJson and a new document {equipmentId : "$$this.relatedJson.equipmentID",} which has the value equipmentId set to the same value as the equipmentID in the relatedJson object.
Then, I'm replacing the relatedJson document with the newly created equipmentId (not my intention).
And finally, deleting the original equipmentID - which doesn't actually exist any more because I've replaced the whole relatedJson object.

How can I insert the new equipmentId into the relatedJson object, instead of replacing it entirely.

I have tried variations of the above script that do all sorts of things, inserting a copy of relatedJson into itself, inserting a copy of devices into relatedJson, deleting everything inside devices, but I can't get it to do what I want.
I feel I'm close to a solution, and maybe I need to modify the $input: but I can't figure out how, or maybe my approach is totally wrong.
Any help would be greatly appreciated.

The end result should be the same document, but relatedJson.equipmentID should be renamed relatedJson.equipmentId (with a lower-case 'd' at the end);

mal
  • 3,022
  • 5
  • 32
  • 62
  • 1
    can you add what is your expected output? I'm not sure I understood it – Tom Slabbaert Sep 21 '22 at 13:04
  • I want to rename `relatedJson.equipmentID` to `relatedJson.equipmentId` (lower case 'd' at the end) so the document should remain the same except for that change. – mal Sep 21 '22 at 13:11

1 Answers1

1

You're close, you just had some syntax issues.

The update should look like this:

db.collection.updateMany({"basicData.devices.relatedJson.equipmentID": {$exists: true}},
[
  {
    $set: {
      "basicData.devices": {
        $map: {
          input: "$basicData.devices",
          in: {
            $mergeObjects: [
              "$$this",
              {
                $cond: [
                  {
                    $ne: [
                      "$$this.relatedJson",
                      undefined
                    ]
                  },
                  {
                    relatedJson: {
                      $mergeObjects: [
                        "$$this.relatedJson",
                        {
                          equipmentId: "$$this.relatedJson.equipmentID"
                        }
                      ]
                    }
                  },
                  {}
                ]
              }
            ]
          }
        }
      }
    }
  },
  {
    $unset: "basicData.devices.relatedJson.equipmentID"
  }
])

Mongo Playground

Tom Slabbaert
  • 21,288
  • 10
  • 30
  • 43
  • So you're adding a condition saying if the document is != relatedJson then do nothing? (I'll google all this later, just curious if my interpretation is right). – mal Sep 21 '22 at 14:23
  • 1
    Basically yes.and obviously the additional syntax changes. – Tom Slabbaert Sep 21 '22 at 14:35
  • Hi again, I'm having trouble getting my head around how the `$cond` and `$ne` part of the script works. The way I'm understanding it is like `$cond[ , , ]` but then the `$ne` part I'm not getting, does it say "if serviceAgreementStatus is not equal to undefined" ? The mongodb docs don't explain that syntax you are using. Any chance you could explain it? – mal Sep 23 '22 at 12:43
  • 1
    Yes, essentially if "relatedJson" does not exist ( like the in the second device in your sample ) i don't want to create an empty object for it. so basically i tell the "$cond" if this object does not exists just merge with the empty object `{}`. specifically the `$ne` part just tells mongo if this field is not undefined meaning "relatedJson" exists, it's basically a way to replace the "$exists" operator which is not part of the aggregation expression language. – Tom Slabbaert Sep 23 '22 at 16:27
  • Thank you! You've helped my understanding of these scripts immensely! I've had a lot of trouble getting my head around this stuff. – mal Sep 24 '22 at 06:20
  • If I wanted to update the `$cond` function to exclude objects/fields with a value set to `null` too, how can I do that? I've come across a situation where if for example `relatedJson` is `null`, then the script sets it to an empty object (obviously because we're merging it with a new `relatedJson` object), which is problematic for me. – mal Sep 27 '22 at 13:59