'How to count how many sub-documents have been updated? (mongodb)
I have this fairly simple Chat object:
{
_id: ObjectId('a4bd8c2f5g19b0a1d1'),
chatId: 1,
userId1: 100,
userId2: 234,
messages: [
{msgId: 1, senderId: 100, msgSentOnUnix: 1652046779, msgReadOnUnix: 1652046787, content: 'hello'},
{msgId: 2, senderId: 100, msgSentOnUnix: 1652046786, msgReadOnUnix: 1652046787, content: 'world'},
{msgId: 3, senderId: 234, msgSentOnUnix: 1652046795, msgReadOnUnix: -1, content: 'right back...'},
{msgId: 4, senderId: 234, msgSentOnUnix: 1652046802, msgReadOnUnix: -1, content: 'at you'},
]
}
It's a chat between 2 people, with a chatId, both users' IDs, and all their messages. I wish to update many sub-documents, and get the count of how many sub-documents were updated, in one single update query.
To be more specific:
I want to update user XXX's messages as "read" once user YYY enters the "chatroom", and I want to know how many "unread" messages were there. I don't want to do this with 2 queries, as I am concerned with concurrency issues...
Right now, for the update alone (without the count) I use this update command:
db.chats.updateOne(
{
userID1: 100,
userID2: 234,
},
{
$set: {
'messages.$[i].msgReadOnUnix': $$NOW
}
},
{
arrayFilters: [
{
$and: [
{ 'i.senderID': 234 }, // user 100 is reading users 234's messages
{ 'i.msgReadOnUnix': -1 }, // -1 Marks that a message is unread
]
}
]
},
)
Could a count be returned as a result here? to state how many sub-documents were modified?
Solution 1:[1]
So I eventually overcame the issue the way @prasad_ had mentioned,
the approach of running the test twice!
Once inside mongoDB, and once on the application level.
I.E. I used findOneAndUpdate({}) to return the document, without the { new: true } property, in order to get the document prior to the update, and then ran the same check that mongo ran:
(i am using node JS)
const { error, errorMsg, data } = await Chat.findOneAndUpdate(
{
userID1: 100,
userID2: 234,
},
{
$set: {
'messages.$[i].msgReadOnUnix': new Date().getTime(),
},
},
{
arrayFilters: [
{
$and: [{ 'i.senderID': 234}, { 'i.msgReadOnUnix': -1 }],
},
],
},
)
.lean()
.then((resultSet) => ({ error: false, data: resultSet }))
.catch((errorMsg) => ({ error: true, errorMsg }));
// running the check again here on the application level ?
for(let i=0; i < ... ; i++){
// write check logic here and count
}
Since it's the same document coming in it solves the concurrency issue that I was so afraid of. While this works great, can anyone think of a better way? One where I don't have to run my check twice?
Feels like there should be a better way query-wise to be able to solve this...
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 |
