2

私は、Node モデルのすばらしいネストされたセットを介して表される組織階層を持っています。すばらしい、うまく機能する、更新には費用がかかりますが、検索は非常に効率的です。

各 Node モデルには他のモデルの has_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

現在のノードから見下ろす、特定のサブツリーのすべての foo または bar を検索したいことがよくあります。素朴に、私はできる:

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

N+1 クエリを回避するために ActiveRecord .includes(:foos) を使用することもできます。私が本当に欲しいのはただ尋ねる@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 だけでなく、それ以上のものを「収集」したいとしましょう。たとえば、このモデルを 6 つまたは 12 個持っているとしましょう。現在、Node クラスまたはいくつかの lib に all_* メソッドの束を散らかしています。

ノードを引数として受け入れ、そのサブツリーのすべての foos/bars を返すクラス メソッドを foos と bars に定義する必要がありますか? ただし、foo と bar は node.self_and_descendants を理解している必要があります。

または、Foo クラス メソッドはノードのコレクションを受け入れることができ、ネストされた set メソッドを意識する必要はありませんが、その場合、経由node.all_foosなどの簡単なインターフェイスが失われます。

ここで見逃しているパターンまたはメソッドは何ですか? method_missingを介してキャッチオールを実装しnode.all_*て遊んだことがありますが、パフォーマンスの低下は気に入りません。ここで実行しようとしているのは、基本的にデータベースの検索であり、効率的で洗練されたものでなければなりません。

4

2 に答える 2

1

結合とマージに関して正しい方向を示してくれた@adamsandersonに感謝します。Merge を使用すると、別のスコープまたは関係を介して、結合された関連付けをフィルター処理できます。素晴らしい!

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

アプリケーションの all_* メソッドの 1 つでテストを検証し、この joins.merge 手法を使用してメソッドを書き直し、テストを再実行しました。オールグリーン!

この回答は、このメソッドを多くの異なる has_many リレーションに適用する方法の設計上の問題には対処していませんが、読者 (私) の演習として残します。

マージに関しては、http: //blog.mitchcrowe.com/blog/2012/04/14/10-most-underused-activerecord-relation-methodsも参照してください。

于 2013-06-26T19:42:46.540 に答える