2013-07-19 7 views
9

के साथ उपरोक्त और डाउनवोट मैं स्टैक ओवरफ्लो या रेडडिट के समान मतदान प्रणाली को कार्यान्वित करने की कोशिश कर रहा हूं जहां उपयोगकर्ता को किसी दिए गए पोस्ट पर केवल एक बार मतदान करने की अनुमति होगी।बैकबोन, एक्सप्रेस और मोंगोस

यहाँ दी

storing upvotes/downvotes in mongodb

मैं upvotes और downvotes स्टोर करने के लिए दो स्कीमा बनाया है सलाह पालन करने के बाद। प्रत्येक उपयोगकर्ता के लिए मैं उन पदों का ट्रैक रख रहा हूं जिन पर उपयोगकर्ता ने मतदान किया है।

पोस्ट स्कीमा:

var postSchema = new Schema({ 
    name: String, 
    votes: Number, 
    votetype: Number, 
    postedBy: { type: String, ref: 'User' }, 
}); 

उपयोगकर्ता स्कीमा:

var userSchema = new Schema({ 
    twittername: String, 
    twitterID: Number, 
    votedPosts : [{ _id : mongoose.Schema.Types.ObjectId , votetype: Number }] 
}); 

वर्तमान उपयोगकर्ता के आधार पर प्रत्येक पोस्ट, एक अलग दृष्टिकोण है करने के लिए जा रहा है, तो उपयोगकर्ता से पहले पद पर मतदान किया है अपवॉट बटन या डाउनवोट बटन नारंगी (स्टैक ओवरफ्लो के समान) होने जा रहा है, इसलिए मेरे पास पोस्ट के लिए निम्नलिखित (सरलीकृत) रीढ़ की हड्डी मॉडल है:

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); 
      } 

     }); 

    }, 


}); 

तो वोटटाइप "0" से शुरू होता है यदि उपयोगकर्ता ने पहले पोस्ट पर मतदान नहीं किया है, और वोट के आधार पर इसका "1" या "-1" है। वोट दें समारोह में, के रूप में मैं अद्यतन और उस पोस्ट की votetype बचाने के लिए, मैं भी एक ajax अनुरोध के बाद की तरह पद नियंत्रक में उपयोगकर्ता के वोट पदों सरणी के लिए उस पोस्ट को जोड़ने के लिए भेजें:

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(); 
}; 

मैं हो सकता है कुछ खराब डिजाइन फैसले लेकिन अब तक ऐसा लगता है कि यह ठीक काम करता है। कृपया मुझे बताएं कि क्या मैं अपने कोड पर कुछ सुधार कर सकता हूं, या मेरे डिजाइन पर कुछ और कर सकता हूं।

अब मुश्किल हिस्सा आता है।

https://gist.github.com/gorkemyurt/6042558

(मैं: किसी तरह मैं इन स्कीमा के दोनों के माध्यम से देखने के लिए और एक collection.fetch() करने से पहले प्रत्येक पोस्ट की "votetype" बदलना होगा .. मैं इस तरह एक बदसूरत समाधान के साथ आया था इसे एक गिट्स में डाल दें ताकि शायद बदसूरत कोड के लिए खेद हो। ..

और एक बार जब मैं उपयोगकर्ता के आधार पर प्रत्येक पोस्ट के वोट प्रकार को अपडेट करता हूं तो मैं इसे अपने रीढ़ की हड्डी के दृश्य में भेजता हूं, और मेरे टेम्पलेट में कुछ बहुत बुनियादी करें:

<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> 

यह समाधान काम करता है, लेकिन मुझे लगता है कि सभी पदों और सभी पोस्टों को देखने के लिए यह वास्तव में कुशल नहीं है कि उपयोगकर्ता ने हर बार मतदान किया है जब उपयोगकर्ता पोस्ट के कस्टम दृश्य को प्रस्तुत करने के लिए पृष्ठ खोलता है .. क्या कोई भी इस बारे में सोच सकता है ऐसा करने का बेहतर तरीका? मैं अपने कोड के बारे में कोई सलाह या आलोचना के लिए खुला रहा हूँ .. अग्रिम

उत्तर

7

में धन्यवाद वहाँ कई चीजें हैं जो सुधार किया जा सकता कर रहे हैं:

सबसे पहले, आप क्लाइंट-साइड कोड एक हमलावर के लिए एक कम फांसी फल है - आप दो अनुरोध के साथ एक परमाणु आपरेशन (वोट दें/downvote) करते हैं, और पहले अनुरोध न केवल वोट प्रकार भेजता है, लेकिन यह भी वोटों की कुल संख्या भेजता है:

this.set ({votetype : 1 }, {votes : this.get('votes') + 1}); 
this.save(); 
// btw this looks broken, the second argument for `set` is options, so 
// if you want to set votes, you should move them to the first argument: 
this.set ({votetype : 1, votes : this.get('votes') + 1}); 

लेकिन, आपके आवेदन का जवाब देंगे यदि हमलावर भेज देंगे 100 या 1000 वोट भी? यह ऑपरेशन परमाणु होना चाहिए, और जब आप /upvote एंडपॉइंट पर POST अनुरोध करते हैं तो आपको सर्वर पर वोट बढ़ाना चाहिए।

दूसरा, आपको वास्तव में पोस्ट पर वोटटाइप स्टोर करने की ज़रूरत नहीं है - जब भी उपयोगकर्ता वोट देता है, तो आप सभी उपयोगकर्ताओं के लिए दिखाई देने वाले वोटटाइप को बदलते हैं लेकिन बाद में आप इसे लूप से छुपाते हैं और यह एक वोटटाइप स्टोर करने के लिए बस अजीब है उस पोस्ट पर आखिरी वोट के बारे में जहां आपको स्पष्ट उपयोगकर्ता के वोटटाइप की स्पष्ट रूप से आवश्यकता है, इसलिए आपको स्कीमा में इसकी आवश्यकता नहीं है और इसे रिमोट कर सकते हैं। आप पोस्ट से वोटटाइप निकाल सकते हैं और आप पोस्ट पर वोट इतिहास संग्रहीत करके लूप को हटा सकते हैं, इसलिए जब भी आप कोई पोस्ट या पोस्ट की सूची प्रदर्शित करना चाहते हैं, तो आप आसानी से सरणी को फ़िल्टर कर सकते हैं दिए गए उपयोगकर्ता है, तो आप स्कीमा इस तरह दिखेगा:

var postSchema = new Schema({ 
    name: String, 
    votesCount: Number, 
    votes: [{ user_id : mongoose.Schema.Types.ObjectId , type: Number }], 
    postedBy: { type: String, ref: 'User' }, 
}); 

और फिर आप की तरह कुछ के साथ एक पोस्ट और फिल्टर वोट प्राप्त कर सकते हैं: यदि आप नवीनतम उपयोगकर्ता में पदों मतदान प्रदर्शित करने के लिए की जरूरत है

Post.findOne({_id:<post-id>)}, function(err, post){ 
    post.vote = post.votes.filter(function(vote){ 
     return vote.user_id === req.body.userID; 
    })[0].type; 
    res.send(post) 
    } 
) 
// or list of posts 
Post.find(function(err, posts){ 
    posts.forEach(function(post){ 
     post.vote = post.votes.filter(function(vote){ 
      return vote.user_id === req.body.userID; 
     })[0].type; 
    }); 
    res.send(posts) 
    } 
) 
// you can move vote finding logic in a function: 
function findVote(post) { 
    var vote = post.votes.filter(function(vote){ 
     return vote.user_id === req.body.userID; 
    })[0] 
    if(vote) return vote.type; 
} 

प्रोफ़ाइल आप उपयोगकर्ता द्वारा मतदान पोस्ट फ़िल्टर कर सकते हैं:

Post.find({'votes.user_id': req.body.userID}, function(err, posts){ 
    posts.forEach(function(post){ 
     // using findVote defined above 
     post.vote = findVote(post); 
    }); 
    res.send(posts) 
    } 
) 

ग्राहक पक्ष पर टेम्पलेट कोड लगभग समान रहना चाहिए।

+0

आपके उत्तर के लिए बहुत बहुत धन्यवाद, यह वास्तव में सहायक था। मैं सिर्फ सीखने की कोशिश कर रहा हूं .. क्या आप इस भाग को फिर से स्पष्ट कर सकते हैं "लेकिन, अगर आपका हमलावर 100 से 1000 वोट भेजेगा तो आपका आवेदन कैसे प्रतिक्रिया देगा? यह ऑपरेशन परमाणु होना चाहिए, और जब आप बनाते हैं तो आपको सर्वर पर वोट बढ़ाना चाहिए पोस्ट अप/अपवोट एंडपॉइंट के लिए अनुरोध। " –

+1

हां, आपने जो लिखा है, मैंने निष्कर्ष निकाला है कि आप दो अनुरोध करते हैं: एक जब आप 'पोस्टमोडेल' को सहेजते हैं: this.save(); और जब आप 'POST/upvote' बनाते हैं तो इसके ठीक बाद। आप इन अनुरोधों को उपयोगकर्ता वोट असाइन करने के लिए करते हैं, और कहीं और की तरह, आपको यह सुनिश्चित करना होगा कि उपयोगकर्ता इसका शोषण नहीं कर सकें। क्या होगा यदि कुछ उपयोगकर्ता केवल पहला अनुरोध भेजे जाएंगे - जो कोई दूसरा वोट भेजे बिना वोट बढ़ाता है - यह मूल रूप से अपने वोटों को अनाम बना देता है। उचित तरीका सिर्फ 'this.save' को कॉल किए बिना मॉडल में नए मानों को निर्दिष्ट करेगा, और */upvote' –

+0

पर * आपके * सर्वर-साइड कोड से डेटाबेस में लिखना होगा, मैं वोट कैसे जोड़ूं या बदलूं? मोंगोडीबी की मेरी वर्तमान समझ से मुझे पहले देखना होगा कि क्या उपयोगकर्ता के लिए वोट है या वोट जोड़ने या बदलने से पहले। लेकिन क्या होता है अगर वोट देने के बाद उसके लिए एक और वोट जोड़ा जाता है? –