使用Arel

时间:2015-07-14 16:25:13

标签: ruby-on-rails postgresql activerecord sql-update arel

让我们假设我们使用简单的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更容易实现这个目标?

0 个答案:

没有答案