Rails 5: ActiveRecord OR query

Jawaban:

228

Kemampuan untuk merangkai orklausa bersama dengan whereklausa dalam ActiveRecordkueri akan tersedia di Rails 5 . Lihat diskusi terkait dan permintaan tarik .

Jadi, Anda akan dapat melakukan hal-hal berikut di Rails 5 :

Untuk mendapatkan postdengan id1 atau 2:

Post.where('id = 1').or(Post.where('id = 2'))

Beberapa contoh lain:

(A && B) || C:

    Post.where(a).where(b).or(Post.where(c))

(A || B) && C:

    Post.where(a).or(Post.where(b)).where(c)
KM Rakibul Islam
sumber
3
Bagaimana saya bisa mendapatkan (A || B) && (C || D). Saya mencoba Post.where (a) .or (Post.where (b)). Where (c) .or (Post.where (d)) tetapi menghasilkan sebagai: (A || B) && C || D
Imran Ahmad
3
@Imran saya percaya Post.where(a).or(Post.where(b)).where(Post.where(c).or(Post.where(d)))ini harus membuat (a || b) && (c || d)
engineermnky
1
@Imran Sepertinya ini tidak berhasil untuk saya: Saya mengertiArgumentError: Unsupported argument type: #<MyModel::ActiveRecord_Relation:0x00007f8edbc075a8> (MyModel::ActiveRecord_Relation)
Suan
5
Setara dengan .or yang mengambil relasi dan menghasilkan dan adalah .merge. (A || B) && (C || D) dapat diproduksi oleh Post.where (a) .or (Post.where (b)). Merge (Post.where (c) .or (Post.where (d )))
Siim Liiser
2
@Bayu_joo Ini ActiveRecord :: Relation # merge. api.rubyonrails.org/classes/ActiveRecord/…
Siim Liiser
13

Kami tidak perlu menunggu rel 5 untuk menggunakan ORkueri ini . Kami juga bisa menggunakannya dengan rails 4.2.3. Ada backport di sini .

Terima kasih kepada Eric-Guo untuk permata di mana-atau , Sekarang kita dapat menambahkan ORfungsi ini >= rails 4.2.3juga dengan menggunakan permata ini.

Dipak Gupta
sumber
6

(Sekadar tambahan jawaban KM Rakibul Islam.)

Dengan menggunakan cakupan, kodenya bisa menjadi lebih cantik (tergantung pada pandangan mata):

scope a,      -> { where(a) }
scope b,      -> { where(b) }

scope a_or_b, -> { a.or(b) }
Nicholas
sumber
5

Saya perlu melakukan a (A && B) || (C && D) || (E && F)

Tapi dalam kondisi Rails 5.1.4saat ini, hal ini terlalu rumit untuk diselesaikan dengan Arel atau rantai. Tapi saya masih ingin menggunakan Rails untuk menghasilkan kueri sebanyak mungkin.

Jadi saya membuat retasan kecil:

Dalam model saya, saya membuat metode pribadi yang disebut sql_where:

private
  def self.sql_where(*args)
    sql = self.unscoped.where(*args).to_sql
    match = sql.match(/WHERE\s(.*)$/)
    "(#{match[1]})"
  end

Selanjutnya dalam ruang lingkup saya, saya membuat sebuah array untuk menampung OR

scope :whatever, -> {
  ors = []

  ors << sql_where(A, B)
  ors << sql_where(C, D)
  ors << sql_where(E, F)

  # Now just combine the stumps:
  where(ors.join(' OR '))
}

Yang akan menghasilkan hasil query yang diharapkan: SELECT * FROM `models` WHERE ((A AND B) OR (C AND D) OR (E AND F)).

Dan sekarang saya dapat dengan mudah menggabungkan ini dengan cakupan lain, dll. Tanpa OR yang salah.

Keindahannya adalah bahwa sql_where saya mengambil argumen klausa where-clause yang normal: sql_where(name: 'John', role: 'admin')akan menghasilkan (name = 'John' AND role = 'admin').

mtrolle
sumber
Saya pikir Anda dapat menggunakan .mergesebagai padanan &&, dan membangun pohon yang tepat untuk menangkap orang tua Anda. Sesuatu seperti ... (scopeA.merge(scopeB)).or(scopeC.merge(scopeD)).or(scopeE.merge(scopeF)), dengan asumsi masing-masing cakupan terlihat sepertiModel.where(...)
nar8789
Lihat ini sebelum menggunakan merge - github.com/rails/rails/issues/33501
Aarthi
1

Rails 5 memiliki kemampuan untuk orklausa dengan where. Sebagai contoh.

User.where(name: "abc").or(User.where(name: "abcd"))
Foram Thakral
sumber