2つのActiveRecord :: Relationオブジェクトを組み合わせる


174

次の2つのオブジェクトがあるとします。

first_name_relation = User.where(:first_name => 'Tobias') # ActiveRecord::Relation
last_name_relation  = User.where(:last_name  => 'Fünke') # ActiveRecord::Relation

2つの関係を組み合わせて、ActiveRecord::Relation両方の条件を含む1つのオブジェクトを生成することは可能ですか?

注:この動作を取得する場所をチェーンできることを知っていActiveRecord::Relationます。私が本当に興味を持っているのは、2つの個別のオブジェクトがある場合です。


役立つかもしれません:ActiveRecord ORクエリハッシュ表記
potashin

回答:


202

AND(交差)を使用して組み合わせる場合は、次を使用しますmerge

first_name_relation.merge(last_name_relation)

OR(union)を使用して組み合わせる場合は、†を使用します。or

first_name_relation.or(last_name_relation)

ActiveRecord 5以降のみ。4.2ではwhere- または backportをインストールします。


結果をActiveRecord::Relationタイプにしたい場合はどうですか?
新しいアレクサンドリア

1
@NewAlexandriaはい、すべてのケースで、Railsはオブジェクトのクラスについて嘘をついています。
アンドリューマーシャル

77
マージの「OR」バージョンはありますか?
Arcolye 2013

8
@minohimself多分それはあなたの二つの関係をマージした実際の結果だからです。mergeは、和集合ではなく、交差点であることに注意してください。
アンドリューマーシャル

5
今後の参考のために、#orメソッドは2015年1月にActiveRecord :: Relationに追加されました。これは、2015年後半に出荷されるRails 5.0の一部になります。OR演算子を使用して、WHERE句またはHAVING句を組み合わせることができます。公式リリースの前に必要な場合は、HEADをチェックアウトできます。マージプルリクエスト#16052を参照@Arcolye @AndrewMarshall @ Aldo-xoen-Giambelluca
Claudio Floreani

37

Relationオブジェクトは配列に変換できます。これにより、後でActiveRecordメソッドを使用できるようになりますが、必要はありませんでした。これは私がしました:

name_relation = first_name_relation + last_name_relation

Ruby 1.9、rails 3.2


更新:これは日付までにマージされないのが残念です
ダニエルモリス

68
配列としては機能せず、関係を配列に変換します。次に、.where()のようなActiveRecordメソッドを使用できなくなります...
Augustin Riedinger

1
戻り値の型がリレーションであることを気にしない場合は、first_name_relation |を使用して複数の結果を組み合わせることができます。last_name_relation。「|」演算子は複数のリレーションでも機能します。結果の値は配列です。
MichaelZ

11
元の質問は、「2つの関係を組み合わせて、両方の条件を含む1つのActiveRecord :: Relationオブジェクトを生成することは可能ですか?」でした。この回答は配列を返します...
コートシマ2017

これは多くの場合に優れたソリューションです。ループするレコードの列挙が必要なだけで(たとえば、それが配列であっても、ActiveRecord :: Relationであっても構いません)、2つのクエリのオーバーヘッドを気にしない場合、これは明白で単純な構文で問題を解決します。+2できたらいいのに!
デビッドヘンピー2018

21

merge実際にはのようには動作しませんOR。単に交差点です(AND

私はActiveRecord :: Relationオブジェクトを1つに結合するためにこの問題に苦労しましたが、有効な解決策が見つかりませんでした。

これらの2つのセットからユニオンを作成する正しい方法を探す代わりに、セットの代数に焦点を当てました。ドモルガンの法則を使用して別の方法でそれを行うことができます

ActiveRecordはマージメソッド(AND)を提供し、notメソッドまたはnone_of(NOT)も使用できます。

search.where.none_of(search.where.not(id: ids_to_exclude).merge(search.where.not("title ILIKE ?", "%#{query}%")))

あなたはここにいます(A u B) '= A' ^ B '

更新:上記のソリューションは、より複雑なケースに適しています。あなたの場合、そのようなsmthで十分です:

User.where('first_name LIKE ? OR last_name LIKE ?', 'Tobias', 'Fünke')

15

Railsの組み込みArelを使用することで、多くの奇妙な状況でもこれを実現できました。

User.where(
  User.arel_table[:first_name].eq('Tobias').or(
    User.arel_table[:last_name].eq('Fünke')
  )
)

これは、Arel またはを使用して、両方のActiveRecord関係をマージします。


ここで提案されているように、Mergeは私には機能しませんでした。結果から関係オブジェクトの2番目のセットを削除しました。


2
これが最良の答えです、IMO。ORタイプのクエリでは問題なく機能します。
thekingoftruth 2015年

これを指摘してくれてありがとう、たくさん助けてくれました。他の回答は、フィールドの小さなサブセットでのみ機能しますが、INステートメントのIDが1万を超えると、パフォーマンスが非常に低下する可能性があります。
onetwopunch 2015

1
@KeesBriggs —そうではありません。私はRailsの4のすべてのバージョンでこれを使用していた
6フィートダン

14

あなたが探しているものかもしれないactive_record_unionと呼ばれる宝石があります。

使用例は次のとおりです。

current_user.posts.union(Post.published)
current_user.posts.union(Post.published).where(id: [6, 7])
current_user.posts.union("published_at < ?", Time.now)
user_1.posts.union(user_2.posts).union(Post.published)
user_1.posts.union_all(user_2.posts)

共有いただきありがとうございます。でも4年あなたのポストの後にこれは私がから組合を作成するための唯一の解決策となっているransackacts-as-taggable-on、私保ったまま、ActiveRecordそのままのインスタンスを。
Benjamin Bojko

このgemを使用する場合、モデルにスコープが定義されていると、union()呼び出しからActiveRecord :: Relationが返されないことがあります。ReadmeのActiveRecordのState of the Unionを参照してください。
16

9

これは、pluckを使用して各レコードの識別子を取得し、配列を結合して、最後にそれらの結合されたIDに対してクエリを実行した場合の「処理」方法です。

  transaction_ids = @club.type_a_trans.pluck(:id) + @club.type_b_transactions.pluck(:id) + @club.type_c_transactions.pluck(:id)
  @transactions = Transaction.where(id: transaction_ids).limit(100)

6

activerecord関係の配列があり、それらをすべてマージしたい場合は、

array.inject(:merge)

array.inject(:merge)レール5.1.4の一連のactiverecordリレーションでは機能しませんでした。しかしarray.flatten!そうした。
zhisme 2018年

0

それをブルートフォース:

first_name_relation = User.where(:first_name => 'Tobias') # ActiveRecord::Relation
last_name_relation  = User.where(:last_name  => 'Fünke') # ActiveRecord::Relation

all_name_relations = User.none
first_name_relation.each do |ar|
  all_name_relations.new(ar)
end
last_name_relation.each do |ar|
  all_name_relations.new(ar)
end

「NoMethodError(#<のために、mymodelた:0x ... Rails3で、でもRails3が持っていないので、MyModel.where(1 = 2)にMyModel.noneを変更した後、未定義のメソッド`stringify_keys ')。どれも'メソッドを
ジョセフK
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.