Когда вы делаете это Something.find(array_of_ids)
в Rails, порядок результирующего массива не зависит от порядка array_of_ids
.
Есть ли способ сделать поиск и сохранить порядок?
Банкомат Я вручную сортирую записи в соответствии с порядком идентификаторов, но это отчасти неубедительно.
UPD: если можно указать порядок с помощью :order
параметра и какого-то SQL-предложения, то как?
ruby-on-rails
ruby
activerecord
Леонид Шевцов
источник
источник
Ответы:
Ответ только для mysql
В mysql есть функция FIELD ()
Вот как вы можете использовать его в .find ():
Обновление: это будет удалено в исходном коде Rails 6.1 Rails.
источник
FIELDS()
в Postgres?Object.where(id: ids).order("field(id, #{ids.join ','})")
Как ни странно, никто не предлагал ничего подобного:
Насколько он эффективен, если не считать того, что это делает серверная часть SQL.
Изменить: чтобы улучшить мой собственный ответ, вы также можете сделать это так:
#index_by
и#slice
являются довольно удобными дополнениями в ActiveSupport для массивов и хэшей соответственно.источник
Как заявил Майк Вудхаус в своем ответе , это происходит потому, что под капотом Rails использует SQL-запрос с a
WHERE id IN... clause
для получения всех записей в одном запросе. Это быстрее, чем получение каждого идентификатора по отдельности, но, как вы заметили, он не сохраняет порядок извлекаемых записей.Чтобы исправить это, вы можете отсортировать записи на уровне приложения в соответствии с исходным списком идентификаторов, которые вы использовали при поиске записи.
Основываясь на множестве отличных ответов о сортировке массива по элементам другого массива , я рекомендую следующее решение:
Или, если вам нужно что-то немного более быстрое (но, возможно, менее читаемое), вы можете сделать это:
источник
Кажется, это работает для postgresql ( источник ) и возвращает отношение ActiveRecord
может быть расширен и для других столбцов, например, для
name
столбца используйтеposition(name::text in ?)
...источник
["position((',' || somethings.id::text || ',') in ?)", ids.join(',') + ',']
полная версия, которая у меня сработала:scope :for_ids_with_order, ->(ids) { order = sanitize_sql_array( ["position((',' || somethings.id::text || ',') in ?)", ids.join(',') + ','] ) where(:id => ids).order(order) }
спасибо @gingerlime @IrishDubGuyКак я здесь ответил , я только что выпустил гем ( order_as_specified ), который позволяет вам выполнять нативное упорядочивание SQL следующим образом:
Насколько мне удалось проверить, он изначально работает во всех СУБД и возвращает отношение ActiveRecord, которое можно связать в цепочку.
источник
Невозможно в SQL, который работал бы во всех случаях, к сожалению, вам нужно будет либо писать отдельные находки для каждой записи или заказа в ruby, хотя, вероятно, есть способ заставить его работать, используя проприетарные методы:
Первый пример:
ОЧЕНЬ НЕЭФФЕКТИВНЫЙ
Второй пример:
источник
sorted = arr.map { |val| Model.find(val) }
sorted = arr.map{|id| unsorted.detect{|u|u.id==id}}
Ответ @Gunchars отличный, но он не работает из коробки в Rails 2.3, потому что класс Hash не упорядочен. Простым обходным
index_by
путем является расширение класса Enumerable для использования класса OrderedHash:Теперь подход @Gunchars будет работать
Бонус
затем
источник
Предполагая
Model.pluck(:id)
возврат,[1,2,3,4]
и вам нужен порядок[2,4,1,3]
Идея состоит в том, чтобы использовать предложение
ORDER BY CASE WHEN
SQL. Например:В Rails вы можете добиться этого, используя в своей модели общедоступный метод для построения аналогичной структуры:
Затем сделайте:
Это хорошо. Результаты возвращаются как ActiveRecord отношений ( что позволяет использовать такие методы , как
last
,count
,where
,pluck
и т.д.)источник
Существует драгоценный камень find_with_order, который позволяет вам делать это эффективно, используя собственный SQL-запрос.
И он поддерживает как, так
Mysql
иPostgreSQL
.Например:
Если вы хотите отношения:
источник
Под капотом
find
с массивом идентификаторов будет сгенерировано предложениеSELECT
сWHERE id IN...
предложением, что должно быть более эффективным, чем цикл по идентификаторам.Таким образом, запрос удовлетворяется за одно обращение к базе данных, но
SELECT
объекты безORDER BY
предложений не сортируются. ActiveRecord понимает это, поэтому мы расширяем нашеfind
следующим образом:Если порядок идентификаторов в вашем массиве является произвольным и значительным (то есть вы хотите, чтобы порядок возвращаемых строк соответствовал вашему массиву независимо от последовательности идентификаторов, содержащихся в нем), тогда я думаю, что вы будете лучшим сервером, постобработав результаты в код - вы можете создать
:order
предложение, но оно будет чертовски сложным и совсем не раскрывает намерения.источник
:order => id
)Хотя я нигде не упоминаю об этом в CHANGELOG, похоже, что эта функция была изменена с выпуском версии
5.2.0
.Здесь фиксируется обновление документов, помеченных как.
5.2.0
Однако, похоже, он также был перенесен в версию5.0
.источник
Что касается ответа здесь
Object.where(id: ids).order("position(id::text in '#{ids.join(',')}')")
работает для Postgresql.источник
В find (: order => '...') есть предложение порядка, которое делает это при выборке записей. Здесь вы также можете получить помощь.
текст ссылки
источник