Skip to content

Instantly share code, notes, and snippets.

@stevecass
Last active January 18, 2016 22:52
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save stevecass/2afff7285a1099e2243f to your computer and use it in GitHub Desktop.
Save stevecass/2afff7285a1099e2243f to your computer and use it in GitHub Desktop.
Using ActiveRecord associations to select and order data sets

##Letting Your ActiveRecord Associations Help

Suppose you have the following models:

class User < ActiveRecord::Base
  has_many :blogs
  has_many :posts, through: :blogs
  has_many :received_comments, through: :posts, source: :comments
  has_many :expressed_comments, class_name: 'Comment', foreign_key: 'user_id'
end

# Note: 
  # received comments is comments from any user on posts written by this user
  # expressed comments is comments this user has made on any user's posts

class Blog < ActiveRecord::Base
  belongs_to :author, class_name: 'User', foreign_key: 'user_id'
  has_many :posts
  has_many :comments, through: :posts
end

class Post < ActiveRecord::Base
  belongs_to :blog
  delegate :author, to: :blog, prefix: false, allow_nil: true
end

class Comment < ActiveRecord::Base
  belongs_to :user
  belongs_to :post
end

Examples:

Posts ordered by the number of comments on them:
Post.joins(:comments).group(:id).order('count(comments.id) desc')

Posts that have no comments on them:
Post.includes(:comments).where(comments: {id:nil})

Using the has_many through association you can do the same for Blog:
Blog.includes(:comments).where(comments: {id:nil})

Suppose comments have a like / dislike attribute stored as +1 / -1 in a like_value field. Posts ordered by most liked is then:
Post.joins(:comments).group(:id).order('sum(comments.like_value) desc')

Getting the net like value for a particular post:

p = Post.last
p.comments.sum(:like_value)`

Or the overall like value for a whole blog:
Blog.last.comments.sum(:like_value)

Or an author:
User.last.received_comments.sum(:like_value)

Total number of comments an author got:
User.last.received_comments.count

Authors ranked by popularity (highest net likes first):
User.joins(:received_comments).group(:id).order('sum(comments.like_value) desc')

Authors with at least five likes regardless of dislikes:
User.joins(:received_comments).where('comments.like_value = 1').group(:id).having('count(comments.id) >= 5')
or equivalently
User.joins(:received_comments).where(comments: {like_value: 1}).group(:id).having('count(comments.id) >= 5')

Authors whose net like balance is at least 3:
User.joins(:received_comments).group(:id).having('sum(comments.like_value) >= 3')

Users who have commented on blog 3:
User.where(id: Comment.joins(:post).where(posts: {blog_id: 3}).pluck(:user_id))

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment