如何在此可过滤的,分页的Rails API中优化此活动记录查询?

时间:2019-06-09 13:14:16

标签: ruby-on-rails json activerecord will-paginate has-scope

我正在从具有几个嵌套关系(多对一和多对多)的模型Service构建Rails API。

基本索引路由具有足够快的响应时间(〜150ms),但是当我向URL添加过滤查询(例如?category=MyCategory,MyOtherCategory)时,响应时间非常慢(〜20s)。

如何优化活动记录范围和查询以解决此问题?

我从rails日志中看到,这绝对是庞大的查询的罪魁祸首:

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 :(得分:0)

我首先要检查的是数据库中的Service表是否在Category上正确索引。我以为可以,但是最好确认一下。显然,如果没有,请在其中添加索引。您可能还需要检查一下类别索引是否在名称上。

现在,我从未使用过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

听起来好像是优化问题,因为您的数据库没有正确索引,因此如果没有更多信息,我认为修复该问题应该可以解决您在那方面的不良表现。