Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Complex JOIN with ActiveRecord and Rails 3

I have the following models:

class User < ActiveRecord::Base
  has_many :memberships
  has_many :groups, :through => :memberships
end

class Group < ActiveRecord::Base
  has_many :memberships
  has_many :users, :through => :memberships
end

class Membership < ActiveRecord::Base
  belongs_to :user
  belongs_to :group
end

class Post < ActiveRecord::Base
  belongs_to :group
end

I have to find all posts that belong to groups where user is a member. I have made it with this method:

@post = Post
  .joins(:group => {:memberships => :user})
  .where(:memberships => {:user_id => current_user.id})

but it produces unefficient SQL:

SELECT "posts".* FROM "posts" 
INNER JOIN "groups" ON "groups"."id" = "posts"."group_id" 
INNER JOIN "memberships" ON "memberships"."group_id" = "groups"."id" 
INNER JOIN "users" ON "users"."id" = "memberships"."user_id" 
WHERE "memberships"."user_id" = 1

I want to make a query like this:

SELECT posts.* FROM posts 
INNER JOIN memberships ON memberships.group_id = posts.group_id 
WHERE memberships.user_id = 1

How can I do this without using raw SQL?

like image 588
whitered Avatar asked Apr 08 '11 09:04

whitered


2 Answers

You can get closer without changing your model at all, by removing the unused join from your call:

Post.joins(group: :memberships).where(memberships: { user_id: 1 })

compiles to SQL

SELECT "posts".* FROM "posts"
INNER JOIN "groups" ON "groups"."id" = "posts"."group_id"
INNER JOIN "memberships" ON "memberships"."group_id" = "groups"."id" 
WHERE ("memberships"."user_id" = 1)
like image 177
jamuraa Avatar answered Oct 05 '22 23:10

jamuraa


something like this should work for you, although it requires mixing in a little raw SQL

Post
  .joins("INNER JOIN memberships ON memberships.group_id = posts.group_id")
  .where(:memberships => {:user_id => current_user.id})
like image 39
Will Ayd Avatar answered Oct 06 '22 01:10

Will Ayd