python - 以参数作为列表的 neo4j 密码性能

标签 python neo4j cypher

我们正在运行 neo4j 3.3 版。 我们有 2 种类型的节点(总共 10k 个节点),它们总共有 650k 个关系。我们内存的堆大小是 8GB。
节点在节点 ID 上有一个 UNIQUE CONSTRAINT
我们使用的是官方的neo4j-python官方驱动(我们也尝试过使用py2neo驱动,但是性能更差)。
运行以下查询时,性能非常有问题。对于 1 跳距离,它需要几分钟(即使对于多个节点的列表)。对于具有 1 个节点列表的 2 跳距离(如下面的查询),它需要超过 40 分钟。
有什么想法可以提高性能吗?

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

最佳答案

  1. 您声明您在节点 ID 上有一个“UNIQUE CONSTRAINT”,但实际上您需要 2 个约束(或索引)。每个节点标签(label1label2)都需要在 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 子句来给 Cypher 规划器一个提示,它应该使用索引来快速获取感兴趣的起始节点(因为规划器可能不会自动这样做)。然后,它使用 2 个 OPTIONAL MATCH 子句来仅查找 label1label2 结束节点。

[更新]

您可以利用 Path Expander 之一,而不是上面的 #2| APOC 过程,因为许多过程允许您在生成路径时指定结束节点标签。

例如:

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;

关于python - 以参数作为列表的 neo4j 密码性能,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47429591/

相关文章:

python - 使用 spaCy 3.0 将数据从旧的 Spacy v2 格式转换为全新的 Spacy v3 格式

mapreduce - GraphDB 查询和分片

python - 如何用keras近似行列式

python - PyInstaller 生成的可执行文件在 MacOS X 上的代码签名不被接受

neo4j - 无法在 Neo4j/console http ://localhost:7474 中看到创建的节点

javascript - 在nodejs中使用neo4j_driver时如何解决这些问题?

neo4j - Neo.TransientError.General.OutOfMemoryError

neo4j - 使用 apoc.convert.toTree 时按属性对分层数据进行排序

neo4j - 带数组的 CSV 的 Cypher 导入

python - Django:Gmail SMTP 错误:请先运行 connect()