Reputation: 15982
My collection looks like this. A document contains an array of suggestions
. Each suggestion can have an array of emotes
. A user can only emote once per suggestion. Note that a document can have multiple suggestions
{
id: 1,
suggestions:[
{
id: 463,
emotes:[
{
id: 35,
userId: 2
},
{
id: 23,
userId: 3
},
]
},
...
]
},
...
emotes
can only contain 1 instance of a userId
. I need to check if userId
exists in emotes
before pushing a new emote element.
My attempted solutions:
(I'm using the update command syntax to access MongoDB 3.6 features.)
First attempt:
addToSet
will not work because the emote
array elements are keyed by userId
, not the entire object.
db.get().command({
update: 'channels',
updates:[
{
q:{
channelId,
"suggestions.id": suggestionOid,
},
u:{
$addToSet:
{
"suggestions.$[s].emotes": data
}
},
arrayFilters:[
{ 's.id': suggestionOid }
]
}
]
})
Second attempt:
The below doesn't work. The user could already have an emote for another suggestion, which will cause the $ne
query condition to fail. Which means the user can only emote one suggestion per document, they should be able to emote all suggestions for a document.
db.get().command({
update: 'channels',
updates:[
{
q:{
channelId,
'suggestions.emotes.userId': {$ne: userId },
"suggestions.id": suggestionOid,
},
u:{
$push:
{
"suggestions.$[s].emotes": data
}
},
arrayFilters:[
{ 's.id': suggestionOid }
]
}
]
})
I am open to moving emotes
to a new collection or changing its structure, if that makes things easier. I can't change anything else though.
Upvotes: 2
Views: 988
Reputation: 15982
I solved this by using the $elemMatch
operator.
return channels.updateOne(
{
channelId,
"suggestions":{
$elemMatch:{
id: suggestionOid,
'emotes.user': {$ne: user }
}
},
},
{
$push:
{
"suggestions.$.emotes": data
}
}
)
Important bit from the mongodb docs https://docs.mongodb.com/manual/reference/operator/update/positional/
If the query matches the array using a negation operator, such as $ne, $not, or $nin, then you cannot use the positional operator to update values from this array.
However, if the negated portion of the query is inside of an $elemMatch expression, then you can use the positional operator to update this field.
Upvotes: 2