Rails3:避免使用祖先宝石选择 n+1?
要求:
我正在构建一个任务列表应用程序,并希望任务能够有子任务。
我还希望任务能够同时存在于树中的多个位置,例如,如果我有 2 个任务:
- 建造狗窝
- 放置新栅栏
如果我计划用与狗窝相同的材料建造狗窝栅栏,这两个任务都有一个子任务“购买栅栏栅栏”。
我的有问题的实现(欢迎反馈):
我有2个模型:
- Node(has_ancestry和belongs_to:task)
- Task(has_many:nodes)
这意味着树(允许我拥有子任务)本身不存储任务,只是一个引用到一个任务对象。
下面是使用 Rails 控制台的示例:
t1 = Task.create :name => "Build dog kennel"
n1 = Node.create :task => t1
t2 = Task.create :name => "Put up new fence"
n2 = Node.create :task => t2
t3 = Task.create :name => "Buy fence palings"
n11 = Node.create :task => t3, :parent => n1
n21 = Node.create :task => t3, :parent => n2
t4 = Task.create :name => "Construct the fence"
n22 = Node.create :task => t4, :parent => n2
n2.children.each { |c| puts c.task.name }
最后一行给出以下输出,表示选择 n+1:
Node Load (0.2ms) SELECT "nodes".* FROM "nodes" WHERE "nodes"."ancestry" = '12'
Task Load (0.2ms) SELECT "tasks".* FROM "tasks" WHERE "tasks"."id" = 11 LIMIT 1
Buy fence palings
Task Load (0.2ms) SELECT "tasks".* FROM "tasks" WHERE "tasks"."id" = 10 LIMIT 1
Put up new fence
帮助?
我对 Ruby on Rails 和 ActiveRecord 很陌生,但我认为我需要做的就是根据nodes.task_id 外键将节点表与任务表连接起来,但我已经查看了 祖先文档并且找不到任何有用的东西。
将来我计划也通过外键从任务对象中获取更多信息,例如作者、相关评论等,通过这种实现,一页加载可能会触发相当多的选择查询:(
任何人都可以给我建议吗如何实现这一目标?
有没有办法强制急切加载? (这有帮助吗?)
如果您有更好的想法如何实现这一点,我愿意接受反馈。
提前致谢!
Requirements:
I am building a task list application and wanted tasks to be able to have sub-tasks.
I also wanted tasks to be able to exist in multiple places in the tree at once, for example, if I had the 2 tasks:
- Build dog kennel
- Put up new fence
If I planned on building the dog kennel out of the same material as the fence, both of these tasks would have a subtask of "Buy fence palings".
My problematic implementation (feedback welcome):
I have 2 models:
- Node (has_ancestry and belongs_to :task)
- Task (has_many :nodes)
This means the tree (that allows me to have subtasks) does not store the task it self, just a reference to a task object.
Here is an example using the rails console:
t1 = Task.create :name => "Build dog kennel"
n1 = Node.create :task => t1
t2 = Task.create :name => "Put up new fence"
n2 = Node.create :task => t2
t3 = Task.create :name => "Buy fence palings"
n11 = Node.create :task => t3, :parent => n1
n21 = Node.create :task => t3, :parent => n2
t4 = Task.create :name => "Construct the fence"
n22 = Node.create :task => t4, :parent => n2
n2.children.each { |c| puts c.task.name }
This last line gives the following output, indicating a select n+1:
Node Load (0.2ms) SELECT "nodes".* FROM "nodes" WHERE "nodes"."ancestry" = '12'
Task Load (0.2ms) SELECT "tasks".* FROM "tasks" WHERE "tasks"."id" = 11 LIMIT 1
Buy fence palings
Task Load (0.2ms) SELECT "tasks".* FROM "tasks" WHERE "tasks"."id" = 10 LIMIT 1
Put up new fence
Help?
I'm quite new to Ruby on Rails and ActiveRecord, but I would think all I need to do is join the nodes table with the tasks table based on the nodes.task_id foreign key, but I have looked through the Ancestry documentation and cant find anything useful.
In the future I plan on fetching more information from the task object via foreign keys too, such as author, related comments, etc. and with this implementation, one page load could trigger quite a lot of select queries :(
Can anyone offer me suggestions on how to accomplish this?
Is there a way to force eager loading? (Would that help?)
I'm open to feedback if you have a better idea how to accomplish this.
Thanks in advance!
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
因此,经过一段时间的研究,我终于找到了一种方法来做到这一点。
而不是这一行:
导致这样:
我使用了这一行:
导致这样:
这应该只执行 2 个查询,无论大小如何,结果集将包括任务!
我知道这可能是基本的东西,但对于像我这样的新手来说,它可能有点令人困惑,因为 部分引用预加载的 Rails 指南 仅显示类方法
includes()
So after playing around for a while, I finally found a way to do this.
Instead of this line:
which results in this:
I used this line:
Which resulted in this:
This should only ever execute 2 queries, regardless of the size and the resulting set will include the tasks!
I know this is probably basic stuff, but it may be a little confusing for new-comers like myself as the section of the rails guides that refers to eager loading only shows the class method
includes()