Gabungkan dua objek ActiveRecord :: Relation


174

Misalkan saya memiliki dua objek berikut:

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

Apakah mungkin untuk menggabungkan kedua relasi untuk menghasilkan satu ActiveRecord::Relationobjek yang mengandung kedua kondisi?

Catatan: Saya sadar bahwa saya bisa rantai di mana saja untuk mendapatkan perilaku ini, apa yang saya benar-benar tertarik adalah kasus di mana saya memiliki dua ActiveRecord::Relationobjek terpisah .


Jawaban:


202

Jika Anda ingin menggabungkan menggunakan AND(persimpangan), gunakan merge:

first_name_relation.merge(last_name_relation)

Jika Anda ingin menggabungkan menggunakan OR(gabungan), gunakan :or

first_name_relation.or(last_name_relation)

Hanya di ActiveRecord 5+; untuk 4.2, instal di mana-atau backport.


bagaimana kalau saya ingin hasilnya menjadi ActiveRecord::Relationtipe?
New Alexandria

1
@NewAlexandria Ya, semua kasus, Rails berbohong kepada Anda tentang kelas objek.
Andrew Marshall

77
Apakah ada versi penggabungan "ATAU"?
Arcolye

8
@minohimself Mungkin karena itulah hasil aktual dari penggabungan dua relasi Anda. Perhatikan bahwa itu mergeadalah persimpangan, bukan penyatuan.
Andrew Marshall

5
Untuk referensi di masa mendatang, #ormetode telah ditambahkan ke ActiveRecord :: Relation pada Jan 2015, dan itu akan menjadi bagian dari Rails 5.0 , yang akan dikirimkan pada akhir 2015. Ini memungkinkan penggunaan operator OR untuk menggabungkan klausa WHERE atau HAVING. Anda dapat checkout KEPALA jika Anda membutuhkannya sebelum rilis resmi. Lihat Permintaan Gabung Tarik # 16052 @Arcolye @AndrewMarshall @ Aldo-xoen-Giambelluca
Claudio Floreani

37

Objek relasi dapat dikonversi menjadi array. Ini meniadakan kemampuan untuk menggunakan metode ActiveRecord setelahnya, tetapi saya tidak perlu melakukannya. Saya melakukan ini:

name_relation = first_name_relation + last_name_relation

Ruby 1.9, rails 3.2


Perbarui: kasihan ini tidak bergabung berdasarkan tanggal
Daniel Morris

68
Itu tidak bertindak sebagai sebuah array, itu mengubah hubungan Anda ke sebuah array. Maka Anda tidak dapat menggunakan metode ActiveRecord di atasnya seperti .where () lagi ...
Augustin Riedinger

1
Jika Anda tidak peduli tentang hubungan tipe pengembalian, Anda dapat menggabungkan beberapa hasil dengan first_name_relation | last_name_relation. "|" Operator bekerja pada banyak hubungan juga. Nilai hasil adalah array.
MichaelZ

11
Pertanyaan awal adalah: "apakah mungkin untuk menggabungkan dua relasi untuk menghasilkan satu ActiveRecord :: Objek relasi yang mengandung kedua kondisi?" Jawaban ini mengembalikan array ...
courtimas

Ini adalah solusi yang SANGAT BAIK untuk banyak kasus. Jika Anda hanya perlu enumerasi catatan untuk mengulang (mis. Anda tidak peduli apakah itu Array atau ActiveRecord :: Relation) dan tidak keberatan dengan overhead dua kueri, ini menyelesaikan masalah dengan sintaksis yang jelas dan sederhana. Seandainya aku bisa +2 itu!
David Hempy

21

mergesebenarnya tidak bekerja OR. Ini hanya persimpangan ( AND)

Saya kesulitan dengan masalah ini untuk menggabungkan ke objek ActiveRecord :: Relation menjadi satu dan saya tidak menemukan solusi yang berfungsi untuk saya.

Alih-alih mencari metode yang tepat membuat penyatuan dari dua set ini, saya fokus pada aljabar set. Anda dapat melakukannya dengan cara yang berbeda menggunakan hukum De Morgan

ActiveRecord menyediakan metode gabungan (DAN) dan Anda juga dapat menggunakan metode tidak atau none_dari (TIDAK).

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

Anda miliki di sini (A u B) '= A' ^ B '

UPDATE: Solusi di atas baik untuk kasus yang lebih kompleks. Dalam kasus Anda, pertanda seperti itu akan cukup:

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

15

Saya sudah bisa mencapai ini, bahkan dalam banyak situasi aneh, dengan menggunakan Arel bawaan Rails .

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

Ini menggabungkan kedua hubungan ActiveRecord dengan menggunakan Arel atau .


Penggabungan, seperti yang disarankan di sini, tidak berhasil untuk saya. Itu menjatuhkan set objek relasi ke-2 dari hasil.


2
Ini jawaban terbaik, IMO. Ia bekerja dengan sempurna untuk kueri tipe ATAU.
thekingoftruth

Terima kasih telah menunjukkan ini, banyak membantu saya. Jawaban lainnya hanya berfungsi untuk sebagian kecil bidang, tetapi untuk lebih dari sepuluh ribu id dalam pernyataan IN, kinerjanya bisa sangat buruk.
onetwopunch

1
@KeesBriggs — itu tidak benar. Saya telah menggunakan ini di semua versi Rails 4.
6ft Dan

14

Ada permata bernama active_record_union yang mungkin Anda cari.

Contoh penggunaannya adalah sebagai berikut:

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)

Terima kasih telah berbagi. Bahkan empat tahun setelah posting Anda ini telah menjadi satu-satunya solusi bagi saya untuk membuat persatuan ransackdan acts-as-taggable-onmenjaga ActiveRecordinstans saya tetap utuh.
Benjamin Bojko

Saat menggunakan permata ini, Anda mungkin tidak mendapatkan panggilan ActiveRecord :: Relation yang dikembalikan oleh union () jika Anda memiliki cakupan yang ditentukan pada model. Lihat Status Serikat di ActiveRecord di Readme.
dechimp

9

Ini adalah cara saya "menanganinya" jika Anda menggunakan pluck untuk mendapatkan pengidentifikasi untuk masing-masing rekaman, bergabung dengan array bersama dan akhirnya melakukan kueri untuk id yang bergabung:

  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

Jika Anda memiliki array hubungan activerecord dan ingin menggabungkan semuanya, Anda bisa melakukannya

array.inject(:merge)

array.inject(:merge)tidak bekerja untuk array hubungan activerecord di rel 5.1.4. Tapi itu array.flatten!benar.
zhisme

0

Paksa itu:

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 (metode yang tidak ditentukan` stringify_keys 'untuk # <MyModel: 0x ...) di Rails3, bahkan setelah mengubah MyModel.none ke MyModel.where (1 = 2), karena Rails3 tidak memiliki metode' none '.
JosephK
Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.