railsのActiveRecordでandとorの複合条件の絞り込みをする時
railsのActiveRecordで
AかつBorC(A and (B or C))の条件を満たす書き方のメモです。
結論: .mergeを使用する
.where(条件A) .merge(モデル名.where(条件B).or(モデル名.where(条件c)))
SQLを書いてしまった方が楽なのですがActiveRecordで書きたい場面があったので
何らかの理由でSQLを書きたくない、そういった時に使います。
今回想定するテーブル構造は下記です。
テーブル構造
class MasterProduct < ApplicationRecord has_many :order_product end # ### Columns # # Name | Type | Attributes # ------------------------------------------ | ------------------ | --------------------------- # **`created_at`** | `datetime` | `not null` # **`distribution_packaging_code`** | `string(14)` | # **`id`** | `integer` | `not null, primary key` # **`jan_code`** | `string(13)` | `not null` | # **`updated_at`** | `datetime` | `not null`
class OrderProduct < ApplicationRecord belongs_to :master_product end # ### Columns # # Name | Type | Attributes # ---------------------------- | ------------------ | --------------------------- # **`created_at`** | `datetime` | `not null` # **`id`** | `integer` | `not null, primary key` # **`pharmacy_id`** | `integer` | `not null` # **`wholesaler_id`** | `integer` | # **`master_product_id`** | `integer` | `not null`
MasterProductに紐づくOrderProductテーブルがある
OrderProductのpharmacy_idがxかつMasterProductのdistribution_packaging_codeかjan_codeが指定の値のレコードを取り出す
SQLにするとこう
(SELECT order_products.wholesaler_id, order_products.master_product_id FROM order_products INNER JOIN master_products ON master_products.id = order_products.master_product_id WHERE order_products.pharmacy_id = :pharmacy_id AND (master_products.distribution_packaging_code = :distribution_packaging_code OR master_products.jan_code = :jan_code) ORDER BY order_appointed_on DESC, order_products.created_at DESC LIMIT 1)
ActiveRecordにするとこう
OrderProduct.select(:id) .where(pharmacy_id: pharmacy.id) .joins(:master_product) .merge(MasterProduct.where(distribution_packaging_code: params[:id]).or(MasterProduct.where(jan_code: params[:id]))) .order(order_appointed_on: :desc, created_at: :desc) .limit(1)