Reputation: 2933
The documents in the collection have an array field of sub-documents, each with a counter that should be increased up to three, but if the array doesn't have a sub-document with a given key it should create it with the default values.
The documentation for $addToSet says:
Behavior
$addToSet only ensures that there are no duplicate items added to the set and does not affect existing duplicate elements. $addToSet does not guarantee a particular ordering of elements in the modified set.
Missing Field
If you use $addToSet on a field is absent in the document to update, $addToSet creates the array field with the specified value as its element.
The problem is that the array field is not created in the document if it doesn't exist, as stated in the documentation.
This is what I'm currently using to accomplish the operation:
// increase counter in element of array if element exist and counter is less than 3
collection.updateOne({
key_1,
"array.key_2": key_2,
"array.counter": {$lt: 3}
}, {
$inc: {"array.$.counter": 1}
})
.then(res => {
console.log("!!!!1:", res.modifiedCount, res.upsertedId, res.upsertedCount, res.matchedCount);
if (res.matchedCount) return res.matchedCount;
// create element in array with the default values,
// also create the array field if it doesn't exist
collection.updateOne({
key_1
}, {
$addToSet: {
array: {key_2, counter: 1}
}
})
.then(res => {
console.log("!!!!2:", res.modifiedCount, res.upsertedId, res.upsertedCount, res.matchedCount);
return res.matchedCount;
})
.catch(e => console.log(e))
})
.catch(e => console.log(e))
Using upsert
in the second query, it creates the array field if it doesn't exist but then when array.counter
reaches 3 in subsequent calls to increase its value, the operation creates a new sub-document in the array with the same values for array.key_2
and array.date
, effectible duplicating the entry although with array.counter
set to 1 instead of 3.
I'm using mongo version 4.2.1
Update:
Below there is a sample document before trying to run the operation on the second subdocument:
{
"key_1": 1,
"array": [
{
"key_2": 1,
"counter" 1
}, {
"key_2": 2,
"counter" 3
}
]
}
This is what I'm getting as a result when using upsert
:
{
"key_1": 1,
"array": [
{
"key_2": 1,
"counter" 1
}, {
"key_2": 2,
"counter" 3
}, {
"key_2": 2,
"counter" 1
}
]
}
The operation is duplicating the second subdocument in array, but if upsert
is not used then the array field is not created if it's not already in the parent document, which is the oposite of the expected behavior for $addToSet
from what it says in the documentation.
Update 2
These are the steps to reproduce the issue:
Run the operation with key_1
set to 1, and upsert
disabled. None of the queries modifies the document. The array
field is not created.
{
"key_1": 1
}
Enable upsert
and run the operation again. The array
field is created in the second query:
{
"key_1": 1,
"array": [
{
"key_2": 1,
"counter" 1
}
]
}
Run the operation again twice more. The first query modifies the document twice:
{
"key_1": 1,
"array": [
{
"key_2": 1,
"counter" 3
}
]
}
Run the operation once more. The first query doesn't modifies the document. The second query creates a duplicate:
{
"key_1": 1,
"array": [
{
"key_2": 1,
"counter" 3
}, {
"key_2": 1,
"counter" 1
}
]
}
Upvotes: 1
Views: 1748
Reputation: 17915
Please try this :
var key_2Value = 2;
var firstFilterQuery = {
key_1: 1,
array: {
$elemMatch: {
"key_2": key_2Value,
"date": 'someDate',
"conter": { $lte: 3 }
}
}
}
var secondFilterQuery = {
key_1: 1,
"array.key_2": {$ne: key_2Value}
}
var defaultDoc = {key_2 : key_2Value, "date": 'someDefaultDate',counter: 1}
Query :
collection.bulkWrite([
{
updateOne:
{
"filter": firstFilterQuery,
"update": { $inc: { "array.$.conter": 1 } }
}
}, {
updateOne:
{
"filter": secondFilterQuery,
"update": { $push: { array: defaultDoc }
}
}
}
])
With the above query, you can achieve what you wanted in one DB call(at any given case only one 'updateOne' should update the DB), Output should look something like :
{
"acknowledged" : true,
"deletedCount" : 0.0,
"insertedCount" : 0.0,
"matchedCount" : 1.0,
"upsertedCount" : 0.0,
"insertedIds" : {},
"upsertedIds" : {}
}
Upvotes: 1