如何修复此 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
表达得很好。
我正在尝试 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
表达得很好。