Heirarchical,记忆中的协会,没有糟糕的工艺

时间:2015-05-21 20:23:41

标签: ruby-on-rails ruby activerecord rails-activerecord

我需要一个由ActiveRecord提取的嵌套的分层数据结构,当然所有早期加载,我都可以JSONify。 TaskSetTaskTodoLineItem都是关联的,因为后者是前者的孩子。很明显,我不是Ruby天才:我提出的最好的是

   @hsh = TaskSet.all.
                  includes(:tasks => {:todos => :lineitems} ).
                  map{ |ts| ts => ts.tasks.
                    map{ |t| t => t.todos.
                      map{ |td| td => td.lineitems }}}.
                  as_json

对于非常小的N(对于每个级别)而言,这是可疑的 - 并且对于较大的N而言根本无法使用。我正在寻找一种不会再次发生N + 1问题的解决方案。

奖金:

有没有办法将音译抢占到另一个数据结构中并让AR急切加载内存中的所有深度关联,这样@task_set.as_json可以完成同样的事情而无需访问数据库?

2 个答案:

答案 0 :(得分:1)

我认为

@hsh = TaskSet.all
              .includes(:tasks => {:todos => :lineitems} )
              .joins(:tasks, :todos, :lineitems)
              .as_json

会这样做。

它将进行1个DB查询。我不知道它将数据结构转换为JSON的效率如何,或者数据结构中有多少项。

请参阅Rails Guides Eager Loading

以下是一个示例,说明如何使用单个数据库查询查询关联模型: Preload, Eagerload, Includes and Joins

答案 1 :(得分:1)

目前还不清楚您在何处调用此代码,因此这可能不适用,但我会假设您在控制器中执行此操作。如果是这种情况,我建议采用以下方法

首先在您的控制器中,加载所有TaskSet并急切加载相关模型

def index
  @task_sets = TaskSet.includes(tasks: { todos: :lineitems })
end

这将对数据库进行4次调用,但会将所有相关模型加载到内存中,这样循环关联和内部关联不会导致更多数据库调用(即没有N + 1问题)。

要构建实际的JSON,我强烈建议您使用jbuilderRABL之类的JSON构建器。由于jbuilder附带了最新版本的Rails,我将以此为例

app/views/task_sets/index.json.jbuilder下创建一个jbuilder视图,并构建您需要的JSON。

json.array! @task_sets do |task_set|
  json.some_attribute task_set.some_attribute

  json.tasks task_set.tasks, :attribute_1, :attribute_2
  # ... #
end

使用JSON构建器可以提供更多控制,而不仅仅是调用as_json,尤其是在您包含子对象的情况下。希望这会有所帮助。