I'm using activeadmin for showing branch model records, the branch model has many branchphones, and I want to show the first branchphone created for this branch as an extra custom attribute on activeadmin branch model screen, so I wrote it like that
show do
attributes_table do
row :id
row :manager_name
row :manager_email
row :phone_number do |branch|
branch&.branch_phones&.order(created_at: :asc)&.first&.phone_number
end
end
end
index do
column :id
column :manager_name
column :manager_email
column :phone_number do |branch|
branch&.branch_phones&.order(created_at: :asc)&.first&.phone_number
end
actions
end
the problem with that code is that it causes me n+1 queries, every time the code get a branch, it goes and create an extra query inside it to get the branch's branch phones, which results on something like that when calling the branch model screen on activeadmin
Processing by Admin::BranchesController#index as HTML
Parameters: {"subdomain"=>""}
AdminUser Load (0.8ms) SELECT "admin_users".* FROM "admin_users" WHERE "admin_users"."id" = $1 ORDER BY "admin_users"."id" ASC LIMIT $2 [["id", 1], ["LIMIT", 1]]
Rendering /usr/local/bundle/gems/activeadmin-2.7.0views/active_admin/resource/index.html.arb
(0.6ms) SELECT COUNT(*) FROM (SELECT 1 AS one FROM "branches" LIMIT $1 OFFSET $2) subquery_for_count [["LIMIT", 30], ["OFFSET", 0]]
CACHE (0.1ms) SELECT COUNT(*) FROM (SELECT 1 AS one FROM "branches" LIMIT $1 OFFSET $2) subquery_for_count [["LIMIT", 30], ["OFFSET", 0]]
(0.4ms) SELECT COUNT(*) FROM "branches"
CACHE (0.0ms) SELECT COUNT(*) FROM (SELECT 1 AS one FROM "branches" LIMIT $1 OFFSET $2) subquery_for_count [["LIMIT", 30], ["OFFSET", 0]]
Branch Load (0.5ms) SELECT "branches".* FROM "branches" ORDER BY "branches"."id" desc LIMIT $1 OFFSET $2 [["LIMIT", 30], ["OFFSET", 0]]
Store Load (0.6ms) SELECT "stores".* FROM "stores" WHERE "stores"."id" IN ($1, $2, $3) [["id", 21], ["id", 1], ["id", 2]]
BranchPhone Load (0.4ms) SELECT "branch_phones".* FROM "branch_phones" WHERE "branch_phones"."branch_id" IN ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11) [["branch_id", 25], ["branch_id", 24], ["branch_id", 22], ["branch_id", 20], ["branch_id", 19], ["branch_id", 14], ["branch_id", 11], ["branch_id", 4], ["branch_id", 3], ["branch_id", 2], ["branch_id", 1]]
BranchPhone Load (0.7ms) SELECT "branch_phones".* FROM "branch_phones" WHERE "branch_phones"."branch_id" = $1 ORDER BY "branch_phones"."created_at" ASC LIMIT $2 [["branch_id", 25], ["LIMIT", 1]]
BranchPhone Load (0.5ms) SELECT "branch_phones".* FROM "branch_phones" WHERE "branch_phones"."branch_id" = $1 ORDER BY "branch_phones"."created_at" ASC LIMIT $2 [["branch_id", 24], ["LIMIT", 1]]
BranchPhone Load (0.3ms) SELECT "branch_phones".* FROM "branch_phones" WHERE "branch_phones"."branch_id" = $1 ORDER BY "branch_phones"."created_at" ASC LIMIT $2 [["branch_id", 22], ["LIMIT", 1]]
BranchPhone Load (0.6ms) SELECT "branch_phones".* FROM "branch_phones" WHERE "branch_phones"."branch_id" = $1 ORDER BY "branch_phones"."created_at" ASC LIMIT $2 [["branch_id", 20], ["LIMIT", 1]]
BranchPhone Load (0.3ms) SELECT "branch_phones".* FROM "branch_phones" WHERE "branch_phones"."branch_id" = $1 ORDER BY "branch_phones"."created_at" ASC LIMIT $2 [["branch_id", 19], ["LIMIT", 1]]
BranchPhone Load (0.3ms) SELECT "branch_phones".* FROM "branch_phones" WHERE "branch_phones"."branch_id" = $1 ORDER BY "branch_phones"."created_at" ASC LIMIT $2 [["branch_id", 14], ["LIMIT", 1]]
BranchPhone Load (0.2ms) SELECT "branch_phones".* FROM "branch_phones" WHERE "branch_phones"."branch_id" = $1 ORDER BY "branch_phones"."created_at" ASC LIMIT $2 [["branch_id", 11], ["LIMIT", 1]]
BranchPhone Load (0.2ms) SELECT "branch_phones".* FROM "branch_phones" WHERE "branch_phones"."branch_id" = $1 ORDER BY "branch_phones"."created_at" ASC LIMIT $2 [["branch_id", 4], ["LIMIT", 1]]
BranchPhone Load (0.3ms) SELECT "branch_phones".* FROM "branch_phones" WHERE "branch_phones"."branch_id" = $1 ORDER BY "branch_phones"."created_at" ASC LIMIT $2 [["branch_id", 3], ["LIMIT", 1]]
BranchPhone Load (0.2ms) SELECT "branch_phones".* FROM "branch_phones" WHERE "branch_phones"."branch_id" = $1 ORDER BY "branch_phones"."created_at" ASC LIMIT $2 [["branch_id", 2], ["LIMIT", 1]]
BranchPhone Load (0.2ms) SELECT "branch_phones".* FROM "branch_phones" WHERE "branch_phones"."branch_id" = $1 ORDER BY "branch_phones"."created_at" ASC LIMIT $2 [["branch_id", 1], ["LIMIT", 1]]
Rendered /usr/local/bundle/gems/activeadmin-2.7.0views/active_admin/resource/index.html.arb (219.4ms)
Completed 200 OK in 258ms (Views: 208.7ms | ActiveRecord: 23.2ms)
so, any solutions to eliminate the n+1 query for the branch phone ?
knowing that I tried to eliminate it using controller actions like this
but still the branch phones N+1 query not solved
You are getting (N+1) query because you are ordering branch_phones
. By default Rails returns records in assenting order. You don't have to order again.
I believe you have some association like bellow:
# app/models/branch.rb
class Branch < ApplicationRecord
has_many :branch_phones
end
# app/models/branch_phone.rb
class BranchPhone < ApplicationRecord
belongs_to :branch
end
Please add this lines in your app/admin/brach.rb
file.
# app/admin/brach.rb
controller do
def scoped_collection
super.includes(:branch_phones)
end
end
index do
column :id
column :manager_name
column :manager_email
column :phone_number do |branch|
branch.branch_phones.first&.phone_number
end
actions
end