TL; DR: У этого вопроса есть свое собственное примерное приложение в https://github.com/skensell/SO-question-example, которое вы можете использовать для отлаживайте себя. Я поставил щедрость уже один раз на этот вопрос, но я не убежден (или не понимаю) аргументы главного ответчика. Я собираюсь поставить еще одну награду за это, потому что это вызывает у меня много разочарований.
ОРИГИНАЛЬНЫЙ ВОПРОС
У меня есть модель User
, которая имеет такую ассоциацию:
has_many :avatars, -> { order([:sort_order => :asc,:created_at => :asc])}
У меня есть конечная точка, которая выполняет поиск пользователей и устанавливает переменную @users
, которая будет использоваться представлением. Здесь приходит жуткая часть, которую я нашел в отладчике:
@users.first.avatars[0..2].map(&:id)
# => [2546, 2547, 2548]
# This is the correct order.
@users.to_a.first.avatars[0..2].map(&:id)
# => [2548, 2546, 2547]
# Wrong order.
Что здесь происходит?
Единственное отличие - to_a
. Я даже попытался оставить to_a
, но я думаю, что он неявно вызывается jbuilder в любом случае, поскольку я устанавливаю его в json-массив.
Может, способ, которым я ищу User
, имеет к этому какое-то отношение? Я использую несколько включений и соединений.
UPDATE
Здесь я могу показать вам простой пример этого странного поведения с консоли rails. Кажется, что include..references является нарушителем, но я не понимаю, почему и как.
User.order(id: :desc)
.includes(:avatars, :industries)
.where(industries: {id: [5]})
.references(:industries)
.limit(5).to_a.second.avatars.map(&:id)
# => [2751, 2748, 2749]
# Wrong order.
User.order(id: :desc)
.includes(:avatars, :industries)
.where(industries: {id: [5]})
.references(:industries)
.limit(5).second.avatars.map(&:id)
# => [2748, 2749, 2751]
# Correct order.
Я могу проверить, что эти запросы относятся к одному и тому же пользователю, и что тот, который помечен Правильный порядок, действительно прав. w.r.t sort_order
и created_at
(так определяется ассоциация упорядочения).
ОБНОВЛЕНИЕ 2
Прикреплен запрошенный SQL-журнал. Я изменил ненужные поля на "OMITTED", и я заменил 34 нерелевантных пользовательских поля на "...".
>> User.order(id: :desc).includes(:avatars, :industries).where(industries: {id: [5]}).references(:industries).limit(5).to_a.second.avatars.map(&:id)
SQL (18.5ms) SELECT DISTINCT "users"."id", "users"."id" AS alias_0 FROM "users" LEFT OUTER JOIN "avatars" ON "avatars"."user_id" = "users"."id" LEFT OUTER JOIN "user_professions" ON "user_professions"."user_id" = "users"."id" LEFT OUTER JOIN "industries" ON "industries"."id" = "user_professions"."industry_id" WHERE "industries"."id" IN (5) ORDER BY "users"."id" DESC LIMIT 5
SQL (8.3ms) SELECT "users"."id" AS t0_r0, "users"."OMITTED" AS t0_r1, "users"."OMITTED" AS t0_r2, ... AS t0_r36, "avatars"."id" AS t1_r0, "avatars"."user_id" AS t1_r1, "avatars"."avatar" AS t1_r2, "avatars"."created_at" AS t1_r3, "avatars"."updated_at" AS t1_r4, "avatars"."OMITTED" AS t1_r5, "avatars"."OMITTED" AS t1_r6, "avatars"."sort_order" AS t1_r7, "industries"."id" AS t2_r0, "industries"."name" AS t2_r1, "industries"."created_at" AS t2_r2, "industries"."updated_at" AS t2_r3 FROM "users" LEFT OUTER JOIN "avatars" ON "avatars"."user_id" = "users"."id" LEFT OUTER JOIN "user_professions" ON "user_professions"."user_id" = "users"."id" LEFT OUTER JOIN "industries" ON "industries"."id" = "user_professions"."industry_id" WHERE "industries"."id" IN (5) AND "users"."id" IN (1526, 945, 927, 888, 884) ORDER BY "users"."id" DESC
=> [2751, 2748, 2749]
>> User.order(id: :desc).includes(:avatars, :industries).where(industries: {id: [5]}).references(:industries).limit(5).second.avatars.map(&:id)
SQL (0.9ms) SELECT DISTINCT "users"."id", "users"."id" AS alias_0 FROM "users" LEFT OUTER JOIN "avatars" ON "avatars"."user_id" = "users"."id" LEFT OUTER JOIN "user_professions" ON "user_professions"."user_id" = "users"."id" LEFT OUTER JOIN "industries" ON "industries"."id" = "user_professions"."industry_id" WHERE "industries"."id" IN (5) ORDER BY "users"."id" DESC LIMIT 1 OFFSET 1
SQL (0.8ms) SELECT "users"."id" AS t0_r0, "users"."OMITTED" AS t0_r1, "users"."OMITTED" AS t0_r2, ... AS t0_r36, "avatars"."id" AS t1_r0, "avatars"."user_id" AS t1_r1, "avatars"."avatar" AS t1_r2, "avatars"."created_at" AS t1_r3, "avatars"."updated_at" AS t1_r4, "avatars"."OMITTED" AS t1_r5, "avatars"."OMITTED" AS t1_r6, "avatars"."sort_order" AS t1_r7, "industries"."id" AS t2_r0, "industries"."name" AS t2_r1, "industries"."created_at" AS t2_r2, "industries"."updated_at" AS t2_r3 FROM "users" LEFT OUTER JOIN "avatars" ON "avatars"."user_id" = "users"."id" LEFT OUTER JOIN "user_professions" ON "user_professions"."user_id" = "users"."id" LEFT OUTER JOIN "industries" ON "industries"."id" = "user_professions"."industry_id" WHERE "industries"."id" IN (5) AND "users"."id" IN (945) ORDER BY "users"."id" DESC
=> [2748, 2749, 2751]
>>
И здесь я приложу журнал, в котором будут отображаться аватары пользователя (id, sort_order и created_at), чтобы вы могли видеть, что порядок должен быть детерминированным.
>> User.find(945).avatars.pluck(:id,:sort_order,:created_at)
User Load (5.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1 [["id", 945]]
(0.2ms) SELECT "avatars"."id", "avatars"."sort_order", "avatars"."created_at" FROM "avatars" WHERE "avatars"."user_id" = $1 ORDER BY "avatars"."sort_order" ASC, "avatars"."created_at" ASC [["user_id", 945]]
=> [[2748, 0, Fri, 13 Nov 2015 00:32:53 UTC +00:00], [2749, 0, Fri, 13 Nov 2015 00:47:02 UTC +00:00], [2751, 0, Fri, 13 Nov 2015 00:48:05 UTC +00:00]]
Кроме того, я использую Rails 4.1.4 и Ruby 2.1.10.
ОБНОВЛЕНИЕ 3
Я создал здесь пример приложения: https://github.com/skensell/SO-question-example. Что еще более странно в этом примере приложения, что to_a
даже не имеет значения. Я получаю неправильный порядок даже с помощью includes... references
.