ActiveRecord ATAU kueri

181

Bagaimana Anda melakukan kueri OR di Rails 3 ActiveRecord. Semua contoh yang saya temukan hanya memiliki DAN kueri.

Edit: ATAU metode tersedia sejak Rails 5. Lihat ActiveRecord :: QueryMethods

pho3nixf1re
sumber
7
Pelajari SQL. ActiveRecord membuatnya mudah untuk membuat semuanya berfungsi, tetapi Anda masih perlu tahu apa yang dilakukan pertanyaan Anda, jika tidak proyek Anda mungkin tidak berskala. Saya juga berpikir saya bisa bertahan tanpa harus berurusan dengan SQL, dan saya sangat salah tentang itu.
ryan0
1
@ ryan0, kamu benar sekali. Kita mungkin menggunakan permata mewah dan hal-hal lain, tetapi kita harus menyadari apa yang permata ini lakukan di dalam dan apa teknologi yang mendasarinya, dan mungkin menggunakan teknologi yang mendasarinya tanpa bantuan permata, jika perlu, demi kinerja. Permata dibuat dengan sejumlah usecases dalam pikiran, tetapi mungkin ada situasi di mana salah satu usecase dalam proyek kami mungkin berbeda.
rubyprince
2
Mungkin bisa membantu: ActiveRecord ATAU permintaan Notasi Hash
potashin
1
Sejak Rails 5, IMHO jawaban yang diterima adalah versi Greg Olsen:Post.where(column: 'something').or(Post.where(other: 'else'))
Philipp Claßen
6
Pertanyaan ini memiliki tag ruby-on-rails-3. Mengapa jawaban yang diterima hanya berhubungan dengan Rails 5?
iNulty

Jawaban:

109

Gunakan ARel

t = Post.arel_table

results = Post.where(
  t[:author].eq("Someone").
  or(t[:title].matches("%something%"))
)

SQL yang dihasilkan:

ree-1.8.7-2010.02 > puts Post.where(t[:author].eq("Someone").or(t[:title].matches("%something%"))).to_sql
SELECT     "posts".* FROM       "posts"  WHERE     (("posts"."author" = 'Someone' OR "posts"."title" LIKE '%something%'))
Dan McNevin
sumber
Terasa agak berantakan, tapi setidaknya saya tidak menulis sql, yang terasa salah! Saya harus melihat lebih dalam menggunakan Arel.
pho3nixf1re
55
Saya tidak bisa percaya betapa Sequel jauh lebih elegan
Macario
3
Tidak ada yang salah dengan SQL. Hal yang bisa salah adalah bagaimana kita membangun string SQL yaitu SQL Injection . Jadi, kita harus membersihkan input pengguna sebelum memberikannya ke query SQL yang harus dijalankan terhadap database. Ini akan ditangani oleh ORM , dan ini telah menangani kasus tepi, yang cenderung kita lewatkan. Jadi, selalu disarankan untuk menggunakan ORM untuk membuat query SQL.
rubyprince
5
Masalah lain dengan SQL adalah bahwa itu bukan database agnostik. Sebagai contoh matchesakan menghasilkan LIKEuntuk sqlite (case sensitif pada pengaturan standar) dan ILIKEpada postgres (membutuhkan operator case seperti sensitif).
amoebe
1
@ToniLeigh ActiveRecord menggunakan SQL di bawah tenda. Ini hanya sebuah sintaks untuk menulis query SQL yang menangani sanitasi SQL dan membuat DB query Anda agnostik. Satu-satunya overhead adalah di mana Rails mengubah sintaks ke query SQL. Dan itu hanya masalah milidetik. Tentu, Anda harus menulis kueri SQL berkinerja terbaik dan mencoba mengubahnya menjadi sintaks ActiveRecord. Jika SQL tidak dapat direpresentasikan dalam sintaks ActiveRecord, maka Anda harus menggunakan SQL biasa.
rubyprince
208

Jika Anda ingin menggunakan operator OR pada nilai satu kolom, Anda dapat meneruskan array ke .wheredan ActiveRecord akan menggunakan IN(value,other_value):

Model.where(:column => ["value", "other_value"]

output:

SELECT `table_name`.* FROM `table_name` WHERE `table_name`.`column` IN ('value', 'other_value')

Ini harus mencapai persamaan ORpada kolom tunggal

deadkarma
sumber
13
ini adalah jawaban "jalan rel" terbersih, seharusnya diterima
koonse
10
Terima kasih @koonse, itu tidak persis sebuah 'OR' query, tetapi menghasilkan hasil yang sama untuk situasi ini.
deadkarma
Bantuan jika Anda bisa - stackoverflow.com/questions/15517286/…
Pratik Bothra
80
Solusi ini bukan pengganti untuk OR karena Anda tidak dapat menggunakan dua kolom yang berbeda dengan cara ini.
fotanus
152

dalam Rails 3, seharusnya

Model.where("column = ? or other_column = ?", value, other_value)

Ini juga termasuk sql mentah tapi saya rasa tidak ada cara di ActiveRecord untuk melakukan operasi ATAU. Pertanyaan Anda bukan pertanyaan noob.

rubyprince
sumber
41
Bahkan jika ada sql mentah - pernyataan ini terlihat lebih jelas bagi saya daripada Arel. Mungkin bahkan KERING.
jibiel
6
jika Anda perlu untuk rantai, tabel lain bergabung yang mungkin memiliki kolom dengan nama kolom yang sama, Anda harus menggunakannya seperti ini,Page.where("pages.column = ? or pages.other_column = ?", value, other_value)
rubyprince
1
Dan itu gagal jika kueri alias tabel. Saat itulah arel bersinar.
DGM
58

Versi Rails / ActiveRecord yang diperbarui mungkin mendukung sintaksis ini secara asli. Itu akan terlihat mirip dengan:

Foo.where(foo: 'bar').or.where(bar: 'bar')

Seperti yang tercantum dalam permintaan tarik ini https://github.com/rails/rails/pull/9052

Untuk saat ini, cukup bertahan dengan karya-karya berikut hebat:

Foo.where('foo= ? OR bar= ?', 'bar', 'bar')

Update: Menurut https://github.com/rails/rails/pull/16052 yang orfitur akan tersedia di Rails 5

Pembaruan: Fitur telah digabungkan ke cabang Rails 5

Christian Fazzini
sumber
2
orsekarang tersedia Rails 5 tetapi tidak diimplementasikan dengan cara ini karena mengharapkan 1 argumen untuk disahkan. Ia mengharapkan objek Arel. Lihat jawaban yang diterima
El'Magnifico
2
The ormetode dalam ActiveRecord bekerja jika Anda menggunakannya secara langsung, tetapi dapat mematahkan harapan Anda jika digunakan dalam lingkup yang kemudian dirantai.
Daftar Jeremy
Apa yang dikatakan @JeremyList tepat. Itu menggigit saya.
courtimas
23

Rails 5 hadir dengan sebuah ormetode. (l tinta ke dokumentasi )

Metode ini menerima suatu ActiveRecord::Relationobjek. misalnya:

User.where(first_name: 'James').or(User.where(last_name: 'Scott'))
Santhosh
sumber
14

Jika Anda ingin menggunakan array sebagai argumen, kode berikut berfungsi di Rails 4:

query = Order.where(uuid: uuids, id: ids)
Order.where(query.where_values.map(&:to_sql).join(" OR "))
#=> Order Load (0.7ms)  SELECT "orders".* FROM "orders" WHERE ("orders"."uuid" IN ('5459eed8350e1b472bfee48375034103', '21313213jkads', '43ujrefdk2384us') OR "orders"."id" IN (2, 3, 4))

Informasi lebih lanjut: ATAU kueri dengan array sebagai argumen di Rails 4 .

Rafał Cieślak
sumber
9
Atau ini: Order.where(query.where_values.inject(:or))untuk menggunakan arel sepenuhnya.
Cameron Martin
> ATAU kueri dengan array sebagai argumen di Rails 4. Tautan yang berguna! Terima kasih!
Zeke Fast
7

The MetaWhere Plugin benar-benar menakjubkan.

Gabungkan OR's dan AND's dengan mudah, gabung dengan kondisi di asosiasi mana pun, dan bahkan tentukan OUTER JOIN!

Post.where({sharing_level: Post::Sharing[:everyone]} | ({sharing_level: Post::Sharing[:friends]} & {user: {followers: current_user} }).joins(:user.outer => :followers.outer}
Bangsawan tinggi
sumber
6

Tambahkan saja ATAU dalam kondisi

Model.find(:all, :conditions => ["column = ? OR other_column = ?",value, other_value])
Toby Hede
sumber
4
Ini lebih merupakan sintaks untuk Rails 2, dan itu mengharuskan saya untuk menulis setidaknya sebagian dari string sql.
pho3nixf1re
3

Saya baru saja mengekstrak plugin ini dari pekerjaan klien yang memungkinkan Anda menggabungkan cakupan dengan .or., mis. Post.published.or.authored_by(current_user). Squeel (implementasi MetaSearch yang lebih baru) juga bagus, tetapi tidak membiarkan Anda ATAU ruang lingkup, jadi logika kueri bisa menjadi sedikit berlebihan.

Woahdae
sumber
3

Dengan rel + arel, cara yang lebih jelas:

# Table name: messages
#
# sender_id:    integer
# recipient_id: integer
# content:      text

class Message < ActiveRecord::Base
  scope :by_participant, ->(user_id) do
    left  = arel_table[:sender_id].eq(user_id)
    right = arel_table[:recipient_id].eq(user_id)

    where(Arel::Nodes::Or.new(left, right))
  end
end

Menghasilkan:

$ Message.by_participant(User.first.id).to_sql 
=> SELECT `messages`.* 
     FROM `messages` 
    WHERE `messages`.`sender_id` = 1 
       OR `messages`.`recipient_id` = 1
itsnikolay
sumber
3

Anda bisa melakukannya seperti:

Person.where("name = ? OR age = ?", 'Pearl', 24)

atau lebih elegan, pasang rails_or gem dan lakukan seperti:

Person.where(:name => 'Pearl').or(:age => 24)
khiav reoy
sumber
-2

Saya ingin menambahkan ini adalah solusi untuk mencari beberapa atribut dari ActiveRecord. Sejak

.where(A: param[:A], B: param[:B])

akan mencari A dan B.

Tomás Gaete
sumber
-4
Book.where.any_of(Book.where(:author => 'Poe'), Book.where(:author => 'Hemingway')
Matthew Rigdon
sumber
2
Tambahkan beberapa penjelasan pada jawaban Anda
kvorobiev
1
Saya percaya Matius mengacu pada permata activerecord_any_of yang menambahkan dukungan untuk sintaks ini.
DannyB
2
Jika benar, terima kasih @DannyB. Jawabannya harus menjelaskan konteksnya.
Sebastialonso