voteable_mongo allows you to make your Mongoid::Document or MongoMapper::Document objects voteable and tabulate votes count and votes point for you. For instance, in a forum, a user can vote up (or down) on a post or a comment. It’s optimized for speed by using only ONE database request per collection to validate, update, and retrieve updated data.
Initial idea based on cookbook.mongodb.org/patterns/votes.
Sample app at github.com/vinova/simple_qa.
Wonder how fast voteable_mongo is compare to other SQL & MongoDB solutions? Visit benchmarks at github.com/vinova/voteable_benchmarks
There are various solutions for up / down voting problem (1, 2, 3, 4, …). Most of them using additional votes table (SQL) or votes collection (MongoDB) to store votes and do data tabulation on that votes table or votes collection.
voteable_mongo is different. It takes advantage of document-oriented database to store all related votes data inside voteable document. That has following benefits:
-
Don’t have to maintain additional votes table or votes collection.
-
When voteable document is loaded, all votes data related to it also be loaded, no more additional database requests to see how many votes this document got, who give up votes who give down vote, total vote points, votes count …
-
When vote up, vote down, revote, unvote, voteable_mongo validates vote data, updates voteable document and retrieves updated data using only ONE database request thanks to atomic findAndModify operation.
-
Atomic operations on single document warranty data integrity that makes sure if votes created / changed / deleted their associated counters and points will be updated.
So use voteable_mongo for less maintain cost, data integrity and save database requests for other tasks.
To install the gem, add this to your Gemfile
gem 'mongoid' gem 'voteable_mongo'
After that, remember to run “bundle install”
post.rb
class Post include Mongoid::Document include Mongo::Voteable # set points for each vote voteable self, :up => +1, :down => -1 has_many :comments end
comment.rb
require 'post' class Comment include Mongoid::Document include Mongo::Voteable belongs_to :post voteable self, :up => +1, :down => -3 # each vote on a comment can affect votes count and point of the related post as well voteable Post, :up => +2, :down => -1 end
user.rb
class User include Mongoid::Document include Mongo::Voter end
image.rb
class Image include Mongoid::Document include Mongo::Voteable embedded_in :post voteable self, :up => +1, :down => -1, :index => true voteable Post, :up => +2, :down => -1 end
video.rb
class Video include Mongoid::Document include Mongo::Voteable embedded_in :dynamic_doc voteable self, :up => +1, :down => -1, :index => true, :voting_field => :reviews voteable DynamicDoc, :up => +2, :down => -2, :voting_field => :moderations end
dynamic_doc.rb
class DynamicDoc include Mongoid::Document include Mongo::Voteable voteable self, :voting_field => :moderations voteable self, :voting_field => :likes, :up => +2, :down => -2 voteable User, :voting_field => :points, :up => +5, :down => -5 belongs_to :user embeds_many :videos end
user.rb
class User include Mongoid::Document include Mongo::Voter include Mongo::Voteable voteable self, :voting_field => :points has_many :dynamic_docs end
Just omit the user responsible for the vote during a #set_vote call. You can optionally provide an IP to avoid vote duplication
post.rb
class Post include MongoMapper::Document include Mongo::Voteable # set points for each vote voteable self, :up => +1, :down => -1 many :comments end
comment.rb
require 'post' class Comment include MongoMapper::Document include Mongo::Voteable belongs_to :post voteable self, :up => +1, :down => -3 voteable Post, :up => +2, :down => -1 end
user.rb
class User include MongoMapper::Document include Mongo::Voter end
use #vote when calling from Voter user #set_vote when calling from Voteable @user.vote(@post, :up)
Is equivalent to
@user.vote(:votee => @post, :value => :up) @post.set_vote(:voter => @user, :value => :up)
In case you don’t need to init voter and / or votee objects you can
@user.vote(:votee_class => Post, :votee_id => post_id, :value => :down) @post.set_vote(:voter_id => user_id, :value => :up) Post.set_vote(:voter_id => user_id, :votee_id => post_id, :value => :up)
@user.unvote(@comment)
If have voter_id, votee_id and vote value you don’t need to init voter and votee objects (suitable for API calls)¶ ↑
New vote
Post.set_vote(:voter_id => user_id, :votee_id => post_id, :value => :up)
Re-vote
Post.set_vote(:voter_id => user_id, :votee_id => post_id, :value => :up, :revote => true)
Un-vote
Post.set_vote(:voter_id => user_id, :votee_id => post_id, :value => :up, :unvote => true)
Note: vote function always return updated votee object
@user.voter_vote_value(:votee => @post) @user.voter_vote_value(:votee_class => Post, :votee_id => post_id) @post.vote_value(@user) @post.vote_value(user_id)
@user.voter_vote_value(:votee => @post, :voting_field => "moderations") @user.voter_vote_value(:votee_class => Post, :votee_id => post_id, :voting_field => "moderations") @post.vote_value(@user, "moderations") @post.vote_value(user_id, "moderations")
@user.voter_vote_value?(:votee => @post) @user.voter_vote_value?(:votee_class => Post, :votee_id => post_id) @post.voted_by?(@user) @post.voted_by?(user_id)
@user.voter_vote_value?(:votee => @post, :voting_field => "moderations") @user.voter_vote_value?(:votee_class => Post, :votee_id => post_id, :voting_field => "moderations") @post.voted_by?(@user, "moderations") @post.voted_by?(user_id, "moderations")
puts @post.votes_point || @post.votes_point("moderations") puts @post.votes_count || @post.votes_count("moderations") puts @post.up_votes_count || @post.up_votes_count("moderations") puts @post.down_votes_count || @post.down_votes_count("moderations")
@post.up_voters(User) || @post.up_voters(User, "moderations") @post.down_voters(User) || @post.down_voters(User, "moderations") @post.voters(User) || @post.voters(User, "moderations") - or - User.up_voted_for(@post) User.down_voted_for(@post) User.voted_for(@post)
Post.voted_by(@user) Post.up_voted_by(@user) Post.down_voted_by(@user)
Rails
rake mongo:voteable:init_stats
Ruby
Mongo::Voteable::Tasks::init_stats
Rails
mongo:voteable:reset_stats[klass]
Ruby
Mongo::Voteable::Tasks::reset_stats(klass)
Rails
rake mongo:voteable:remake_stats
Ruby
Mongo::Voteable::Tasks.remake_stats
Rails
rake mongo:voteable:migrate_old_votes
Ruby
Mongo::Voteable::Tasks.migrate_old_votes
-
Alex Nguyen - Author
-
Alexandre Angelim - Mongoid Exclusive Features
Copyright © 2010-2011 Vinova Pte Ltd
Licensed under the MIT license.