'Max size of MongoDB array when using $addToSet
using:
db.collection('users').update(
{ "_id": user._id},
{ "$addToSet": { "keywords.RFD": keywords } },
function(err, result) {
if (err) {
console.log(
'failed to add keyword "%s" for user: %s', keywords, user.email);
throw err;
}
else {
console.log('Keyword added: ' + keywords );
res.end('{"success" : "Updated Successfully.", "status" : 200}');
}
}
);
I would like to limit the number of keywords to 50 and throw an error if the keywords exceed 50. Is there anyway I can check with MongoDB if there are already 50 in the database?
Solution 1:[1]
I thought maybe there was a way to add a strict limit to the array itself using MongoDB like a capped array but I don't think that is possible. I ended up just using logic.
if (user.settings.max_keywords <= result.keywords.RFD.length){
res.end('{"error" : "Too many keywords.", "status" : 401}');
}
Solution 2:[2]
const MAX_KEYWORDS = 50;
const maxArrayPath = `keywords.RFD.${MAX_KEYWORDS - 1}`;
db.collection('users').updateOne(
{ _id: user._id,
[maxArrayPath]: { $exists: false }
},
{
$addToSet: {
"keywords.RFD": keywords,
}
}
);
Solution 3:[3]
Tell you what. You didn't read the documentation properly. So keywords is a "misnomer" here as it can only be a singular string or other object in this context. Certainly not an array.
But yes MongoDB has a way to handle this. Much as I gave the pointer, you need to read the $each documentation, along with other modifiers:
db.collection('users').update(
{ "_id": user._id},
{
"$addToSet": {
"keywords.RFD": {
"$each": [keywords],
}
}
)
Then you can actually invoke a separate update statement invoking $slice with a blank call to $each. Which, funnily enough, works perfectly.
db.collection('users').update(
{ "_id": user._id},
{
"$push": {
"keywords.RFD": {
"push": {"$each": [] },
},
"$slice": -50
}
)
The main key modifier here is $slice which is a form to "limit" the size of an array.
Take note that you need MongoDB 2.6 or greater as a server version for these operators to be valid on updates. It's a compelling reason to upgrade though if you need this sort of interaction.
P.S.: As I said earlier, the context of "keywords" in your example was a "misnomer" beacause it hat to be a "scalar" value. Under the $each modifier, this can indeed be an array. Therefore:
db.collection('users').update(
{ "_id": user._id},
{
"$addToSet": {
"keywords.RFD": {
"$each": { "$each": keywords },
}
}
)
db.collection('users').update(
{ "_id": user._id},
{
"$push": {
"keywords.RFD": {
"push": [],
},
"$slice": -50
}
)
Not atomic. But as close as you can get, especially when using the Bulk API.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | davegallant |
| Solution 2 | Milan Rakos |
| Solution 3 | benediktwerner |
