Neo4j:集合中的WHERE子句ID(节点)缓慢

时间:2015-08-18 15:19:00

标签: neo4j

以下Cypher查询执行速度非常慢。它的轮廓看起来很好 - 有人可以指出我做错了什么。它应该根据运行每个部分所需的时间快速返回。

PROFILE 
MATCH 
   (deal2:lDeal)   <-[tr2:PARTICIPATES_IN]- (frComp:lCompany { id:2353462}) //1944 rows
MATCH
   (inter) -[tr1:WORKED_IN | PARTICIPATES_IN]-> (deal2) //58,373
WITH 
   collect(distinct id(inter)) as interCol
MATCH 
   (article:lArticle { articleId:13194153})  -[r:WRITTEN_ABOUT] ->  (Comp1:lCompany)
MATCH 
   (Comp1) -[fr1:PARTICIPATES_IN]-> (deal1:lDeal)  //6671
MATCH 
   (deal1) <-[fr2:WORKED_IN | PARTICIPATES_IN]- (inter) //135,011
WHERE 
   id(inter) in interCol
RETURN 
   inter

enter image description here

EDIT 8/18/2014 2:41 PM EST:

根据@jjaderberg的建议尝试了多种变体而没有任何性能改进:

变化1:

MATCH 
  (inter)-[:WORKED_IN|PARTICIPATES_IN]->(:lDeal)<-[:PARTICIPATES_IN]-(:lCompany {id:2353462})
WHERE 
   inter-[:WORKED_IN|PARTICIPATES_IN]->(:lDeal)<-[:PARTICIPATES_IN]-(:lCompany)<-[:WRITTEN_ABOUT]-(:lArticle {articleId:13194153})

变化2:

MATCH 
    (inter)-[:WORKED_IN|PARTICIPATES_IN]->(cDeal:lDeal)
WHERE 
    cDeal<-[:PARTICIPATES_IN]-(:lCompany {id:2353462}) 
WITH 
    inter
MATCH 
    inter-[:WORKED_IN|PARTICIPATES_IN]->(:lDeal)<-[:PARTICIPATES_IN]-(:lCompany)<-[:WRITTEN_ABOUT]-(:lArticle {articleId:13194153})
RETURN inter

变化3:

MATCH 
    (inter)-[:WORKED_IN|PARTICIPATES_IN]->(cDeal:lDeal)
WHERE 
    cDeal<-[:PARTICIPATES_IN]-(:lCompany {id:2353462}) 
WITH 
    inter
MATCH 
    inter-[:WORKED_IN|PARTICIPATES_IN]->(:lDeal)<-[:PARTICIPATES_IN]-(:lCompany)<-[:WRITTEN_ABOUT]-(:lArticle {articleId:13194153})
RETURN 
    inter

变化4:

MATCH 
    (inter)-[:WORKED_IN|PARTICIPATES_IN]->(cDeal:lDeal)
WHERE 
    cDeal<-[:PARTICIPATES_IN]-(:lCompany {id:2353462}) 
WITH 
    inter
MATCH 
    inter-[:WORKED_IN|PARTICIPATES_IN]->(aDeal:lDeal)
WHERE 
    aDeal<-[:PARTICIPATES_IN]-(:lCompany)<-[:WRITTEN_ABOUT]-(:lArticle {articleId:13194153})
RETURN 
    inter

还尝试了@ MichaelHunger的解决方案 - 配置文件仍然需要很长时间。 Per Michael的请求 - 这里有一些额外的查询配置文件:

PROFILE
MATCH 
  (inter)-[:WORKED_IN|PARTICIPATES_IN]->(deal:lDeal)<-[:PARTICIPATES_IN]-  (c:lCompany {id:2353462})
USING 
   index c:lCompany(id)
RETURN 
   count(*), count(distinct inter), count(distinct deal), count(distinct c);

enter image description here enter image description here

PROFILE
MATCH   
   inter-[:WORKED_IN|PARTICIPATES_IN]->(deal:lDeal)<-[:PARTICIPATES_IN]-(c:lCompany)<-[:WRITTEN_ABOUT]-(a:lArticle {articleId:13194153})
USING 
    INDEX a:lArticle(articleId)
RETURN 
    count(*), count(distinct inter), count(distinct deal), count(distinct a), count(distinct c);

enter image description here enter image description here

我们迄今为止所做的最好的是以下仍然很慢(COST.26370330总分数达到72828毫秒)。

MATCH 
   (comp2:lCompany {id:2353462})-[tr2:PARTICIPATES_IN]->(deal2:lDeal)<-[tr1]-(inter)-[fr2]->(deal1:lDeal)
WITH 
   DISTINCT deal1
MATCH 
   deal1<-[fr1:PARTICIPATES_IN]-(comp1:lCompany)<-[r:WRITTEN_ABOUT]-(article:lArticle)
   USING INDEX article:lArticle(articleId)
WHERE 
   article.articleId=13194153
RETURN
   DISTINCT count(comp1) as count, comp1.id ,comp1.name order by count desc, comp1.name

其中提供了以下资料:

enter image description here

3 个答案:

答案 0 :(得分:2)

(与泰德合作)限制中间结果,以下是我们提出的最佳结果(在15,222毫秒内成本总计8,354,507点击率):

MATCH (comp2:lCompany {id:2353462})-[tr2:PARTICIPATES_IN]->(deal2:lDeal)<-[tr1]-(inter)
WITH DISTINCT inter
MATCH inter-[fr2]->(deal1:lDeal)
WITH DISTINCT deal1
MATCH deal1<-[fr1:PARTICIPATES_IN]-(comp1:lCompany)<-[r:WRITTEN_ABOUT]-(article:lArticle)
USING INDEX article:lArticle(articleId)
WHERE article.articleId=13194153
RETURN DISTINCT count(comp1) as count, comp1.id ,comp1.name order by count desc, comp1.name

Execution Plan

答案 1 :(得分:1)

为了优化查询,我会将其分解为部分并进行优化。在不了解您的域名的情况下确定建议很难。如果您在http://console.neo4j.org分享一小部分样本,可能会有所帮助。除了领域理解之外,我还会看到以下两点。

1)路径上的过滤优于匹配匹配交叉点

如果我理解了您的查询,那么您将从两个不同的单个起点匹配两个不同路径上的两组节点,然后返回这些结果的交集。您的查询的人工重建可能是

  

请告诉我该公司参与的所有交易,或者更确切地说,参与或参与这些交易的所有利益相关方(仅告诉我每次交易)。
  我还有一篇关于一家或多家公司的文章,我想知道参与或参与这些公司参与的任何交易的所有利益相关者。
  开个玩笑,我不想知道所有这些利益相关者(联盟),只想知道两条路径上发生的那些(交集)!

如果这至少是一个有点公平的重建,那么你可以考虑将你的查询改为

  

请告知所有参与或参与本公司参与的任何交易的利益相关者   如果他们也参与或参与了本文所述的一家或多家公司参与的交易。

这可能会被转换回Cypher

MATCH (inter)-[:WORKED_IN|PARTICIPATES_IN]->(:lDeal)-[:PARTICIPATES_IN]->(:lCompany {id:2353462})
WHERE inter-[:WORKED_IN|PARTICIPATES_IN]->(:lDeal)<-[:PARTICIPATES_IN]-(:lCompany)<-[:WRITTEN_ABOUT]-(:lArticle {articleId:13194153})

显着的区别在于使用其中一个路径作为WHERE子句中的过滤器,而不是进行额外的MATCH和交叉操作。你可能会发现在MATCHWHERE之间切换模式也会影响基数的表现,但我不确定会有多大差异。

2)匹配匹配b与匹配a,b

将匹配模式分成不同的MATCH子句,而不是在一个MATCH子句中用逗号分隔它们之间存在很大的区别。区别在于逗号分隔列表会将模式视为相同模式的一部分,尽管可能是不相交的模式,并且会在遍历时强制执行关系唯一性。使用单独的MATCH子句中的模式,可以单独处理模式,并且可以再次遍历相同的关系。也许你建议将模式分开。但是,由于您对节点ID的集合执行了DISTINCT操作,因此您可能需要重新访问并确保不同的MATCH子句是您真正想要的。在http://console.neo4j.org

的默认图表上查看以下两个查询
MATCH (n:Crew { name:"Neo" })-[:KNOWS|LOVES]->(m)
MATCH n-[:LOVES]->o
RETURN n,m,o
----
n                       m                           o
(0:Crew {name:"Neo"})   (1:Crew {name:"Morpheus"})  (2:Crew {name:"Trinity"})
(0:Crew {name:"Neo"})   (2:Crew {name:"Trinity"})   (2:Crew {name:"Trinity"})

MATCH (n:Crew { name:"Neo" })-[:KNOWS|LOVES]->(m), n-[:LOVES]->o
RETURN n,m,o
----
n                        m                            o
(0:Crew {name:"Neo"})    (1:Crew {name:"Morpheus"})   (2:Crew {name:"Trinity"})

不同之处在于,允许第一个查询(具有单独的MATCH子句)重用第一个查询中匹配的关系,因此匹配次数多于第二个查询。您可以在手册中的section 8.4处阅读相关内容。

上面建议的查询,将所有匹配拉成一个MATCH子句,如果确实是低效率的原因,也会处理这个问题。

*)标识符

使用上面我的查询建议中表达的模式,不再需要大多数标识符。我放弃了这些,不是因为性能,而是因为我认为它使查询更具可读性,而且我认为这也是一种效率。

答案 2 :(得分:0)

您尝试的是一种SQL样式的连接。

请改为尝试:

MATCH 
  (inter)-[:WORKED_IN|PARTICIPATES_IN]->(:lDeal)<-[:PARTICIPATES_IN]-(:lCompany {id:2353462})
using index c:lCompany(id)

WITH distinct inter

MATCH   inter-[:WORKED_IN|PARTICIPATES_IN]->(:lDeal)<-[:PARTICIPATES_IN]-(:lCompany)<-[:WRITTEN_ABOUT]-(a:lArticle {articleId:13194153})
USING INDEX a:lArticle(articleId)
RETURN inter;

它减少了中间结果的数量,即你有多少不同的inter,以减少后续查询部分必须完成的工作量。

看一下你有多少行与总行数也总是很有趣。

请分享数字和计划,这两个查询返回:

PROFILE
MATCH 
      (inter)-[:WORKED_IN|PARTICIPATES_IN]->(deal:lDeal)<-[:PARTICIPATES_IN]-(c:lCompany {id:2353462})
using index c:lCompany(id)
RETURN count(*), count(distinct inter), count(distinct deal), count(distinct c);


PROFILE
    MATCH   inter-[:WORKED_IN|PARTICIPATES_IN]->(deal:lDeal)<-[:PARTICIPATES_IN]-(c:lCompany)<-[:WRITTEN_ABOUT]-(a:lArticle {articleId:13194153})
    USING INDEX a:lArticle(articleId)
    RETURN count(*), count(distinct inter), count(distinct deal), count(distinct a), count(distinct c);