Your first and second updateOne queries are identical. I think the first one isn’t supposed to use the aggregation syntax and was actually meant to be:

const { insertedId } = await col.insertOne({
  array: [{ name: "Obj1" }, { name: "Obj2" }, { name: "Obj3" }],
});

await col.updateOne(
  {
    _id: insertedId,
  },
  {
    $set: { "array.1.name": "Update second array element only" },
  },
);

Fully agreed. But I don’t know if the issue is with $set specifically or aggregations in general.

To fully appreciate the complicated-ness of it, if you were inserting at the 5th position of a longer array, you would need $concat: [slice-before, $merge: [updated-elem], slice-after]:

[
  {
    $set: {
      array: {
        $concatArrays: [
          { $slice: ["$array", 0, 5] },
          [
            {
              $mergeObjects: [
                { $arrayElemAt: ["$array", 5] },
                { name: "Update fifth entry only" },
              ],
            },
          ],
          { $slice: ["$array", 6, { $size: "$array" }] },
        ],
      },
    },
  },
]

There are several operators that have different behaviours in aggregations vs updates, or just aren’t available. Like $push as an array operator just not being available in aggregations (in updates agg pipelines and agg queries). For aggregations, it’s only available in $bucket, $bucketAuto, $group, $setWindowFields.

Same goes for Array $addToSet vs Agg $addToSet.

Well, same goes for all Array update operators. Either not available in aggregations or have a completely different behaviour.

This is a good workaround but is only applicable for cases where “I want to update a single element in an array”. It doesn’t work for situations where you have a multi-stage aggregation pipeline where moving out one update op is impractical (and possibly incorrect).