Truy vấn ActiveRecord HOẶC


181

Làm thế nào để bạn thực hiện một truy vấn OR trong Rails 3 ActiveRecord. Tất cả các ví dụ tôi tìm thấy chỉ có truy vấn AND.

Chỉnh sửa: Phương thức OR khả dụng kể từ Rails 5. Xem ActiveRecord :: QueryMethods


7
Tìm hiểu SQL. ActiveRecord giúp bạn dễ dàng làm mọi thứ hoạt động, nhưng bạn vẫn cần biết các truy vấn của mình đang làm gì, nếu không dự án của bạn có thể không mở rộng. Tôi cũng nghĩ rằng tôi có thể nhận được mà không bao giờ phải đối phó với SQL, và tôi đã rất sai về điều đó.
ryan0

1
@ ryan0, bạn rất đúng. Chúng ta có thể sử dụng đá quý ưa thích và những thứ khác, nhưng chúng ta nên biết những viên đá quý này đang làm gì bên trong và công nghệ cơ bản là gì, và có thể sử dụng các công nghệ cơ bản mà không cần sự trợ giúp của đá quý, nếu cần có thể phát sinh hiệu suất. Đá quý được tạo ra với một số lượng cụ thể trong tâm trí, nhưng có thể có những tình huống mà một trong những usecase trong dự án của chúng tôi có thể khác nhau.
rubyprince


1
Kể từ Rails 5, IMHO câu trả lời được chấp nhận phải là phiên bản Greg Olsen:Post.where(column: 'something').or(Post.where(other: 'else'))
Philipp Claßen

6
Câu hỏi này có một thẻ ruby-on-rails-3. Tại sao câu trả lời được chấp nhận chỉ liên quan đến Rails 5?
iN Khoa

Câu trả lời:


109

Sử dụng ARel

t = Post.arel_table

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

SQL kết quả:

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%'))

Cảm thấy hơi lộn xộn, nhưng ít nhất tôi không viết sql, điều này chỉ cảm thấy sai! Tôi sẽ phải xem xét sử dụng Arel nhiều hơn.
pho3nixf1re

55
Tôi không thể tin được phần tiếp theo thanh lịch hơn thế nào
Macario

3
Không có gì sai với SQL. Điều có thể sai là cách chúng tôi xây dựng chuỗi SQL cụ thể là SQL Injection . Vì vậy, chúng tôi sẽ phải vệ sinh đầu vào của người dùng trước khi cung cấp cho truy vấn SQL phải chạy trên cơ sở dữ liệu. Điều này sẽ được xử lý bởi ORM và chúng đã được xử lý các trường hợp cạnh mà chúng ta có xu hướng bỏ lỡ. Vì vậy, luôn luôn nên sử dụng ORM để tạo các truy vấn SQL.
rubyprince

5
Một vấn đề khác với SQL là nó không phải là cơ sở dữ liệu bất khả tri. Ví dụ matchessẽ sản xuất LIKEcho sqlite (trường hợp không nhạy cảm theo mặc định) và ILIKEtrên postgres (cần trường hợp không nhạy cảm rõ ràng như toán tử).
amoebe

1
@ToniLeigh ActiveRecord đang sử dụng SQL trong phần mềm. Nó chỉ là một cú pháp để viết các truy vấn SQL xử lý vệ sinh SQL và làm cho các truy vấn của bạn không rõ ràng. Chi phí duy nhất là nơi Rails chuyển đổi cú pháp thành truy vấn SQL. Và nó chỉ là vấn đề của một phần nghìn giây. Tất nhiên, bạn nên viết truy vấn SQL hiệu suất tốt nhất và cố gắng chuyển đổi nó thành cú pháp ActiveRecord. Nếu SQL không thể được biểu diễn theo cú pháp ActiveRecord, thì bạn nên dùng SQL đơn giản.
rubyprince

208

Nếu bạn muốn sử dụng toán tử OR trên một giá trị của một cột, bạn có thể chuyển một mảng tới .wherevà ActiveRecord sẽ sử dụng IN(value,other_value):

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

đầu ra:

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

Điều này sẽ đạt được tương đương với một ORtrên một cột duy nhất


13
đây là câu trả lời "đường ray" sạch nhất, đáng lẽ phải được chấp nhận
koonse

10
Cảm ơn @koonse, đây không chính xác là truy vấn 'HOẶC', nhưng nó tạo ra kết quả tương tự cho tình huống này.
deadkarma


80
Giải pháp này không thay thế cho OR vì bạn không thể sử dụng hai cột khác nhau theo cách này.
fotanus

152

trong Rails 3, nó phải là

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

Điều này cũng bao gồm sql thô nhưng tôi không nghĩ có một cách nào trong ActiveRecord để thực hiện thao tác HOẶC. Câu hỏi của bạn không phải là một câu hỏi không.


41
Ngay cả khi có sql thô - tuyên bố này đối với tôi rõ ràng hơn Arel. Thậm chí có thể DRYer.
jibiel

6
nếu bạn cần xâu chuỗi, các bảng khác tham gia có thể có các cột có cùng tên cột, bạn nên sử dụng nó như thế này,Page.where("pages.column = ? or pages.other_column = ?", value, other_value)
rubyprince

1
Và điều đó không thành công nếu truy vấn bí danh các bảng. Đó là khi arel tỏa sáng.
DGM

58

Phiên bản cập nhật của Rails / ActiveRecord có thể hỗ trợ cú pháp này nguyên bản. Nó sẽ trông giống như:

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

Như đã lưu ý trong yêu cầu kéo này https://github.com/rails/rails/pull/9052

Bây giờ, chỉ cần gắn bó với các công việc sau đây là tuyệt vời:

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

Cập nhật: Theo https://github.com/rails/rails/pull/16052 , ortính năng này sẽ khả dụng trong Rails 5

Cập nhật: Tính năng đã được hợp nhất với chi nhánh Rails 5


2
orhiện có sẵn Rails 5 nhưng không được thực hiện theo cách này vì nó hy vọng 1 đối số sẽ được thông qua. Nó mong đợi một đối tượng Arel. Xem câu trả lời được chấp nhận
El'Magnifico

2
Các orphương pháp trong ActiveRecord hoạt động nếu bạn đang sử dụng nó trực tiếp: nhưng có thể phá vỡ sự mong đợi của bạn nếu nó được sử dụng trong một phạm vi mà sau đó được xích.
Danh sách Jeremy

Những gì @JeremyList nói là tại chỗ. Điều đó làm tôi khó khăn.
Courtimas


23

Rails 5 đi kèm với một orphương pháp. (l mực vào tài liệu )

Phương pháp này chấp nhận một ActiveRecord::Relationđối tượng. ví dụ:

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

14

Nếu bạn muốn sử dụng mảng làm đối số, đoạn mã sau hoạt động trong 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))

Thông tin thêm: HOẶC truy vấn với mảng dưới dạng đối số trong Rails 4 .


9
Hoặc này: Order.where(query.where_values.inject(:or))để sử dụng arel tất cả các cách.
Cameron Martin

> HOẶC truy vấn với mảng dưới dạng đối số trong Rails 4. Liên kết hữu ích! Cảm ơn!
Zeke nhanh

7

Các MetaWhere plugin là hoàn toàn tuyệt vời.

Dễ dàng trộn lẫn OR và AND, tham gia các điều kiện trên bất kỳ liên kết nào và thậm chí chỉ định OUTER THAM GIA!

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

6

Chỉ cần thêm OR trong điều kiện

Model.find(:all, :conditions => ["column = ? OR other_column = ?",value, other_value])

4
Đây là cú pháp nhiều hơn cho Rails 2 và nó yêu cầu tôi phải viết ít nhất một phần của chuỗi sql.
pho3nixf1re

3

Tôi vừa trích xuất plugin này từ công việc của khách hàng cho phép bạn kết hợp phạm vi với .or., ví dụ. Post.published.or.authored_by(current_user). Squeel (triển khai MetaSearch mới hơn) cũng rất tuyệt, nhưng không cho phép bạn HOẶC phạm vi, vì vậy logic truy vấn có thể có một chút dư thừa.


3

Với đường ray + arel, một cách rõ ràng hơn:

# 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

Sản xuất:

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

3

Bạn có thể làm điều đó như:

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

hoặc thanh lịch hơn, cài đặt rails_or gem và làm như sau:

Person.where(:name => 'Pearl').or(:age => 24)

-2

Tôi muốn thêm đây là một giải pháp để tìm kiếm nhiều thuộc tính của ActiveRecord. Từ

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

sẽ tìm kiếm A và B.


-4
Book.where.any_of(Book.where(:author => 'Poe'), Book.where(:author => 'Hemingway')

2
Thêm một số giải thích cho câu trả lời của bạn
kvorobiev

1
Tôi tin rằng Matthew đã đề cập đến đá quý Activerecord_any_of có thêm hỗ trợ cho cú pháp này.
DannyB

2
Nếu đúng, cảm ơn @DannyB. Câu trả lời phải giải thích bối cảnh của nó.
Sebastialonso
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.