Sto tentando di implementare un sistema di voto simile a StackOverflow o reddit in cui un utente può votare solo una volta su un determinato post.Voto superiore e inferiore con Backbone, Express e Mongoose
Dopo aver seguito il consiglio dato qui
storing upvotes/downvotes in mongodb
ho creato due schemi per memorizzare i upvotes ei downvotes. Per ogni utente tengo traccia dei post che l'utente ha votato.
Messaggio Schema: Schema
var postSchema = new Schema({
name: String,
votes: Number,
votetype: Number,
postedBy: { type: String, ref: 'User' },
});
utente:
var userSchema = new Schema({
twittername: String,
twitterID: Number,
votedPosts : [{ _id : mongoose.Schema.Types.ObjectId , votetype: Number }]
});
A seconda del l'utente corrente ogni post sta per avere una visione diversa, se l'utente ha votato il post prima della il pulsante upvote o downvote sarà arancione (simile allo stackoverflow), quindi ho il seguente modello (semplificato) backbone per un post:
var PostModel = Backbone.Model.extend({
urlRoot : '/tweet',
idAttribute: '_id',
defaults:{
name: '',
votes: 0,
votetype: 0,
postedBy : '',
},
upvote: function(){
this.set ({votetype : 1 }, {votes : this.get('votes') + 1});
this.save();
$.ajax({
type: "POST",
url:"/upvote",
data : {postID : this.id , userID : window.userID , vote: 1},
success : function(result){
console.log(result);
},
error: function(jqXHR, textStatus, errorThrown) {
console.log(textStatus, errorThrown);
}
});
},
});
Quindi il votetype inizia con uno "0" se l'utente non ha votato sul post prima e il suo "1" o "-1" dipende dal voto. Nella funzione upvote, come ho aggiornare e salvare la votetype di quel post, ho anche inviare una richiesta Ajax di aggiungere che post per voto messaggi di matrice dell'utente nel controller post come il seguente:
exports.upvote = function(req,res){
var postID = req.body.postID;
var newvotetype = req.body.vote;
User.findOne({twitterID : req.body.userID}, {votedPosts : { $elemMatch: { "_id": postID }}},
function(err, post) {
if (post.votedPosts.length == 0) {
//append to the array
User.update({twitterID : req.body.userID} , { $push : {votedPosts : {_id : postID , votetype: newvotetype}}} ,function (err, user, raw) {
if (err){console.log(err);}
});
console.log(post);
console.log("no one has voted on this before");
}
else {
//update in the existing array
User.update({twitterID : req.body.userID, 'votedPosts._id': postID }, { $set : {'votedPosts.$.votetype' : newvotetype}} ,function (err, user, raw) {
if (err){console.log(err);}
});
}
}
);
res.send("success");
res.end();
};
potrei avere alcune cattive decisioni di progettazione ma finora sembra che questo funzioni bene. Per favore, dimmi se posso apportare alcuni miglioramenti sul mio codice o su qualsiasi altra cosa sul mio progetto.
Ora arriva la parte difficile. In qualche modo devo guardare attraverso entrambi questi schemi e cambiare il "votetype" di ogni post prima di fare un collection.fetch() .. mi si avvicinò con una soluzione brutto come questo:
https://gist.github.com/gorkemyurt/6042558
(i inseriscilo in un gitano quindi forse è più leggibile, mi dispiace per il brutto codice ..)
e una volta che aggiorno il tipo di voto di ogni post a seconda dell'utente lo passo alla mia vista backbone, e nel mio modello I fare qualcosa di molto semplice come:
<div class="post-container">
<div id="arrow-container">
<% if (votetype == 1) { %>
<p><img id="arrowup" src="/images/arrow-up-orange.jpg"></p>
<p><img id="arrowdown" src="/images/arrow-down.jpg"></p>
<% } %>
<% if (votetype == 0) { %>
<p><img id="arrowup" src="/images/arrow-up.jpg"></p>
<p><img id="arrowdown" src="/images/arrow-down.jpg"></p>
<% } %>
<% if (votetype == -1) { %>
<p><img id="arrowup" src="/images/arrow-up.jpg"></p>
<p><img id="arrowdown" src="/images/arrow-down-orange.jpg"></p>
<% } %>
</div>
<div id="text-container">
<p><h2><%- name %></h2></p>
<p><%- dateCreated %></p>
<p>Posted by: <%- postedBy %></p>
</div>
</div>
Questa soluzione funziona, ma non penso che sia davvero efficiente cercare tutti i post e tutti i post che l'utente ha votato ogni volta che un utente apre la pagina per visualizzare la visualizzazione personalizzata dei post. Qualcuno può pensare a un modo migliore per farlo? Sono aperto a qualsiasi consiglio o critica circa il mio codice .. grazie in anticipo
grazie mille per la risposta, è stato davvero utile. Sto solo cercando di imparare .. puoi chiarire di nuovo questa parte "Ma come risponderà la tua applicazione se l'aggressore invierà 100 o anche 1000 voti? Questa operazione dovrebbe essere atomica, e dovresti incrementare i voti sul server quando fai Richiesta POST all'endpoint/upvote. " –
Sì, da quello che hai scritto, ho concluso che fai due richieste: una quando salvi 'PostModel': this.save(); e uno subito dopo quando si esegue 'POST/upvote'. Fai queste richieste per assegnare un voto dell'utente e, come in qualsiasi altro luogo, devi assicurarti che gli utenti non possano sfruttarlo. Cosa succede se qualche utente invierà solo la prima richiesta - quella che incrementa il voto, senza inviare il secondo - fondamentalmente rendendo i propri voti anonimi. Il modo corretto sarebbe solo assegnando nuovi valori nel modello senza chiamare 'this.save' e scrivendo nel databe * il * codice lato server su'/upvote' –
Come aggiungere o modificare I vote? Dalla mia attuale comprensione di MongoDB dovrei prima vedere se c'è un voto per l'utente e poi aggiungere o modificare il voto. Ma cosa succede se un altro voto per lui viene aggiunto dopo aver controllato se c'è un voto? –