查找多态关联或 nil 的记录
Finding record where polymorphic association or nil
我有一个 rails app 运行ning ruby 2.1.2 和 rails 4.1.1 并且在其中我有一个多态关联,如下所示:
class Picture < ActiveRecord::Base
belongs_to :imageable, polymorphic: true
end
class Employee < ActiveRecord::Base
has_many :pictures, as: :imageable
end
class Product < ActiveRecord::Base
has_many :pictures, as: :imageable
end
我希望能够找到属于给定员工或没有关联 "imageable" 记录的所有图片。
# These both work fine
Picture.where(imageable: Employee.first) # finds all associated with Employee.first
Picture.where(imageable: nil) # finds all un-associated Pictures
#This does not work
Picture.where(imageable: [Employee.first, nil])
一旦看到 SQL 即 运行
,它不起作用的原因就很明显了
SELECT "pictures".* FROM "pictures"
WHERE "pictures"."imageable_type" = 'Employee' AND
(("pictures"."imageable_id" = 1 OR "pictures"."imageable_id" IS NULL))
什么时候我需要
SELECT "pictures".* FROM "pictures"
WHERE (("pictures"."imageable_type" = 'Employee' AND "pictures"."imageable_id" = 1)) OR
"pictures"."imageable_id" IS NULL
知道如何在不写出 SQL 的情况下 运行 rails 中的查询吗?
所以下面的方法可行,但我不确定它是否是最好的或者甚至是唯一的方法:
@empolyee = Employee.first
Picture.where(imageable_type: ["Employee", nil], imageable_id: [@empolyee.id, nil])
这会运行以下内容,我不确定它是否更慢。
SELECT "pictures".* FROM "pictures"
WHERE (("pictures"."imageable_type" = 'Organization' OR "pictures"."imageable_type" IS NULL)) AND
(("pictures"."imageable_id" = 1 OR "pictures"."imageable_id" IS NULL))
编辑
虽然这可行,但可能会出现失败的极端情况。
我认为如果一张图片属于一个产品并且该产品被删除(并且我的依赖策略设置为无效)那么图片具有 null 作为 'imageable_id' 但仍然有 "Product" 作为 'imageable_type'。
我上面的回答找不到这样的图片,因为 "imageable_type" 既不是 "Employee" 也不是 NULL。
试试这个:
from_employee = Picture.where(imageable: Employee.first).where_values.reduce(:and)
from_nil = Picture.where(imageable: nil).where_values.reduce(:and)
@from_employee_or_nil = Picture.where(from_employee.or(from_nil))
编辑
在 Rails 4.1 上,where_values
在 query_methods 处动态定义。它映射了与当前ActiveRecord::Relation
相关的所有条件的Arel::Nodes
"Conditions"。此节点响应 and(other)
并使用 reduce(:and)
您可以将它们全部放在一起。
在 Rails 4.2 上这不起作用。您可以让它工作,但它变得很奇怪(请参阅:OR operator in WHERE clause with Arel in Rails 4.2 - 对问题的编辑 -)。我们能做什么?
使用普通查询(1):
@employee = Employee.first
Picture.where("(pictures.imageable_type = ? AND pictures.imageable_id = ?) OR pictures.imageable_id IS NULL", @employee.class, @employee.id)
破解 Arel(2):(正如@cristian 所说)
@employee = Employee.first
Picture.where( ( Picture.arel_table[:imageable_type].eq(@employee.class).and( Picture.arel_table[:imageable_id].eq(@employee.id) )).or(Picture.arel_table[:imageable_id].eq(nil) ))
使用复合体 where_values(3):
employee_scope = Picture.where(imageable: Employee.first)
nil_scope = Picture.where(imageable: nil)
Picture.where( employee_scope.where_values.reduce(:and).or(nil_scope.where_values.reduce(:and)).to_sql, employee_scope.bind_values.map(&:last) + nil_scope.bind_values.map(&:last) )
我的选择:对于这种情况,普通查询 (1)。我的第一个答案(我一直在使用)在 4.2 停止工作,需要重构 (3)。 Arel 让您独立于数据库管理器(我建议学习 Arel)。你发布的答案给我一些奇怪的感觉,因为它工作正常但它使用了一个无效条件:imageable_id IS NULL AND imageable_type = 'Employee'
理论上由搜索返回。
为了获得您正在寻找的确切查询,请使用 AREL:
employee_condition = Picture.arel_table[:imageable_id]
.eq( Employee.first )
.and( Picture.arel_table[:imageable_type].eq('Employee') )
nil_condition = Picture.arel_table[:imageable_id].eq(nil)
Picture.where(
Picture.arel_table.grouping(employee_condition).or(nil_condition)
)
这将生成:
SELECT "pictures".* FROM "pictures" WHERE (
("pictures"."imageable_id" = 1 AND "pictures"."imageable_type" = 'Employee')
OR "pictures"."imageable_id" IS NULL
)
为了更好地理解上面代码中发生的事情,请阅读
Using Arel to Compose SQL Queries and Rails/Arel - precedence when combining AREL predicates
我有一个 rails app 运行ning ruby 2.1.2 和 rails 4.1.1 并且在其中我有一个多态关联,如下所示:
class Picture < ActiveRecord::Base
belongs_to :imageable, polymorphic: true
end
class Employee < ActiveRecord::Base
has_many :pictures, as: :imageable
end
class Product < ActiveRecord::Base
has_many :pictures, as: :imageable
end
我希望能够找到属于给定员工或没有关联 "imageable" 记录的所有图片。
# These both work fine
Picture.where(imageable: Employee.first) # finds all associated with Employee.first
Picture.where(imageable: nil) # finds all un-associated Pictures
#This does not work
Picture.where(imageable: [Employee.first, nil])
一旦看到 SQL 即 运行
,它不起作用的原因就很明显了SELECT "pictures".* FROM "pictures"
WHERE "pictures"."imageable_type" = 'Employee' AND
(("pictures"."imageable_id" = 1 OR "pictures"."imageable_id" IS NULL))
什么时候我需要
SELECT "pictures".* FROM "pictures"
WHERE (("pictures"."imageable_type" = 'Employee' AND "pictures"."imageable_id" = 1)) OR
"pictures"."imageable_id" IS NULL
知道如何在不写出 SQL 的情况下 运行 rails 中的查询吗?
所以下面的方法可行,但我不确定它是否是最好的或者甚至是唯一的方法:
@empolyee = Employee.first
Picture.where(imageable_type: ["Employee", nil], imageable_id: [@empolyee.id, nil])
这会运行以下内容,我不确定它是否更慢。
SELECT "pictures".* FROM "pictures"
WHERE (("pictures"."imageable_type" = 'Organization' OR "pictures"."imageable_type" IS NULL)) AND
(("pictures"."imageable_id" = 1 OR "pictures"."imageable_id" IS NULL))
编辑
虽然这可行,但可能会出现失败的极端情况。
我认为如果一张图片属于一个产品并且该产品被删除(并且我的依赖策略设置为无效)那么图片具有 null 作为 'imageable_id' 但仍然有 "Product" 作为 'imageable_type'。
我上面的回答找不到这样的图片,因为 "imageable_type" 既不是 "Employee" 也不是 NULL。
试试这个:
from_employee = Picture.where(imageable: Employee.first).where_values.reduce(:and)
from_nil = Picture.where(imageable: nil).where_values.reduce(:and)
@from_employee_or_nil = Picture.where(from_employee.or(from_nil))
编辑
在 Rails 4.1 上,where_values
在 query_methods 处动态定义。它映射了与当前ActiveRecord::Relation
相关的所有条件的Arel::Nodes
"Conditions"。此节点响应 and(other)
并使用 reduce(:and)
您可以将它们全部放在一起。
在 Rails 4.2 上这不起作用。您可以让它工作,但它变得很奇怪(请参阅:OR operator in WHERE clause with Arel in Rails 4.2 - 对问题的编辑 -)。我们能做什么?
使用普通查询(1):
@employee = Employee.first
Picture.where("(pictures.imageable_type = ? AND pictures.imageable_id = ?) OR pictures.imageable_id IS NULL", @employee.class, @employee.id)
破解 Arel(2):(正如@cristian 所说)
@employee = Employee.first
Picture.where( ( Picture.arel_table[:imageable_type].eq(@employee.class).and( Picture.arel_table[:imageable_id].eq(@employee.id) )).or(Picture.arel_table[:imageable_id].eq(nil) ))
使用复合体 where_values(3):
employee_scope = Picture.where(imageable: Employee.first)
nil_scope = Picture.where(imageable: nil)
Picture.where( employee_scope.where_values.reduce(:and).or(nil_scope.where_values.reduce(:and)).to_sql, employee_scope.bind_values.map(&:last) + nil_scope.bind_values.map(&:last) )
我的选择:对于这种情况,普通查询 (1)。我的第一个答案(我一直在使用)在 4.2 停止工作,需要重构 (3)。 Arel 让您独立于数据库管理器(我建议学习 Arel)。你发布的答案给我一些奇怪的感觉,因为它工作正常但它使用了一个无效条件:imageable_id IS NULL AND imageable_type = 'Employee'
理论上由搜索返回。
为了获得您正在寻找的确切查询,请使用 AREL:
employee_condition = Picture.arel_table[:imageable_id]
.eq( Employee.first )
.and( Picture.arel_table[:imageable_type].eq('Employee') )
nil_condition = Picture.arel_table[:imageable_id].eq(nil)
Picture.where(
Picture.arel_table.grouping(employee_condition).or(nil_condition)
)
这将生成:
SELECT "pictures".* FROM "pictures" WHERE (
("pictures"."imageable_id" = 1 AND "pictures"."imageable_type" = 'Employee')
OR "pictures"."imageable_id" IS NULL
)
为了更好地理解上面代码中发生的事情,请阅读 Using Arel to Compose SQL Queries and Rails/Arel - precedence when combining AREL predicates