2017-11-22 13 views
1

neo4jバージョン3.3を実行しています。 合計650kの関係を持つ2種類のノード(合計10kノード)があります。メモリのヒープサイズは8GBです。
ノードのノードにUNIQUE CONSTRAINTがあります。IDです。
公式neo4j-pythonドライバを使用しています(py2neoドライバを使用しようとしましたが、パフォーマンスはさらに悪化しました)。
次のクエリを実行すると、パフォーマンスが非常に問題になります。 1ホップ距離の場合、数分(複数のノードのリストの場合でも)かかる。 1ノードのリストを持つ2ホップ距離(以下の照会のような)については、40分以上かかる。
パフォーマンスを改善する方法はありますか?結果を処理するためneo4j cypherのパフォーマンスをリストとしてパラメータ

query = '''MATCH (n1:label1) 
      WHERE n1.ID IN {list}    
      MATCH paths=((n1)-[:relType*..2]->(n2))    
      WHERE n1.ID <> n2.ID AND (n2:label1 OR n2:label2) 
      RETURN DISTINCT paths 
      UNION 
      MATCH (n1:label2) 
      WHERE n1.ID IN {list}    
      MATCH paths=((n1)-[:relType*..2]->(n2))    
      WHERE n1.ID <> n2.ID AND (n2:label1 OR n2:label2) 
      RETURN DISTINCT paths''' 

with driver.session() as session: 
    results = list(session.run(query, parameters={'list':list_nodes})) 
    if results: 
     df = neo4j_graph_to_df(results) 

機能は以下の通りです:

def neo4j_graph_to_df(paths): 
    paths_dict=OrderedDict() 
    print(paths) 
    for (pathID, e) in enumerate(paths): 
     paths_dict[pathID]=OrderedDict() 
     nodes_list = [n for n in e['paths'].nodes] 
     rels_list = [r for r in e['paths'].relationships] 
     pathl = [x for x in itertools.chain.from_iterable(itertools.zip_longest(nodes_list, rels_list)) if x ] 
     for i, p in enumerate(pathl):   
      if isinstance(p, neo4j.v1.types.Node):    
       paths_dict[pathID]['Node'+str(i)+'Label']= str(next(iter(p.labels))) 
       dicti = dict(zip(['Node'+str(i)+str(np) for np in p.properties.keys()], p.properties.values())) 
       paths_dict[pathID] = OrderedDict({**paths_dict[pathID], **dicti})      
      if isinstance(p, neo4j.v1.types.Relationship): 
       paths_dict[pathID]['Rel'+str(i-1)]=p.type 
       dicti = dict(zip(['Rel'+str(i)+str(rp) for rp in p.properties.keys()], p.properties.values())) 
       paths_dict[pathID] =OrderedDict({**paths_dict[pathID], **dicti }) 
    df = pd.DataFrame.from_dict(paths_dict, orient='index').fillna('0') 
    df = df.drop_duplicates().reset_index() 
    return df 

答えて

0

あなたはn.IDプロパティにインデックスを作成してみてくださいすることができます:インデックスはバックグラウンドタスクで作成されていることを

CREATE INDEX ON :label1(ID) 
CREATE INDEX ON :label2(ID) 

注意データベース。 Neo4jは、索引が作成されてオンラインになったら、索引の使用を自動的に開始して開始します。

また、一つだけMATCH 2つの代わりに使用するようにクエリを変更してみてください:

MATCH paths=((n1:label1)-[:relType*..2]->(n2))    
WHERE n1.ID IN {list} AND n1.ID <> n2.ID AND (n2:label1 OR n2:label2) 
RETURN DISTINCT paths 
UNION 
MATCH paths=((n1:label2)-[:relType*..2]->(n2))    
WHERE n1.ID IN {list} AND n1.ID <> n2.ID AND (n2:label1 OR n2:label2) 
RETURN DISTINCT paths 
+0

Thxを。私には制約があるので、インデックスは必要ありません。 https://neo4j.com/docs/developer-manual/current/get-started/cypher/labels-constraints-and-indexes/ 1試合に変更しても、パフォーマンスは向上しませんでした。 –

+0

こんにちは@JosephBerry!'UNION'の2つのクエリの唯一の違いは関係方向ですから、' MATCH'の方向を取り除き、 'UNION'を避けてみませんか?このようにして、次のようになります。 'MATCH paths =((n1) - [:relType * .. 2] - >(n2)) where {list} AND n1.ID <> n2.ID AND(n1:label1 OR n1:label2)AND(n2:ラベル1またはn2:ラベル2) パスを返す。それを試して助けがあれば教えてください! :) –

1
  1. あなたが「ノードIDUNIQUE CONSTRAINT」を持っていると述べたが、あなたは実際に2つの制約を必要とします(またはインデックス)。各ノードラベル(label1およびlabel2)は、IDプロパティで独自の制約(またはインデックス)を必要とします。例:上記の制約(またはインデックス)で

    CREATE CONSTRAINT ON (lab1:label1) ASSERT lab1.ID IS UNIQUE; 
    
    CREATE CONSTRAINT ON (lab2:label2) ASSERT lab2.ID IS UNIQUE; 
    
  2. 、このクエリは速くする必要があります:

    MATCH (n1:label1) 
    USING INDEX n1:label1(ID) 
    WHERE n1.ID IN {list} 
    WITH COLLECT(n1) AS ns1 
    MATCH (n2:label2) 
    USING INDEX n2:label2(ID) 
    WHERE n2.ID IN {list} 
    WITH ns1 + COLLECT(n2) AS ns 
    UNWIND ns AS n 
    OPTIONAL MATCH path1=(n)-[:relType*..2]->(n31:label1) 
    WHERE n.ID <> n31.ID 
    OPTIONAL MATCH path2=(n)-[:relType*..2]->(n32:label2) 
    WHERE n.ID <> n32.ID 
    WITH COLLECT(path1) + COLLECT(path2) AS paths 
    UNWIND paths AS path 
    RETURN DISTINCT path 
    

    それはサイファープランナーにそれがインデックスを使用すべきであるというヒントを与えることをUSING INDEX条項が組み込まれて関心のある開始ノードをすばやく取得します(プランナーが自動的に実行しない可能性があるため)。次に、label1label2のエンドノードだけを検索するために、2つのOPTIONAL MATCH節を使用します。

[UPDATE]

代わりに上記#2の多くは、パスを生成するときに、エンド・ノード・ラベルを指定することを可能にするので、あなたは、Path Expander APOC手順の1つの利点を取ることができます。例えば

:あなたの提案のための

MATCH (n1:label1) 
WHERE n1.ID IN {list} 
WITH COLLECT(n1) AS ns1 
MATCH (n2:label2) 
WHERE n2.ID IN {list} 
WITH ns1 + COLLECT(n2) AS startNodes 
CALL apoc.path.expandConfig(
    startNodes, 
    {labelFilter: '>label1|>label2', minLevel: 1, maxLevel: 2} 
) YIELD path 
RETURN DISTINCT path; 
関連する問題