Neo4j遍历API与Cypher的比较

16

我何时应该选择Neo4j的遍历框架而不是Cypher?

例如,对于一个朋友的朋友查询,我会编写以下Cypher查询:

MATCH (p:Person {pid:'56'})-[:FRIEND*2..2]->(fof) 
WHERE NOT (p)-[:FRIEND]->(fof) 
RETURN fof.pid

对应的遍历实现需要对friends_at_depth_1friends_at_depth_2进行两次遍历(或者使用核心API调用来获取关系),并使用纯Java构造找到这两个集合的差异,在遍历描述之外。如果我说错了,请纠正我。

你有什么想法吗?

2个回答

29
记住Cypher与遍历API的主要区别在于,遍历API是一种访问图形的命令式方式,而Cypher是一种访问图形的声明式方式。您可以在此处阅读更多有关这种差异的信息但简要来说,在命令式访问中,您正在告诉数据库如何准确地获取图形。(例如,我想进行深度优先搜索,在这些分支处进行修剪,停止当我到达某些节点时等)。在声明式图查询中,您正在指定您想要什么,并将如何获取它的所有方面委托给Cypher实现。
在您的查询中,我会稍微修改一下:
MATCH (p:Person {pid:'56'})-[:FRIEND*2..2]->(fof) 
WHERE NOT (p)-[:FRIEND]->(fof) AND
      p <> fof
RETURN fof.pid

我添加了确保 p<>fof,因为友情链接可能会返回到原始人。
要在遍历器中执行此操作,您不需要两个遍历器,只需要一个。您只需遍历 FRIEND 关系,停止于深度 2,并累积结果集。
现在,我将尝试论证,除非您有非常特定的情况,否则几乎总是应该使用 Cypher,而不是使用遍历 API。以下是我的理由:
  1. 声明式查询非常强大,因为它使您免于考虑如何实现。您所需要知道的只是您想要什么。这意味着您花费更多时间关注代码应该做什么,而不是在实现细节上浪费时间。
  2. Cypher查询执行器正在不断改进(版本2.2将拥有基于成本的计划),当然他们也付出了很多努力,以确保Cypher利用所有可用的索引。除非您在编写遍历时非常小心,否则对于许多查询,Cypher可能比遍历更好地找到您的数据。
  3. Cypher比编写自己的遍历代码少得多,后者通常需要您实现某些类来执行特定的停止条件等。
  4. 目前,Cypher可以在嵌入式数据库或服务器上运行。如果要运行遍历,则无法将其远程发送到服务器进行执行;最多可以编写一个服务器扩展来执行遍历。因此,我认为在目前情况下,Cypher更加灵活。

好的,那么什么时候应该使用遍历?我知道两个主要案例(其他人可能会提出其他建议)

有时您需要在遍历每个内容时执行复杂的自定义java代码操作。在这种情况下,您将遍历器用作某种“访问函数”,有时根据运行在节点上的Java的性质,使用遍历比Cypher更方便。 有时您的性能要求非常严格,需要手动遍历图形,因为您可以利用遍历器中的一些图形结构方面来加速它,而Cypher无法利用此优势。尽管确实会出现这种情况,但首先采取此方法通常不是一个好主意。

5
对Cypher进行PROFILE分析可以帮助我们确定查询是否在底层执行了一些浪费的操作。 - Michael Hunger
1
您的链接已损坏。我用一次负评扣留了您的一些积分。赎金是修复链接,而非删除链接。 - Guy Coder
1
我更新了链接。这是一个重要的讨论点,人们可以从中学到很多东西,所以值得更新这个2015年的答案,而不是为了那些虚拟的网络积分。 :) - FrobberOfBits

8

书籍摘录

核心API,遍历框架还是Cypher?

核心API允许开发人员微调他们的查询,以使其与底层图表现出高度相关性。一个写得好的核心API查询通常比任何其他方法都要快。缺点是这种查询可能很冗长,需要相当大的开发人员努力。此外,它们与底层图形的高亲和性使它们与其结构紧密耦合。当图形结构发生变化时,它们经常会失效。Cypher可以更容忍结构性变化——诸如变长路径之类的东西有助于减轻变化和改变。

遍历框架比核心API更松散耦合(因为它允许开发人员声明信息目标),并且不那么冗长,因此使用遍历框架编写的查询通常需要比使用核心API编写的等效查询少得多的开发人员努力。然而,由于它是一个通用框架,遍历框架往往比写得好的核心API查询执行得稍微差一些。

如果我们发现自己处于使用核心API或遍历框架(因此避免使用Cypher及其优势)的不寻常情况下,那是因为我们正在处理需要精细制作算法的边缘案例,这些算法不能有效地使用Cypher的模式匹配表达。在核心API和遍历框架之间进行选择是一个问题,即是否选择遍历框架的高抽象度/低耦合足够,还是核心API的接近金属/更高耦合实际上是必要的,以正确地实现算法并符合我们的性能要求。

参考: 图形数据库,连接数据的新机会, p161

Cypher是什么?

开发人员文档中的定义如下:Cypher是一种声明性的、受SQL启发的语言,用ASCII艺术语法可视化地描述图形中的模式。

你可以在这里找到更多信息。

核心API实际上是什么?

我发现这个页面有以下句子:

除了面向对象的图数据库API,使用NodeRelationshipPath对象,它还提供高度可定制的、高速的遍历和图算法实现。

因此,从实际角度来看,核心API处理基本对象,如属于org.neo4j.graphdb包的NodeRelationship等。
你可以在开发人员指南中找到更多信息。

实际上什么是遍历API?

遍历 API 添加了更多的接口到核心 API 中,以帮助我们方便地执行遍历,而不是从头开始编写整个遍历逻辑。这些接口包含在 org.neo4j.graphdb.traversal 包中。

您可以在 其开发者指南 中找到更多信息。

所有三者之间的关系

根据 此答案 所述:

遍历 API 建立在核心 API 上,Cypher 建立在遍历 API 上。因此,您在 Cypher 中可以做任何事情,在其他两种方法中也可以完成。

使用所有三种方法完成相同示例

此教程 来自 2012 年,展示了使用所有三种方法执行相同任务,其中核心 API 是最快的。它包括 Andres Taylor 的一句话引用:

Cypher刚刚一岁。由于开发人员非常受限,我们必须非常挑剔地选择我们要处理的内容。在第一阶段,我们的重点是探索语言,了解用户如何使用查询语言,并将功能集扩展到合理水平。
我相信Cypher是我们未来的API。我知道你可以通过手写查询轻松地超越Cypher。就像创建的每种语言一样,在开始时,您总是可以通过手写来比编译器做得更好,但最终编译器会赶上来。

文章结论:
到目前为止,我只使用Java Core API与neo4j一起工作,我将继续这样做。
如果您处于高速场景中(我认为每个Web应用程序都是一个),您应该真正考虑切换到neo4j Java core API来编写查询。它可能不像Cypher或遍历器框架那样漂亮,但速度提升是值得的。
此外,我个人喜欢在自己遍历核心时拥有的控制量。

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