Порядок ассоциации Rails 3.2 (ActiveRecord) с быстрой загрузкой и упорядочением

Я столкнулся с этим поведением в рельсах, которое я не могу найти в документации.

Похоже, что если вы усложняете запрос ActiveRecord до такой степени, что он объединяет базовый SQL в «сложный» запрос (см. ниже), он не учитывает порядок, указанный в ассоциациях.

Вот мой пример модели:

class Article < ActiveRecord::Base
  belongs_to :user
  has_many :categories, :order => :name
  attr_accessible :title, :user_id, :user
end

Если я выполняю «простой» запрос, я могу получить связанные категории, упорядоченные по их имени:

> Article.includes(:categories).first.categories.map &:name
  Article Load (0.1ms)  SELECT "articles".* FROM "articles" LIMIT 1
  Category Load (0.2ms)  SELECT "categories".* FROM "categories" WHERE "categories"."article_id" IN (11) ORDER BY name
 => ["category 3059", "category 3212", "category 3240", "category 3651", "category 4371", "category 5243", "category 6176", "category 6235", "category 6468", "category 654", "category 6804", "category 6892", "category 7026", "category 8929", "category 9653"]

Вы можете видеть, что имена категорий находятся в ожидаемом порядке (примечание: я ожидаю, что категории с 3-значными номерами будут в алфавитном порядке из-за того, как база данных упорядочивает строки).

Следующий пример представляет собой более сложный запрос, использующий другой атрибут ассоциаций для упорядочивания:

> Article.includes(:user, :categories).order('users.name').first.categories.map &:name
  Article Load (0.3ms)  SELECT DISTINCT "articles".id FROM "articles" LEFT OUTER JOIN "users" ON "users"."id" = "articles"."user_id" LEFT OUTER JOIN "categories" ON "categories"."article_id" = "articles"."id" ORDER BY users.name LIMIT 1
  SQL (0.2ms)  SELECT "articles"."id" AS t0_r0, "articles"."title" AS t0_r1, "articles"."user_id" AS t0_r2, "articles"."created_at" AS t0_r3, "articles"."updated_at" AS t0_r4, "users"."id" AS t1_r0, "users"."name" AS t1_r1, "users"."created_at" AS t1_r2, "users"."updated_at" AS t1_r3, "categories"."id" AS t2_r0, "categories"."name" AS t2_r1, "categories"."article_id" AS t2_r2, "categories"."created_at" AS t2_r3, "categories"."updated_at" AS t2_r4 FROM "articles" LEFT OUTER JOIN "users" ON "users"."id" = "articles"."user_id" LEFT OUTER JOIN "categories" ON "categories"."article_id" = "articles"."id" WHERE "articles"."id" IN (16) ORDER BY users.name
 => ["category 5023", "category 728", "category 3306", "category 8170", "category 5957", "category 7190", "category 4427", "category 3435", "category 1274", "category 7251", "category 7368", "category 682", "category 2918"]

Как видите, когда AR объединяет запрос в один из своих «сложных» SQL-запросов, порядок теряется для ассоциации categories.

Ожидается ли такое поведение? Это задокументировано где-то, чего я не смог найти?

Спасибо!


person Iamvery    schedule 16.08.2012    source источник


Ответы (1)


Дело в том, что вы выполняете соединение и упорядочиваете его по «users.name».

Когда вы выполняете объединение, вы объединяете 2 таблицы, и любой порядок будет влиять на всю объединенную таблицу.

Поскольку вы все равно делаете 2 запроса, я бы написал так:

Article.includes(:user, :categories).order('users.name').first.categories.order(:name).map &:name

Или, что еще лучше, используйте метод выщипывания. Он возвращает только одно значение столбца:

Article.includes(:user, :categories).order('users.name').first.categories.order(:name).pluck(:name)

Таким образом, вам не нужно загружать все атрибуты категорий, чтобы перечислить их имена. знак равно

PS:

Если вы хотите сделать двойной заказ, вы можете передать два параметра для заказа:

Article.includes(:user, :categories).order('users.name', 'categories.name')

Я не писал весь запрос, потому что не понял, что именно вы пытаетесь восстановить. Если вы объясните больше, я могу попробовать. До сих пор я понимал, что вы пытаетесь получить все категории, отсортированные по имени, статьи, принадлежащей первому пользователю, отсортированные по имени. Но для меня этот запрос не имеет особого смысла без контекста.

person hsgubert    schedule 22.08.2012
comment
Спасибо за ответ! Дело в том, что второй пример фактически выполняет только один запрос. Я понимаю, ПОЧЕМУ он не может упорядочить как users.name, так и categories.name для одного запроса, но я ожидаю, что он сделает что-то вроде categories.order_by &:name в случае, если он пришел к такому сложному соединению для загруженных с нетерпением записей. Как вы думаете? - person Iamvery; 23.08.2012
comment
Обновил ответ! Пожалуйста, объясните лучше свой контекст, если вы хотите, чтобы я попытался написать какой-то запрос. Кстати, я думаю, что второй пример выполняет 2 запроса, один из 0,2 мс и один из 0,3 мс, я прав? - person hsgubert; 23.08.2012
comment
Туш. Я полностью упустил из виду верхний запрос, в основном потому, что не похоже, что это будет/должно быть два запроса. В любом случае, дайте мне время все обдумать и переформулировать вопрос. Спасибо за помощь :) - person Iamvery; 23.08.2012