【问题标题】:Adding include argument to render method doesn't fix N+1 query向渲染方法添加包含参数不能修复 N+1 查询
【发布时间】:2017-07-26 23:10:49
【问题描述】:

我有以下型号:

class Request < ApplicationRecord
    belongs_to :contact
    belongs_to :hub_post
end

class Contact < ApplicationRecord
    has_many :requests
    has_many :likes
end

class Like < ApplicationRecord
    belongs_to :hub_post
    belongs_to :contact
end

class HubPost < ApplicationRecord
    has_many :requests
    has_many :likes
end

在我的HubPostSerializer,我有以下方法:

def liked_by_current_contact
    scope.present? && object.likes.where(contact_id: scope.id).present?
end

在我的RequestsController 中,我使用hub_postslikes 获取请求,然后返回JSON 响应(我使用active_model_serializers 0.10.4 和:json 适配器),如下所示:

requests = current_contact.requests.includes(hub_post: [:contact])

# More controller code here

respond_to do |format|
    format.json { render json: requests, each_serializer: PortalRequestSerializer, scope: current_contact }
end

这当然是一个明显的 N+1 问题(令人惊讶的是,Bullet gem 似乎并没有意识到这一点)。查看日志时,我可以看到每个 hub_post 的 SELECT 语句。现在我尝试像这样包含likes

requests = current_contact.requests.includes(hub_post: [:contact, :likes])
requests = current_contact.requests.includes(hub_post: [contact: [:likes]])

有趣的是,Bullet gem 不支持 N+1,但 Scout 可以。

我还尝试按照documentation 在我的渲染方法中添加一个include 参数,如下所示:

respond_to do |format|
    format.json { render json: requests, include: 'hub_posts,hub_posts.likes', each_serializer: PortalRequestSerializer, scope: current_contact }
end

我也尝试过像这样使用单级和多级通配符:

respond_to do |format|
    format.json { render json: requests, include: '*', each_serializer: PortalRequestSerializer, scope: current_contact }
end

respond_to do |format|
    format.json { render json: requests, include: '**', each_serializer: PortalRequestSerializer, scope: current_contact }
end

但我所做的一切似乎都没有消除 N+1。我不确定这是否是 gem 如何处理嵌套序列化程序中包含的关系的问题,或者我只是遗漏了一些东西。

更新

这是来自服务器日志的查询的gist

【问题讨论】:

    标签: ruby-on-rails active-model-serializers


    【解决方案1】:

    在我看来,您应该将 includes 声明从

    requests = current_contact.requests.includes(hub_post: [:contact])
    

    requests = current_contact.requests.includes(:hub_post, :contact)
    

    【讨论】:

    • 不。试过这个,没有为我做任何事情。
    • 我认为有些混乱。是likes 你遇到了N+1 问题,还是hub_postscontacts
    • 查看您的关联,我不太清楚为什么当requests 直接关联到contacts 和@ 时,您为什么要在hub_posts (requests = current_contact.requests.includes(hub_post: [:contact, :likes])) 下嵌套contacts 987654332@不是
    • HubPost 也属于_to :contact 因为联系人可以创建 HubPost,但 HubPost 也可以由雇主创建。我只是省略了那行,因为我认为它不相关。 N+1 的问题不在于 hub_posts 或contacts,而在于likes,属于:contact 和:hub_post。但是,如果您查看我的 HubPost 序列化程序方法,您会发现我正在查询 object.likes,其中 object 是 HubPost 的一个实例。如果还不清楚,请告诉我
    【解决方案2】:

    我假设你的 Request 模型有 contact_id 来存储 current_contact 的 id,所以你可以试试:

    request = Request.where(contact_id: current_contact.id).includes(:contact, { hub_post: :likes })
    

    【讨论】:

    • 这对 N+1 问题没有任何作用。我已经尝试了在 includes 语句中放置 :likes 的所有变体,但没有任何效果
    • 用日志要点的链接(帖子结尾)编辑了我上面的问题。
    • request = Request.where(contact_id: current_contact.id).includes(contact: [:likes, :requests], hub_post: [:likes, :requests])怎么样
    • 不。我强烈怀疑问题不在于includes 语句,而在于active_model_serializers 如何处理嵌套关联。我的理论是,由于某种原因,它“忘记”了这些嵌套关联(在本例中为 likes)已经被包含并再次获取它们。
    • @ACIDSTEALTH 在这个 PR 中抢夺一下,它可能会对你有一些想法github.com/rails-api/active_model_serializers/issues/1672
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-09-13
    • 1970-01-01
    • 1970-01-01
    • 2017-03-16
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多