Is it possible to set a field in the last element in an array? I tried a couple of options but sadly did not work for me. (I’m using NodeJS)
I have added an example document at bottom in which chat is an array field with objects. Each object has question and answer fields. I want to update the document by setting answer in the last element, where answer will have the following shape:
It sounds like you’re actually trying to add a new element to the array rather than just updating the last element. If that’s the case, the correct approach would be to use the $push operator. This is the easiest way to append a new object to the end of an array in MongoDB.
const result = await collection.updateOne(
{ _id: documentId },
{ $push: { chat: newChat } } // Adds the new object to the end of the 'chat' array
);
$push: Adds a new object to the end of the chat array.
What I understand is that you want the chat array to become:
That is you want the 2nd element, that is chat[ 1 ], to the answer object because the question object is present and the answer is not.
If that is the case, look at this playground. It uses the $ operator to update the element specified by $elemMatch to specify an element that has a question but no answer.
It will not be necessarily the last, in fact it is the first that matches the $elemMatch.
It works the way I want, however, I’m still curious if it is possible to directly reference the last element of an array instead of matching the first element where answer is missing. Do you know if this is possible?
I wanted to ask a second follow-up question. I would like to set endedAt field to current date once all the questions have been answered. I guess I would need to use some conditional logic, but I’m not sure if the condition would be apply before or after I add an answer.
I was thinking to add all questions the first time the document is created:
{
"_id": {
"$oid": "670555ba370866c4aa470ad2"
},
"sessionId": "d32UwrkS5RnNKxukLh0a1Q1uMb6I7VdT",
"createdAt": {
"$date": "2024-10-08T15:54:34.035Z"
},
"updatedAt": {
"$date": "2024-10-08T15:54:34.035Z"
},
"chat": [
{
"question": {
"id": "cf97bfdd-fbd6-4297-8a36-57d95846a631",
"message": "What is your favorite breed of cat, and why?"
}
},
{
"question": {
"id": "fd0db069-483a-4d3e-b4f6-d08bc2929c2e",
"message": "How do you think cats communicate with their owners?"
}
},
{
"question": {
"id": "85879575-d7c3-4091-a6f9-2775ad9f2d8e",
"message": "Have you ever owned a cat? If so, what was their name and personality like?"
}
}
]
}
The only way I can think now is the following ugly way:
1 - update with aggregation syntax
2 - use $slice to get all the elements except the last one
3 - use $last to get the last element
4 - use $mergeObject to update the last element you got in 3.
5 - use $concatArray to reassemble the slice you got in 2 with an array containing the update last element of 4.
It is the best I can do.
But on this type of use case, you can have the arrary for this that are completed. And have a special object for the things that needs to be completed. Basically, rather that updating the last element of an array you update a current object. Once the current objet is completed you push it into the array. So the array only contains completed things. In your case the question without an answer would not be in the array, it would be in last_question object. Something like:
Rather than having to update the last element of the array, you update the first. And this is quite easy to do with the dot notation because you can update with set : { "chat.0.answer" : ... }
This could be even better than
With the reversed array the current incomplete object is the first one of array. I like it.