Uw vraag is waarschijnlijk oplosbaar zonder snijpunt, zoiets als:
Person.joins(:services).where(services: {service_type: [1,2]}).group(
people: :id).having('COUNT("people"."id")=2')
Het volgende is echter een algemene benadering die ik gebruik voor het construeren van intersectie-achtige query's in 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'))
Het genereert SQL van het formulier:
SELECT "people".*
FROM "people"
WHERE "people"."id" in (SELECT "people"."id" FROM ...)
AND "people"."id" in (SELECT ...)
AND ...
U kunt zoveel subquery's maken als nodig is met de bovenstaande aanpak op basis van alle voorwaarden/joins enz., zolang elke subquery de id van een overeenkomende persoon in zijn resultatenset retourneert.
Elke resultaatset voor subquery's wordt aan elkaar gekoppeld, waardoor de overeenkomende set wordt beperkt tot het snijpunt van alle subquery's.
UPDATE
Voor degenen die AR4 gebruiken waar scoped
is verwijderd, biedt mijn andere antwoord een semantisch equivalent scoped
polyfil die all
is geen gelijkwaardige vervanging voor ondanks wat de AR-documentatie suggereert. Antwoord hier:Met Rails 4 is Model.scoped verouderd, maar kan Model.all het niet vervangen