Rails3:用Ancestry Gem避免选择n + 1?

时间:2011-08-05 11:50:10

标签: ruby-on-rails-3 nested-sets

的要求:

我正在构建一个任务列表应用程序,并希望任务能够完成子任务 我还希望任务能够同时存在于树中的多个位置,例如,如果我有两个任务:

  1. 建造犬舍
  2. 竖起新围栏
  3. 如果我打算用与篱笆相同的材料建造狗窝,这两项任务都会有一个“买篱笆”的子任务。

    我有问题的实施(反馈欢迎):

    我有2个型号:

    • 节点(has_ancestry和belongs_to: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外键将nodes表与tasks表连接起来,但我查看了{{ 3}}并且找不到任何有用的东西。

    将来我计划通过外键从任务对象中获取更多信息,例如作者,相关评论等。通过这种实现,一个页面加载可能会触发很多选择查询:(

    有人能提供关于如何实现这一目标的建议吗? 有没有办法强制加载? (这会有帮助吗?)
    如果您对如何实现这一目标有更好的了解,我愿意接受反馈。

    提前致谢!

1 个答案:

答案 0 :(得分:7)

所以在玩了一会儿之后,我终于找到了办法做到这一点。

而不是这一行:

n2.children.each { |c| puts c.task.name }

结果如下:

Node Load (0.2ms)  SELECT "nodes".* FROM "nodes" WHERE "nodes"."ancestry" = '27'
Task Load (0.2ms)  SELECT "tasks".* FROM "tasks" WHERE "tasks"."id" = 23 LIMIT 1
Buy fence palings
Task Load (0.2ms)  SELECT "tasks".* FROM "tasks" WHERE "tasks"."id" = 24 LIMIT 1
Construct the fence

我用过这一行:

n2.children.find(:all, :include => :task).each { |c| puts c.task.name }

导致了这个:

Node Load (0.2ms)  SELECT "nodes".* FROM "nodes" WHERE "nodes"."ancestry" = '27'
Task Load (0.2ms)  SELECT "tasks".* FROM "tasks" WHERE "tasks"."id" IN (23, 24)
Buy fence palings
Construct the fence

这应该只执行2个查询,无论大小如何,结果集将包含任务! 我知道这可能是基本的东西,但对于像我这样的新人来说可能有点混乱,因为section of the rails guides that refers to eager loading只显示了类方法includes()