Something.find(array_of_ids)
Rails에서 수행 할 때 결과 배열의 순서는array_of_ids
.
주문을 찾고 보존 할 수있는 방법이 있습니까?
ATM ID 순서에 따라 수동으로 레코드를 정렬하지만 다소 절름발이입니다.
UPD : :order
param과 어떤 종류의 SQL 절 을 사용하여 순서를 지정할 수 있다면 어떻게할까요?
답변
대답은 mysql에만 해당됩니다.
mysql에는 FIELD () 라는 함수가 있습니다.
.find ()에서 사용하는 방법은 다음과 같습니다.
>> ids = [100, 1, 6]
=> [100, 1, 6]
>> WordDocument.find(ids).collect(&:id)
=> [1, 6, 100]
>> WordDocument.find(ids, :order => "field(id, #{ids.join(',')})")
=> [100, 1, 6]
For new Version
>> WordDocument.where(id: ids).order("field(id, #{ids.join ','})")
업데이트 : 이것은 Rails 6.1 Rails 소스 코드 에서 제거됩니다.
답변
이상하게도 아무도 다음과 같은 제안을하지 않았습니다.
index = Something.find(array_of_ids).group_by(&:id)
array_of_ids.map { |i| index[i].first }
SQL 백엔드를 사용하는 것 외에도 효율적입니다.
편집 : 내 대답을 개선하려면 다음과 같이 할 수도 있습니다.
Something.find(array_of_ids).index_by(&:id).slice(*array_of_ids).values
#index_by
그리고 #slice
꽤 편리 추가됩니다 ActiveSupport 각각 배열과 해시에 대한이.
답변
로 마이크 우드 하우스가 에 명시된 그의 대답 이는 후드 아래, 레일이 가진 SQL 쿼리를 사용하고, becase 발생 WHERE id IN... clause
한 쿼리에서 모든 레코드를 검색 할 수 있습니다. 이 방법은 각 ID를 개별적으로 검색하는 것보다 빠르지 만 검색중인 레코드의 순서를 유지하지 않습니다.
이 문제를 해결하기 위해 레코드를 조회 할 때 사용한 원래 ID 목록에 따라 애플리케이션 수준에서 레코드를 정렬 할 수 있습니다.
Sort an array of the elements of another array 에 대한 많은 훌륭한 답변을 기반으로 다음 솔루션을 권장합니다.
Something.find(array_of_ids).sort_by{|thing| array_of_ids.index thing.id}
또는 조금 더 빠른 것이 필요하면 (하지만 다소 읽기 어려운) 다음과 같이 할 수 있습니다.
Something.find(array_of_ids).index_by(&:id).values_at(*array_of_ids)
답변
이것은 postgresql ( source ) 에서 작동하는 것으로 보이며 ActiveRecord 관계를 반환합니다.
class Something < ActiveRecrd::Base
scope :for_ids_with_order, ->(ids) {
order = sanitize_sql_array(
["position((',' || id::text || ',') in ?)", ids.join(',') + ',']
)
where(:id => ids).order(order)
}
end
# usage:
Something.for_ids_with_order([1, 3, 2])
다른 열에 대해서도 확장 할 수 있습니다 (예 : name
열의 경우 position(name::text in ?)
…
답변
여기 에서 대답했듯이 다음과 같이 네이티브 SQL 주문을 수행 할 수 있는 gem ( order_as_specified )을 출시했습니다 .
Something.find(array_of_ids).order_as_specified(id: array_of_ids)
내가 테스트 할 수있는 한 모든 RDBMS에서 기본적으로 작동하며 연결할 수있는 ActiveRecord 관계를 반환합니다.
답변
불행히도 모든 경우에 작동하는 SQL에서는 가능하지 않습니다. 독점 기술을 사용하여 작동하도록 만드는 방법이 있지만 각 레코드 또는 주문에 대해 단일 찾기를 작성해야합니다.
첫 번째 예 :
sorted = arr.inject([]){|res, val| res << Model.find(val)}
매우 비효율적
두 번째 예 :
unsorted = Model.find(arr)
sorted = arr.inject([]){|res, val| res << unsorted.detect {|u| u.id == val}}
답변
@Gunchars 대답은 훌륭하지만 Hash 클래스가 주문되지 않았기 때문에 Rails 2.3에서 상자 밖으로 작동하지 않습니다. 간단한 해결 방법은 index_by
OrderedHash 클래스를 사용하도록 Enumerable 클래스를 확장하는 것입니다 .
module Enumerable
def index_by_with_ordered_hash
inject(ActiveSupport::OrderedHash.new) do |accum, elem|
accum[yield(elem)] = elem
accum
end
end
alias_method_chain :index_by, :ordered_hash
end
이제 @Gunchars의 접근 방식이 작동합니다.
Something.find(array_of_ids).index_by(&:id).slice(*array_of_ids).values
보너스
module ActiveRecord
class Base
def self.find_with_relevance(array_of_ids)
array_of_ids = Array(array_of_ids) unless array_of_ids.is_a?(Array)
self.find(array_of_ids).index_by(&:id).slice(*array_of_ids).values
end
end
end
그때
Something.find_with_relevance(array_of_ids)