分层的、内存中的关联,做工不差
Heirarchical, in-memory associations without poor workmanship
我需要一个由 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
可以在不访问数据库的情况下完成同样的事情?
我觉得
@hsh = TaskSet.all
.includes(:tasks => {:todos => :lineitems} )
.joins(:tasks, :todos, :lineitems)
.as_json
会这样做。
它将进行 1 个数据库查询。我不知道它能多高效地将数据结构转换为JSON,或者数据结构中有多少项。
下面是一个示例,说明如何使用单个数据库查询来查询关联模型:
Preload, Eagerload, Includes and Joins
不清楚您在哪里调用此代码,因此这可能不适用,但我假设您是在控制器中执行此操作。如果是这样的话,我会推荐以下方法
首先在您的控制器中加载所有任务集并预加载相关模型
def index
@task_sets = TaskSet.includes(tasks: { todos: :lineitems })
end
这将对数据库进行 4 次调用,但会将所有关联的模型加载到内存中,这样遍历关联和内部关联就不会导致更多的数据库调用(即没有 N+1 问题)。
要构造实际的 JSON,我强烈建议使用 JSON 生成器,例如 jbuilder or RABL。由于 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
给您更多的控制权,尤其是在您包含子对象的情况下。希望这有帮助。
我需要一个由 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
可以在不访问数据库的情况下完成同样的事情?
我觉得
@hsh = TaskSet.all
.includes(:tasks => {:todos => :lineitems} )
.joins(:tasks, :todos, :lineitems)
.as_json
会这样做。
它将进行 1 个数据库查询。我不知道它能多高效地将数据结构转换为JSON,或者数据结构中有多少项。
下面是一个示例,说明如何使用单个数据库查询来查询关联模型: Preload, Eagerload, Includes and Joins
不清楚您在哪里调用此代码,因此这可能不适用,但我假设您是在控制器中执行此操作。如果是这样的话,我会推荐以下方法
首先在您的控制器中加载所有任务集并预加载相关模型
def index
@task_sets = TaskSet.includes(tasks: { todos: :lineitems })
end
这将对数据库进行 4 次调用,但会将所有关联的模型加载到内存中,这样遍历关联和内部关联就不会导致更多的数据库调用(即没有 N+1 问题)。
要构造实际的 JSON,我强烈建议使用 JSON 生成器,例如 jbuilder or RABL。由于 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
给您更多的控制权,尤其是在您包含子对象的情况下。希望这有帮助。