Hello everyone,
I have been struggling with this topic for a while, maybe you have an idea how to solve this better. I am using the NodeJS SKD.
I have an array of objects, something like:
{
array: [
{ name: "Obj1" },
{ name: "Obj2" },
{ name: "Obj3" }
],
}
```
I want to (a) update the property of a single array element and (b) after the update insert a new element into the array.
Ideally, I would like to do this on one update operation, to save roundtrips and to avoid an inconsistent state.
What I found is, that updating works fine, as long as I do not use an aggregation:
`
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" },
},
);
`
The snippet above works fine and only updates the second array element. BUT if I want to do the same in an aggregation, it does not work anymore:
`
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" },
},
],
);
`
This statement will update every field in the array, create a new object with the name 1 and a property called name:
``
{
"_id": "67939109ca5e4686dc8f2c91",
"array": [
{
"1": {
"name": "Update second array element only"
},
"name": "Obj1"
},
{
"1": {
"name": "Update second array element only"
},
"name": "Obj2"
},
{
"1": {
"name": "Update second array element only"
},
"name": "Obj3"
}
]
}
`
The only alternative that I have found, is to use a combination of $`arrayElemAt` with `$mergeObject` to select and update the array element in question and then use `$concantArray` and `$slice` to construct a new array:
`
const { insertedId } = await col.insertOne({
array: [{ name: "Obj1" }, { name: "Obj2" }, { name: "Obj3" }],
});
await col.updateOne(
{
_id: insertedId,
},
[
{
$set: {
array: {
$concatArrays: [
[
{
$mergeObjects: [
{ $arrayElemAt: ["$array", 0] },
{ name: "Update first entry only" },
],
},
],
{ $slice: ["$array", 1, { $size: "$array" }] },
],
},
},
},
],
);
`
This solution seems to be very complicated, hard to read and extend, and gets very very complicated, if you work with nested arrays , i.e. doing an update in an array in an object of arrays.
I also find this very confusing, why does the `$set` behave differently, depending on whether it is part of an aggregation or not.
1. Is this desired behavior?
2. Is there a better way to achieve the same result?