2

我有一个通过很棒的嵌套集表示的组织层次结构Node 模型上的太好了,效果很好,更新很昂贵,但发现非常有效。

每个 Node 模型在其他模型上都有_many,我们称它们为 Foo 和 Bar。

class Node < ActiveRecord::Base
  acts_as_nested_set

  has_many :foos
  has_many :bars
end

class Foo < ActiveRecord::Base
  belongs_to :node
end

通常,我想找到给定子树的所有 foos 或 bar,从当前节点向下看。天真地,我可以这样做:

@foos = @node.self_and_descendants.collect(&:foos).compact  

我什至可以使用 ActiveRecord .includes(:foos) 来避免 N+1 查询。我真正想要的只是要求,@node.all_foos所以我实现了这样的东西:

class Node < ActiveRecord::Base
  def all_foos
    Foo.where(node_id: self_and_descendants.pluck(:id))
  end

  # More efficient?
  def all_foos_alternately
    Foo.where(node_id: self_and_descendants.includes(:foos).pluck(:id))
  end
end

但是,假设我想“收集”的不仅仅是 foos 和 bar,假设我有半打或一打这样的模型。现在我用一堆 all_* 方法在我的 Node 类或一些库中乱扔垃圾。

我是否应该在接受节点作为参数的 foos 和 bar 上定义类方法并返回该子树的所有 foos/bars?但是 foos 和 bar 需要了解/了解 node.self_and_descendants。

或者 Foo 类方法可以接受节点集合,不需要知道嵌套的 set 方法,但是我失去了简单的接口 vianode.all_foos等。

我在这里缺少什么模式或方法?我已经node.all_*尝试通过 method_missing 实现一个包罗万象的方法,但不喜欢性能下降。我在这里尝试执行的是,其核心是数据库查找,因此它应该是高效和优雅的。

4

2 回答 2

1

Thank you to @adamsanderson for pointing me in the right direction with regards to joins and merge. Merge allows you to filter a joined association via another scope or relation. Brilliant!

def all_foos
  Foo.joins(:node).merge(self_and_descendants)
end

I verified tests on one of my all_* methods in my application, rewrote the method using this joins.merge technique, and reran my tests. All green!

This answer does not address the design issue of how to apply this method across many different has_many relations but I will leave that as an exercise for the reader (me).

See also, with regards to merge: http://blog.mitchcrowe.com/blog/2012/04/14/10-most-underused-activerecord-relation-methods

于 2013-06-26T19:42:46.540 回答
0

我将提出一个我认为不是很好的解决方案,以尝试启动讨论......

class Node < ActiveRecord::Base
  acts_as_nested_set

  has_many :foos
  has_many :tree_foos, :through => :children

  def all_foos
    # Shoot, how do I write this so I still get a relation back?
    foos + tree_foos
    # Nope, how about...
    Foo.where(id: foo_ids + tree_foo_ids)
  end
end

布莱赫。我不喜欢那样。而且我仍然必须在所有 has_many 模型关联中重复此代码。

于 2013-06-24T17:20:32.010 回答