Twoje pytanie prawdopodobnie można rozwiązać bez przecięcia, coś takiego:
Person.joins(:services).where(services: {service_type: [1,2]}).group(
people: :id).having('COUNT("people"."id")=2')
Poniżej znajduje się jednak ogólne podejście, którego używam do konstruowania skrzyżowań, takich jak zapytania w 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'))
Wygeneruje SQL w postaci:
SELECT "people".*
FROM "people"
WHERE "people"."id" in (SELECT "people"."id" FROM ...)
AND "people"."id" in (SELECT ...)
AND ...
Możesz utworzyć tyle podzapytań, ile jest to wymagane, przy użyciu powyższego podejścia w oparciu o dowolne warunki/połączenia itp., o ile każde podzapytanie zwraca identyfikator pasującej osoby w swoim zestawie wyników.
Każdy zestaw wyników podzapytania będzie połączony operatorem AND, ograniczając w ten sposób zestaw dopasowywania do przecięcia wszystkich podzapytań.
AKTUALIZUJ
Dla tych, którzy używają AR4, gdzie scoped
została usunięta, moja druga odpowiedź zawiera semantycznie równoważny scoped
polyfil, który all
nie jest równoważnym zamiennikiem, pomimo tego, co sugeruje dokumentacja AR. Odpowiedz tutaj:W Rails 4, Model.scoped jest przestarzały, ale Model.all nie może go zastąpić