如何构建此Rails/PostgreSQL查询以基于模型子项的属性返回模型的实例?

Sim*_*mon 5 postgresql ruby-on-rails

我正在开发一个应用程序,其中User有许多项目,并且Project有很多角色.Role有一个布尔filled属性来指示某人何时接受了该角色.

我想构建一个查询,它返回所有没有角色或具有已经填充的角色的项目.这是我到目前为止最接近的:

@user.projects.includes(:roles).where("roles.id IS NULL OR roles.filled IS TRUE").references(:roles)
Run Code Online (Sandbox Code Playgroud)

问题是roles.filled IS TRUE查询的一部分是匹配具有填充和未填充角色的项目.我需要这个只匹配所有角色都填充的项目.

搜索PostgreSQL文档,它看起来bool_and可能是要走的路,但我的SQL技能不是很好,我还没有设法让它工作.

我意识到我可以轻松地使用select或者这样做reject但我想通过查询数据库来保持它的效率.

有人可以提供一些建议吗?

更新:(简化)模型及其关系:

应用程序/模型/ user.rb

class User < ActiveRecord::Base
  has_many :projects
end
Run Code Online (Sandbox Code Playgroud)

应用程序/模型/ project.rb

class Project < ActiveRecord::Base
  belongs_to :user
  has_many :roles
end
Run Code Online (Sandbox Code Playgroud)

应用程序/模型/ role.rb

class Role < ActiveRecord::Base
  belongs_to :project

  # `filled` is a boolean attribute with a default value of false
end
Run Code Online (Sandbox Code Playgroud)

cha*_*sto 2

您必须求助于聚合函数,因为条件适用于许多记录的组合( 的所有字段都roles.filled必须为 true)。因此,您将所有角色分组projects.id并检查是否every(roles.filled)为真:

这是一个建议:

@user.projects.
      joins("left outer join roles on roles.project_id = projects.id").
      group("projects.id").
      having("every(roles.filled IS TRUE) OR every(roles.id IS NULL)")
      select("projects.*")
Run Code Online (Sandbox Code Playgroud)

更新:好的,这是这个 SQL 背后的逻辑:

对于每个项目,我都会检查它是否没有 Roles.id 或只有 Roles.filled。不能使用 where 子句的原因roles.id IS TRUE是,它只会在子句中进行分组和筛选之前选择这些行having,从而排除这些roles.filled行。换句话说,这相当于做roles.id is NULL AND Roles.filled IS TRUE