开发者

Querying with model associations in ruby on rails

Model A has_many Model B. I want to return all Model As with certain types of Model Bs.

For example, I want to return all Users who have both a Vehicle of type Ford and a Vehicle of type Chevrolet (Ford and Chevrolet are properties of the Vehicle model)

I can easily query to find all users who have a ford or all users who hav开发者_Python百科e a chevy but I don't want to do 2 queries and then intersect the results. Instead I want to perform only 1 query. How do I combine this into 1 query?


Solution 1

User.joins([:vehicles, :vehicles]).
     where("vehicles.model = ? AND vehicles_users.model = ?", "Ford", "Chevy")

Solution 2

Supports arbitrary models.

models = ["Ford", "Chevy"]
User.where(:id => User.joins(:select => "users.id", :vehicles).
  where(:vehicles => {:model => models}).group("users.id").
  having("COUNT(*) = #{models.size}").limit(30)
)

Reference 1

Solution 3

Slower solution, in case you needed one..

User.where([
"EXISTS (
   SELECT A.* 
   FROM vehicles A 
   WHERE A.user_id = users.id AND 
         A.model = ?) AND 
 EXISTS (
   SELECT B.* 
   FROM vehicles B 
   WHERE B.user_id = users.id AND 
         B.model = ?)
 ", "Ford", "Chevy"
])


Edit: To get users with both vehicles Ford and Chevrolet, you can do this:

User.     
  select("DISTINCT users.*").
  joins("INNER JOIN vehicles AS v1 ON v1.user_id = users.id").
  joins("INNER JOIN vehicles AS v2 ON v2.user_id = users.id").
  where("v1.type" => "Ford", "v2.type" => "Chevrolet").

Edit 2: Or, if you prefer having clean code and can tolerate 2 queries:

class User < ActiveRecord::Base
  scope :with_vehicule, lambda { |type| joins(:vehicles).where(:type => type) }
  # ...
end

users_with_both = User.with_vehicle("Ford").to_a & User.with_vehicle("Chevrolet").to_a

Old answer (to retrieve users with any of the two types):

User.joins(:vehicle).where(:vehicles => {:type => ["Ford", "Chevrolet"]})

Or (I'm not sure what "properties" means in your question):

User.joins(:vehicle).where("vehicles.ford = 1 OR vehicles.chevrolet = 1")
0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜