Your question will probably be resolved without intersection, something like:
Person.joins(:services).where(services: {service_type: [1,2]}).group( people: :id).having('COUNT("people"."id")=2')
However, the following general approach that I use to build intersections, such as queries 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'))
It will generate SQL forms:
SELECT "people".* FROM "people" WHERE "people"."id" in (SELECT "people"."id" FROM ...) AND "people"."id" in (SELECT ...) AND ...
You can create as many subqueries as needed using the above approach based on any conditions / joins, etc., as long as each subquery returns the identifier of the matching person in its result set.
Each result set in the subquery will be equal to AND, however, limiting the set of correspondence to the intersection of all subqueries.
UPDATE
For those using AR4 where scoped was removed, my other answer contains a semantically equivalent scoped polyfil, which all not an equivalent replacement, despite the fact that the AR documentation offers. The answer is here: With Rails 4, Model.scoped is deprecated, but Model.all cannot replace it