to_json中的会话感知模型方法导致n + 1个查询
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了to_json中的会话感知模型方法导致n + 1个查询相关的知识,希望对你有一定的参考价值。
这是现有应用程序的优化问题,我已经使代码通用,既使它变得更加容易理解,也更容易理解,而不是我描述论坛讨论类型情况的专有模型。我为这个例子修改了所有这些代码并没有对它进行测试,所以如果有任何错别字我道歉,如果有人向我指出,我会尝试修复它们。
假设我有一个带有四种型号的rails应用程序:Event,User,Forum和Post。
重要的关系如下:
- 用户有很多活动。
- 论坛有很多帖子。
- 邮政有很多活动。
前端是单页javascript应用程序,因此所有数据库数据都需要以json格式返回。
语境:
- 当用户点击帖子时,会创建一个名为“显示”的事件,该事件将该帖子标记为不再是新的。
- 用户需要登录以查看哪些帖子是新点击论坛调用以下端点:
- 有多个用户,因此事件能够是帖子和用户之间的多对多关系。
example.com/forum/15/all_posts
继承人相关代码:
论坛管制员:
#forums_controller.rb
def all_posts
current_user = User.find(session[:user_id])
forum = Forum.includes(:posts).where(id: params[:id]).take
forum.posts.each do |post|
post.current_user = current_user
end
render json: forum.to_json(
include: [
{ posts: {
methods: [:is_new]
}}
]
)
end
帖子模型:
#post.rb (posts model)
has_many :events
attr_accessor :current_user
def is_new
if current_user #user may not be logged in
!!self.events.where(user_id: current_user.id, name: 'Show').take
else
false
end
end
模型是动作所在的位置,所以我们试图将逻辑保留在控制器之外,但由于会话在模型中不可用,我们最终将这个疯狂的工作添加到将at__adcess作为attr_accessor,以便方法可以为有问题的用户返回适当的数据....我不喜欢这样但我从来没有想出更好的方法来做到这一点。我们在其他地方重复了这种模式,我很乐意听到其他选择。
这是我的问题:
在前端使用is_new的调用来确定哪些帖子到了高光,但它也触发了n + 1场景如果有10个帖子,这个端点会给我带来总共12个查询,如果我的事件表是不好的很大。如果我将所有逻辑移动到控制器,我可以在2个查询中执行此操作。
总之,我有两个问题:
- 最重要的是:我如何解决这个n + 1的情况?
- 一般来说有更好的方法吗?我不喜欢在调用to_json之前需要每个循环我没有发现这个模式优雅或易于理解。同时我不想将所有代码都移动到控制器中。 rails的方法是什么?
如果使用scope
是一个选项,我会尝试类似:
class Post < ApplicationRecord
scope :is_new, -> { where(user_id: current_user.id, name: 'Show') } if current_user.id?
end
如果在您的情况下发送current_user
是更好的选择,您也可以这样做:
class Post < ApplicationRecord
scope :is_new, ->(current_user) {...}
end
这只是伪代码举个例子:
第一个答案
当我发布这个时,我忘了你正在从ForumsController渲染json。
Post
scope :for_user, -> (user = nil) do
includes(events: :users).where(users: {id: user.id}) if user
end
def is_new_for_user?(user = nil)
return true if user.nil?
self.events.empty?{ |e| e.name == 'Show' }
end
PostController
def index
@posts = Post.for_user(current_user)
end
posts/index.html.erb
...
<% if post.is_new_for_user?(current_user) %>
...
<% end
...
第二个答案
这仍然是伪代码。我没有测试任何东西。
Forum
scope :for_user, -> (user = nil) do
if user
includes(posts: [events: :users]).where(users: {id: user.id})
else
includes(:posts)
end
end
ForumsController
def all_posts
current_user = User.find(session[:user_id])
forum = Forum.for_user(current_user).where(id: params[:id]).take
render json: forum.to_json(
include: [
{ posts: {
methods: [:is_new_for_user?(current_user)]
}}
]
)
end
以上是关于to_json中的会话感知模型方法导致n + 1个查询的主要内容,如果未能解决你的问题,请参考以下文章