두 개의 ActiveRecord :: Relation 객체를 결합


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객체 가있는 경우 입니다.


도움이 될 수 있습니다 : 액티브 OR 쿼리 해시 표기법
potashin

답변:


202

AND(교차)를 사용 하여 결합 하려면 merge다음을 사용하십시오 .

first_name_relation.merge(last_name_relation)

OR(union)을 사용 하여 결합 하려면 †를 사용하십시오 .or

first_name_relation.or(last_name_relation)

ActiveRecord 5+에서만; 4.2의 경우 위치 또는 백 포트를 설치하십시오 .


결과를 ActiveRecord::Relation유형으로 만들고 싶다면 어떻습니까?
새로운 Alexandria

1
@NewAlexandria 네, 모든 경우에 Rails는 객체의 클래스에 대해 당신에게 거짓말하고 있습니다.
Andrew Marshall

77
"OR"버전의 병합이 있습니까?
Arcolye

8
@minohimself 아마도 두 관계를 병합 한 결과 일 것입니다. 참고 merge교차로가 아닌 노동 조합이다.
Andrew Marshall

5
향후 참조를 위해 #or2015 년 1 월에 ActiveRecord :: Relation에 메소드가 추가되었으며 2015 년 후반에 출시 될 Rails 5.0에 포함될 예정입니다. OR 연산자를 사용하여 WHERE 또는 HAVING 절을 결합 할 수 있습니다. 공식 릴리스 이전에 HEAD가 필요한 경우 체크 아웃 할 수 있습니다. 참고 병합 끌어 오기 요청 # 16052 @Arcolye @AndrewMarshall @ 알도 --Giambelluca xoen
클라우디오 Floreani

37

관계 객체는 배열로 변환 될 수 있습니다. 이것은 나중에 ActiveRecord 메소드를 사용할 수 없다는 것을 부정하지만, 그럴 필요는 없었습니다. 나는 이걸했다:

name_relation = first_name_relation + last_name_relation

루비 1.9, 레일 3.2


업데이트 :이 날짜별로 병합되지 않습니다 동정
Daniel Morris

68
배열로 작동하지 않으며 관계를 배열로 변환합니다. 그러면 더 이상 .where ()처럼 ActiveRecord 메소드를 사용할 수 없습니다.
Augustin Riedinger

1
리턴 유형이 관계가 아닌 경우 여러 결과를 first_name_relation | last_name_relation. "|" 연산자는 여러 관계에서도 작동합니다. 결과 값은 배열입니다.
MichaelZ

11
원래 질문은 "두 조건을 모두 포함하는 하나의 ActiveRecord :: Relation 객체를 생성하기 위해 두 관계를 결합 할 수 있습니까?"였습니다. 이 답변은 배열을 반환합니다 ...
courtsimas

이것은 많은 경우에 탁월한 솔루션입니다. 반복 할 레코드 열거 만 필요하고 (예 : Array 또는 ActiveRecord :: Relation인지 상관하지 않음) 두 쿼리의 오버 헤드를 신경 쓰지 않으면 명확하고 간단한 구문으로 문제를 해결합니다. 내가 +2 할 수 있으면 좋겠다!
David Hempy

21

merge실제로는 작동하지 않습니다 OR. 단순히 교차로입니다 ( AND)

이 문제를 해결하기 위해 ActiveRecord :: Relation 객체를 하나로 결합했지만 아무런 해결책이 없었습니다.

이 두 세트에서 합집합을 만드는 올바른 방법을 찾는 대신 대수 세트에 중점을 두었습니다. De Morgan의 법칙을 사용하여 다른 방법으로 할 수 있습니다

ActiveRecord는 병합 방법 (AND)을 제공하며, not method 또는 none_of (NOT)를 사용할 수 있습니다.

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

Rails의 내장 Arel 을 사용하여 많은 이상한 상황 에서도이 작업을 수행 할 수있었습니다 .

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

Arel 또는 을 사용하여 두 ActiveRecord 관계를 병합합니다 .


여기에 제안 된 것처럼 병합은 나를 위해 작동하지 않았습니다. 결과에서 두 번째 관계 개체 집합을 삭제했습니다.


2
이것이 가장 좋은 답입니다. IMO. OR 형식 쿼리에는 완벽하게 작동합니다.
thekingoftruth

이것을 지적 해 주셔서 감사합니다. 다른 답변은 필드의 작은 하위 집합에서만 작동하지만 IN 문에서 10,000 개가 넘는 ID의 경우 성능이 매우 떨어질 수 있습니다.
onetwopunch

1
@KeesBriggs — 사실이 아닙니다. 나는 이것을 모든 Rails 4 버전에서 사용했다.
6ft 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

이 gem을 사용할 때 모델에 범위가 정의되어 있으면 union () 호출에서 반환 된 ActiveRecord :: Relation을 얻지 못할 수 있습니다. 추가 정보에서 ActiveRecord의 연합 상태를 참조하십시오.
데킴

9

pluck을 사용하여 각 레코드의 식별자를 가져 와서 배열을 결합한 다음 마지막으로 결합 된 ID에 대한 쿼리를 수행하는 경우 다음과 같이 처리합니다.

  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

Rails3에는 'none'메소드가 없으므로 MyModel.none을 MyModel.none (1 = 2)으로 변경 한 후에도 Rails3의 NoMethodError (# <MyModel : 0x ...에 대해 정의되지 않은`stringify_keys '메소드)
JosephK
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.