Mongoose update sub document if exists

2020-06-27 13:48发布

问题:

I have following model :

var VoteSchema = new Schema({
    up : Boolean
    , createdBy:{type:ObjectId, ref:'users'}
    , createdOn : {type:Date, default:Date.now}
});

var QuestionSchema = newSchema({
    title:String
    , description:String
    , votes : [VoteSchema]
    , createdBy:{type:ObjectId, ref:'users'}
    , createdOn : {type:Date, default:Date.now}
});

var Question = mongoose.model('questions',QuestionSchema);

Suppose user1 is logged in user and question1 is current / viewing question.The user can upvote({up:true}) or downvote({up:false}) a question at any time. How can I add a new vote if a user1 have not casted a vote for question1 else update the vote.

I have been able to write the following lines of code:

QuestionSchema.statics.castVote = function(questionId, vote) {
    //NOTE : vote.createdBy equalsto loggedInUserID

    Q.nbind(Question.findOne, Question)({
        $and:[
            {_id:questionId},
            {'votes.createdBy':vote.createdBy}
        ]
    }).then(function(doc) {

        if(doc) {
           //I am clue less
           //doc.votes is a list of votes for this question
           // how can I get the particular vote casted by the user - vote.createdBy
        }else {
           //Question.votes.push(vote);
        }
    });

});

回答1:

So you're part of the way there, but of course when you don't find a doc then you will not have a doc to work with in the callback. MongoDB has native ways of handling these sorts of updates, but of course you do need to test for the match as you are.

What we can do here is just work within the true or false condition of where the document exists.

Considering vote.value to be your true or false for the "upvote"

Where you do find that there is a matching document you can issue an update like this:

Question.update(
  { 
    _id: questionId, 
    "votes.createdBy" vote.createdBy,
    "votes.up": {"$ne": vote.value }
  },
  { $set: { "votes.$.up": vote.value } }
);

So that matches and uses a positional $ operator to make sure the correct index of the matching item is updated. What I added there makes sure that you don't even touch the document where the vote.vaule is already of the same value.

And in the false condition you want to $push onto the array with the new item:

Question.update(
  { 
    _id: questionId
  },
  { 
    $push: { 
      "votes": {
        "up": vote.value,
        "createdBy": vote.createdBy
      }
    }
  }
);

Of course add the callback details where writeConcern is applied, which you probably do.