備忘録的な何か

技術ブログ的な何かです

ActiveRecordで複数条件のorクエリまたはinクエリを投げる(Rails小ネタ)

ActiveRecordでor検索をしようと思うと、意外と簡単にはできない。

Table.where(id: 1).or(id: 2) とかでできそうなものだができない。

 

真面目にやるには、Arelを使ってやるのが一番のようだ。

http://techracho.bpsinc.jp/tsunekawa/2013_05_24/8502

 

非常に出来合いのコードだが、or検索のscopeを作成した。(SQLインジェクション対策はなし。プレースホルダを使うscopeってどう書くの?)

 

下記のように呼び出す。

Table.or([{id: 1,val: 2},{id:4,val:5}]) #=> (id=1 and val=2) or (id=4 and val=5)

でも、結局 Table.where(id: 1).or(id: 2)はできない...(おぃ)

 

  scope :or, ->(condition){

    where(condition) unless condition.class == Array

 

    table_name = self.table_name

    sql = condition.map{|cond|

      partial = cond.map{|col,val|

        %{"#{table_name}"."#{col}" = #{sanitize(val)}}

      }.join(" AND ")

      "(#{partial})"

    }.join(" OR ")

    where(sql)

  }

 

また、PostgreSQLは複数カラムのwhere INができるので、inのscopeも作成した。http://blog.fusic.co.jp/archives/1765

下記のように呼び出す。

Table.in([{id: 1,val: 2},{id:4,val:5}]) #=> (id,val) IN ( (1,2),(4,5) )

 

  scope :in, ->(condition){

    where(condition) unless condition.class == Array

 

    table_name = self.table_name

 

    columns_sql = condition.first.map{|col,val| %{"#{table_name}"."#{col}"} }.join(" , ")

    columns = condition.first.map{|col,val| col }

 

    values = condition.map{|cond|

      value = columns.map{|col| sanitize(cond[col]) }.join(" , ")

      "(#{value})"

    }.join(" , ")

    where("(#{columns_sql}) IN (#{values})")

  }