Neo4j cypher查询中的性能

Øyv*_*ind 1 neo4j cypher

我有以下密码查询:

MATCH (country:Country { name: 'norway' }) <- [:LIVES_IN] - (person:Person)
WITH person
MATCH (skill:Skill { name: 'java' }) <- [:HAS_SKILL] - (person)
WITH person 
OPTIONAL MATCH (skill:Skill { name: 'javascript' }) <- [rel:HAS_SKILL] - (person)
WITH person, CASE WHEN skill IS NOT NULL THEN 1 ELSE 0 END as matches 
ORDER BY matches DESC 
LIMIT 50 
RETURN COLLECT(ID(person)) as personIDs
Run Code Online (Sandbox Code Playgroud)

添加更多节点时似乎表现更差.现在只有5000个Person节点(Person节点可以与Skill节点有多个HAS_SKILL关系).现在,执行查询大约需要180毫秒,但添加另外1000个具有关系的Person节点会为查询添加30-40毫秒.我们计划拥有数百万个Person节点,因此每1000人添加40毫秒是不行的.

我在查询中使用参数而不是上面查询中的'norway','java','javascript'.我已经创建了索引:国家(名称)和:技能(名称).

我的查询目标是匹配生活在指定国家(挪威)的每个人,这些人也具有'java'技能.如果此人也具有技能'javascript',则应在结果中命令更高.

如何重构查询以提高性能?

编辑:

如果我转出,那么:Country节点似乎也存在问题

MATCH (country:Country { name: 'norway' }) <- [:LIVES_IN] - (person:Person)
Run Code Online (Sandbox Code Playgroud)

MATCH (city:City { name: 'vancouver' }) <- [:LIVES_IN] - (person:Person)
Run Code Online (Sandbox Code Playgroud)

查询时间会下降到大约15-50毫秒,具体取决于我查询的城市.添加更多节点时,查询时间仍然明显增加.

编辑2:

当第一个匹配子句中有很多行时,我似乎查询时间增加了很多.因此,如果我首先切换查询以匹配技能节点,则查询时间会大幅减少.该查询是API的一部分,它是动态创建的,我不知道哪个匹配子句将返回最小的行数.当数据库增长时,每个匹配子句中可能还会有更多行.

编辑3

我已经从答案中做了一些测试,现在我有以下查询:

MATCH (country:Country { name: 'norway'}) 
WITH country 
MATCH (country) <- [:LIVES_IN] - (person:Person) 
WITH person 
MATCH (person) - [:HAS_SKILL] -> (skill:Skill) WHERE skill.name = 'java' 
MATCH (person) - [:MEMBER_OF_GROUP] -> (group:Group) WHERE group.name = 'some_group_name' 
RETURN DISTINCT ID(person) as id 
LIMIT 50 
Run Code Online (Sandbox Code Playgroud)

这仍然存在性能问题,首先匹配所有技能等可能更好,比如Country节点?查询也可以变得更大,我可能必须添加匹配多个技能,组,项目等.

编辑4

我略微修改了查询,看起来这样做了.我现在首先匹配所有需要的技能,公司,团体,国家等.然后在查询中使用它们.在分析器中,这将数据库命中数从700k减少到188或其他.它与我的原始查询(不同的标记节点等)略有不同,但它解决了同样的问题.我想这可以通过首先匹配具有最少关系等的节点来进一步改进,以减少节点数量开始.我稍后会做一些测试!

MATCH (company:Company { name: 'relinkgroup' }) 
WITH company 
MATCH (skill:Skill { name: 'java' }) 
WITH company, skill 
MATCH (skill2:Skill { name: 'ajax' }) 
WITH company, skill, skill2 
MATCH (country:Country { name: 'canada' }) 
WITH company, skill, skill2, country 
MATCH (company) <- [:WORKED_AT] - (person:Person) 
, (person) - [:HAS_SKILL] -> (skill) 
, (person) - [:HAS_SKILL] -> (skill2) 
, (person) - [:LIVES_IN] -> (country) 
RETURN DISTINCT ID(person) as id 
LIMIT 50
Run Code Online (Sandbox Code Playgroud)

Chr*_*sen 6

对于查询的第一行,执行必须查找国家/地区之间的所有可能路径.限制您的初始匹配(从而为遍历定义更准确的起点)您将赢得一些表现.

而不是

MATCH (country:Country { name: 'norway' }) <- [:LIVES_IN] - (person:Person)
Run Code Online (Sandbox Code Playgroud)

尝试分两步:

MATCH (country:Country { name: 'norway' })
WITH country
MATCH (country)<-[:LIVES_IN]-(person:Person)
WITH person
Run Code Online (Sandbox Code Playgroud)

举个例子,我将在neo4j控制台中使用简单的电影应用程序:http://console.neo4j.org/

查找与您相同的查询以查找知道密码的人:

 MATCH (n:Crew)-[r:KNOWS]-m WHERE n.name='Cypher' RETURN n, m
Run Code Online (Sandbox Code Playgroud)

执行计划将是:

Execution Plan
ColumnFilter
  |
  +Filter
    |
    +TraversalMatcher

+------------------+------+--------+-------------+----------------------------------------+
|         Operator | Rows | DbHits | Identifiers |                                  Other |
+------------------+------+--------+-------------+----------------------------------------+
|     ColumnFilter |    2 |      0 |             |                      keep columns n, m |
|           Filter |    2 |     14 |             | Property(n,name(0)) == {  AUTOSTRING0} |
| TraversalMatcher |    7 |     16 |             |                                m, r, m |
+------------------+------+--------+-------------+----------------------------------------+

Total database accesses: 30
Run Code Online (Sandbox Code Playgroud)

并通过定义一个准确的起点:

 MATCH (n:Crew) WHERE n.name='Cypher' WITH n MATCH (n)-[:KNOWS]-(m) RETURN n,m
Run Code Online (Sandbox Code Playgroud)

导致以下执行计划:

Execution Plan
ColumnFilter
  |
  +SimplePatternMatcher
    |
    +Filter
      |
      +NodeByLabel

+----------------------+------+--------+-------------------+----------------------------------------+
|             Operator | Rows | DbHits |       Identifiers |                                  Other |
+----------------------+------+--------+-------------------+----------------------------------------+
|         ColumnFilter |    2 |      0 |                   |                      keep columns n, m |
| SimplePatternMatcher |    2 |      0 | m, n,   UNNAMED53 |                                        |
|               Filter |    1 |      8 |                   | Property(n,name(0)) == {  AUTOSTRING0} |
|          NodeByLabel |    4 |      5 |              n, n |                                  :Crew |
+----------------------+------+--------+-------------------+----------------------------------------+

Total database accesses: 13
Run Code Online (Sandbox Code Playgroud)

正如您所看到的,第一种方法使用遍历模式,这对于节点数量来说是非常昂贵的,并且您在图上进行了全局匹配.

第二个使用标签索引使用显式起点.

编辑

对于技能部分,我会做这样的事情,如果你有一些测试数据提供它可能对测试更有帮助:

MATCH (country:Country { name: 'norway' })
WITH country
MATCH (country)<-[:LIVES_IN]-(person:Person)-[:HAS_SKILL]->(skill:Skill)
WHERE skill.name = 'java'
WITH person
OPTIONAL MATCH (person)-[:HAS_SKILL]->(skillb:Skill) WHERE skillb.name = 'javascript'
WITH person, skillb
Run Code Online (Sandbox Code Playgroud)

不需要全局查找,因为他已经找到了人,他只是遵循"HAS_SKILL"关系并过滤skill.name值

编辑2:

关于你的上一次编辑,可能是查询的最后一部分:

MATCH (company) <- [:WORKED_AT] - (person:Person) 
, (person) - [:HAS_SKILL] -> (skill) 
, (person) - [:HAS_SKILL] -> (skill2) 
, (person) - [:LIVES_IN] -> (country) 
Run Code Online (Sandbox Code Playgroud)

可以写得更好:

MATCH (person:Person)-[:WORKED_AT]->(company)
WHERE (person)-[:HAS_SKILL]->(skill)
AND (person)-[:HAS_SKILL]->(skill2)
AND (person)-[:LIVES_IN]->(country)
Run Code Online (Sandbox Code Playgroud)