Votre question peut probablement être résolue sans intersection, quelque chose comme :
Person.joins(:services).where(services: {service_type: [1,2]}).group(
people: :id).having('COUNT("people"."id")=2')
Cependant, voici une approche générale que j'utilise pour construire des intersections comme des requêtes dans ActiveRecord :
class Service < ActiveRecord::Base
belongs_to :person
def self.with_types(*types)
where(service_type: types)
end
end
class City < ActiveRecord::Base
has_and_belongs_to_many :services
has_many :people, inverse_of: :city
end
class Person < ActiveRecord::Base
belongs_to :city, inverse_of: :people
def self.with_cities(cities)
where(city_id: cities)
end
def self.with_all_service_types(*types)
types.map { |t|
joins(:services).merge(Service.with_types t).select(:id)
}.reduce(scoped) { |scope, subquery|
scope.where(id: subquery)
}
end
end
Person.with_all_service_types(1, 2)
Person.with_all_service_types(1, 2).with_cities(City.where(name: 'Gold Coast'))
Il générera du SQL de la forme :
SELECT "people".*
FROM "people"
WHERE "people"."id" in (SELECT "people"."id" FROM ...)
AND "people"."id" in (SELECT ...)
AND ...
Vous pouvez créer autant de sous-requêtes que nécessaire avec l'approche ci-dessus basée sur toutes les conditions/jointures, etc. tant que chaque sous-requête renvoie l'identifiant d'une personne correspondante dans son jeu de résultats.
Chaque ensemble de résultats de sous-requête sera combiné par AND, limitant ainsi l'ensemble correspondant à l'intersection de toutes les sous-requêtes.
MISE À JOUR
Pour ceux qui utilisent AR4 où scoped
a été supprimé, mon autre réponse fournit un scoped
sémantiquement équivalent polyfil qui all
n'est pas un remplacement équivalent pour malgré ce que la documentation AR suggère. Répondez ici :Avec Rails 4, Model.scoped est obsolète mais Model.all ne peut pas le remplacer