2

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.

1 Answer 1

2

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.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.