2017-03-10 12 views
1

私は木のような構造を作るために自身を参照するエンティティNodeを持っています。ここでEctoに木のような構造をロードする

移行です:

create table(:nodes) do 
    add :name, :string, null: false, size: 64 
    add :parent_id, references(:nodes, on_delete: :nothing) 
end 

そしてここでスキーマ定義:

schema "nodes" do 
    field :name, :string 
    belongs_to :parent, Node 
    has_many :children, Node, foreign_key: :parent_id 
end 

私は、このアプローチを使用してツリー全体をロードしようとしています:

root_nodes = Repo.all(
    from n in Node, 
    where: is_nil(n.parent_id) # Root nodes don't have a parent 
) 

nodes = Enum.map(root_nodes, fn(n) -> 
    Ecto.build_assoc(n, :children, load_children(n.id)) 
end) 

defp load_children(parent_id) do 
    nodes = Repo.all(
    from n in Node, 
     where: n.parent_id == ^parent_id 
) 
    if nodes != [] do 
    # If children aren't empty, apply recursively 
    nodes = Enum.map(nodes, fn(n) -> 
     Ecto.build_assoc(n, :children, load_children(n.id)) 
    end) 
    end 

    nodes 
end 

が、私は得る:

** (FunctionClauseError) no function clause matching in Ecto.drop_meta/1 

一般的に、私はエクトORMを使用すべきかの理解に苦労だと思います。ほとんどのチュートリアルでは、孤立した行をフェッチする方法や、1レベルのプリロードを行う方法の例だけを示しています。私は木のような構造をどのように読み込むべきですか?何か助けてくれてありがとう。

+0

私が見る限り、build_assocは間違って使用されています。単に値をchildrenに設定しないと、クエリの結果にparent_idを含むすべての値が含まれます。 あなたが実際に直面している問題になると、Michalはposgresqlを使用している場合、以下の答えを再帰的に使用します。頻繁に読んで、このテーブルに新しい行を追加することはめったにない場合は、ネストされたセットを使用してください。 –

答えて

4

Ectoは、ほとんどのORMがデータベースを完全に抽象化しようとする場合、通常のORMではありません。エクトは、基本となるデータベースのセマンティクスに近い傾向があります。

これは、実際の質問は「どのようにして樹木状の構造にエクトをロードするのか」ではないことを意味します。しかし、 "どのように私はSQLで木のような構造をロードするのですか?"(あなたが使っていると仮定して)。

当然のことながら、その答えは、それは非常に複雑だということである - のいずれかの各ネストレベル(恐ろしく非効率的な)、recursive queriesまたは(フルパスを格納し、ちょうどPARENT_IDない、またはNested Set modelで)表現を変更するための1つのクエリが必要です。

ただ1つの方法をとっておくと、最も簡単な方法は、正しい場所にロードした後にすべてのものをロードして適切にステッチすることです(利点は1つのクエリを実行することです)。

これは(良い答えがないので)直接質問には答えませんが、解決策を探す場所と、簡単にする方法を変更する方法を教えてくれることを願っています。

関連する問題