如何修复Rails职位列表页收藏功能产生的N+1查询问题
修复职位列表页收藏功能N+1查询问题
问题根源
你在Job模型的sort_by_date方法中预加载的是职位对应的所有favorite_jobs关联数据,但判断用户是否收藏的逻辑current_user.favorite?(job)内部调用了exists?方法,该方法无论是否预加载了关联都会直接触发新的数据库查询,所以之前的预加载完全没有起到作用,最终产生N+1问题。
最优解决方法(推荐)
提前拉取当前登录用户的所有收藏职位ID,在循环中直接用内存判断,仅触发1次额外查询。
1. 修改JobsController的index逻辑
# jobs_controller.rb def index if job_params.present? search else @jobs = Job.sort_by_date(page: params[:page], per_page: Job::JOB_PER_PAGE) # 新增:用户登录时预加载所有收藏的职位ID,转成Set保证O(1)的查询效率 @favorite_job_ids = user_signed_in? ? current_user.favorite_jobs.pluck(:job_id).to_set : Set.new end end
2. 修改视图判断逻辑
替换原来调用current_user.favorite?(job)的代码,直接用预加载的ID集合判断:
# index.html.slim - @jobs.each do |job| - if user_signed_in? && @favorite_job_ids.include?(job.id) = render 'shared/unfavorite', job_id: job.id - else = render 'shared/favorite', job_id: job.id
替代解决方法(用已有Job关联预加载数据)
如果你不想修改控制器逻辑,也可以直接复用Job模型预加载的favorite_jobs数据,把数据库查询改成内存判断,无需修改控制器,仅调整视图判断逻辑即可:
# index.html.slim - @jobs.each do |job| - if user_signed_in? && job.favorite_jobs.any? { |fj| fj.user_id == current_user.id } = render 'shared/unfavorite', job_id: job.id - else = render 'shared/favorite', job_id: job.id
注意:该方法仅适合职位收藏人数较少的场景,如果职位被大量用户收藏,预加载全量favorite_jobs会产生多余的数据传输,性能不如第一种方法。
关键注意点
Rails的exists?方法是数据库层查询方法,永远会触发新的SQL请求,不会复用已预加载到内存的关联数据,要做内存判断请使用any?、include?等集合方法。
内容的提问来源于stack exchange,提问作者Hà Mai
相关产品推荐
相关产品推荐

