模式(pattern)
- 节点用圆括号表示
()
- 我们使用冒号来表示标签,例如:
(:Person)
- 节点间的关系用两个破折号表示,例如:
(:Person)--(:Movie)
- 关系的方向使用大于或小于符号表示,例如:
(:Person)-->(:Movie)
- 关系的类型用两个破折号之间的方括号表示,例如:
[:ACTED_IN]
- 在语音气泡中绘制的属性是用类似JSON的语法指定的。Neo4j中的属性是键/值对。例如
{name:'Tom Hanks'}
示例:
(m:Movie {title: 'Cloud Atals'})<-[ACTED_IN]-(p:Person)
该模式中的两个节点类型是Movie和Person。Person节点与Movie节点有指向的ACTED_IN关系。该模式中的特定Movie节点由值为Cloud Atlas的title属性过滤。这个图形代表了所有在电影《Cloud Atlas》中扮演过角色的人。
How Cypher works
使用 MATCH
关键字从图中检索数据。你可以认为 MATCH
子句类似于SQL语句中的 FROM
例如,如果我们想在图中查找Person,我们将匹配单个节点的模式,其标签为 :Person
MATCH (:Person)
假设我们想从图中检索所有Person
节点。我们可以在冒号前放置一个值来赋值一个变量。
使用变量p
表示从图中检索到的所有Person
节点,使用RETURN
子句返回它们。
MATCH (p:Person)
RETURN P
假如我们想查询name为Tom Hanks
的节点。我们所有的Person
节点有name
属性。我们可以使用大括号指定name
和Tom Hanks
的键值对作为过滤器。 由于Tom Hanks
是一个字符串,我们需要将它放在单引号或双引号中。
MATCH (p:Person {name: 'Tom Hanks'})
RETURN p
该查询返回一个表示Tom Hanks的节点。在Neo4j Browser的图形视图中,节点被可视化为一个气泡。
在Cypher语句中,可以使用点表示法访问属性。例如,使用name属性键p.name
返回name属性值。
MATCH (p:Person {name: 'Tom Hanks'})
RETURN p.born
这个查询返回Tom Hanks节点的born属性的值。
在Cypher中,标签、属性键和变量是区分大小写的。Cypher关键字不区分大小写。最佳实践:
- 使用驼峰命名标签。
- 使用驼峰命名属性和变量
- 关键字大写
筛选查询的另一种方式是使用WHERE
子句,而不是使用大括号指定属性值。
MATCH (p:Person)
WHERE p.name = 'Tom Hanks'
RETURN p.name
可以向WHERE
子句添加根复杂的逻辑
MATCH (p:Person)
WHERE p.name = 'Tom Hanks' OR p.name = 'Rita Wilson'
RETURN p.name, p.born
遍历
MATCH (p:Person {name: 'Tom Hanks'})-[:ACTED_IN]->(m:Movie)
RETURN m.title
过滤查询
1.按节点标签过滤
MATCH (p:Person)-[:ACTED_IN]->(m:Movie)
WHERE m.title='The Matrix'
RETURN p.name
MATCH (p)-[:ACTED_IN]->(m)
WHERE p:Person AND m:Movie AND m.title='The Matrix'
RETURN p.name
这两个查询执行的方式相同
2.范围过滤
MATCH (p:Person)-[:ACTED_IN]->(m:Movie)
WHERE 2000 <= m.released <= 2003
RETURN p.name, m.title, m.released
3.根据属性的存在进行过滤
MATCH (p:Person)-[:ACTED_IN]->(m:Movie)
WHERE p.name='Jack Nicholson' AND m.tagline IS NOT NULL
RETURN m.title, m.tagline
4.通过部分字符串进行过滤
Cypher有一组与字符串相关的关键字,您可以在WHERE子
句中使用它们来测试字符串属性值。你可以指定STARTS WITH
,END WITH
和CONTAINS
。
MATCH (p:Person)-[:ACTED_IN]->()
WHERE p.name STARTS WITH 'Michael'
RETURN p.name
字符串测试区分大小写,因此您可能需要使用toLower()
或toUpper()
函数来确保测试产生正确的结果。例如:
MATCH (p:Person)-[:ACTED_IN]->()
WHERE toLower(p.name) STARTS WITH 'michael'
RETURN p.name
5.根据图中的模式进行过滤
MATCH (p:Person)-[:WROTE]->(m:Movie)
WHERE NOT exists( (p)-[:DIRECTED]->(m) )
RETURN p.name, m.title
6.列表过滤
可以在WHERE子句中定义列表。在查询期间,图形引擎将把每个属性与列表中的值进行比较。可以在列表中放置数值值或字符串值,但通常情况下,列表的元素是相同类型的数据。如果您正在使用字符串类型的属性进行测试,那么列表的所有元素都将是字符串。
MATCH (p:Person)
WHERE p.born IN [1965, 1970, 1975]
RETURN p.name, p.born
MATCH (p:Person)-[r:ACTED_IN]->(m:Movie)
WHERE 'Neo' IN r.roles AND m.title='The Matrix'
RETURN p.name, r.roles
7.节点或关系具有哪些属性?
具有给定标签的节点的属性不必相同。发现节点属性的一种方法是使用keys()
函数。这个函数返回一个节点的所有属性键的列表。
通过运行以下代码来发现图中Person节点的键:
MATCH (p:Person)
RETURN p.name, keys(p)
8.图中有什么属性?
CALL db.propertyKeys()
一旦定义了属性键,即使目前没有使用该属性键的节点或关系,属性键仍然保留在图中。
创建节点
MERGE
MERGE关键字在数据库中创建模式
在MERGE关键字之后,指定要创建的模式。通常这将是单个节点或两个节点之间的关系。
假设我们想要创建一个节点来表示Michael Cain。运行Cypher代码创建节点。
MERGE (p:Person {name: 'Michael Cain'})
在使用
MERGE
创建节点时,必须执行至少一个属性作为节点的唯一主键。
Executing multiple Cypher clauses
我们还可以在单个Cypher代码块中链接多个MERGE
子句。
MERGE (p:Person {name: 'Katie Holmes'})
MERGE (m:Movie {title: 'The Dark Knight'})
RETURN p, m
CREATE
CREATE
子句也可以创建节点。使用CREATE
的好处是它在添加节点之前不查找主键。如果您确定数据是干净的,并且希望在导入过程中提高速度,则可以使用CREATE
。使用MERGE,因为它消除了节点的重复。
创建关系
就像可以使用MERGE在图中创建节点一样,也可以使用MERGE创建两个节点之间的关系。首先,您必须有对将要为其创建关系的两个节点的引用。当你在两个节点之间创建关系时,它必须有:
-
Type
(类型) -
Direction
(方向)
例如,如果Person
和Movie
节点都已经存在,我们可以在创建它们之间的关系之前使用MATCH
子句找到它们。
MATCH (p:Person {name: 'Michael Cain'})
MATCH (m:Movie {title: 'The Dark Knight'})
MERGE (p)-[:ACTED_IN]->(m)
在这里,我们找到了想要创建关系的两个节点。然后使用对找到的节点的引用来创建ACTED_IN
关系。
我们可以确认这种关系存在如下:
MATCH (p:Person {name: 'Michael Cain'})-[:ACTED_IN]-(m:Movie {title: 'The Dark Knight'})
RETURN p, m
默认情况下,在Neo4j Browser中,可视化连接节点之间有关系的节点。
不需要在MATCH模式中指定方向,因为查询引擎将查找所有连接的节点,而不管关系的方向如何。
例如,如果我们指定了这个关系模式:
MATCH (p:Person {name: 'Michael Cain'})<-[:ACTED_IN]-(m:Movie {title: 'The Dark Knight'})
RETURN p, m
该查询不返回节点,因为在图中没有与Person
节点具有ACTED_IN
关系的节点。
使用多个子句创建节点和关系
我们还可以在单个Cypher代码块中链接多个MERGE
子句。
MERGE (p:Person {name: 'Chadwick Boseman'})
MERGE (m:Movie {title: 'Black Panther'})
MERGE (p)-[:ACTED_IN]-(m)
注意,在我们创建关系的MERGE子句中,我们没有指定关系的方向。默认情况下,如果在创建关系时不指定方向,则总是假设从左到右。
我们可以确认这种关系存在如下:
MATCH (p:Person {name: 'Chadwick Boseman'})-[:ACTED_IN]-(m:Movie {title: 'Black Panther'})
RETURN p, m
使用MERGE在单个子句中创建节点和关系
MERGE所做的是在图中不存在节点或关系时创建节点或关系。
这段代码成功创建了节点和关系:
MERGE (p:Person {name: 'Emily Blunt'})-[:ACTED_IN]->(m:Movie {title: 'A Quiet Place'})
RETURN p, m
您可以多次执行这个Cypher代码,它不会创建任何新的节点或关系。
属性设置
为节点或关系添加属性
有两张方式设置节点或关系的属性
1. 内联作为MERGE子句的一部分
您已经了解了如何为节点创建主键属性。您还可以为关系内联设置如下属性:
MERGE (p:Person {name: 'Michael Cain'})
MERGE (m:Movie {title: 'Batman Begins'})
MERGE (p)-[:ACTED_IN {roles: ['Alfred Penny']}]->(m)
RETURN p,m
2. 对节点或关系的引用使用set
关键字
MATCH (p:Person)-[r:ACTED_IN]->(m:Movie)
WHERE p.name = 'Michael Cain' AND m.title = 'The Dark Knight'
SET r.roles = ['Alfred Penny']
RETURN p, r, m
设置多个属性
如果需要设置多个属性,可以用逗号分隔它们。
MATCH (p:Person)-[r:ACTED_IN]->(m:Movie)
WHERE p.name = 'Michael Cain' AND m.title = 'The Dark Knight'
SET r.roles = ['Alfred Penny'], r.year = 2008
RETURN p, r, m
更新属性
如果对节点或关系有引用,也可以使用SET修改属性
MATCH (p:Person)-[r:ACTED_IN]->(m:Movie)
WHERE p.name = 'Michael Cain' AND m.title = 'The Dark Knight'
SET r.roles = ['Mr. Alfred Penny']
RETURN p, r, m
移除属性
通过使用remove关键字或将属性设置为null,可以从节点或关系中删除或删除属性。
MATCH (p:Person)-[r:ACTED_IN]->(m:Movie)
WHERE p.name = 'Michael Cain' AND m.title = 'The Dark Knight'
REMOVE r.roles
RETURN p, r, m
MATCH (p:Person)
WHERE p.name = 'Gene Hackman'
SET p.born = null
RETURN p
永远不要删除用作节点主键的属性
合并处理
您已经了解了可以使用MERGE在图中创建节点和关系。MERGE操作首先试图在图中找到一个模式。如果找到了模式,则数据已经存在,并且没有创建。如果没有找到模式,则可以创建数据。
定制化MERGE
的行为
你可以在运行时指定行为,使你能够在创建节点或找到节点时设置属性。
我们可以使用ON CREATE SET
或ON MATCH SET
条件,或者SET
关键字来设置任何附加属性。
在本例中,如果McKenna Grace的Person节点不存在,则创建该节点并设置createdAt属性。如果找到节点,则设置updatedAt属性。在这两种情况下,都设置了born属性。
// Find or create a person with this name
MERGE (p:Person {name: 'McKenna Grace'})
// Only set the `createdAt` property if the node is created during this query
ON CREATE SET p.createdAt = datetime()
// Only set the `updatedAt` property if the node was created previously
ON MATCH SET p.updatedAt = datetime()
// Set the `born` property regardless
SET p.born = 2006
RETURN p
如果要为ON CREATE set或ON MATCH set子句设置多个属性,可以用逗号分隔它们。例如:
ON CREATE SET m.released = 2020, m.tagline = `A great ride!'
关系的MERGE
// Find or create a person with this name
MERGE (p:Person {name: 'Michael Cain'})
// Find or create a movie with this title
MERGE (m:Movie {title: 'The Cider House Rules'})
// Find or create a relationship between the two nodes
MERGE (p)-[:ACTED_IN]->(m)
另一种创建这些节点和关系的方法如下:
MERGE (p:Person {name: 'Michael Cain'})-[:ACTED_IN]->(m:Movie {title: 'The Cider House Rules'})
RETURN p, m
删除数据
Neo4j中,可以删除:
- 节点
- 关系
- 属性
- 标签
为了删除数据,你必须先检索到它,然后才能删除。
删除节点
MATCH (p:Person)
WHERE p.name = 'Jane Doe'
DELETE p
删除关系
MATCH (p:Person {name: 'Jane Doe'})-[r:ACTED_IN]->(m:Movie {title: 'The Matrix'})
DELETE r
RETURN p, m
如果我们试图删除Jane Doe节点,我们将收到一个错误,因为它在图中有关系。您应该会收到一个错误。Neo4j防止了图中的孤立关系。
删除节点和关系
Neo4j提供了一个功能,如果节点具有传入或传出关系,则不能删除该节点。这可以防止图表中出现孤立关系。
MATCH (p:Person {name: 'Jane Doe'})
DETACH DELETE p
这段代码删除了关系和Person节点。
您还可以使用此代码删除数据库中的所有节点和关系。
MATCH (n)
DETACH DELETE n
删除标签
最佳实践是一个节点至少有一个标签,但不要超过四个。
MATCH (p:Person {name: 'Jane Doe'})
REMOVE p:Developer
RETURN p
图中有什么标签?
这段代码返回图中定义的所有节点标签。
CALL db.labels()
图遍历
- 查询的锚通常基于MATCH子句。锚点通常由存储在图中的元数据或内联或WHERE子句中提供的筛选器确定。
- 在检索了锚节点之后,如果查询指定了一个路径,那么下一步就是遵循该路径。作为锚集一部分的加载的初始节点具有指向关系的指针,这些关系指向关系的另一端的节点。
Avoiding labels for better performance
用于锚节点检索的Person标签在这里很好,但是用于模式另一端的标签是不必要的。在非锚节点上设置标签将强制进行标签检查,这实际上是不必要的。
PROFILE MATCH (p:Person)-[:ACTED_IN]->(m:Movie)
WHERE p.name = 'Tom Hanks'
RETURN m.title AS movie
更好的写法:
PROFILE MATCH (p:Person)-[:ACTED_IN]->(m)
WHERE p.name = 'Tom Hanks'
RETURN m.title AS movie
Returning paths
在Neo4j Browser中,当您返回节点时,默认情况下关系是可视化的。例如,这个查询与它所遍历的相关路径是可视化的:
MATCH (person:Person)-[]->(movie)
WHERE person.name = 'Walt Disney'
RETURN person, movie
可变长度遍历
neo4j使用深度优先遍历
Neo4j始终坚持关系的唯一性。也就是说,两个节点之间永远不会有两个相同类型和方向的关系。这使Neo4j能够避免图遍历中的循环或无限循环。
最短路径
MATCH p = shortestPath((p1:Person)-[*]-(p2:Person))
WHERE p1.name = "Eminem"
AND p2.name = "Charlton Heston"
RETURN p
可变长度遍历
MATCH (p:Person {name: 'Eminem'})-[:ACTED_IN*2]-(others:Person)
RETURN others.name
遍历长度限制
MATCH (p:Person {name: 'Eminem'})-[:ACTED_IN*1..4]-(others:Person)
RETURN others.name
全局变量
可以用with
子句定义和初始化要在查询中使用的变量。
WITH 'Tom Hanks' AS actorName
MATCH (p:Person)-[:ACTED_IN]->(m:Movie)
WHERE p.name = actorName
RETURN m.title AS movies
使用WITH定义表达式。在定义表达式(例如toLower(m.c etitle))时,必须指定用AS关键字定义的别名。
结果限制
WITH 'Tom Hanks' AS theActor
MATCH (p:Person)-[:ACTED_IN]->(m:Movie)
WHERE p.name = theActor
RETURN m.title AS movies LIMIT 2
WITH 'Tom Hanks' AS theActor
MATCH (p:Person)-[:ACTED_IN]->(m:Movie)
WHERE p.name = theActor
WITH m LIMIT 2
// possibly do more with the two m nodes
RETURN m.title AS movies
结果排序
WITH 'Tom Hanks' AS theActor
MATCH (p:Person)-[:ACTED_IN]->(m:Movie)
WHERE p.name = theActor
WITH m ORDER BY m.year LIMIT 5
// possibly do more with the five m nodes in a particular order
RETURN m.title AS movies, m.year AS yearReleased
在WITH子句中使用映射投影
MATCH (n:Movie)
WHERE n.imdbRating IS NOT NULL
AND n.poster IS NOT NULL
WITH n {
.title,
.year,
.languages,
.plot,
.poster,
.imdbRating,
directors: [ (n)<-[:DIRECTED]-(d) | d { tmdbId:d.imdbId, .name } ]
}
ORDER BY n.imdbRating DESC LIMIT 4
RETURN collect(n)
尽管这对客户端处理很好,但它占用服务器上更多的内存,因为记录不能流到客户端,而是被收集到服务器上的列表结构中。
流水线似的查询
Using WITH for aggregation
MATCH (:Movie {title: 'Toy Story'})-[:IN_GENRE]->(g:Genre)<-[:IN_GENRE]-(m)
WHERE m.imdbRating IS NOT NULL
WITH g.name AS genre,
count(m) AS moviesInCommon,
sum(m.imdbRating) AS total
RETURN genre, moviesInCommon,
total/moviesInCommon AS score
ORDER By score DESC
Using WITH for collecting
MATCH (m:Movie)--(a:Actor)
WHERE m.title CONTAINS 'New York'
WITH m, collect (a.name) AS actors,
count(*) AS numActors
RETURN m.title AS movieTitle, actors
ORDER BY numActors DESC
MATCH (m:Movie)<-[:ACTED_IN]-(a:Actor)
WHERE m.title CONTAINS 'New York'
WITH m, collect (a.name) AS actors,
count(*) AS numActors
ORDER BY numActors DESC
RETURN collect(m { .title, actors, numActors }) AS movies
Using LIMIT early
PROFILE MATCH (p:Actor)
WHERE p.born.year = 1980
WITH p LIMIT 3
MATCH (p)-[:ACTED_IN]->(m:Movie)
WITH p, collect(m.title) AS movies
RETURN p.name AS actor, movies
Use DISTINCT when necessary
MATCH (p:Actor)
WHERE p.born.year = 1980
WITH p LIMIT 3
MATCH (p)-[:ACTED_IN]->(m:Movie)-[:IN_GENRE]->(g:Genre)
WITH p, collect(DISTINCT g.name) AS genres
RETURN p.name AS actor, genres
Unwinding Lists
UNWIND
为列表的每个元素返回一行。
MATCH (m:Movie)-[:ACTED_IN]-(a:Actor)
WHERE a.name = 'Tom Hanks'
UNWIND m.languages AS lang
RETURN m.title AS movie,
m.languages AS languages,
lang AS language
MATCH (m:Movie)
UNWIND m.languages AS lang
WITH m, trim(lang) AS language
// this automatically, makes the language distinct because it's a grouping key
WITH language, collect(m.title) AS movies
RETURN language, movies[0..10]
子查询
CALL {
MATCH (m:Movie) WHERE m.year = 2000
RETURN m ORDER BY m.imdbRating DESC LIMIT 10
}
MATCH (:User)-[r:RATED]->(m)
RETURN m.title, avg(r.rating)
MATCH (m:Movie)
CALL {
WITH m
MATCH (m)<-[r:RATED]-(u:User)
WHERE r.rating = 5
RETURN count(u) AS numReviews
}
RETURN m.title, numReviews
ORDER BY numReviews DESC
Combining query results with UNION
MATCH (m:Movie) WHERE m.year = 2000
RETURN {type:"movies", theMovies: collect(m.title)} AS data
UNION ALL
MATCH (a:Actor) WHERE a.born.year > 2000
RETURN { type:"actors", theActors: collect(DISTINCT a.name)} AS data
UNION ALL返回所有结果,这在内存上效率更高,但可能导致重复。UNION返回不同的结果。
Using UNION with subqueries
UNION的结果不能直接后处理。但是如果在子查询中封装UNION,则可以进一步处理结果。
MATCH (p:Person)
WITH p LIMIT 100
CALL {
WITH p
OPTIONAL MATCH (p)-[:ACTED_IN]->(m:Movie)
RETURN m.title + ": " + "Actor" AS work
UNION
WITH p
OPTIONAL MATCH (p)-[:DIRECTED]->(m:Movie)
RETURN m.title+ ": " + "Director" AS work
}
RETURN p.name, collect(work)
Cypher参数
在测试Cypher语句时,您将使用各种文字值来确保Cypher查询是正确的。但您不希望每次测试时都更改Cypher语句。事实上,任何对Cypher语句的更改都需要重新编译Cypher代码,这是非常昂贵的。您创建了不会更改的Cypher语句,除非在查询中替换占位符(参数)。最佳实践是在Cypher语句中参数化值。
:param actorName: 'Tom Hanks'
设置参数之后,执行查询
MATCH (p:Person)-[:ACTED_IN]->(m:Movie)
WHERE p.name = $actorName
RETURN m.released AS releaseDate,
m.title AS title
ORDER BY m.released DESC
在Neo4j Browser会话中设置整数值时应该特别注意。由于JavaScript中的整数与Neo4j类型系统中的整数存在差异,所以在设置参数时,任何整数都将转换为浮点值。这是为了避免大量数据的任何数据丢失。
要强制该数字为整数,可以使用=>
操作符。
:param number=> 10
Setting multiple parameters
您还可以使用json样式的语法来设置Neo4j Browser会话中的所有参数。可以在此对象中指定的值有数字、字符串和布尔值。在这个例子中,我们为会话设置了两个参数:
:params {actorName: 'Tom Cruise', movieName: 'Top Gun'}
如果设置了多个参数,可以使用前面看到的=>
操作符向参数集添加更多的参数
:param number=> 10
Using multiple parameters
MATCH (p:Person)-[:ACTED_IN]->(m:Movie)
WHERE p.name = $actorName
AND m.title = $movieName
RETURN p, m
查看所有参数
:params
删除参数
:params {}