skip to Main Content

I have a very simple document structure like:

{
  "_id": "...",
  "vector": [100,50,4000],
  //...
}

I want to write an update statement, that changes the vector of a single given document id by a given delta (also a vector of integers). [100,50,4000] with delta [-50,0,-2000] would result in [50,50,2000].

I also want the statement to fail, if the resulting vector would have any negative values.

How can I do this?

2

Answers


  1. You can achieve this using an update with aggregation pipeline. In the first step, you apply a filter for both the ids and vector values greater than the ones you want to subtract. The next step is to reduce the array so that the subtraction is achieved:

    db.collection.update({
      _id: 1,
      "vector.0": {
        $gte: 50
      },
      "vector.1": {
        $gte: 0
      },
      "vector.2": {
        $gte: 2000
      }
    },
    [
      {
        $set: {
          vector: {
            $reduce: {
              input: "$vector",
              initialValue: [],
              in: {
                $concatArrays: [
                  "$$value",
                  [
                    {
                      $add: [
                        "$$this",
                        {
                          $arrayElemAt: [
                            [
                              -50,
                              0,
                              -2000
                            ],
                            {
                              $size: "$$value"
                            }
                          ]
                        }
                      ]
                    }
                  ]
                ]
              }
            }
          }
        }
      }
    ])
    

    This sample assumes that the length of the original vector matches the one of the vector that should be subtracted.

    See this mongoplayground to test.

    As for the requirement that the update should fail: this is achieved by applying the filter for the update. If you know that a document with the specified id exists and no document is matched by the update, you can assume that the update "failed" because the vector values were out of range.

    Login or Signup to reply.
  2. As a matter of personal flavour, I find $sum a $zip vector a elegant solution. The idea is to create sum tuples through $zip and use $map to iterate through the sum vector and apply $sum on it.

    $zip also allows you to handle exceptional case like lengths mismatch through parameters like useLongestLength and defaults

    db.collection.update({
      _id: "..."
    },
    [
      {
        "$set": {
          "vector": {
            "$let": {
              "vars": {
                "delta": [
                  -50,
                  0,
                  -2000
                ]
              },
              "in": {
                "$map": {
                  "input": {
                    "$zip": {
                      "inputs": [
                        "$vector",
                        "$$delta"
                      ]
                    }
                  },
                  "as": "tuple",
                  "in": {
                    "$sum": "$$tuple"
                  }
                }
              }
            }
          }
        }
      }
    ])
    

    Mongo Playground

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search