Skip to content

Add up / down voteability to Mongoid and MongoMapper documents. Optimized for speed by using only ONE request to validate, update, and retrieve updated data.

Notifications You must be signed in to change notification settings

angelim/voteable_mongo

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Voteable Mongo

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

Why voteable_mongo?

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.

Sites using voteable_mongo

Installation

Rails 3.x

To install the gem, add this to your Gemfile

gem 'mongoid'
gem 'voteable_mongo'

After that, remember to run “bundle install”

Usage

Make Post and Comment voteable, User become the voter

Mongoid

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

Exclusive Features

Embedded Documents

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

Custom Voting Field Name

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

Multiple Voting Field For Document

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

Document as Voteable and Voter

user.rb

class User
  include Mongoid::Document
  include Mongo::Voter
  include Mongo::Voteable

  voteable self, :voting_field => :points
  has_many :dynamic_docs
end

Anonymous Votes

Just omit the user responsible for the vote during a #set_vote call.
You can optionally provide an IP to avoid vote duplication

MongoMapper

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

Make a vote

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)

Undo a vote

@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

Get vote_value for default “votes” field

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

Get vote_value for custom voting field “moderations”

@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")

Check if voted? for default field name “votes”

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

Check if voted? for custom voting field “moderations”

@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")

Get votes counts and points

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

Get voters given voted object and voter class

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

Get the list of voted objects of a class

Post.voted_by(@user)
Post.up_voted_by(@user)
Post.down_voted_by(@user)

Utilities

Set counters and point to 0 for uninitialized voteable objects in order sort and query

Rails

rake mongo:voteable:init_stats

Ruby

Mongo::Voteable::Tasks::init_stats

Reset all voting fields for a particular class (including the ones with content)

Rails

mongo:voteable:reset_stats[klass]

Ruby

Mongo::Voteable::Tasks::reset_stats(klass)

Re-generate counters and vote points in case you change :up / :down vote points

Rails

rake mongo:voteable:remake_stats

Ruby

Mongo::Voteable::Tasks.remake_stats

Migrate from voteable_mongoid version < 0.7.0

Rails

rake mongo:voteable:migrate_old_votes

Ruby

Mongo::Voteable::Tasks.migrate_old_votes

Credits

  • Alex Nguyen - Author

  • Alexandre Angelim - Mongoid Exclusive Features

  • Contributors

Copyright © 2010-2011 Vinova Pte Ltd

Licensed under the MIT license.

About

Add up / down voteability to Mongoid and MongoMapper documents. Optimized for speed by using only ONE request to validate, update, and retrieve updated data.

Resources

Stars

Watchers

Forks

Packages

No packages published

Languages

  • Ruby 100.0%