如何使用AR在Rails 5中正确地调整和优化此模型查询

时间:2017-08-10 01:32:43

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

我在API模式下使用Rails 5.0.5的API。在每个控制器请求中,我们使用Active Model Serializers以render json:进行响应。我们使用postgresql作为数据库。

让我们假设这是我的数据库:

enter image description here

鉴于所有查询都需要用户登录,我才能获得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,我如何至少回复用户所有Projects的所有Activities#index

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

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

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

1 个答案:

答案 0 :(得分:0)

我可能在这里遗漏了一些东西,但在@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}