kikeda1104's blog

備忘録・技術に関することを書いています。(webエンジニア)

ActiveRecord::Relation#orのworkaround

先日書いた記事の付け足しになりますね。orを利用して検索ロジックを書いていましたが mergeや、whereを利用することで構造に不一致になるケースがあり、HACK,WORKAROUNDで対処しました。

原因はなんとなくイメージできているんですが、詳しい方いましたら、教えて頂きたいです。

  • 完全に再現できるソースではなく、イメージです。

models

まず、モデルはこの親子関係になります。

# app/models/user.rb

class user < ApplicationRecord
  has_many :shops
end


# app/models/shop.rb
class shop < ApplicationRecord
  belongs_to :user
end

controller

app/controllers/users.rb

class Users < ApplicationController
  def search
    relations = user.joins(:shop).where(name: 'hoge')
    
    relations = if params[:conditions] == 'or'
                  relations.or(relations.where(age: 19)
                else
                  # relations.joins_valuesが更新される
                  relations.merge(relations.where(age: 19))
                end
    
    render json: relations.to_json({ include: :shops })
  end
end

relations.joins_values

これで、引数とレシーバーの構造が一致しているか検証しています。 joinsの引数(上記のサンプルコードでは、:shop)が入っていますので、これが一致するか見ています。

捕捉ですが、limit, offset, distinctも見ています。

WORKAROUND

修正したのは下記。

app/controllers/users.rb

class Users < ApplicationController
  def search
    relations = user.joins(:shop).where(name: 'hoge')
    
    relations = if params[:conditions] == 'or'
                  relations.joins_values = relatoins.joins_values.uniq # HACK:
                  relations.or(relations.where(age: 19)
                else
                  relations.merge(relations.where(age: 19)
                end
    
    render json: relations.to_json({ include: :shops })
  end
end

joins_valuesを上書きすることで対応した。これmergeをしているからjoins_valuesの値も追加されているのかと思い、whereに変えてみたが、 joins_valuesの値は変わらず。githubで調べるのはこのぐらいにして一時的な対応です。

参考

https://github.com/rails/rails/blob/0f80cb1b2b12f3c220391784d3a16473d7b87669/activerecord/lib/active_record/relation.rb#L3

to_json (Hash) - APIdock

関係ないけど後輩に教わった

vim正規表現\@=を教わった。vim情報はありがたい。