Cuando lo hace Something.find(array_of_ids)
en Rails, el orden de la matriz resultante no depende del orden de array_of_ids
.
¿Hay alguna forma de buscar y conservar el orden?
Cajero automático Ordeno manualmente los registros según el orden de las identificaciones, pero eso es un poco escaso.
UPD: si es posible especificar el orden usando el :order
parámetro y algún tipo de cláusula SQL, ¿cómo?
ruby-on-rails
ruby
activerecord
Leonid Shevtsov
fuente
fuente
Respuestas:
La respuesta es solo para mysql
Hay una función en mysql llamada FIELD ()
Así es como puede usarlo en .find ():
Actualización: esto se eliminará en el código fuente de Rails 6.1 Rails
fuente
FIELDS()
en Postgres?Object.where(id: ids).order("field(id, #{ids.join ','})")
Curiosamente, nadie ha sugerido algo como esto:
Tan eficiente como sea, además de permitir que el backend SQL lo haga.
Editar: para mejorar mi propia respuesta, también puede hacerlo así:
#index_by
y#slice
son adiciones bastante útiles en ActiveSupport para matrices y hashes respectivamente.fuente
Como dijo Mike Woodhouse en su respuesta , esto ocurre porque, bajo el capó, Rails está usando una consulta SQL con
WHERE id IN... clause
para recuperar todos los registros en una consulta. Esto es más rápido que recuperar cada ID individualmente, pero como notó, no conserva el orden de los registros que está recuperando.Para solucionar este problema, puede ordenar los registros en el nivel de la aplicación de acuerdo con la lista original de ID que utilizó al buscar el registro.
Basado en las muchas respuestas excelentes para ordenar una matriz de acuerdo con los elementos de otra matriz , recomiendo la siguiente solución:
O si necesita algo un poco más rápido (pero posiblemente algo menos legible) puede hacer esto:
fuente
Esto parece funcionar para postgresql ( fuente ) y devuelve una relación ActiveRecord
también se puede ampliar para otras columnas, por ejemplo, para la
name
columna, useposition(name::text in ?)
...fuente
["position((',' || somethings.id::text || ',') in ?)", ids.join(',') + ',']
versión completa que funcionó para mí:scope :for_ids_with_order, ->(ids) { order = sanitize_sql_array( ["position((',' || somethings.id::text || ',') in ?)", ids.join(',') + ','] ) where(:id => ids).order(order) }
gracias @gingerlime @IrishDubGuyComo respondí aquí , acabo de publicar una gema ( order_as_specified ) que le permite hacer pedidos de SQL nativo como este:
Por lo que he podido probar, funciona de forma nativa en todos los RDBMS y devuelve una relación ActiveRecord que se puede encadenar.
fuente
Desafortunadamente, no es posible en SQL que funcionaría en todos los casos, necesitaría escribir hallazgos únicos para cada registro u orden en ruby, aunque probablemente haya una manera de hacerlo funcionar utilizando técnicas propietarias:
Primer ejemplo:
MUY INEFICIENTE
Segundo ejemplo:
fuente
sorted = arr.map { |val| Model.find(val) }
sorted = arr.map{|id| unsorted.detect{|u|u.id==id}}
La respuesta de @Gunchars es excelente, pero no funciona de manera inmediata en Rails 2.3 porque la clase Hash no está ordenada. Una solución alternativa simple es extender la clase Enumerable '
index_by
para usar la clase OrderedHash:Ahora el enfoque de @Gunchars funcionará
Prima
Luego
fuente
Suponiendo
Model.pluck(:id)
devoluciones[1,2,3,4]
y quieres el orden de[2,4,1,3]
El concepto es utilizar la
ORDER BY CASE WHEN
cláusula SQL. Por ejemplo:En Rails, puede lograr esto al tener un método público en su modelo para construir una estructura similar:
Entonces hazlo:
Lo bueno de esto. Los resultados se devuelven como Relaciones ActiveRecord (lo que permite utilizar métodos como
last
,count
,where
,pluck
, etc)fuente
Hay una gema find_with_order que le permite hacerlo de manera eficiente utilizando una consulta SQL nativa.
Y es compatible con
Mysql
yPostgreSQL
.Por ejemplo:
Si quieres relacion:
fuente
Bajo el capó,
find
con una matriz de identificadores se generaráSELECT
unaWHERE id IN...
cláusula con a, que debería ser más eficiente que recorrer los identificadores.Entonces, la solicitud se satisface en un viaje a la base de datos, pero
SELECT
losORDER BY
correos electrónicos sin cláusulas no están ordenados. ActiveRecord entiende esto, por lo que expandimos nuestro de lafind
siguiente manera:Si el orden de los identificadores en su matriz es arbitrario y significativo (es decir, si desea que el orden de las filas se devuelva para que coincida con su matriz, independientemente de la secuencia de identificadores que contiene), entonces creo que sería el mejor servidor procesando posteriormente los resultados en código: podría crear una
:order
cláusula, pero sería diabólicamente complicado y no revelaría ninguna intención.fuente
:order => id
)Aunque no veo que se mencione en ninguna parte de un CHANGELOG, parece que esta funcionalidad se cambió con el lanzamiento de la versión
5.2.0
.Aquí compruebe la actualización de los documentos etiquetados con
5.2.0
Sin embargo, parece que también se ha actualizado a la versión5.0
.fuente
Con referencia a la respuesta aquí
Object.where(id: ids).order("position(id::text in '#{ids.join(',')}')")
funciona para Postgresql.fuente
Hay una cláusula de orden en find (: order => '...') que hace esto al buscar registros. También puede obtener ayuda desde aquí.
Texto del enlace
fuente