我们正在运行 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
最佳答案
您声明您在节点
ID
上有一个“UNIQUE 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;
有了上面的约束(或索引),这个查询应该会更快:
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
子句来仅查找label1
和label2
结束节点。
[更新]
您可以利用 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/