Saya menggunakan Rails3, ActiveRecord
Hanya ingin tahu bagaimana cara merangkai cakupan dengan pernyataan ATAU daripada DAN.
misalnya
Person.where(:name => "John").where(:lastname => "Smith")
Itu biasanya mengembalikan:
name = 'John' AND lastname = 'Smith'
tapi saya suka:
`name = 'John' OR lastname = 'Smith'
ruby-on-rails
ruby-on-rails-3
activerecord
rails-activerecord
pengguna410715
sumber
sumber
Jawaban:
Anda akan melakukannya
Person.where('name=? OR lastname=?', 'John', 'Smith')
Saat ini, tidak ada dukungan ATAU lainnya oleh sintaks AR3 baru (tanpa menggunakan permata pihak ketiga).
sumber
.or
terpasang .Menurut permintaan tarik ini , Rails 5 sekarang mendukung sintaks berikut untuk merangkai kueri:
Post.where(id: 1).or(Post.where(id: 2))
Ada juga fungsionalitas backport ke Rails 4.2 melalui permata ini.
sumber
Gunakan ARel
t = Person.arel_table results = Person.where( t[:name].eq("John"). or(t[:lastname].eq("Smith")) )
sumber
Hanya posting sintaks Array untuk sama kolom OR query untuk bantuan mengintip keluar.
Person.where(name: ["John", "Steve"])
sumber
Pembaruan untuk Rails4
tidak membutuhkan permata pihak ke-3
a = Person.where(name: "John") # or any scope b = Person.where(lastname: "Smith") # or any scope Person.where([a, b].map{|s| s.arel.constraints.reduce(:and) }.reduce(:or))\ .tap {|sc| sc.bind_values = [a, b].map(&:bind_values) }
Jawaban lama
tidak membutuhkan permata pihak ke-3
Person.where( Person.where(:name => "John").where(:lastname => "Smith") .where_values.reduce(:or) )
sumber
or
atau operator lain dalam blok teks, tidak ada yang tercermin dalam kode OP....where_values.join(' OR ')
dalam kasus saya.tap {|sc| sc.bind_values = [a, b].map(&:bind_values) }
bagian tersebut jika cakupan Anda tidak memiliki variabel yang perlu diikat.Jika ada yang mencari jawaban terbaru untuk pertanyaan ini, sepertinya sudah ada permintaan pull untuk memasukkan ini ke Rails: https://github.com/rails/rails/pull/9052 .
Berkat patch monyet @ j-mcnally untuk ActiveRecord ( https://gist.github.com/j-mcnally/250eaaceef234dd8971b ) Anda dapat melakukan hal berikut:
Person.where(name: 'John').or.where(last_name: 'Smith').all
Yang lebih berharga adalah kemampuan untuk merangkai cakupan dengan
OR
:scope :first_or_last_name, ->(name) { where(name: name.split(' ').first).or.where(last_name: name.split(' ').last) } scope :parent_last_name, ->(name) { includes(:parents).where(last_name: name) }
Kemudian Anda dapat menemukan semua Orang dengan nama depan atau belakang atau yang orang tuanya dengan nama belakang
Person.first_or_last_name('John Smith').or.parent_last_name('Smith')
Bukan contoh terbaik untuk penggunaan ini, tetapi hanya mencoba menyesuaikannya dengan pertanyaan.
sumber
.or
fungsionalitas rantai. Saya bisa menggunakan patch monyet yang saya sebutkan di Rails> = 3.2; namun Anda mungkin ingin menunggu Rails 5 untuk memperkenalkan perubahan lama pada kode Anda.Anda juga dapat menggunakan permata MetaWhere untuk tidak mencampurkan kode Anda dengan barang SQL:
Person.where((:name => "John") | (:lastname => "Smith"))
sumber
Bagi saya (Rails 4.2.5) hanya berfungsi seperti ini:
{ where("name = ? or name = ?", a, b) }
sumber
Ini akan menjadi kandidat yang baik untuk MetaWhere jika Anda menggunakan Rails 3.0+, tetapi tidak berfungsi di Rails 3.1. Anda mungkin ingin mencoba squeel . Itu dibuat oleh penulis yang sama. Inilah cara Anda melakukan rantai berbasis ATAU:
Person.where{(name == "John") | (lastname == "Smith")}
Anda dapat mencampur dan mencocokkan DAN / ATAU, di antara banyak hal keren lainnya .
sumber
Versi terbaru dari Rails / ActiveRecord mungkin mendukung sintaks ini secara native. Ini akan terlihat seperti:
Foo.where(foo: 'bar').or.where(bar: 'bar')
Seperti disebutkan dalam permintaan tarik ini https://github.com/rails/rails/pull/9052
Untuk saat ini, cukup berpegang pada hal-hal berikut sudah bagus:
Foo.where('foo= ? OR bar= ?', 'bar', 'bar')
sumber
Foo.where(foo: 'bar1').or.where(bar: 'bar2')
tidak bekerja. Ini harus Foo.where (foo: 'bar1'). Or.where (Foo.where (bar: 'bar2'))Rel 4 + Lingkup + Arel
class Creature < ActiveRecord::Base scope :is_good_pet, -> { where( arel_table[:is_cat].eq(true) .or(arel_table[:is_dog].eq(true)) .or(arel_table[:eats_children].eq(false)) ) } end
Saya mencoba merangkai lingkup bernama dengan .atau dan tidak berhasil, tetapi ini berhasil menemukan apa pun dengan set boolean itu. Menghasilkan seperti SQL
SELECT 'CREATURES'.* FROM 'CREATURES' WHERE ((('CREATURES'.'is_cat' = 1 OR 'CREATURES'.'is_dog' = 1) OR 'CREATURES'.'eats_children' = 0))
sumber
Jika Anda ingin memberikan ruang lingkup (alih-alih secara eksplisit mengerjakan seluruh kumpulan data) inilah yang harus Anda lakukan dengan Rails 5:
scope :john_or_smith, -> { where(name: "John").or(where(lastname: "Smith")) }
Atau:
def self.john_or_smith where(name: "John").or(where(lastname: "Smith")) end
sumber
Rel 4
scope :combined_scope, -> { where("name = ? or name = ?", 'a', 'b') }
sumber
Jika Anda tidak dapat menulis klausa where secara manual untuk menyertakan pernyataan "atau" (yaitu, Anda ingin menggabungkan dua cakupan), Anda dapat menggunakan gabungan:
Model.find_by_sql("#{Model.scope1.to_sql} UNION #{Model.scope2.to_sql}")
(sumber: ActiveRecord Query Union )
Ini akan mengembalikan semua rekaman yang cocok dengan kueri mana pun. Namun, ini mengembalikan array, bukan arel. Jika Anda benar-benar ingin mengembalikan arel, Anda memeriksa inti ini: https://gist.github.com/j-mcnally/250eaaceef234dd8971b .
Ini akan berhasil, selama Anda tidak keberatan dengan rel penambal monyet.
sumber
Lihat juga pertanyaan terkait ini: di sini , di sini , dan di sini
Untuk rel 4, berdasarkan artikel ini dan jawaban asli ini
Person .unscoped # See the caution note below. Maybe you want default scope here, in which case just remove this line. .where( # Begin a where clause where(:name => "John").where(:lastname => "Smith") # join the scopes to be OR'd .where_values # get an array of arel where clause conditions based on the chain thus far .inject(:or) # inject the OR operator into the arels # ^^ Inject may not work in Rails3. But this should work instead: .joins(" OR ") # ^^ Remember to only use .inject or .joins, not both ) # Resurface the arels inside the overarching query
Perhatikan peringatan artikel di akhir:
sumber
Ini adalah cara yang sangat nyaman dan berfungsi dengan baik di Rails 5:
Transaction .where(transaction_type: ["Create", "Correspond"]) .or( Transaction.where( transaction_type: "Status", field: "Status", newvalue: ["resolved", "deleted"] ) ) .or( Transaction.where(transaction_type: "Set", field: "Queue") )
sumber
yang
squeel
permata menyediakan cara yang sangat mudah untuk mencapai hal ini (sebelum ini saya menggunakan sesuatu seperti metode @ coloradoblue ini):names = ["Kroger", "Walmart", "Target", "Aldi"] matching_stores = Grocery.where{name.like_any(names)}
sumber
Jadi, jawaban untuk pertanyaan awal, dapatkah Anda menggabungkan cakupan dengan 'atau' bukannya 'dan' tampaknya "tidak, Anda tidak bisa". Tetapi Anda dapat memberikan kode pada lingkup atau kueri yang sama sekali berbeda yang melakukan pekerjaan itu, atau menggunakan kerangka kerja yang berbeda dari ActiveRecord misalnya MetaWhere atau Squeel. Tidak berguna dalam kasus saya
I'm 'or'ing a scope generated by pg_search, yang melakukan lebih dari sekadar memilih, itu termasuk pesanan oleh ASC, yang membuat kekacauan dari persatuan yang bersih. Saya ingin 'atau' dengan cakupan buatan tangan yang melakukan hal-hal yang tidak dapat saya lakukan di pg_search. Jadi saya harus melakukannya seperti ini.
Product.find_by_sql("(#{Product.code_starts_with('Tom').to_sql}) union (#{Product.name_starts_with('Tom').to_sql})")
Yaitu mengubah cakupan menjadi sql, menempatkan tanda kurung di sekitar masing-masing, menggabungkannya bersama-sama dan kemudian find_by_sql menggunakan sql yang dihasilkan. Agak sampah, tapi berhasil.
Tidak, jangan beri tahu saya bahwa saya dapat menggunakan "against: [: name,: code]" di pg_search, saya ingin melakukannya seperti itu, tetapi kolom 'name' adalah hstore, yang tidak dapat ditangani pg_search namun. Jadi ruang lingkup berdasarkan nama harus dibuat dengan tangan dan kemudian digabungkan dengan ruang lingkup pg_search.
sumber
names = ["tim", "tom", "bob", "alex"] sql_string = names.map { |t| "name = '#{t}'" }.join(" OR ") @people = People.where(sql_string)
sumber