1

Intro: I am creating a StackExchange clone using Node and Mongo to learn the language. I am currently working on the API.

I have the following 'questionSchema':

var questionSchema = new Schema({
  _id       : {type: String, default: shortid.generate},
  title     : {type: String, required: true},
  question  : {type: String, required: true},
  user      : {type: Schema.ObjectId, ref: 'User'},
  points    : {type: Number, default: 0},
  date      : {type: Date, default: Date.now},
  answers   : [answerSchema],
  votes     : [{
                  user: {type: Schema.ObjectId, ref: 'User', required: true},
                  vote: {type: Number, enum: [-1,0,1]}
              }],
  __v       : {type: Number, select: false}
});

The idea is that when a user votes on a question the points field is incremented (or decremented) and the userid and vote added to the votes array. I have the vote array to detect if the user has already voted and prevent additional votes.

The problem: I'm having trouble actually checking if the user has voted (checking if their userid exists in the votes array). I have been playing around with adding the method 'hasVoted' to the questionSchema but:

  1. I'm not sure how to actually make the check happen.
  2. I'm also not sure if there is a way for me to filter the votes array during the query (at MongoDB) instead of after node gets the results.

This is my attempt at the method which I know is wrong:

//Has the user already voted on this question?
questionSchema.methods.hasVoted = function (userid, cb) {
  this.votes.filter(function(vote) {
    if(userid == vote._id) {
        return '1';
    } else {
        return '0';
    }
  });
};
N10
  • 43
  • 5

1 Answers1

2

I would recommend to make vote schema like so

var voteSchema = new Schema({
  user: {type: Schema.ObjectId, ref: 'User', required: true},
  vote     : {type: Number, required: true}
})
var questionSchema = new Schema({
  _id       : {type: String, default: shortid.generate},
  title     : {type: String, required: true},
  question  : {type: String, required: true},
  points    : {type: Number, default: 0},
  date      : {type: Date, default: Date.now},
  answers   : [answerSchema],
  votes     : [{type: Schema.ObjectId, ref: 'Vote', required: false}]
});

Then just get your question and go through all the votes.

QuestionSchema.findById(question.id)
     .populate('votes')
     .exec(function (err, question) {
     // go through all the votes here
 }

or query if there is an question with your user id inside the votes

 QuestionSchema.find()
     .and([{_id:questionId,},{votes.user:userId}])
     .populate('votes') //dunno if you really have to populate i think you don't have to
     .exec(function (err, user) {
     // check if(user)
 }

or do it like described here findOne Subdocument in Mongoose

//EDIT or if you don't change your schema

QuestionSchema.find({votes.user:userId})
 .exec(function (err, user) {
   // should return ALL questions where the user id is in the votes your want a specific question do it in a and like in the example above
 }

and if you only want that one element from the array you have to make a projection like described here How to find document and single subdocument matching given criterias in MongoDB collection

Community
  • 1
  • 1
Sprotte
  • 541
  • 6
  • 19
  • The problem I am having is 'going through all the votes'. I don't quite now the right way to do it. Am I just doing a for loop for each for in there and returning a value as soon as I find the thing I'm looking for? If I do have to iterate through each element in the array, can I also get Mongo to only return a single element from that array given the userid (user)? – N10 Jan 28 '16 at 22:43
  • It turns out I was using methods and statics incorrectly by not using callbacks (I'm new to JS). This answer helped me get on the right path. Thank you. – N10 Mar 22 '16 at 08:49