当查询具有WHERE和WITH子句时的Neo4j Cypher执行计划

时间:2018-07-24 16:54:26

标签: neo4j cypher

我有一个Neo4j图形数据库,用于存储人员关系和节点。我必须写一个能找到家庭和办公室地址的密码 资源(或员工)及其empId和名称。 这是必需的,以便“人员配备解决方案”可以根据他们的家庭位置以及靠近办公室的位置来配备资源。

MATCH (employee:Employee) <-[:ADDRESS_TO_EMPLOYEE]- (homeAddress:HomeAddress) 
WHERE employee.id = '70' 
WITH  employee, homeAddress 
MATCH (employee)-[:EMPLOYEE_TO_OFFICEADDRESS]->(officeAddress:OfficeAddress) 
RETURN employee.empId, employee.name,  
homeAddress.street, homeAddress.area, homeAddress.city,  
officeAddress.street, officeAddress.area, officeAddress.city

此密码返回所需的结果。

但是,如果我将WHERE条件放在最后一个位置,就在RETURN子句之前。

MATCH (employee:Employee) <-[:ADDRESS_TO_EMPLOYEE]- (homeAddress:HomeAddress) 
WITH  employee, homeAddress  
MATCH (employee)-[:EMPLOYEE_TO_OFFICEADDRESS]->(officeAddress:OfficeAddress) 
WHERE employee.id = '70' 
RETURN employee.empId, employee.name,  
homeAddress.street, homeAddress.area, homeAddress.city,  
officeAddress.street, officeAddress.area, officeAddress.city 

它再次给了我相同的结果。

那么在这两种情况下,哪个查询查询计划是最优化的呢?我的意思是数据库命中次数和返回的记录数相同。

现在,如果我删除WITH子句,

MATCH (employee:Employee) <-[:ADDRESS_TO_EMPLOYEE]- 
(homeAddress:HomeAddress),
MATCH (employee)-[:EMPLOYEE_TO_OFFICEADDRESS]->(officeAddress:OfficeAddress) 
WHERE employee.id = '70' 
RETURN employee.empId, employee.name, 
homeAddress.street, homeAddress.area, homeAddress.city, 
officeAddress.street, officeAddress.area, officeAddress.city

然后结果相同,执行计划也相同。

在这种情况下,我真的需要WITH吗?

任何帮助将不胜感激。

2 个答案:

答案 0 :(得分:1)

首先,您可以使用Profile and Explain来获得查询的性能。但是,只要您在想要的时间内获得想要的结果,密码就不会太在意,因为行为会根据数据库中运行的密码计划器(版本)而改变。因此,只要密码通过了单元测试和负载测试,其余的都没关系(假设测试相当准确)。

第二,通常,少即是多。想象一下,您必须阅读自己的密码,然后在纸质打印件上自行查找信息。 MATCH (officeAddress:OfficeAddress)<-[:EMPLOYEE_TO_OFFICEADDRESS]-(employee:Employee {id:'70'})<-[:ADDRESS_TO_EMPLOYEE]-(homeAddress:HomeAddress)难道不是很容易告诉您所要查找的内容吗? Cypher计划者越容易阅读您想要的内容,Cypher计划者就越有可能计划最有效的查找策略。另外,将WHERE子句保持在相关匹配范围附近也有助于计划者。因此,尝试使密码尽可能简单,同时仍能准确满足您的需求。

在您的Cypher中,唯一重要的部分是WITH。 WITH将在密码中产生逻辑中断,并更改变量的范围。由于您对with并没有做任何事情,因此最好将其删除。在这种情况下,它可能产生的唯一副作用是诱使Cypher进行比第一次比赛所需的工作更多的工作,以便稍后对其进行过滤。如果一个员工的家庭住址超过1个,则WITH employee, COLLECT(homeAddress) as homeAdress会将每个员工的匹配数减少到1行,从而使下一场匹配更便宜,但是由于我确定匹配的双方都只能产生1结果,计划者先做什么并不重要。 (通常,您可以使用with将结果汇总到更少的行,以使其余密码更便宜。在这种情况下,该方法不适用)

答案 1 :(得分:1)

  1. 您应该始终在查询中尽早放置WHERE子句。这样可以过滤掉其余查询不需要处理的数据,从而避免了不必要的工作。

  2. 您应该避免编写一个WITH子句,该子句只是将所有定义的变量传递给 all (并且在语法上不是必需的),因为它本质上是空操作。计划者要浪费时间,使Cypher代码更难理解。

这个简单的查询版本应产生相同的查询计划:

MATCH (officeAddress:OfficeAddress)<-[:EMPLOYEE_TO_OFFICEADDRESS]-(employee:Employee)<-[:ADDRESS_TO_EMPLOYEE]-(homeAddress:HomeAddress) 
WHERE employee.id = '70' 
RETURN
  employee.empId, employee.name,  
  homeAddress.street, homeAddress.area, homeAddress.city,  
  officeAddress.street, officeAddress.area, officeAddress.city

以下版本(使用map projection语法)更加简单(具有类似的查询计划)。

MATCH (officeAddress:OfficeAddress)<-[:EMPLOYEE_TO_OFFICEADDRESS]-(employee:Employee)<-[:ADDRESS_TO_EMPLOYEE]-(homeAddress:HomeAddress) 
WHERE employee.id = '70' 
RETURN
  employee{.empId, .name},  
  homeAddress{.street, .area, .city},  
  officeAddress{.street, .area, .city}

上述查询的结果具有不同的结构,

╒═══════════════════════════╤══════════════════════════════════════╤══════════════════════════════════════╕
│"employee"                 │"homeAddress"                         │"officeAddress"                       │
╞═══════════════════════════╪══════════════════════════════════════╪══════════════════════════════════════╡
│{"name":"sam","empId":"70"}│{"area":1,"city":"foo","street":"123"}│{"area":2,"city":"bar","street":"345"}│
└───────────────────────────┴──────────────────────────────────────┴──────────────────────────────────────┘