Как я могу оптимизировать этот запрос активной записи в этом фильтруемом API-интерфейсе с разбивкой по страницам? - PullRequest
0 голосов
/ 09 июня 2019

Я создаю Rails API из модели Service с несколькими вложенными отношениями (многие-к-одному и многие-ко-многим).

Базовый индексный маршрут имеет достаточно быстрое время отклика(~ 150 мс), но когда я добавляю запрос фильтра к URL-адресу (например, ?category=MyCategory,MyOtherCategory), время отклика абсурдно медленно (~ 20 с).

Как я могу оптимизировать область активной записи и запрос кисправить это?

Я вижу из журнала рельсов, что виноват этот абсолютно массивный запрос:

SELECT "services"."id" AS t0_r0, "services"."name" AS t0_r1, "services"."parent_organisation" AS t0_r2, "services"."description" AS t0_r3, "services"."created_at" AS t0_r4, "services"."updated_at" AS t0_r5, "services"."url" AS t0_r6, "services"."contact_name" AS t0_r7, "services"."phone" AS t0_r8, "services"."email" AS t0_r9, "services"."review_status" AS t0_r10, "services"."review_number" AS t0_r11, "services"."review_notes" AS t0_r12, "services"."clo_notes" AS t0_r13, "services"."health_safety" AS t0_r14, "services"."insurance" AS t0_r15, "services"."safeguarding" AS t0_r16, "services"."vol_dbs_check" AS t0_r17, "services"."review_date" AS t0_r18, "services"."laf_area" AS t0_r19, "services"."ccg_locality" AS t0_r20, "services"."venue" AS t0_r21, "services"."area" AS t0_r22, "services"."postcode" AS t0_r23, "services"."latitude" AS t0_r24, "services"."longitude" AS t0_r25, "services"."price" AS t0_r26, "services"."daytime" AS t0_r27, "services"."frequency" AS t0_r28, "services"."assigned_to" AS t0_r29, "services"."services" AS t0_r30, "services"."category_id" AS t0_r31, "days"."id" AS t1_r0, "days"."name" AS t1_r1, "days"."created_at" AS t1_r2, "days"."updated_at" AS t1_r3, "categories"."id" AS t2_r0, "categories"."name" AS t2_r1, "categories"."created_at" AS t2_r2, "categories"."updated_at" AS t2_r3, "keywords"."id" AS t3_r0, "keywords"."name" AS t3_r1, "keywords"."created_at" AS t3_r2, "keywords"."updated_at" AS t3_r3, "accessibilities"."id" AS t4_r0, "accessibilities"."name" AS t4_r1, "accessibilities"."created_at" AS t4_r2, "accessibilities"."updated_at" AS t4_r3, "suitabilities"."id" AS t5_r0, "suitabilities"."name" AS t5_r1, "suitabilities"."created_at" AS t5_r2, "suitabilities"."updated_at" AS t5_r3, "age_groups"."id" AS t6_r0, "age_groups"."name" AS t6_r1, "age_groups"."created_at" AS t6_r2, "age_groups"."updated_at" AS t6_r3 FROM "services" INNER JOIN "categories" ON "categories"."id" = "services"."category_id" LEFT OUTER JOIN "days_services" ON "days_services"."service_id" = "services"."id" LEFT OUTER JOIN "days" ON "days"."id" = "days_services"."day_id" LEFT OUTER JOIN "keywords_services" ON "keywords_services"."service_id" = "services"."id" LEFT OUTER JOIN "keywords" ON "keywords"."id" = "keywords_services"."keyword_id" LEFT OUTER JOIN "accessibilities_services" ON "accessibilities_services"."service_id" = "services"."id" LEFT OUTER JOIN "accessibilities" ON "accessibilities"."id" = "accessibilities_services"."accessibility_id" LEFT OUTER JOIN "services_suitabilities" ON "services_suitabilities"."service_id" = "services"."id" LEFT OUTER JOIN "suitabilities" ON "suitabilities"."id" = "services_suitabilities"."suitability_id" LEFT OUTER JOIN "age_groups_services" ON "age_groups_services"."service_id" = "services"."id" LEFT OUTER JOIN "age_groups" ON "age_groups"."id" = "age_groups_services"."age_group_id" WHERE "categories"."name" = $1 AND "services"."id" IN ($2, $3, $4, $5, $6, $7, $8, $9, $10, $11)

Мои модели:

# models/service.rb
class Service < ApplicationRecord

  belongs_to :category

  has_and_belongs_to_many :days
  has_and_belongs_to_many :keywords
  has_and_belongs_to_many :accessibilities
  has_and_belongs_to_many :suitabilities
  has_and_belongs_to_many :age_groups
  has_and_belongs_to_many :legacy_category

  scope :category, -> (category) {
    category_array = category.split(',')
    joins(:category).where(categories: {name: category_array})
  }

end
# models/category.rb
class Category < ApplicationRecord
    has_many :services
end

Мой контроллер:

class ServicesController < ApplicationController

    has_scope :category, only: :index

    def index
        services = apply_scopes(Service.includes(
            :category, 
            :days, 
            :keywords, 
            :accessibilities,
            :suitabilities,
            :age_groups
        ))
        paginate json: services, per_page: 10
    end

end

Я использую has_scope для применения областей в контроллере.

Я долженбыть в состоянии добавить запрос в конец моей конечной точки API и отфильтровать список служб, соответствующих указанным категориям.

Обратите внимание, что важно иметь возможность фильтровать наличие любых категорий в URL.-поддерживаемый массив, а не один.

1 Ответ

0 голосов
/ 09 июня 2019

Первое место, которое я бы проверил, правильно ли индексирует ваша таблица Service в базе данных для Category. Я предполагаю, что это так, но лучше убедиться в этом. Если нет, то, очевидно, добавьте туда индекс. Возможно, вы также захотите проверить, что Categories также индексирует имя.

Теперь я никогда не использовал библиотеку has_scope (и при этом я не использовал библиотеку пагинации, которую вы показали в вашем примере), поэтому я не могу говорить с ними конкретно, но для достижения этого стандартными средствами я бы это не работает для вас?

category_names = params[:category].split(",")
per_page = params[:per_page].to_i
page = params[:page].to_i * per_page

category_ids = Category.where(name: category_names).pluck(:id)
services = Service
    .includes(
      :category, 
      :days, 
      :keywords, 
      :accessibilities,
      :suitabilities,
      :age_groups
    ).where(category: category_ids)
    .limit(per_page)
    .offset(page)


render json: services, status: :ok

Звучит так, что проблема оптимизации заключается в том, что ваша база данных не проиндексирована должным образом, поэтому без дополнительной информации я думаю, что исправление должно устранить вашу низкую производительность.

...