查询语言Cypher最初是专门为图形 DBMS Neo4j开发的 。 Cypher的目标是为图形数据库提供一种人类可读的SQL数据库查询语言。 如今,Cypher得到了几种图形DBMS的支持。 OpenCypher的创建是为了标准化Cypher。
在浏览器中使用Neo4j的基础知识文章中描述了使用Neo4j DBMS的基础知识 。
为了使自己熟悉Cypher,请考虑一本从I. Bratko的经典Prolog教科书中借来的家谱的例子。 此示例将说明如何向图添加节点和链接,如何为它们分配标签和属性以及如何提出问题。

因此,让我们在下图中显示一棵家谱。

让我们看看如何在Cypher中形成相应的图形:
CREATE (pam:Person {name: "Pam"}), (tom:Person {name: "Tom"}), (kate:Person {name: "Kate"}), (mary:Person {name: "Mary"}), (bob:Person {name: "Bob"}), (liz:Person {name: "Liz"}), (dick:Person {name: "Dick"}), (ann:Person {name: "Ann"}), (pat:Person {name: "Pat"}), (jack:Person {name: "Jack"}), (jim:Person {name: "Jim"}), (joli:Person {name: "Joli"}), (pam)-[:PARENT]->(bob), (tom)-[:PARENT]->(bob), (tom)-[:PARENT]->(liz), (kate)-[:PARENT]->(liz), (mary)-[:PARENT]->(ann), (bob)-[:PARENT]->(ann), (bob)-[:PARENT]->(pat), (dick)-[:PARENT]->(jim), (ann)-[:PARENT]->(jim), (pat)-[:PARENT]->(joli), (jack)-[:PARENT]->(joli)
将数据添加到图形DBMS的CREATE请求由两部分组成:添加节点以及在它们之间添加链接。 在此请求的框架内,将为每个要添加的节点分配一个名称,然后将其用于创建链接。 节点和通信可以存储文档。 在我们的例子中,节点包含带有名称字段的文档,而文档链接不包含。 还可以标记节点和链接。 在我们的例子中,节点被分配标签为Person,链接为PARENT。 请求中的标签在名称前用冒号突出显示。
因此,Neo4j告诉我们: Added 12 labels, created 12 nodes, set 12 properties, created 11 relationships, completed after 9 ms.
让我们看看我们得到了什么:
MATCH (p:Person) RETURN p

没有人禁止我们编辑结果图的外观:

该怎么办? 您可以验证,例如,Pam是
鲍勃的父母:
MATCH ans = (:Person {name: "Pam"})-[:PARENT]->(:Person {name: "Bob"}) RETURN ans
我们得到相应的子图:

但是,这并不是我们真正需要的。 更改请求:
MATCH ans = (:Person {name: "Pam"})-[:PARENT]->(:Person {name: "Bob"}) RETURN ans IS NOT NULL
现在,我们得到了回应。 如果我们问:
MATCH ans = (:Person {name: "Pam"})-[:PARENT]->(:Person {name: "Liz"}) RETURN ans IS NOT NULL
我们什么都不会得到。。。在这里您需要添加单词OPTIONAL
,然后如果
结果为空,然后返回false
:
OPTIONAL MATCH ans = (:Person {name: "Pam"})-[:PARENT]->(:Person {name: "Liz"}) RETURN ans IS NOT NULL
现在我们得到预期的答案为false
。
接下来,您可以看到谁是谁的父母:
MATCH (p1:Person)-[:PARENT]->(p2:Person) RETURN p1, p2
使用“ Text
打开结果选项卡,然后查看包含两列的表:
╒═══════════════╤═══════════════╕ │"p1" │"p2" │ ╞═══════════════╪═══════════════╡ │{"name":"Pam"} │{"name":"Bob"} │ ├───────────────┼───────────────┤ │{"name":"Tom"} │{"name":"Bob"} │ ├───────────────┼───────────────┤ │{"name":"Tom"} │{"name":"Liz"} │ ├───────────────┼───────────────┤ │{"name":"Kate"}│{"name":"Liz"} │ ├───────────────┼───────────────┤ │{"name":"Mary"}│{"name":"Ann"} │ ├───────────────┼───────────────┤ │{"name":"Bob"} │{"name":"Ann"} │ ├───────────────┼───────────────┤ │{"name":"Bob"} │{"name":"Pat"} │ ├───────────────┼───────────────┤ │{"name":"Dick"}│{"name":"Jim"} │ ├───────────────┼───────────────┤ │{"name":"Ann"} │{"name":"Jim"} │ ├───────────────┼───────────────┤ │{"name":"Pat"} │{"name":"Joli"}│ ├───────────────┼───────────────┤ │{"name":"Jack"}│{"name":"Joli"}│ └───────────────┴───────────────┘
我们还能学到什么? 例如,谁是属的特定成员的父母,例如Bob:
MATCH (parent:Person)-[:PARENT]->(:Person {name: "Bob"}) RETURN parent.name
╒═════════════╕ │"parent.name"│ ╞═════════════╡ │"Tom" │ ├─────────────┤ │"Pam" │ └─────────────┘
在这里,作为答案,我们不请求整个节点,而仅请求其特定属性。
我们还可以找出鲍勃的孩子是谁:
MATCH (:Person {name: "Bob"})-[:PARENT]->(child:Person) RETURN child.name
╒════════════╕ │"child.name"│ ╞════════════╡ │"Ann" │ ├────────────┤ │"Pat" │ └────────────┘
我们也可以问谁有孩子:
MATCH (parent:Person)-[:PARENT]->(:Person) RETURN parent.name
╒═════════════╕ │"parent.name"│ ╞═════════════╡ │"Pam" │ ├─────────────┤ │"Tom" │ ├─────────────┤ │"Tom" │ ├─────────────┤ │"Kate" │ ├─────────────┤ │"Mary" │ ├─────────────┤ │"Bob" │ ├─────────────┤ │"Bob" │ ├─────────────┤ │"Dick" │ ├─────────────┤ │"Ann" │ ├─────────────┤ │"Pat" │ ├─────────────┤ │"Jack" │ └─────────────┘
嗯,汤姆和鲍勃相遇了两次,解决了这个问题:
MATCH (parent:Person)-[:PARENT]->(:Person) RETURN DISTINCT parent.name
我们在查询的返回结果中添加了单词DISTINCT
,这意味着
与SQL中的类似。
╒═════════════╕ │"parent.name"│ ╞═════════════╡ │"Pam" │ ├─────────────┤ │"Tom" │ ├─────────────┤ │"Kate" │ ├─────────────┤ │"Mary" │ ├─────────────┤ │"Bob" │ ├─────────────┤ │"Dick" │ ├─────────────┤ │"Ann" │ ├─────────────┤ │"Pat" │ ├─────────────┤ │"Jack" │ └─────────────┘
您可能还会注意到Neo4j按照在CREATE
请求中输入的顺序将父母退还给我们。
现在让我们问谁是祖父或祖母:
MATCH (grandparent:Person)-[:PARENT]->()-[:PARENT]->(:Person) RETURN DISTINCT grandparent.name
太好了,就是这样:
╒══════════════════╕ │"grandparent.name"│ ╞══════════════════╡ │"Tom" │ ├──────────────────┤ │"Pam" │ ├──────────────────┤ │"Bob" │ ├──────────────────┤ │"Mary" │ └──────────────────┘
在查询模板中,我们使用了一个中间的无名节点()
和两个类型为PARENT
关系。
现在我们找出谁是父亲。 父亲是一个有孩子的男人。 因此,我们缺乏有关该男子是谁的数据。 因此,要确定谁是母亲,您需要知道谁是女人。 将相关信息添加到我们的数据库中。 为此,我们将标签Male
和Female
分配给现有节点。
MATCH (p:Person) WHERE p.name IN ["Tom", "Dick", "Bob", "Jim", "Jack"] SET p:Male
MATCH (p:Person) WHERE p.name IN ["Pam", "Kate", "Mary", "Liz", "Ann", "Pat", "Joli"] SET p:Female
让我们解释一下我们在这里所做的事情:我们选择了所有标有Person
节点,并选中了它们
根据在方括号中指定的给定列表指定name
属性,并分别为相应的节点分配标签Male
或Female
。
检查:
MATCH (p:Person) WHERE p:Male RETURN p.name
╒════════╕ │"p.name"│ ╞════════╡ │"Tom" │ ├────────┤ │"Bob" │ ├────────┤ │"Dick" │ ├────────┤ │"Jack" │ ├────────┤ │"Jim" │ └────────┘
MATCH (p:Person) WHERE p:Female RETURN p.name
╒════════╕ │"p.name"│ ╞════════╡ │"Pam" │ ├────────┤ │"Kate" │ ├────────┤ │"Mary" │ ├────────┤ │"Liz" │ ├────────┤ │"Ann" │ ├────────┤ │"Pat" │ ├────────┤ │"Joli" │ └────────┘
我们请求所有标记为Person
节点,每个节点也分别标记为Male
或Female
。 但是我们可以使我们的请求有所不同:
MATCH (p:Person:Male) RETURN p.name MATCH (p:Person:Female) RETURN p.name
让我们再次看一下我们的图:

Neo4j Browser根据Male和Mark的标记以两种不同的颜色绘制节点
女款
好的,现在我们可以从数据库中查询所有父亲:
MATCH (p:Person:Male)-[:PARENT]->(:Person) RETURN DISTINCT p.name
╒════════╕ │"p.name"│ ╞════════╡ │"Tom" │ ├────────┤ │"Bob" │ ├────────┤ │"Dick" │ ├────────┤ │"Jack" │ └────────┘
和母亲:
MATCH (p:Person:Female)-[:PARENT]->(:Person) RETURN DISTINCT p.name
╒════════╕ │"p.name"│ ╞════════╡ │"Pam" │ ├────────┤ │"Kate" │ ├────────┤ │"Mary" │ ├────────┤ │"Ann" │ ├────────┤ │"Pat" │ └────────┘
现在让我们建立一个兄弟姐妹关系。 X是Y的兄弟,
如果他是男人,并且对于X和Y,至少有一个共同的父母。 同样的
关系姐姐。
兄弟对密码的态度:
MATCH (brother:Person:Male)<-[:PARENT]-()-[:PARENT]->(p:Person) RETURN brother.name, p.name
╒══════════════╤════════╕ │"brother.name"│"p.name"│ ╞══════════════╪════════╡ │"Bob" │"Liz" │ └──────────────┴────────┘
Cypher的姐妹态度:
MATCH (sister:Person:Female)<-[:PARENT]-()-[:PARENT]->(p:Person) RETURN sister.name, p.name
╒═════════════╤════════╕ │"sister.name"│"p.name"│ ╞═════════════╪════════╡ │"Liz" │"Bob" │ ├─────────────┼────────┤ │"Ann" │"Pat" │ ├─────────────┼────────┤ │"Pat" │"Ann" │ └─────────────┴────────┘
因此,我们可以找出谁是谁的父母,以及谁是谁的祖父或祖母。 但是,更远的祖先呢? 有曾祖父,曾曾祖父等等? 我们不会为每种情况编写相应的规则,并且每次都会有更多问题。 实际上,一切都很简单:如果X是父Y的祖先,则X是Y的祖先。Cypher提供了一个模式*
,允许您要求一个任意长度的关系序列:
MATCH (p:Person)-[*]->(s:Person) RETURN DISTINCT p.name, s.name
确实存在一个问题:将是任何连接。 添加对PARENT
链接的引用:
MATCH (p:Person)-[:PARENT*]->(s:Person) RETURN DISTINCT p.name, s.name
为了不增加文章的篇幅,我们找到了Joli
所有祖先:
MATCH (p:Person)-[:PARENT*]->(:Person {name: "Joli"}) RETURN DISTINCT p.name
╒════════╕ │"p.name"│ ╞════════╡ │"Jack" │ ├────────┤ │"Pat" │ ├────────┤ │"Bob" │ ├────────┤ │"Pam" │ ├────────┤ │"Tom" │ └────────┘
考虑一个更复杂的规则,以找出与谁相关的人。
首先,亲戚是祖先和后代,例如儿子和母亲,祖母和孙子。 其次,亲戚是兄弟姐妹,包括堂兄弟姐妹,第二堂兄弟姐妹等等,就祖先而言,这意味着他们拥有共同的祖先。 第三,具有共同后代的亲戚,例如丈夫和妻子,被视为亲戚。
在Cypher上,您需要对多种模式使用UNION
:
MATCH (r1:Person)-[:PARENT*]-(r2:Person) RETURN DISTINCT r1.name, r2.name UNION MATCH (r1:Person)<-[:PARENT*]-(:Person)-[:PARENT*]->(r2:Person) RETURN DISTINCT r1.name, r2.name UNION MATCH (r1:Person)-[:PARENT*]->(:Person)<-[:PARENT*]-(r2:Person) RETURN DISTINCT r1.name, r2.name
在这里,第一条规则是使用连接,连接的方向对我们而言无关紧要。 这样的连接用箭头表示,而不是短划线-
。 第二和第三条规则以显而易见的熟悉方式编写。
我们不会在这里显示总查询的结果,我们只会说找到的亲戚对是132,这与计算值一致,即从12开始的有序对的数量。我们也可以通过将变量r1
或r2
的出现替换为(:Person {name: "Liz"})
,但是,在我们的案例中,这没有多大意义,因为数据库中的所有人显然都是亲戚。
到此,我们结束了在数据库中识别人与人之间关系的讨论。
最后,考虑如何删除节点和链接。
要删除我们所有人,您可以执行以下请求:
MATCH (p:Person) DELETE p
但是,Neo4j将告诉我们您不能删除具有链接的节点。
因此,我们首先删除链接,然后重复删除节点:
MATCH (p1:Person)-[r]->(p2:Person) DELETE r
现在我们要做的事情:比较两个有连接的人,将此连接命名为r
,然后将其删除。
结论
本文通过一个简单的社交图示例显示了如何使用Cypher查询语言的功能。 特别是,我们研究了如何通过一个查询添加节点和链接,如何搜索相关数据(包括间接链接)以及如何为节点分配标签。 有关Cypher的更多信息,请参见下面的链接。 一个很好的起点是“ Neo4j Cypher Refcard”。
Neo4j远非唯一的图形DBMS。 在其他最受欢迎的软件中,包括Cayley ,具有GraphQL查询语言的Dgraph,多模型ArangoDB和OrientDB 。 尤其受关注的Blazegraph支持RDF和SPARQL。
参考文献
参考书目
- 鲁滨逊·扬,韦伯·吉姆,埃弗雷姆·埃米尔。 图形数据库。 新功能
用于处理相关数据/ Per。 来自英语 -第二版 -M。:DMK-Press,
2016-256秒 - Bratko I.使用Prolog语言进行人工智能编程:
反式 来自英语 -M .:米尔(Mir),1990年。-560羽。
后记
本文的作者仅知道两家公司(都来自圣彼得堡)在其产品中使用图形DBMS。 但是我想知道本文的读者中有多少家公司在开发过程中使用它们。 因此,我建议参加调查。 在评论中也写您的经历,这将非常有趣。