关于 neo4j:降低 Cypher Query 的成本

Reducing the cost of Cypher Query

我有一个简单的数据库,我用它来分析特定组的 Twitter 数据。

数据模型为:

(:Person)-[:TWEETS_TO]->(:Twitter_Account)

(:Twitter_Account)-[:FOLLOWS]->(:Twitter_Account)

(:Person) 节点只有 500 多一点,但 (:Twitter_Account) 节点大约有 500,000 个。换句话说,大多数 (:Twitter_Account) 与人没有联系。

我想计算关注关系的数量,但仅在与人相关的 500 个左右的 Twitter 帐户中。环顾四周,我发现了这篇 neo4j 博客文章和这篇 SO 文章,其中建议了这样的查询:

1
2
3
4
5
MATCH (p:Person)-[:TWEETS_TO]->(t1:Twitter_Account)
WITH t1,
size((t1)-[:FOLLOWS]->(:Twitter_Account)<-[:TWEETS_TO]-(:Person))
AS following
RETURN t1, following ORDER BY following LIMIT 5

分析给出:

Cypher version: CYPHER 3.2, planner: COST, runtime: INTERPRETED. 2938092 total db hits in 1356 ms.

如您所见,它相对较快,但我的直觉认为应该有办法编写查询,而无需太多数据库命中,因为我们只查看易于定义的数据的一小部分。我尝试过的所有其他方法(例如首先匹配两个 twitter 帐户)导致笛卡尔乘积比上面的要慢得多。

有没有办法在不查看每个 Twitter 帐户的情况下计算这些关系?


您应该只需要对 Twitter_Account 节点(由任何 Person"拥有")执行一次数据库搜索。

例如:

1
2
3
4
5
6
7
8
MATCH (:Person)-[:TWEETS_TO]->(t1:Twitter_Account)
WITH COLLECT(t1) AS accts
UNWIND accts AS acct
OPTIONAL MATCH (acct)-[:FOLLOWS]->(t2)
WHERE t2 IN accts
RETURN acct, COUNT(t2) AS following
ORDER BY following
LIMIT 5

在这个查询中,我们找到了Person"拥有"的所有Twitter_Account节点,并将该集合保存在accts中。然后,我们 UNWIND 该集合以查找每个拥有帐户 (acct) 后跟有多少拥有帐户 (t2)。最后,我们返回每个拥有的 acct 以及它所遵循的拥有帐户的数量。 (如果您只想返回至少跟随 1 个自有帐户的自有帐户,请将 OPTIONAL MATCH 替换为 MATCH)。


您可能需要考虑为与人相关的 :Twitter_Accounts 添加一个单独的标签,以使您以后的查询更容易。

1
2
3
MATCH (t:Twitter_Account)
WHERE exists(()-[:TWEETS_TO]->(t))
SET t:Connected_Account

如果您的图表需要处理更新,那么您需要确保添加了新帐户,检查是否连接了 :Person 并相应地添加标签。

一旦到位,您稍后的查询将变为:

1
2
3
4
5
MATCH (t1:Connected_Account)
WITH t1, size((t1)-[:FOLLOWS]->(:Connected_Account)) as following
RETURN t1, following
ORDER BY following
LIMIT 5

如果只有 500 个 :Connected_Account 节点,那么这应该会大大减少您的数据库命中并加快您的查询速度。