はじめに
下記のような形でActiveRecordが発行するSQLからWHERE句の部分のSQLだけを取得したいケースがあり、色々やり方とか調べたのでMEMO✍
やりたかったこと
今回やりたかったのは、下記のようなscopeを定義していたときにto_sqlするとSQL全体を取得することが出来ますが、
class Book < ApplicationReacord scope :viewable, ->(now = Time.current) { where(published_at: now..) } end Book.viewable.to_sql #=> select * from books where published_at > '2020-05-23 15:24:34'
そうではなくWHERE句のpublished_at > '2020-05-23 15:24:34'部分だけ取得したいというものです。
where部分だけのSQLを取得する方法
下記のような感じで取れるみたいです👀(⚠nodocなので動作保証されてないですが・・・!)
Book.viewable.values[:where].ast.to_sql #=> published_at > '2020-05-23 15:24:34'
values[:where]でActiveRecord::Relation::WhereClauseのオブジェクトを取得することができて、オブジェクトに対してastメソッドを呼び出すことによってArel::Nodes系のオブジェクトを取得できるようです👀
そして取得したオブジェクトにto_sqlを呼び出すことによってそのNodeの部分だけのSQLを取得できるような形で動いてそうな気がしました・・・!
WHEREも含めて取得したいときは下記のようにするととれるようです👀(⚠こちらもnodocなので動作保証されてないです。)
Book.viewable.arel.where_sql #=> WHERE published_at > '2020-05-23 15:24:34'
ですが、Arel::Nodes::Node#to_sqlはコメントにも無くなる可能性が明記されてるので、素直にSQLをparseした方が良さそうな気もしました😓
# FIXME: this method should go away. I don't like people calling # to_sql on non-head nodes. This forces us to walk the AST until we # can find a node that has a "relation" member. # # Maybe we should just use `Table.engine`? :'( def to_sql(engine = Table.engine) collector = Arel::Collectors::SQLString.new collector = engine.connection.visitor.accept self, collector collector.value end
rails/node.rb at b1f6d8c8d8ad3e2e5b96e95b455c70f2c895ce14 · rails/rails · GitHub