Search code examples
mongodbsubdocument

MongoDB update a specific item of sub document (array) based on condition?


I have a PostSchema which has a property array called votedBy:[],this is of type votedBySchema.Like below:

var votedBySchema = new Schema({
voteType:{
    type:Boolean,default:false
},
voter:{
    type:Schema.Types.ObjectId

}
});
const PostSchema = new Schema({

_authorId:{
    type:Schema.Types.ObjectId,
    required:true
},
title:{
    type:String,
    required:true
},
body:{
    type:String,
},
date:{
    type:Date
},
hidden:{
    type:Boolean,
    default:false
},
popularity:{
       type:Boolean
},
votedBy:[{
    type:votedBySchema,

}]
});

Now I fire a patch request from postman with to url /post/:postId.Here I run a middleware which checks if user requesting has a valid token.And put userObject and token on req object.

Next I want that if user is author of the post then he can change everything about the post but if he is not the author then he can only change popularity i.e upVote or downVote.

My logic was that every voting person must be registered in votedBy array. like:

votedBy:[
    {voteType:true,voter:'[email protected]'}, //upvote
    {voteType:false,voter:'[email protected]'}//downvote
]

As Email is unique one must only be able to upVote or downVote so unique entry on basis of email in votedBy.

my route logic code:

app.patch('/post/:postId',authenticate,(req,res)=>{
console.log("[server.js]=>request ${req.method} to ${req.url}")
let body = _.pick(req.body,["title","body","popularity","hidden"])
let user = req.user;
let userId = user._id;
let postId = req.params.postId;
Post.findById(postId).then((post)=>{
    let oldPost = post;//No use in code
    console.log(`Found post ${post}`);
    let postAuthorId = post._authorId.toString();
    if(postAuthorId == userId){
        post.title = body.title || post.title;//new,old
        post.body = body.body || post.body;
        post.hidden = body.hidden ||post.hidden;
        post.update(post,{new:true}).then((postUpdated)=>{
            console.log("Post Updated");
            res.send(postUpdated);
        });

    }
    else{
        let voter = user.email;
        console.log("\n voter is \n",voter)

        let voteType = body.popularity;
        //below code needs to be changed
       /* post.update(
            {   
                $set:{
                    'votedBy.$.voteType':voteType,
                    'votedBy.$.voter':voter
                }

            },
            {
                new:true
            }
            ).then((iDontKnow)=>{
                console.log('I dont know',iDontKnow);
                res.send(post);
            })*///Update or push the votedBy array for voter







    }  
});
});

Note I want to give a voting feature same as fb/youtube. Also suggest if there is any other way around.


Solution

  • Idea of doing this is fine but if I would have implemented this will have kept userId for mapping rather than user email.

    Eventually you will have that post from front end so you can send value from front end, but that will not be a that good a method.

    post.findOneAndUpdate( { userId : 'id of user from session' }, { "votedBy.$. voteType" : "true/false"}, { upsert : true }, callback );
    

    So you need to perform find operation first to get value of vote type for particular user and then update it

    Route code will be something like

    dbOperation.getPostData(request.body.postId, userId , (err,result)=>{
             if(err){ //log the error} 
             else if(result!=undefined){
                dbOperation.updatePost(request.body, (err,result)=>{
                  if(err){ //log the error} 
                  else{ 
                    response.json({message:'update',code:200,success:true});
                       }
                 });
             else{
                    response.json({message:'post not found',code:404,success:false});
                 }
    });