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个查询中执行此操作。

总之,我有两个问题:

  1. 最重要的是:我如何解决这个n + 1的情况?
  2. 一般来说有更好的方法吗?我不喜欢在调用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个查询的主要内容,如果未能解决你的问题,请参考以下文章

感知器(Perception)

统计学习方法笔记-感知机学习方法

感知子平均算法

感知机模型

感知机模型

感知机算法(Perceptron Learning Algorithm)