3

Let's say a have a simple document:

{
   array: ["a", "b", "c", "d"]
}

How to modify the second value in aggregation?

With update it is very simple:

db.collection.updateOne({},
   { $set: { "array.1": "B" } }
)

gives:

{
   array: ["a", "B", "c", "d"]
}

In aggregation framework you can use this one:

db.collection.aggregate([
   {
      $set: {
         "array": {
            $map: {
               input: "$array",
               in: {
                  $cond: {
                     if: { $eq: [{ $indexOfArray: ["$array", "$$this"] }, 1] },
                     then: "B",
                     else: "$$this"
                  }
               }
            }
         }
      }
   }
])

However, this fails when the array is not a Set, i.e. values are not unique like this one

{
   array: ["a", "b", "c", "b"]
}

3 Answers 3

4

You can loop over the $size(length) of the array using $range and then get the index of the element and can change the value of the index element to whatever you want.

db.collection.aggregate([
  { "$project": {
    "array": {
      "$map": {
        "input": { "$range": [0, { "$size": "$array" }] },
        "in": {
          "$cond": [
            { "$eq": ["$$this", 1] },
            "B",
            { "$arrayElemAt": ["$array", "$$this"] }
          ]
        }
      }
    }
  }}
])

MongoPlayground

Sign up to request clarification or add additional context in comments.

Comments

1

Another solution is to $unwind your array with includeArrayIndex and preserveNullAndEmptyArrays, set value with condition, then group. But beware, although it can be useful in some situations, it can consume more server resources in some cases.

db.collection.aggregate([
  {
    $unwind: {
      path: "$array",
      includeArrayIndex: "index",
      preserveNullAndEmptyArrays: true
    }
  },
  {
    $addFields: {
      array: {
        $cond: {
          if: {
            $eq: [
              "$index",
              1
            ]
          },
          then: "B",
          else: "$array"
        }
      }
    }
  },
  {
    $group: {
      _id: "$_id",
      array: {
        $push: "$array"
      }
    }
  }
])

you can test here

1 Comment

all this to modify 1 element in array :(
0

I found a solution which requires less coding:

db.coll.aggregate([
   {
      $set: {
         array: {
            $function: {
               body: function (array, i, val) { array[i] = val; return array; },
               args: ["$array", 1, "B"],
               lang: "js"
            }
         }
      }
   }
])

When you need to update nested array, then the difference is even bigger. Let's assume a nested array as this:

{
   action: [
      { task: ['a', 'b', 'c', 'd'] },
      { task: ['aa', 'bb', 'cc', 'dd'] },
      { task: ['aaa', 'bbb', 'ccc', 'ddd'] }
   ]
}

It can be updated simply with

db.collection.aggregate([
   {
      $set: {
         action: {
            $function: {
               body: function (action, a, t, val) { action[a].task[t] = val; return action; },
               args: ["$action", 1, 2, "new CC"],
               lang: "js"
            }
         }
      }
   }
])

compared to

let arrayIndex = 1
let taskIndex = 2
db.collection.aggregate([
     {
        $set: {
           action: {
              $map: {
                 input: { $range: [0, { $size: "$action" }] },
                 as: "a",
                 in: {
                    $cond: {
                       if: { $eq: ["$$a", arrayIndex] },
                       then: {
                          $let: {
                             vars: { action: { $arrayElemAt: ["$action", "$$a"] } },
                             in: {
                                $mergeObjects: ["$$action", {
                                   task: {
                                      $map: {
                                         input: { $range: [0, { $size: "$$action.task" }] },
                                         as: "t",
                                         in: {
                                            $cond: {
                                               if: { $eq: ["$$t", taskIndex] },
                                               then: "new CC",
                                               else: { $arrayElemAt: ["$$action.task", "$$t"] }
                                            }
                                         }
                                      }
                                   }
                                }]
                             }
                          }
                       },
                       else: { $arrayElemAt: ["$action", "$$a"] }
                    }
                 }
              }
           }
        }
     }
 ])

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.