让我们假设我们使用简单的Rails迁移创建了一个Postgresql表:
create_table :messages do |t|
t.string :tags, array: true, default: [], null: false
end
使命 - 使用单个查询为多行添加新标记。使用原始SQL
非常简单:
UPDATE messages SET tags = array_append(tags, 'new-tag') WHERE NOT ('new-tag' = ANY(tags)) AND id IN (...);
注意:如果我以不同的方式写这个查询,请纠正我。
但是如何使用Arel
?
经过几个小时的深入研究,我找到了这样的解决方案,但它看起来太麻烦了,我几乎可以肯定它可以做得更好。这是我的解决方案:
class Message < ActiveRecord::Base; end
t = Message.arel_table
has_tag = -> (tag) {
quoted = Arel::Nodes.build_quoted(tag)
fun = Arel::Nodes::NamedFunction.new('ANY', [t[:tags]])
Arel::Nodes::Equality.new(quoted, fun)
}
append = -> (tag) {
quoted = Arel::Nodes.build_quoted(tag)
fun = Arel::Nodes::NamedFunction.new('array_append', [t[:tags], quoted])
literal = Arel::Nodes::SqlLiteral.new(fun.to_sql)
[t[:tags], literal]
}
in_id = -> (ids) {
t[:id].in(ids)
}
um = Arel::UpdateManager.new(Message)
um.table(t)
.where(has_tag['new-tag'].not)
.where(in_ids[[1,2,3]])
.set([append['new-tag']])
ActiveRecord::Base.connection.execute(um.to_sql)
有没有办法用Arel更容易实现这个目标?