ActiveRecord หรือแบบสอบถาม


181

คุณจะทำการสืบค้น OR ใน Rails 3 ActiveRecord ได้อย่างไร ตัวอย่างทั้งหมดที่ฉันพบมีเพียงแบบสอบถาม

แก้ไข: หรือวิธีนี้มีให้ตั้งแต่ Rails 5. ดูActiveRecord :: QueryMethods


7
เรียนรู้ SQL ActiveRecord ทำให้การทำงานของคุณเป็นเรื่องง่าย แต่คุณยังจำเป็นต้องรู้ว่าข้อความค้นหาของคุณทำงานเป็นอย่างไรมิฉะนั้นโครงการของคุณอาจไม่ขยาย ฉันก็คิดว่าฉันสามารถผ่านมาได้โดยไม่ต้องจัดการกับ SQL และฉันก็ผิดมาก
ryan0

1
@ ryan0 คุณพูดถูก เราอาจใช้อัญมณีแฟนซีและสิ่งอื่น ๆ แต่เราควรระวังสิ่งที่อัญมณีเหล่านี้กำลังทำอยู่ข้างในและเทคโนโลยีพื้นฐานคืออะไรและอาจใช้เทคโนโลยีพื้นฐานโดยปราศจากความช่วยเหลือของอัญมณีหากจำเป็นอาจเกิดขึ้นเพื่อประโยชน์ของ ประสิทธิภาพ. อัญมณีถูกสร้างขึ้นด้วยจำนวน usecases ที่เฉพาะเจาะจง แต่อาจมีสถานการณ์ที่ usecase หนึ่งในโครงการของเราอาจแตกต่างกัน
rubyprince


1
ตั้งแต่ Rails 5 IMHO คำตอบที่ยอมรับควรเป็นรุ่น Greg Olsen:Post.where(column: 'something').or(Post.where(other: 'else'))
Philipp Claßen

6
คำถามนี้มีแท็กของ ruby-on-rails-3 ทำไมคำตอบที่ได้รับการยอมรับจึงเกี่ยวข้องกับ Rails 5 เท่านั้น
iNulty

คำตอบ:


109

ใช้ARel

t = Post.arel_table

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

ผลลัพธ์ SQL:

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

รู้สึกยุ่งเล็กน้อย แต่อย่างน้อยฉันไม่ได้เขียน sql ซึ่งรู้สึกผิด! ฉันจะต้องใช้ Arel ให้มากขึ้น
pho3nixf1re

55
ฉันไม่สามารถเชื่อได้ว่าภาคต่อที่งดงามยิ่งกว่านั้นคืออะไร
Macario

3
ไม่มีอะไรผิดปกติกับ SQL สิ่งที่สามารถไปผิดคือวิธีที่เราสร้างสตริง SQL ได้แก่SQL Injection ดังนั้นเราจะต้องฆ่าเชื้ออินพุตของผู้ใช้ก่อนที่จะให้คิวรี SQL ที่ต้องรันกับฐานข้อมูล สิ่งนี้จะได้รับการจัดการโดยORM s และสิ่งเหล่านี้ได้รับการจัดการกรณีขอบที่เรามักจะพลาด ดังนั้นจึงแนะนำให้ใช้ORMเพื่อสร้างแบบสอบถาม SQL
rubyprince

5
ปัญหาอีกอย่างหนึ่งของ SQL ก็คือมันไม่ใช่ฐานข้อมูลไม่เชื่อเรื่องพระเจ้า ตัวอย่างเช่นmatchesจะสร้างLIKEสำหรับ sqlite (case insensitive โดยค่าเริ่มต้น) และILIKEpostgres (ต้องการ case insensitive ที่ชัดเจนเช่นโอเปอเรเตอร์)
อะมีบา

1
@ToniLeigh ActiveRecord กำลังใช้งาน SQL ภายใต้ประทุน มันเป็นเพียงไวยากรณ์สำหรับการเขียนเคียวรี SQL ที่จัดการกับการฆ่าเชื้อ SQL และทำให้เคียวรี DB ของคุณไม่เชื่อเรื่องพระเจ้า โอเวอร์เฮดเดียวคือที่ Rails แปลงไวยากรณ์เป็นเคียวรี SQL และมันก็เป็นเพียงเรื่องของมิลลิวินาที Ofcourse คุณควรเขียนแบบสอบถาม SQL performant ที่ดีที่สุดและพยายามแปลงเป็นไวยากรณ์ ActiveRecord หากไม่สามารถแสดง SQL ในไวยากรณ์ ActiveRecord คุณควรไปที่ SQL ธรรมดา
rubyprince

208

หากคุณต้องการใช้ตัวดำเนินการ OR กับค่าของคอลัมน์หนึ่งคุณสามารถส่งอาร์เรย์ไปยัง.whereและ ActiveRecord จะใช้IN(value,other_value):

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

เอาท์พุท:

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

สิ่งนี้ควรบรรลุความเท่าเทียมกันของORหนึ่งคอลัมน์


13
นี้เป็นที่สะอาดที่สุด "รางทาง" คำตอบที่ควรได้รับการยอมรับ
koonse

10
ขอบคุณ @koonse ก็ไม่ได้ว่าการสอบถาม 'หรือ' แต่มันก่อผลเดียวกันสำหรับสถานการณ์นี้
deadkarma

ช่วยถ้าคุณทำได้ - stackoverflow.com/questions/15517286/…
Pratik Bothra

80
วิธีนี้ไม่ใช่การแทนที่หรือเพราะคุณไม่สามารถใช้สองคอลัมน์ที่แตกต่างกันในลักษณะนี้
fotanus

152

ใน Rails 3 ควรเป็น

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

นอกจากนี้ยังรวมถึง sql ดิบ แต่ฉันไม่คิดว่ามีวิธีใน ActiveRecord ที่จะทำหรือดำเนินการ คำถามของคุณไม่ใช่คำถาม noob


41
แม้ว่าจะมี sql raw - คำสั่งนี้ดูชัดเจนสำหรับฉันมากกว่า Arel แม้กระทั่ง DRYer
jibiel

6
หากคุณต้องการเชื่อมโยงตารางอื่น ๆ ที่เข้าร่วมซึ่งอาจมีคอลัมน์ที่มีชื่อคอลัมน์เดียวกันคุณควรใช้มันเช่นนี้Page.where("pages.column = ? or pages.other_column = ?", value, other_value)
rubyprince

1
และที่ล้มเหลวหากแบบสอบถามนามแฝงตาราง นั่นคือเมื่อ arel ส่องแสง
DGM

58

Rails / ActiveRecord รุ่นที่ปรับปรุงแล้วอาจรองรับไวยากรณ์นี้ได้ มันจะมีลักษณะคล้ายกับ:

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

ตามที่ระบุไว้ในคำขอดึงนี้https://github.com/rails/rails/pull/9052

สำหรับตอนนี้เพียงแค่ติดกับงานต่อไปนี้ได้ดี:

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

ปรับปรุง:ตามhttps://github.com/rails/rails/pull/16052orคุณลักษณะจะสามารถใช้ได้ใน Rails 5

อัพเดท:ฟีเจอร์ถูกรวมเข้ากับ Rails 5 branch


2
orใช้งานได้แล้วตอนนี้ Rails 5 แต่ไม่สามารถใช้งานได้เนื่องจากคาดว่าจะมีการส่งอาร์กิวเมนต์ 1 รายการ มันคาดว่าวัตถุ Arel ดูคำตอบที่ยอมรับ
El'Magnifico

2
orวิธีการใน ActiveRecord ทำงานหากคุณกำลังใช้โดยตรง แต่สามารถทำลายความคาดหวังของคุณถ้ามันใช้อยู่ในขอบเขตซึ่งเป็นที่ถูกล่ามโซ่แล้ว
รายการเจเรมี

@JeremyList พูดว่าอะไรเป็นจุดสนใจ นั่นทำให้ฉันลำบาก
courtimas

36

Rails ได้เพิ่มสิ่งนี้ลงใน ActiveRecord ดูเหมือนว่าจะเปิดตัวใน Rails 5 มุ่งมั่นที่จะโทแล้ว:

https://github.com/rails/rails/commit/9e42cf019f2417473e7dcbfcb885709fa2709f89

Post.where(column: 'something').or(Post.where(other: 'else'))

# => SELECT * FROM posts WHERE (column = 'something') OR (other = 'else)


14

หากคุณต้องการใช้อาร์เรย์เป็นอาร์กิวเมนต์รหัสต่อไปนี้จะทำงานใน 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))

ข้อมูลเพิ่มเติม: หรือแบบสอบถามกับอาร์เรย์เป็นข้อโต้แย้งในทางรถไฟ 4


9
หรือนี่คือOrder.where(query.where_values.inject(:or))การใช้ arel ตลอดทาง
Cameron Martin

> หรือแบบสอบถามที่มีอาร์เรย์เป็นอาร์กิวเมนต์ใน Rails 4. ลิงค์ที่มีประโยชน์! ขอบคุณ!
Zeke Fast

7

MetaWhereปลั๊กอินเป็นที่น่าตื่นตาตื่นใจอย่างสมบูรณ์

ผสม OR หรือ AND และร่วมกับเงื่อนไขในการเชื่อมโยงใด ๆ ได้อย่างง่ายดายและยังระบุ OUTER JOIN ของ!

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

6

เพียงเพิ่มหรือในเงื่อนไข

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

4
นี่เป็นไวยากรณ์สำหรับ Rails 2 มากกว่าและฉันต้องเขียนอย่างน้อยส่วนของสตริง sql
pho3nixf1re

3

ฉันเพิ่งแยกปลั๊กอินนี้จากงานลูกค้าที่ให้คุณรวมขอบเขตกับ.or., ex. Post.published.or.authored_by(current_user). Squeel (การใช้ MetaSearch รุ่นใหม่กว่า) นั้นยอดเยี่ยมเช่นกัน แต่ไม่อนุญาตให้คุณหรือขอบเขตดังนั้นตรรกะการสืบค้นจึงสามารถซ้ำซ้อนได้เล็กน้อย


3

ด้วย Rails + arel วิธีที่ชัดเจนยิ่งขึ้น:

# 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

ผลิต:

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

3

คุณสามารถทำได้เช่น:

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

หรือมากกว่านั้นให้ติดตั้งrails_or gem และทำเช่น:

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

-2

ฉันต้องการเพิ่มสิ่งนี้เป็นวิธีแก้ปัญหาเพื่อค้นหาคุณลักษณะหลายอย่างของ ActiveRecord ตั้งแต่

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

จะค้นหา A และ B


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

2
เพิ่มคำอธิบายให้กับคำตอบของคุณ
kvorobiev

1
ฉันเชื่อว่ามัทธิวพูดถึงอัญมณีactiverecord_any_ofซึ่งเพิ่มการสนับสนุนสำหรับไวยากรณ์นี้
DannyB

2
หากเป็นจริงขอขอบคุณ @DannyB คำตอบต้องอธิบายบริบทของมัน
Sebastialonso
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.