Cypher - 查询优化

5

我的问题是为什么WHERE运算符的速度不如预期? 假设我有7个标记为Consumer的节点。以下是一些示例数据...

MERGE (c:Consumer {mobileNumber: "000000000000"})
MERGE (:Consumer {mobileNumber: "111111111111"})
MERGE (:Consumer {mobileNumber: "222222222222"})
MERGE (:Consumer {mobileNumber: "333333333333"})
MERGE (:Consumer {mobileNumber: "444444444444"})
MERGE (:Consumer {mobileNumber: "555555555555"})
MERGE (:Consumer {mobileNumber: "666666666666"})
WITH c
MATCH (c1:Consumer) WHERE c1.mobileNumber <> "000000000000"
MERGE (c)-[:HAS_CONTACT]->(c1)

:Consumer(mobileNumber:{"000000000000"})和其他6个节点之间存在一个HAS_CONTACT关系。此外,mobileNumber字段上还有一个unique index约束。现在当我尝试执行以下查询时:

    PROFILE MATCH (n:Consumer{mobileNumber : "000000000000"}),
    (m:Consumer{mobileNumber : "111111111111"}) 
    WITH n,m 
    MATCH path = SHORTESTPATH((n)-[contacts:HAS_CONTACT]-(m)) 
    RETURN contacts;

搜索节点基于唯一索引,它的工作正常。以下是它的结果: Without Where clause

现在使用WHERE子句改变上述查询:

PROFILE MATCH (n:Consumer{mobileNumber : "000000000000"}),
(m:Consumer) WHERE m.mobileNumber 
IN (["111111111111"]) 
WITH n,m 
MATCH path = SHORTESTPATH((n)-[contacts:HAS_CONTACT]-(m)) 
RETURN contacts;

查询结果: 带WHERE子句

虽然上面的查询正常工作并给出与旧查询相同的结果。但是对于使用WHERE子句的endNode,它不使用任何索引。 它首先搜索所有现有节点,然后使用WHERE子句过滤结果,如果具有相同标签的数十万个节点,则这可能非常昂贵。

所以我的问题是:

  1. 为什么使用WHERE子句时它不使用索引?
  2. 引用多个节点的最佳方法是什么,以减少数据库访问次数?
  3. 在期望索引搜索的情况下,我可以使用IN运算符吗?

1
有可能查询计划器是根据你的数据库中数据的性质来制定计划的。我在我的本地3.2.2 CE中使用了你的数据和第二个查询。我在 :Consumer(mobileNumber) 上创建了一个约束。运行它后,我收到了你预期的结果。 - Dave Bennett
3个回答

3

正如@DaveBennett所说,这个问题在3.2.2版本中似乎不存在。

如果你使用的是早期版本,请尝试向计划程序提供hints以使用索引:

PROFILE MATCH
  (n:Consumer{mobileNumber : "000000000000"}),
  (m:Consumer)
USING INDEX n:Consumer(mobileNumber)
USING INDEX m:Consumer(mobileNumber)
WHERE m.mobileNumber  IN (["111111111111"])
MATCH path = SHORTESTPATH((n)-[contacts:HAS_CONTACT]-(m))
RETURN contacts;

这也可能有效,因为一些规划器似乎会自动尝试在(仅)第一个MATCH术语上使用索引:

PROFILE MATCH
  (n:Consumer{mobileNumber : "000000000000"}),
  (m:Consumer)
USING INDEX m:Consumer(mobileNumber)
WHERE m.mobileNumber  IN (["111111111111"])
MATCH path = SHORTESTPATH((n)-[contacts:HAS_CONTACT]-(m))
RETURN contacts;

好的,谢谢你的回答。实际上当时我使用的是Cypher版本:CYPHER 3.1.*,它没有在“WHERE”子句中使用索引。但现在我已经将我的Neo4j更新到了3.2.2版本,它正在按照预期工作。 - Afridi
我已经按照你的建议尝试了上述查询,但仍然不起作用,结果相同。但是在更新到最新版本后,现在它正常工作了,谢谢。+1 - Afridi

2

您正在使用哪个版本?我正在使用3.2.2社区版,使用您提供的小测试数据集,您的第二个查询生成了您所需的结果。

话虽如此,在您的实例中,查询规划器是否改变了其方法?

PROFILE 
MATCH (n:Consumer {mobileNumber : "000000000000"})
WITH n,(["111111111111", "222222222222", "333333333333", "444444444444", "555555555555", "666666666666"]) as number_list
UNWIND number_list as number
MATCH (m:Consumer {mobileNumber : number})
MATCH path = SHORTESTPATH((n)-[contacts:HAS_CONTACT]-(m)) 
RETURN contacts;

好的,谢谢你的回答。实际上当时我使用的是Cypher版本:CYPHER 3.1.*,它没有在“WHERE”子句中使用索引。但现在我已经将我的Neo4j更新到了3.2.2版本,它正在按照预期工作。+1 - Afridi

0

在这个例子中,索引实际上是与WHERE子句一起使用的:

PROFILE MATCH (n:Consumer{mobileNumber : "000000000000"}),
(m:Consumer) WHERE m.mobileNumber = "111111111111"
WITH n,m 
MATCH path = shortestPath((n)-[contacts:HAS_CONTACT]-(m)) 
RETURN contacts

使用索引作为您的第一个查询。如果您想引用多个节点,可以使用逻辑谓词,例如:

PROFILE MATCH (n:Consumer{mobileNumber : "000000000000"}),
(m:Consumer) WHERE m.mobileNumber = "111111111111" OR m.mobileNumber = "222222222222"
WITH n,m 
MATCH path = shortestPath((n)-[contacts:HAS_CONTACT]-(m)) 
RETURN contacts

使用AND代替OR

我认为当前版本的Neo4j不支持在使用IN子句查找数组时使用索引。


好的,感谢你的答复。但是我想要引用多个endNodes(这将是任意大小的列表,并且未被预定义),那么如何用索引搜索来解决呢?逻辑谓词将无法解决这个问题。我正在从Spring中使用一个endNodes列表来调用此方法。 - Afridi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接