Does anyone know how to add an array element to a mongodb array and set a "virtual" order to the size of the array, all in an atomic operation?
So, something like:
db.users.updateOne(
{ _id: 1},
{ $addToSet: { images: {name: 'new image', order: "size of the array"} } }
)
The idea is to always have the last array element added have an order that is last.
Update:
Data before op:
{
_id: 1,
images: [
{ name: 'some name', order: 0 }
]
}
Data after op:
{
_id: 1,
images: [
{ name: 'some name', order: 0 },
{ name: 'new image', order: 1 }
]
}
Update 2:
In case anyone is interested, to update the order atomically, you can do something like this (build this dynamically, of course):
db.collection.update({
_id: 1
},
{
$set: {
"images.$[elem].order": 4,
"images.$[elem2].order": 3
}
},
{
arrayFilters: [
{
"elem.name": {
$eq: "some name"
}
},
{
"elem2.name": {
$eq: "new image"
}
}
]
})
Thank you!
Assuming your order
field would be an integer field, you can use $let
to compute the next order number and use $concatArrays
to append it to the end of the array.
Some scenarios:
images
already has data in it: $max
will get the largest order
number, $add
1 to it.images
is an empty array: the $max
result will fallback to 0 due to $ifNull
images.order
is not perfectly 0-th ordered: it may have some gaps in between, or not starting from 0. It will be handled by $max
and $add
1 logic.images
is a null field or does not exists: it will be safeguarded by $ifNull
and fall back to an empty array.db.collection.update({},
[
{
"$set": {
"images": {
"$let": {
"vars": {
"seq": {
"$ifNull": [
{
"$add": [
{
"$max": "$images.order"
},
1
]
},
0
]
}
},
in: {
"$concatArrays": [
{
"$ifNull": [
"$images",
[]
]
},
[
//your payload here
{
"name": "new image",
"order": "$$seq"
}
]
]
}
}
}
}
}
],
{
multi: true
})