รวมสอง ActiveRecord :: วัตถุที่เกี่ยวข้อง


174

สมมติว่าฉันมีวัตถุสองรายการต่อไปนี้:

first_name_relation = User.where(:first_name => 'Tobias') # ActiveRecord::Relation
last_name_relation  = User.where(:last_name  => 'Fünke') # ActiveRecord::Relation

เป็นไปได้หรือไม่ที่จะรวมความสัมพันธ์ทั้งสองเข้าด้วยกันเพื่อสร้างActiveRecord::Relationวัตถุหนึ่งที่มีทั้งสองเงื่อนไข?

หมายเหตุ: ฉันทราบว่าฉันสามารถรวบรวมสิ่งที่จะทำให้เกิดพฤติกรรมนี้ได้สิ่งที่ฉันสนใจจริงๆคือกรณีที่ฉันมีActiveRecord::Relationวัตถุสองชิ้นแยกกัน


คำตอบ:


202

หากคุณต้องการรวมการใช้AND(ทางแยก) ให้ใช้merge:

first_name_relation.merge(last_name_relation)

หากคุณต้องการรวมการใช้OR(สหภาพ) ให้ใช้ :or

first_name_relation.or(last_name_relation)

เฉพาะใน ActiveRecord 5+; สำหรับ 4.2 ติดตั้งWhere -or backport


ถ้าฉันต้องการให้ผลลัพธ์เป็นActiveRecord::Relationประเภทใด
ซานเดรียใหม่

1
@NewAlexandria ใช่ทุกกรณี Rails โกหกคุณเกี่ยวกับคลาสของวัตถุ
แอนดรูมาร์แชลล์

77
มีการผสานเวอร์ชัน "หรือ" หรือไม่
Arcolye

8
@minohimself อาจเป็นเพราะนั่นเป็นผลที่เกิดขึ้นจริงจากการรวมสองความสัมพันธ์ของคุณ โปรดทราบว่าmergeเป็นทางแยกไม่ใช่สหภาพ
Andrew Marshall

5
สำหรับการอ้างอิงในอนาคต#orมีการเพิ่มวิธีการใน ActiveRecord :: Relation ในเดือนมกราคม 2015 และจะเป็นส่วนหนึ่งของRails 5.0ซึ่งจะจัดส่งในปลายปี 2015 อนุญาตให้ใช้ตัวดำเนินการ OR เพื่อรวมคำสั่ง WHERE หรือ HAVING คุณสามารถเช็คเอาต์ HEAD หากคุณต้องการก่อนเปิดตัวอย่างเป็นทางการ ดูคำขอรวมการดึง # 16052 @Arcolye @AndrewMarshall @ Aldo-xoen-Giambelluca
Claudio Floreani

37

วัตถุที่สัมพันธ์สามารถแปลงเป็นอาร์เรย์ สิ่งนี้ขัดแย้งกับความสามารถในการใช้วิธีการใด ๆ ของ ActiveRecord กับพวกเขาในภายหลัง แต่ฉันไม่ต้องการ ฉันทำอย่างนี้:

name_relation = first_name_relation + last_name_relation

Ruby 1.9, rails 3.2


อัปเดต: น่าเสียดายนี่ไม่ได้รวมตามวันที่
Daniel Morris

68
มันไม่ได้ทำหน้าที่เป็นอาร์เรย์ แต่จะแปลงความสัมพันธ์ของคุณเป็นอาร์เรย์ จากนั้นคุณไม่สามารถใช้วิธีการของ ActiveRecord เช่น .where () อีกต่อไป ...
Augustin Riedinger

1
หากคุณไม่สนใจเกี่ยวกับประเภทผลตอบแทนที่สัมพันธ์กันคุณสามารถรวมผลลัพธ์หลายรายการเข้ากับ first_name_relation | last_name_relation "|" ผู้ประกอบการทำงานในหลายความสัมพันธ์เช่นกัน ค่าผลลัพธ์คืออาร์เรย์
MichaelZ

11
คำถามเดิมคือ: "เป็นไปได้หรือไม่ที่จะรวมความสัมพันธ์ทั้งสองเข้าด้วยกันเพื่อสร้างหนึ่งใน ActiveRecord :: วัตถุความสัมพันธ์ที่มีทั้งสองเงื่อนไข" คำตอบนี้จะส่งกลับอาร์เรย์ ...
courtimas

นี่เป็นวิธีแก้ปัญหาที่ยอดเยี่ยมสำหรับหลาย ๆ กรณี หากคุณต้องการให้มีการแจงนับเรคคอร์ดให้วนซ้ำ (เช่นคุณไม่สนใจว่ามันเป็น Array หรือ ActiveRecord :: Relation) และไม่สนใจค่าใช้จ่ายของการสืบค้นสองครั้งการแก้ปัญหาด้วยไวยากรณ์ที่เรียบง่ายและชัดเจน หวังว่าฉันจะได้ +2!
David Hempy

21

mergeORจริงไม่ทำงานเช่น มันเป็นแค่จุดตัด ( AND)

ฉันต่อสู้กับปัญหานี้เพื่อรวมเข้ากับ ActiveRecord :: วัตถุที่เกี่ยวข้องเป็นหนึ่งและฉันไม่พบวิธีแก้ปัญหาการทำงานสำหรับฉัน

แทนที่จะค้นหาวิธีที่ถูกต้องในการสร้างสหภาพจากทั้งสองชุดนี้ฉันมุ่งเน้นไปที่พีชคณิตของเซต คุณสามารถทำได้ด้วยวิธีต่าง ๆ โดยใช้กฎหมายของ De Morgan

ActiveRecord ให้วิธีการผสาน (AND) และคุณสามารถใช้ไม่ได้เมธอดหรือ none_of (ไม่)

search.where.none_of(search.where.not(id: ids_to_exclude).merge(search.where.not("title ILIKE ?", "%#{query}%")))

คุณอยู่ที่นี่ (A u B) '= A' ^ B '

ปรับปรุง: การแก้ปัญหาข้างต้นเป็นสิ่งที่ดีสำหรับกรณีที่ซับซ้อนมากขึ้น ในกรณีของคุณแบบที่จะเพียงพอ:

User.where('first_name LIKE ? OR last_name LIKE ?', 'Tobias', 'Fünke')

15

ฉันได้รับสามารถที่จะบรรลุนี้แม้ในสถานการณ์แปลก ๆ อีกมากมายโดยใช้ทางรถไฟในตัวArel

User.where(
  User.arel_table[:first_name].eq('Tobias').or(
    User.arel_table[:last_name].eq('Fünke')
  )
)

นี้จะผสานทั้ง ActiveRecord ความสัมพันธ์โดยใช้ Arel ของหรือ


ผสานตามที่แนะนำที่นี่ไม่ทำงานสำหรับฉัน มันทำให้วัตถุความสัมพันธ์ชุดที่สองหลุดออกจากผลลัพธ์


2
นี่คือคำตอบที่ดีที่สุด IMO มันทำงานได้อย่างไม่มีที่ติสำหรับการค้นหาหรือพิมพ์
thekingoftruth

ขอบคุณที่ชี้นำสิ่งนี้ช่วยฉันได้มาก คำตอบอื่น ๆ ใช้งานได้เพียงส่วนย่อยของฟิลด์เล็ก ๆ เท่านั้น แต่สำหรับคำสั่ง IN ที่สูงกว่าหมื่นรหัสในคำสั่ง IN ประสิทธิภาพอาจไม่ดีนัก
onetwopunch

1
@ KeesBriggs - ไม่จริง ฉันใช้มันกับ Rails 4 ทุกรุ่น
6 ฟุต Dan

14

มีอัญมณีชื่อactive_record_union ซึ่งอาจเป็นสิ่งที่คุณกำลังมองหา

มันเป็นตัวอย่างการใช้งานดังต่อไปนี้:

current_user.posts.union(Post.published)
current_user.posts.union(Post.published).where(id: [6, 7])
current_user.posts.union("published_at < ?", Time.now)
user_1.posts.union(user_2.posts).union(Post.published)
user_1.posts.union_all(user_2.posts)

ขอบคุณสำหรับการแบ่งปัน. แม้สี่ปีหลังจากโพสต์ของคุณนี่เป็นทางออกเดียวสำหรับฉันในการสร้างสหภาพจากransackและacts-as-taggable-onในขณะที่ทำให้ActiveRecordอินสแตนซ์ของฉันยังคงเหมือนเดิม
Benjamin Bojko

เมื่อใช้อัญมณีนี้คุณอาจไม่ได้รับ ActiveRecord :: Relation ที่ส่งคืนโดยการเรียก union () หากคุณมีขอบเขตที่กำหนดไว้ในโมเดล ดูสถานะของสหภาพใน ActiveRecord ใน Readme
dechimp

9

นี่คือวิธีที่ฉัน "จัดการ" ถ้าคุณใช้ถอนเพื่อรับตัวระบุสำหรับแต่ละระเบียนเข้าร่วมอาร์เรย์ด้วยกันและจากนั้นก็ทำการสอบถามสำหรับรหัสที่เข้าร่วมเหล่านั้น:

  transaction_ids = @club.type_a_trans.pluck(:id) + @club.type_b_transactions.pluck(:id) + @club.type_c_transactions.pluck(:id)
  @transactions = Transaction.where(id: transaction_ids).limit(100)

6

หากคุณมีความสัมพันธ์กับนักแสดงนำหลายคนและต้องการรวมพวกมันทั้งหมดเข้าด้วยกันคุณสามารถทำได้

array.inject(:merge)

array.inject(:merge)ไม่ได้ทำงานให้กับสายสัมพันธ์ของแอคทีฟการ์ดในราง 5.1.4 แต่array.flatten!ทำ
zhisme

0

กำลังดุร้าย:

first_name_relation = User.where(:first_name => 'Tobias') # ActiveRecord::Relation
last_name_relation  = User.where(:last_name  => 'Fünke') # ActiveRecord::Relation

all_name_relations = User.none
first_name_relation.each do |ar|
  all_name_relations.new(ar)
end
last_name_relation.each do |ar|
  all_name_relations.new(ar)
end

"NoMethodError (วิธีที่ไม่ได้กำหนด` stringify_keys 'สำหรับ # <MyModel: 0x ... ) ใน Rails3 แม้หลังจากเปลี่ยน MyModel.none เป็น MyModel.where (1 = 2) เนื่องจาก Rails3 ไม่มีเมธอด' none '
JosephK
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.