Update all elements of an array, reading data from existing elements

This seems like it should be easy but it seems to be annoyingly hard.

Input collection contains documents like this:

{
  "foo": [
    {
      "userId": "e736f24f-164e-46d1-8a8b-6ac2f69237a8",
      "params": {
        "location": {
          "coordinates": {
            "first": -80.514124,
            "second": 45.509854
          }
        },
        "withinDistance": 30,
        ... many other fields ...
      },
    },
    {
      "userId": "b71d2067-9dee-45cc-9903-7dea371ea685",
      "params": {
        "location": {
          "coordinates": {
            "first": -82.923723,
            "second": 47.210577
          }
        },
        "withinDistance": 30,
        ... many other fields ...
      },
    },
  ]
}

And I want to transform the non-standard foo.params.location.coordinates object into the standard GeoJSON format, so the output should look like this:

{
  "foo": [
    {
      "userId": "e736f24f-164e-46d1-8a8b-6ac2f69237a8",
      "params": {
        "location": {
          "type": "Point",
          "coordinates": [
            -80.514124,
            45.509854
          ]
        },
        "withinDistance": 30,
        ... many other fields ...
      },
    },
    {
      "userId": "b71d2067-9dee-45cc-9903-7dea371ea685",
      "params": {
        "location": {
          "type": "Point",
          "coordinates": [
            -82.923723,
            47.210577
          ]
        },
        "withinDistance": 30,
        ... many other fields ...
      },
    },
  ]
}

The closest I’ve come is this but I lose the withinDistance and other fields in the params structure:

db.collection.updateMany({}, [
  {
    "$set": {
      "foo": {
        "$map": {
          "input": "$foo",
          "as": "f",
          "in": {
            $mergeObjects: [
              "$$f",
              { 
                "params": {
                  "location" : {
                    "type": "Point",
                    "coordinates": [
                      "$$f.params.location.coordinates.first",
                      "$$f.params.location.coordinates.second"
                    ]
                  }
                }
              }
            ]
          }
        }
      }
    }
  }
])

I think you are simply missing a extra $mergeObjects from the old params with the modified params.

1 Like

Thank you, that worked. I had actually tried that before, but now I realize I had missed the braces around the location object in the $mergeParams array, and MongoDB gave me a very strange error, which didn’t lead me to my error:

clone(t={}){const r=t.loc||{};return e({loc:new Position("line"in r?r.line:this.loc.line,"column"in r?r.column:...<omitted>...)} could not be cloned.

After your post I reviewed what I had done and found the missing braces.

For posterity, the solution looks like this:

db.collection.updateMany({}, [
  {
    "$set": {
      "foo": {
        "$map": {
          "input": "$foo",
          "as": "f",
          "in": {
            $mergeObjects: [
              "$$f",
              { 
                "params": {
                  $mergeObjects: [
                    "$$f.params",
                    {
                      "location" : {
                        "type": "Point",
                        "coordinates": [
                          "$$f.params.location.coordinates.first",
                          "$$f.params.location.coordinates.second"
                        ]
                      }
                    }
                  ]
                }
              }
            ]
          }
        }
      }
    }
  }
])

Thanks!

1 Like

This topic was automatically closed 5 days after the last reply. New replies are no longer allowed.