skip to Main Content

I’m trying to make a seemingly simple update to a MongoDB collection that looks like the below using Node.

Collection

{
  account_id: "ORG1",
  progress: [{week: 1, goal: 5000, raised: 2400}, {week: 2, goal:5100,  raised: 1000}]
} 

The goal is to be able to

  1. Find the correct org (this works for me)
  2. Add a value to the last array entry’s "raised" value. (e.g. Initially the raised value is 1000, after my update, it will be 1000 + an incoming value).

My hacky approach would be to do a find query to get the initial value and then do an update to add my incoming value to the initial value. But I’m sure there’s a simpler way.

Thanks in advance!

3

Answers


  1. Chosen as BEST ANSWER

    I ended up finding a solution that feels a bit simpler than the solutions I've seen.

    I added a field to the db called "status":

    {
      account_id: "ORG1",
      progress: [{week: 1, goal: 5000, raised: 2400, status: historic}, {week: 2, goal:5100,  raised: 1000, status: current}]
    } 
    

    Then I ended up using the positional operator ($) and increment functions.

    const collection = client.collection(goalCollection);
    
     // Search Query
    const query = {
      'account_id': ORG1,
      'progress.status: "current"
    };
    
    // Update with increment
    const update = {
       $inc: {"progress.$.raised": ADDITIONAL_SUM}
    }
    
    
    // Run the update
    const result = await collection.updateOne(query, update);
    

    Worked smoothly.


  2. One option is using an update pipeline:

    1. Split the array into the last item and all the rest.
    2. update the last item
    3. Build the array again using $concatArrays
    db.collection.update(
      {account_id: "ORG1"},
      [
        {$set: {
          lastItem: {$last: "$progress"},
          rest: {$slice: ["$progress", 0, {$subtract: [{$size: "$progress"}, 1]}]}
        }
        },
        {$set: {"lastItem.raised": {$add: ["$lastItem.raised", incomingValue]}}},
        {$set: {
          progress: {$concatArrays: ["$rest", ["$lastItem"]]},
          lastItem: "$$REMOVE",
          rest: "$$REMOVE"
        }
      }
    ])
    

    See how it works on the playground example – concatArrays

    Another option is using $reduce:

    Here we are iterating on the array, for each item checking if it is the last one, and if so updating it:

    db.collection.update(
      {account_id: "ORG1"},
      [
      {$set: {lastIndex: {$subtract: [{$size: "$progress"}, 1]}}},
      {$set: {
          lastIndex: "$$REMOVE",
          progress: {
            $reduce: {
              input: "$progress",
              initialValue: [],
              in: {
                $concatArrays: [
                  "$$value",
                  [
                    {
                      $cond: [
                        {$eq: [{$size: "$$value"}, "$lastIndex"]},
                        {$mergeObjects: [
                           "$$this",
                            {raised: {$add: ["$$this.raised", incomingValue]}}
                          ]
                        },
                        "$$this"
                      ]
                    }
                  ]
                ]
              }
            }
          }
        }
      }
    ])
    

    See how it works on the playground example – reduce

    Login or Signup to reply.
  3. One way to do it is by using "$function" in the update/aggregation pipeline.

    db.collection.update({
      "account_id": "ORG1"
    },
    [
      {
        "$set": {
          "progress": {
            "$function": {           // your increment value goes here топ
              "body": "function(prog) {prog[prog.length - 1].raised += 75; return prog}",
              "args": ["$progress"],
              "lang": "js"
            }
          }
        }
      }
    ])
    

    Try it on mongoplayground.net.

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