您的问题可能无需交叉即可解决,例如:
Person.joins(:services).where(services: {service_type: [1,2]}).group(
people: :id).having('COUNT("people"."id")=2')
然而,以下是我用于在 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'))
它将生成以下形式的 SQL:
SELECT "people".*
FROM "people"
WHERE "people"."id" in (SELECT "people"."id" FROM ...)
AND "people"."id" in (SELECT ...)
AND ...
只要每个子查询在其结果集中返回匹配人员的 id,您就可以根据任何条件/连接等使用上述方法创建任意数量的子查询。
Each subquery result set will be AND'ed together thus restricting the matching set to the intersection of all of the subqueries.
UPDATE
For those using AR4 where scoped
was removed, my other answer provides a semantically equivalent scoped
polyfil which all
is not an equivalent replacement for despite what the AR documentation suggests. Answer here: With Rails 4, Model.scoped is deprecated but Model.all can't replace it