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

How to fix this n+1 query: Limiting child association to most recent record in JSON api response

我正在尝试 return 一个 parent 记录的列表,以及每条 parent 最近的 child 记录。

在我的控制器中我有:

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

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

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

一个解决方案是定义一个具有关联范围的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   [["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 表达得很好。