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")
精彩评论