我需要一个由ActiveRecord提取的嵌套的分层数据结构,当然所有早期加载,我都可以JSONify。 TaskSet
,Task
,Todo
和LineItem
都是关联的,因为后者是前者的孩子。很明显,我不是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
可以完成同样的事情而无需访问数据库?
答案 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,我强烈建议您使用jbuilder或RABL之类的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
,尤其是在您包含子对象的情况下。希望这会有所帮助。