如何修复此n + 1查询:将子关联限制为JSON api响应中的最新记录

时间:2018-02-03 15:19:39

标签: ruby-on-rails activerecord rails-api

我正在尝试返回父记录列表以及每个父母最近的子记录。

在我的控制器中我有:

def index
  projects = current_user.projects.includes(:tasks)

  render json: projects.as_json(
    methods: [:most_recent_task],
  ), status: 200
end

方法most_recent_task使用明确的here方法,并总结如下:

class Task < ApplicationRecord
  class << self
    def in_order
      order(created_at: :asc)
    end

    def recent(n)
      in_order.endmost(n)
    end

    def endmost(n)
      all.only(:order).from(all.reverse_order.limit(n), table_name)
    end
  end
end

class Project < ApplicationRecord
  has_many :tasks

  def most_recent_task
    tasks.recent(1)[0]
  end
end

这种方法返回正确的JSON响应,但我现在显然在每个请求上处理Task的n + 1个查询。

我尝试使用:includes:limit来链接范围,但似乎无法解决这个问题。也许使用JSON序列化器可以解决它?但我现在正试图避免这种额外的依赖。有什么建议吗?

1 个答案:

答案 0 :(得分:1)

一种解决方案是定义具有关联范围的has_one:

has_one :most_recent_task, -> { order(created_at: :asc) }, class_name: "Task"

然后,您可以使用includes急切加载数据:

>> Project.includes(:most_recent_task).all
  Project Load (0.3ms)  SELECT  "projects".* FROM "projects" LIMIT $1  [["LIMIT", 11]]
  Task Load (0.5ms)  SELECT "tasks".* FROM "tasks" WHERE "tasks"."project_id" IN (1, 2) ORDER BY "tasks"."created_at" ASC

请注意,它正在查询每个项目的所有任务,而不仅仅是最近的任务。但是没有N + 1,Project#most_recent_task很有表现力。