简体   繁体   中英

Rails ActiveAdmin Eliminating n+1 queries on custom attributes belong to another model

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

Now the includes method is available inside the ActiveAdmin. All you need to do is add this into the desired admin file.

# app/admin/brach.rb
ActiveAdmin.register Branch do

  includes :branch_phones

  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
end

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM