MongoDB - Meteor : Update, add and delete an element from an array - mongodb

I am trying to select a specific element in an nested array in a hash like so.
post:{
_id,
comments:[{
comment_id:post.comments.length + 1,
comment: "hello world",
user_id:Meteor.userId()}]
}
My ultimate goal is to be able to add/edit/delete the nested comment by comment_id but I am having trouble trying to select the element I need in the first place.

If you only have comments in your post object you should only have a comments array like this:
Posts {
_id: String;
comments: [];
}
And to delete a comment by his id :
Posts.update({_id: postId, "comments.comment_id" : "commentIdToDelete"},
{ $pull:{"comments": {"comment_id": "commentIdToDelete"}}})
To update a comment by his id :
Posts.update({_id: postId, "comments.comment_id" : "commentIdToUpdate"},
{ $set:{"comments.$.comment": "A new comment"}})
UPDATE
In order to add a comment in the comments array we have to initialiaze the post document (if it's not already done):
Posts.insert({
comments: []
});
Now we have to retrieve the comments array size of the post to update:
let lengthComments = Posts.findOne({_id: postIdToUpdate}).comments.length;
And finally we can add a comment in the comments array:
Posts.update({_id: postIdToUpdate}, {
$push: {
"comments": {
"comment_id": lengthComments + 1,
"comment": "My comment",
"user_id": Meteor.userId(),
}
}
});

Related

Mongo update query for modifying array field failing

Important: Mongo Version I am using 3.4
Just Found out array filters is not available in 3.4
Any suggestion would be appreciated.
I have been trying this for days now I am trying to update particular comment text inside document. My document looks like this:
{
_id: "6ff1...<alphanumeric Id>",
name: "T-00001",
comment: [
{
_id: "6f7123....<alphanumeric Id>",
author: "6fsd...<alphanumeric Id pointing to user schema>"
text: "Hello",
edited: false
},
{
_id: "6f75323....<alphanumeric Id>",
author: "6ffgh21...<alphanumeric Id pointing to user schema>"
text: "Second Comment",
edited: false
}
]
}
What I am trying to achieve is to edit comment text here of particular comment id what my update query looks like is :
db.story.update(
{ _id: mongoose.Types.ObjectId(storyId) }, // Find that story story Id coming from body
{
$set : {
"comment.$[elem].text": "Update text with this",
"comment.$[elem].edited": true
}
},
{ "arrayFilters": [
{ "elem._id": mongoose.Types.ObjectId(commentId),
"elem.author": mongoose.Types.ObjectId(authorId)
} // Update only that comment.. Ids here coming from body
]}
)
Error Message I am getting
"errmsg" : "cannot use the part (comment of comment.$[elem].text) to traverse the element ({comment: [ { posted: new Date(1633092292670), delStatus: 0, _id: ObjectId('615702c4de4cf17dab996b4f'), by: \"User\", author: ObjectId('60ab52b1a3aa844774ca0fb3'), text: \"hello7\", edited: true }, { posted: new Date(1633699486732), delStatus: 0, _id: ObjectId('6160469e8aca585d3909708c'), by: \"User\", author: ObjectId('60ab52b1a3aa844774ca0fb3'), text: \"2021-10-08T13:24:46.671Z\" } ]})"
Found the solution god I am so bad....
db.story.update(
{
$and: [
{ _id: mongoose.Types.ObjectId(storyId)},
{ "comment._id": mongoose.Types.ObjectId(commentId)},
{ "comment.author": mongoose.Types.ObjectId(authorId)}
]
},
{
$set: {
"comment.$.text": body.text,
"comment.$.edited": true
}
}
)
With this I have a big problem whenever I hit this query it is updating some other comment until I remove the line:
{ "comment.author": mongoose.Types.ObjectId(authorId)}
Can someone please help?

$addToSet in nested array

I need to perform a $addToSet on an nested array, here an example:
{
_id: 123,
posts:[
{
_id: 234,
userid: 123 //its same id like the documents id,
likesUser: []
}
]
}
My API requires 2 parameters:
The ID of the document, and the Post ID in witch post i want to add.
The ID of the user that liked that post.
If a user likes an Post his User ID should be stored in likesUser.
Lets say a user likes a post. The document have the id 123, first i want to search the document with that ID. After that i want to search the post with the id 234. Now i want to store the ID of the users that liked the post in likesUser, in this case the user have the ID 5d345
{
_id: 123,
posts:[
{
_id: 234,
userid: 123 //its same id like the documents id,
likesUser: ["5d345"]
}
]
}
How do i archieve it? I have tried it with $elemMatch but it doesnt work it throws me error over error.
I have found out the result:
let result = await Post.findOneAndUpdate(
{
_id: mongoose.Types.ObjectId(userId),
"posts._id": mongoose.Types.ObjectId(postId)
},
{
$addToSet: {
"posts.$.likesUser": USER
}
}
);

Mongoose how to use positional operator to pull from double nested array with specific condition, and return new result

Suppose I have the following schema:
{
_id: ObjectId(1),
title: string,
answers: [
{
_id: ObjectId(2),
text: string,
upVotes: [
{
_id: ObjectId(3),
userId: ObjectId(4)
}
]
}
]
}
What I want is pull vote of a specific user from answer upvotes, and return the new update result.
For example, find a question with id 1, and get its specific answer with id 2, then from that answer pull my vote using userId inside upvotes.
I want to do it with a single findOneAndUpdate query
You can even use single $ positional with the $pull operator to update the nested array
db.collection.findOneAndUpdate(
{ "_id": ObjectId(1), "answers._id": ObjectId(2) },
{ "$pull": { "answers.$.upVotes": { "userId": ObjectId(4) }}}
)
I think I understood that you want to do a search in the specific array
db.collection.update(
{
"_id": "507f1f77bcf86cd799439011", // id field
"answers.upVotes._id":"507f1f77bcf86cd799439011" //id array
}
),{
"$set":{"answers.$.upVotes": {userId :"507f1f77bcf86cd799439011"}}},//edit
//use "addToSet" for add

Return Array of Populated Objects in Mongoose

I have a DB for a forum with 3 collections: Threads, Posts, Comments.
I have a GET request to return an individual forum thread that populates each thread with user's posts, and each user post with any comments that were made on it which is working as shown below:
router.get('/:id', (req, res) => {
Threads
.findById(req.params.id)
.lean()
.populate({path: 'posts'})
.exec(function(err, docs){
var options = {
path: 'posts.comments',
model: 'comments'
};
if(err) return res.json(500);
Threads.populate(docs, options, function(err, thread){
res.json(thread);
})
})
})
When this GET request is made it will return a forum thread like so:
{
"_id": "5924ad549a08ed4e70a9c89f",
"title": "Testing Full Schemas",
"author": "Mongoose",
"content": "Schema Content",
"posts": [
{
"_id": "5924ad999a08ed4e70a9c8a0",
"content": "New Schema Post",
"user": "Mongodb",
"comments": [
{
"_id": "5924ae489a08ed4e70a9c8a1",
"comment": "New Schema Content",
"user": "Matt",
"likes": 0,
"created": "2017-05-25T12:41:58.319Z"
}
]
}
Now I need a GET request to return an array of ALL threads (router.get('/')) with each threads posts and comments to be populated. I tried to replace:
Threads
.findById(req.params.id)
with
Threads
.find(req.params.id)
but it is not working. Does anyone have an idea of how this could be accomplished?
To return all the threads, simply use find without any match condition in it.
Also, populate posts and 'posts.comment' in the find query itself, you don't need to do it in the callback of the find.
use population across multiple levels
**Try this:
Threads.find({})
.populate({
path:'posts',
populate :{
path : comments
}
})
.exec(function(err,docs){
//docs is the array of all the Threads, with posts and comments populated within it
})
Read Mongoose Documentation on Populate and Nested Population for detailed information. ( Search for Populating across multiple levels)
findById and findOne returns a single document, where find returns a cursor. Once you go through the cursor of find, you are at the end, and there are no more documents.
Using find query:-
ModelName.find({_id:req.params.id})
.populate({
path:'posts',
populate :{
path : comments
}
},(error,data)=>{
if(error){
res.json(error);
}
else{
res.json(data);
}
})
Using findById query:-
ModelName.findById(req.params.id)
.populate({
path:'posts',
populate :{
path : comments
}
},(error,data)=>{
if(error){
res.json(error);
}
else{
res.json(data);
}
})

Not sure how to save in MongoDB

I am trying to push to a sub-collection for a MongoDB and I am not sure how to go about it.
So, my post collection structure:
{
"_id": "58a3189b67476b420e465f8b",
"postID": "123456",
"comments": [
{
"subComments": [],
"comment": "This is a test",
"commentID": 1
}
]
}
So, what I want to do is find the above document using findOneAndUpdate and then select this particular comment (commentID == 1), and then push into the subComments of it.
I can see how you can findOneAndUpdate by it's postID and add comments by pushing in Mongoose but how do I find the post, and then the comment by it's commentID and then push into it's subComment?
Thanks in advance guys! Very stuck on this last piece of the puzzle.
It's similar to other find queries.
You can try this:
Post.findOneAndUpdate({
"postID" : givenPostID,
"comments.commentID" : givenCommentID
},{
$push : {
"comment.$.subComments" : givenSubComment
}
},function(err,result){...});
comments.commentID: givenCommentID will find the comment element with commentID == givenCommentID.
comment.$.subComments will push givenSubComment to the subComments array of the matched element of the comments array (matched based on commentID).
For more information Please read MongoDB $(update) operator documentation.