【问题标题】:How to properly scope and optimize this model queries in Rails 5 with AR如何使用 AR 在 Rails 5 中正确确定和优化此模型查询
【发布时间】:2017-08-10 01:32:42
【问题描述】:

我在 API 模式下有一个带有 Rails 5.0.5 的 API。对于每个控制器请求,我们都会使用 Active Model Serializers 回复 render json:。我们使用 postgresql 作为数据库。

假设这是我的数据库:

鉴于所有查询都需要用户登录,那么我可以得到一个current_user 对象:

我可以从 HABTM 关系中为用户获取所有项目,例如: render json: current_user.projects

我只想查询用户所属项目的DepartmentsActivityTypesStatus(不传递参数)。

(在真正的API中,有7个类似的模型属于Project,至少有2个至少有几百条记录。另外,客户端在查看一个时需要加载其中的7个Project,允许他们在同一个视图中构建新的Activities。)

从现在开始,我想出这个来显示在索引上,至少来自组织用户的Departments 属于:

def index
    # @departments = Department.all
    @departments = Department.joins(project: 'organization').where(:project => { 'organizations' => {:id => current_user.organization.id }})

    render json: @departments
end

在数据库中进行 3 次查询:

Started GET "/api/v1/departments" for 127.0.0.1 at 2017-08-09 19:40:57 -0400
Processing by Api::V1::DepartmentsController#index as HTML
  User Load (0.4ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = $1 LIMIT $2  [["id", 1], ["LIMIT", 1]]
  Organization Load (0.4ms)  SELECT  "organizations".* FROM "organizations" WHERE "organizations"."id" = $1 LIMIT $2  [["id", 1], ["LIMIT", 1]]
  Department Load (6.8ms)  SELECT "departments".* FROM "departments" INNER JOIN "projects" ON "projects"."id" = "departments"."project_id" INNER JOIN "organizations" ON "organizations"."id" = "projects"."organization_id" WHERE "organizations"."id" = $1  [["id", 1]]
[active_model_serializers]   Project Load (0.5ms)  SELECT  "projects".* FROM "projects" WHERE "projects"."id" = $1 LIMIT $2  [["id", 1], ["LIMIT", 1]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "projects".* FROM "projects" WHERE "projects"."id" = $1 LIMIT $2  [["id", 1], ["LIMIT", 1]]
[active_model_serializers]   Project Load (0.3ms)  SELECT  "projects".* FROM "projects" WHERE "projects"."id" = $1 LIMIT $2  [["id", 3], ["LIMIT", 1]]
[active_model_serializers]   CACHE (0.0ms)  SELECT  "projects".* FROM "projects" WHERE "projects"."id" = $1 LIMIT $2  [["id", 3], ["LIMIT", 1]]
[active_model_serializers] Rendered ActiveModel::Serializer::CollectionSerializer with ActiveModelSerializers::Adapter::Attributes (21.79ms)
Completed 200 OK in 110ms (Views: 33.5ms | ActiveRecord: 20.4ms)

这是一方面。

另外,相当于我真实 API 中的Activities,实际上每个组织都有数千条记录, 如果我可以将project_id 作为参数传递,我可以获得更窄的子集,但如果没有传递参数,我需要使用默认查询进行响应。

事实上,所有这些查询在生产环境中的响应时间都达到了几秒钟的量级。

所以,我在尝试优化时的问题是:

有没有办法让这 3 个查询只有一个?

有没有办法为这些模型的所有#all 查询设置此默认值?

如果没有将“project_id”传递给Activities#index,我如何至少从用户拥有的所有Projects 中回复所有Activities

或者是否有一些关于如何使用我一直错过的 Rails 更好地编码的约定?

我也安装了 Pundit,但仅用于根据用户的角色授权对用户的操作,我已经看到它可用于为模型编写自定义范围,但我不知道如何正确使用它来处理任何复杂的事情比“用户组织的所有项目”查询(角色为“admin”的用户的用例)。

在开始时也尝试使用“公寓”gem,并为每个组织使用单独的模式,在这种情况下查询更简单,但我给了我奇怪的行为,比如随机无法创建关系,抱怨一些记录在创建时不存在外键。所以我不得不停止使用它。

【问题讨论】:

    标签: rails-activerecord ruby-on-rails-5 active-model-serializers rails-api pundit


    【解决方案1】:

    我可能在这里遗漏了一些东西,但是在 @project 上定义实例方法有什么问题:

    def project_data
       {'project' => self,
        'departments' => departments,
        'activity_types' => activity_types,
        'status' => status
       }
    end
    

    然后在你的控制器中:

    render json: current_user.projects.map{|x| x.project_data}
    

    【讨论】:

    • 对所有像这样映射的对象做出响应会不会更慢?我们单独查询每个资源,因为客户端 UI 在不到一秒的时间内不需要某些资源并且是大型集合。 IE:当我们查询项目时,我们显示该项目列表以选择一个。当您选择一个时,将显示项目的“活动”列表,同时显示“部门”; “活动类型”;如果用户想要使用表单来创建新的“活动”,则会查询等。在选择项目和 200 毫秒后不会发生这种情况,但应该显示活动。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-04-28
    • 1970-01-01
    • 2015-12-26
    • 1970-01-01
    • 1970-01-01
    • 2021-01-16
    相关资源
    最近更新 更多