首页 > 解决方案 > 预加载连接多个值的记录

问题描述

我有一个应用程序可以帮助网球运动员找到对手。我有以下型号:

TennisMatch (id, description, user_id)

User (id, name, type)

AvailabilityWindow(tennis_match_id, start_time, end_time, requester_id)

用户可以使用该应用发布网球比赛列表。作为其中的一部分,他们必须提供一些可用性。例如,如果他们想在周六上午 8 点到 10 点之间进行比赛。这将创建一个可用性记录作为创建匹配的一部分:

AvailabilityWindow.new(tennis_match_id: 1, start_time: '8am Saturday', end_time: '10am Saturday', requester_id: user.id)

如果其他玩家看到列表,他们可以接受并同意在建议的时间和日期进行游戏。他们还可以建议其他可用性,这将导致将记录添加到 AvailabilityWindow 表中。因此,一场网球比赛可以有来自多个不同用户的多个 availability_window 记录。

我的问题是:我需要一个页面,列出所有网球比赛以及创建列表的用户建议的可用性(忽略其他用户建议的任何可用性)。例如

class TennisMatch < ApplicationRecord
   has_many :availability_windows
end
class TennisMatchController < ApplicationController
  def index
    @tennis_matches = TennisMatch.all
  end
end

index.html.erb:

<% @tennis_matches.each do |match| %>
  <h4><%= match.description %>
  <% match.availability_windows.where(requester_id: match.user_id).each do |window| %>
    <p><%= window.start_time %>-<%= window.end_time %>
  <% end %>
<% end %>

这确实有效,但会受到 n+1 的影响,因为它会查询每个匹配项以获取可用性。如何预加载这些数据以避免 n+1?

我们还假设简单地预加载所有窗口,例如TennisMatch.all.includes(:availability_windows)在控制器中,并select(&block)在视图中对集合执行 a 以避免查询不是一种选择。

标签: ruby-on-railsrubyactiverecord

解决方案


尝试这个:

class TennisMatchController < ApplicationController
  def index
    matches = TennisMatch.all
    user_ids = matches.pluck(:user_id)
    @tennis_matches = matches.includes(:availability_windows).
                        where(availability_windows: { user_id: user_ids })
  end
end

然后在 index.html.erb 中:

<% @tennis_matches.each do |match| %>
  <h4><%= match.description %>
  <% match.availability_windows.each do |window| %>
    <p><%= window.start_time %>-<%= window.end_time %>
  <% end %>
<% end %>

推荐阅读