개체 배열을 ActiveRecord :: Relation으로 변환


104

나는 객체의 배열을 가지고 있습니다 Indicator. def self.subjects이 배열 에서 Indicator 클래스 메서드 ( 다양성, 범위 등) 를 실행하고 싶습니다 . 개체 그룹에서 클래스 메서드를 실행하는 유일한 방법은 ActiveRecord :: Relation이되도록하는 것입니다. 그래서 나는 추가에 의지 결국 to_indicators에 방법을 Array.

def to_indicators
  # TODO: Make this less terrible.
  Indicator.where id: self.pluck(:id)
end

때때로 나는 클래스 메소드 내에서 결과를 필터링하기 위해 이러한 범위 중 상당수를 연결합니다. 따라서 ActiveRecord :: Relation에서 메서드를 호출하더라도 해당 개체에 액세스하는 방법을 모릅니다. 나는 그것을 통해서만 그것의 내용을 얻을 수 있습니다 all. 그러나 all배열입니다. 그래서 그 배열을 ActiveRecord :: Relation으로 변환해야합니다. 예를 들어, 다음 방법 중 하나의 일부입니다.

all.to_indicators.applicable_for_bank(id).each do |indicator|
  total += indicator.residual_risk_for(id)
  indicator_count += 1 if indicator.completed_by?(id)
end

나는 이것이 두 가지 질문으로 요약 된 것 같다.

  1. 개체 배열을 ActiveRecord :: Relation으로 어떻게 변환 할 수 있습니까? where매번 하지 않고 가급적이면 .
  2. def self.subjectsActiveRecord :: Relation에서 형식 메서드를 실행할 때 ActiveRecord :: Relation 개체 자체에 액세스하려면 어떻게해야합니까?

감사. 명확히해야 할 점이 있으면 알려주세요.


3
해당 배열을 다시 관계로 변환하려는 유일한 이유가을 통해 가져 왔기 때문이라면 Andrew Marshall의 답변이 나타내는 .all것처럼 사용하십시오 .scoped(레일 4에서는에서 작동합니다 .all). 배열을 관계로
바꿔야

답변:


46

개체 배열을 ActiveRecord :: Relation으로 어떻게 변환 할 수 있습니까? 가급적이면 매번 장소를하지 않고.

Relation은 SQL 쿼리의 작성 기일 뿐이며 해당 메서드는 실제 데이터에서 작동하지 않으므로 Array를 ActiveRecord :: Relation으로 변환 할 수 없습니다.

그러나 원하는 것이 관계라면 :

  • ActiveRecord 3.x의 경우를 호출하지 않고 all대신을 호출 하면 배열에서 제공 scoped하는 것과 동일한 레코드를 나타내는 Relation이 all반환됩니다.

  • ActiveRecord 4.x의 경우 간단히을 호출 all하면 관계가 반환됩니다.

def self.subjectsActiveRecord :: Relation에서 형식 메서드를 실행할 때 ActiveRecord :: Relation 개체 자체에 액세스하려면 어떻게해야합니까?

메서드가 Relation 개체에서 호출 될 때는 self관계입니다 (정의 된 모델 클래스와 반대).


1
솔루션에 대해서는 아래 @Marco Prins를 참조하십시오.
저스틴

레일 .push되지 않는 방법에 대한 어떤 5
Jaswinder

@GstjiSaini 당신이 언급하고있는 정확한 방법을 잘 모르겠습니다. 문서 나 소스 링크를 제공해주세요. 그러나 더 이상 사용되지 않는 경우 곧 사라질 가능성이 있으므로 실행 가능한 솔루션이 아닙니다.
Andrew Marshall

당신이 할 수있는 이유입니다 class User; def self.somewhere; where(location: "somewhere"); end; end다음User.limit(5).somewhere
도리안

이것이 진정으로 OP를 계몽하는 유일한 대답입니다. 이 질문은 ActiveRecord의 작동 방식에 대한 잘못된 지식을 보여줍니다. @Justin은 객체 배열을 계속 전달하여 객체를 매핑하고 또 다른 불필요한 쿼리를 작성하는 것이 왜 나쁜지에 대한 이해 부족을 강화하고 있습니다.
james2m

162

다음 arr과 같이 객체 배열을 ActiveRecord :: Relation으로 변환 할 수 있습니다 (객체가 어떤 클래스인지 알고 있다고 가정 할 때).

MyModel.where(id: arr.map(&:id))

where그래도 사용해야 하지만 사용을 꺼려해서는 안되는 유용한 도구입니다. 이제 배열을 관계로 변환하는 한 줄짜리가 있습니다.

map(&:id)개체 배열을 ID 만 포함하는 배열로 바꿉니다. 그리고 where 절에 배열을 전달하면 IN다음과 같은 SQL 문이 생성됩니다 .

SELECT .... WHERE `my_models`.id IN (2, 3, 4, 6, ....

배열의 순서가 손실된다는 점을 염두에 두십시오. 그러나 귀하의 목표는 이러한 개체 컬렉션 에 대해 클래스 메서드를 실행하는 것이므로 문제가되지 않는다고 가정합니다.


3
잘 했어, 정확히 내가 필요했던 것. 모델의 모든 속성에 사용할 수 있습니다. 나를 위해 완벽하게 작동합니다.
nfriend21 2014-07-30

7
할 수 있는데 왜 리터럴 SQL을 직접 빌드합니까 where(id: arr.map(&:id))? 엄밀히 말하면, 이것은 배열을 관계로 변환하지 않고 대신 배열의 이미 메모리에있는 인스턴스와 다른 속성 값을 가질 수있는 ID를 가진 객체의 새 인스턴스를 가져옵니다 (관계가 실현되면).
Andrew Marshall

7
그러나 이것은 순서를 잃습니다.
Velizar Hristov

1
@VelizarHristov 이제는 관계이기 때문에 원하는 방식이 아닌 열로만 정렬 할 수 있습니다. 관계는 큰 데이터 세트를 처리하는 데 더 빠르며 몇 가지 절충안이 있습니다.
Marco Prins 2015 년

8
매우 비효율적입니다! 이미 메모리에있는 개체 컬렉션을 데이터베이스 쿼리를 통해 액세스 할 개체로 전환했습니다. 배열을 반복하려는 클래스 메서드를 리팩토링하는 방법을 살펴 보겠습니다.
james2m

5

글쎄, 제 경우 에는 개체 배열을 ActiveRecord :: Relation 으로 변환 하고 특정 열 (예 : ID) 로 정렬 해야합니다. MySQL을 사용하고 있으므로 필드 함수 가 도움이 될 수 있습니다.

MyModel.where('id in (?)',ids).order("field(id,#{ids.join(",")})") 

SQL은 다음과 같습니다.

SELECT ... FROM ... WHERE (id in (11,5,6,7,8,9,10))  
ORDER BY field(id,11,5,6,7,8,9,10)

MySQL 필드 기능


PostgreSQL에서 작동하는 버전을 보려면 다음 스레드보다 더 이상 보지
armchairdj

0

ActiveRecord::Relation 데이터베이스에서 데이터를 검색하는 데이터베이스 쿼리를 바인딩합니다.

이해하기 위해 동일한 클래스의 객체가있는 배열이 있다고 가정합니다. 그러면 어떤 쿼리를 사용하여 바인딩할까요?

내가 달릴 때

users = User.where(id: [1,3,4,5])
  User Load (0.6ms)  SELECT `users`.* FROM `users` WHERE `users`.`id` IN (1, 3, 4, 5)  ORDER BY created_at desc

위의 여기에서 객체를 users반환 Relation하지만 그 뒤에 데이터베이스 쿼리를 바인딩하고 볼 수 있습니다.

users.to_sql
 => "SELECT `users`.* FROM `users` WHERE `users`.`id` IN (1, 3, 4, 5)  ORDER BY created_at desc"

따라서 ActiveRecord::RelationSQL 쿼리와 무관 한 객체 배열 에서 반환하는 것은 불가능합니다 .


0

우선, 이것은 은색 총알이 아닙니다. 내 경험에 비추어 볼 때, 관계로 전환하는 것이 때때로 대안보다 쉽다는 것을 알았습니다. 나는이 접근법을 매우 드물게 사용하려고하며 대안이 더 복잡한 경우에만 사용하려고합니다.

여기에 내 솔루션이라고 말하는 것은 Array수업을 확장했습니다.

# lib/core_ext/array.rb

class Array

  def to_activerecord_relation
    return ApplicationRecord.none if self.empty?

    clazzes = self.collect(&:class).uniq
    raise 'Array cannot be converted to ActiveRecord::Relation since it does not have same elements' if clazzes.size > 1

    clazz = clazzes.first
    raise 'Element class is not ApplicationRecord and as such cannot be converted' unless clazz.ancestors.include? ApplicationRecord

    clazz.where(id: self.collect(&:id))
  end
end

사용 예는 array.to_activerecord_relation.update_all(status: 'finished'). 이제 어디서 사용합니까?

ActiveRecord::Relation예를 들어 완료되지 않은 요소 를 제거하는 것과 같이 필터링해야하는 경우가 있습니다 . 이러한 경우 가장 좋은 방법은 범위를 사용 elements.not_finished하는 것이며 ActiveRecord::Relation.

그러나 때로는 그 상태가 더 복잡합니다. 완성되지 않고 지난 4 주 동안 생산되어 검사를 마친 모든 요소를 ​​꺼냅니다. 새 범위를 만들지 않으려면 배열로 필터링 한 다음 다시 변환 할 수 있습니다. DB에 id대한 쿼리는 여전히 쿼리에 의해 검색되므로 빠르게 수행한다는 점을 명심하십시오 .

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.