I needed to run a quick and (I thought) trivial query to update some incorrect data. I'm trying to update the very first entry of an array in a document.
Note: My posted queries run against
db
instead of a collection, and use$documents
to generate test data in the pipline so you can evaluate them straight away. No need to create a collection and seed it with test data.
I thought this would be utterly trivial:
db.aggregate([
{ $documents: [{ values: [-354, 52, 234, 1] }] },
{ $set: { 'values.0': 12 } }
]);
However the result of that is bizarre:
{
values: [
{
'0': 12
},
{
'0': 12
},
{
'0': 12
},
{
'0': 12
}
]
}
I finally managed to get it working, but with a convoluted query with some interesting caveats:
db.aggregate([
{ $documents: [{ values: [-354, 52, 234, 1] }] },
{
$set: {
values: {
$concatArrays: [[12], { $slice: ['$values', 1, 2147483647] }],
}7
},
},
]);
This gives the correct result:
{
values: [
12,
52,
234,
1
]
}
I must first build a single value array for the new first value. Then concatenate the values in the array, skipping the first. MongoDB doesn't seem to have a proper operator for that, so as a tenuous stand-in, I'm using $slice
with the start parameter set to 1, and the number of entries to 2³¹−1, which is the maximum value for that parameter. It could be that there are more array entries than that. But at that point I'm pretty you're going to run into bigger problems, since many more operation parameters are 32-bit integers, and all kinds of operations will fail.
Now this seems utterly ridiculous to me, I think I must be doing something simple wrong. There's no way this janky method is the normal way to to update an array value with a known index in MongoDB.
Since I couldn't find a method that worked online, I'll post this here, and hope someone can tell me how it's really supposed to be done.
$map
/$filter
/$reduce
or arrayFilters to locate array elements and manipulate them.new Array(Math.pow(2,31))
in your browser console, it'll happily make an array of 2³¹ entries. That's why if you want to populate an array with a mapping, you'll have to useArray.from({length: Math.pow(2,31)})
instead. Note: Do not run the latter in you browser. You'll eat your system memory.