Reputation: 579
I'm using ransack and kaminari gems on a Ruby on Rails controller to query and sort records from the following model structure:
Order
- id
- code
- ship_date
OrderDetail
- id
- order_id
- product
- qty
I need to get all Orders with each associated OrderDetails sorted by this criteria orders.ship_date desc, orders.code desc, order_details.qty desc
.
When I sort just by Orders field, it works, does one query per each table and gives me the expected page records.
Order.includes(:order_details).ransack().result.order({ship_date: :desc, code: :desc}).page(1).per(10)
# Order Load (131.7ms) SELECT "orders".* FROM "orders" ORDER BY "orders"."ship_date" DESC, "orders"."code" DESC LIMIT $1 OFFSET $2 [["LIMIT", 10], ["OFFSET", 0]]
# OrderDetail Load (131.6ms) SELECT "order_details".* FROM "order_details" WHERE "order_details"."order_id" IN ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10) [["order_id", 2955], ["order_id", 2956], ["order_id", 2954], ["order_id", 2953], ["order_id", 2952], ["order_id", 2917], ["order_id", 2918], ["order_id", 2919], ["order_id", 2920], ["order_id", 2921]]
The problem happens when I include order_details.qty
to the sort criteria. It does two queries joining Orders with OrderDetails in each one of them and only gives 4 records, instead of the 10 expected.
Order.includes(:order_details).ransack().result.order({ship_date: :desc, code: :desc, "order_details.qty": :desc}).page(1).per(10)
# SQL (142.3ms) SELECT DISTINCT "orders"."ship_date" AS alias_0, "orders"."code" AS alias_1, "order_details"."qty" AS alias_2, "orders"."id" FROM "orders" LEFT OUTER JOIN "order_details" ON "order_details"."order_id" = "orders"."id" ORDER BY "orders"."ship_date" DESC, "orders"."code" DESC, "order_details"."qty" DESC LIMIT $1 OFFSET $2 [["LIMIT", 10], ["OFFSET", 0]]
# SQL (267.7ms) SELECT orders.id AS t0_r0, "orders"."code" AS t0_r1, "orders"."company_id" AS t0_r2, "orders"."customer_id" AS t0_r3, "orders"."ship_date" AS t0_r4, "orders"."centre_id" AS t0_r5, "orders"."created_at" AS t0_r6, "orders"."updated_at" AS t0_r7, "orders"."remote_id" AS t0_r8, "orders"."comment" AS t0_r9, "order_details"."id" AS t1_r0, "order_details"."order_id" AS t1_r1 "order_details"."product" AS t1_r2, "order_details"."qty" AS t1_r4 FROM "orders" LEFT OUTER JOIN "order_details" ON "order_details"."order_id" = "orders"."id" WHERE "orders"."id" IN ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10) ORDER BY "orders"."ship_date" DESC, "orders"."code" DESC, "order_details"."qty" DESC [["id", 2955], ["id", 2956], ["id", 2954], ["id", 2954], ["id", 2954], ["id", 2953], ["id", 2953], ["id", 2953], ["id", 2953], ["id", 2953]]
I need to avoid joins to improve queries performance, so it should do only 2 queries, applying each sort criteria on it's respective query. Example:
# Order.includes(:order_details)... <-- need this statement
# So I could get records using queries like this:
# SELECT * FROM orders ORDER BY ship_date DESC, code DESC LIMIT 10 OFFSET 0
# SELECT * FROM order_details WHERE order_id IN (...) ORDER BY qty DESC
Thanks to @max's comment, I realize that the problem was with the use of include
. As found in this answer and complemmenting with @max's comment, include
will use preload
only if the the associations are included as they are and have no other references in the statement.
So in my case, to call the associations as they are and remove references, I added a default order on the association.
class Order < ApplicationRecord
has_many :order_details, -> { order(qty: :desc) }
end
Now this is my new line and the records are preloaded and ordered by orders.ship_date desc, orders.code desc, order_details.qty desc
, using only 2 simple queries with no joins.
Order.includes(:order_details).ransack().result.order({ship_date: :desc, code: :desc}).page(1).per(10)
# Order Load (136.5ms) SELECT "orders".* FROM "orders" ORDER BY "orders"."ship_date" DESC, "orders"."code" DESC LIMIT $1 OFFSET $2 [["LIMIT", 10], ["OFFSET", 0]]
# OrderDetail Load (138.4ms) SELECT "order_details".* FROM "order_details" WHERE "order_details"."order_id" IN ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10) ORDER BY "order_details"."pending_qty" DESC, "order_details"."qty" DESC [["order_id", 3089], ["order_id", 3090], ["order_id", 3088], ["order_id", 3087], ["order_id", 3085], ["order_id", 3086], ["order_id", 3083], ["order_id", 3084], ["order_id", 3082], ["order_id", 3049]]
Upvotes: 0
Views: 32