日常我们在创建知识图谱的时候,入门的图数据库几乎为Neo4j数据库,Cypher 是图形数据库 Neo4j 的查询语言。我们要掌握Neo4j数据库,必须掌握Cypher语言。以下是我个人学习中的记录,恭请指教!!
Cypher 受到不同方法的启发,如 WHERE
和 ORDER BY
受到了 SQL 的启发,模式匹配则借用了 SPARQL 的表达式方法,一些列表语义借用了 Haskell 和 Python 等语言,正则表达式匹配实现实用Scala programming language语言。
Cypher具备的能力:
数值、字符串、布尔、节点、关系、路径、映射(Map)、列表(List)
断言(Predicate)函数、标量(Scalar)函数、列表(List)函数、数学(Math)函数、字符串(String)函数、自定义函数
函数 | 函数 | 函数 |
---|---|---|
标量(Scalar)函数 | 列表(List)函数 | 数学(Math)函数 |
字符串(String)函数 | 断言(Predicate)函数 | 自定义函数 |
查看数据Schema表结构
CALL db.schema.visualization()
查看节点属性类型
CALL db.schema.nodeTypeProperties()
CALL apoc.meta.nodeTypeProperties()
查看关系属性类型
CALL db.schema.relTypeProperties()
查看关系图中的唯一性约束索引
SHOW CONSTRAINTS
MATCH (p:Person)-[:ACTED_IN]->(m:Movie)<-[:DIRECTED]-(p)
WHERE p.born.year > 1960
RETURN p.name, p.born, labels(p), m.title
MATCH (p:Person)-[r]->(m:Movie)
WHERE p.name = 'Tom Hanks'
RETURN m.title AS movie, type(r) AS relationshipType
MATCH (m:Movie)
WHERE "Israel" IN m.countries
RETURN m.title, m.languages, m.countries
IS NULL
IS NOT NULL
MATCH (p:Person)
WHERE p.died IS NULL
AND p.born.year <= 1922
RETURN p.name, p.born, p.died
MATCH (m:Movie)
WHERE toUpper(m.title) STARTS WITH 'LIFE IS'
RETURN m.title
MATCH (p:Person)-[r]->(m:Movie)
WHERE toLower(r.role) CONTAINS "dog"
RETURN p.name, r.role, m.title
exists
MATCH (p:Person)-[:ACTED_IN]->(m:Movie)
WHERE p.name = 'Tom Hanks'
AND exists {(p)-[:DIRECTED]->(m)}
RETURN p.name, labels(p), m.title
使用关键字查询模式和性能分析
PROFILE
关键字显示从查询中的图形中检索到的总行数
PROFILE MATCH (p:Person)-[:ACTED_IN]->(m:Movie)
WHERE p.name = 'Tom Hanks'
AND exists {(p)-[:DIRECTED]->(m)}
RETURN m.title
OPTIONAL MATCH
将模式与图形匹配,就像这样。不同之处在于,如果未找到匹配项,则将使用 null 来表示模式的缺失部分。
MATCH (m:Movie) WHERE m.title = "Kiss Me Deadly"
MATCH (m)-[:IN_GENRE]->(g:Genre)<-[:IN_GENRE]-(rec:Movie)
OPTIONAL MATCH (m)<-[:ACTED_IN]-(a:Actor)-[:ACTED_IN]->(rec)
RETURN rec.title, a.name
ORDER BY
返回的结果排序 降序 ORDER BY ... DESC
默认升序 ORDER BY ... ASC
MATCH (m:Movie)<-[ACTED_IN]-(p:Person)
WHERE m.imdbRating IS NOT NULL
RETURN m.title, m.imdbRating, p.name, p.born
ORDER BY m.imdbRating DESC, p.born DESC
SKIP
LIMIT
返回结果跳过或者限制数量
MATCH (p:Person)
WHERE p.born.year = 1980
RETURN p.name as name,
p.born AS birthDate
ORDER BY p.born SKIP 40 LIMIT 10
DISTINCT
返回结果 去重
MATCH (p:Person)-[:DIRECTED | ACTED_IN]->(m:Movie)
WHERE p.name = 'Tom Hanks'
RETURN DISTINCT m.title, m.released
ORDER BY m.title
{.··, .··}
返回数据以节点的JSON样式
MATCH (p:Person)
WHERE p.name CONTAINS "Thomas"
RETURN p { .* } AS person
ORDER BY p.name ASC
// 自定义在对象中返回的属性
MATCH (p:Person)
WHERE p.name CONTAINS "Thomas"
RETURN p { .name, .born } AS person
ORDER BY p.name
// 向返回的对象数据添加信息 如例:加favorite属性
MATCH (m:Movie)<-[:DIRECTED]-(d:Director)
WHERE d.name = 'Woody Allen'
RETURN m {.*, favorite: true} AS movie
返回的数据进行运算
MATCH (m:Movie)<-[:ACTED_IN]-(p:Person)
WHERE m.title CONTAINS 'Toy Story' AND
p.died IS NULL
RETURN 'Movie: ' + m.title AS movie,
p.name AS actor,
p.born AS dob,
date().year - p.born.year AS ageThisYear
CASE ... END
返回的结果进行条件更改
// 转换返回的数据以反映电影的时间范围
MATCH (m:Movie)<-[:ACTED_IN]-(p:Person)
WHERE p.name = 'Henry Fonda'
RETURN m.title AS movie,
CASE
WHEN m.year < 1940 THEN 'oldies'
WHEN 1940 <= m.year < 1950 THEN 'forties'
WHEN 1950 <= m.year < 1960 THEN 'fifties'
WHEN 1960 <= m.year < 1970 THEN 'sixties'
WHEN 1970 <= m.year < 1980 THEN 'seventies'
WHEN 1980 <= m.year < 1990 THEN 'eighties'
WHEN 1990 <= m.year < 2000 THEN 'nineties'
ELSE 'two-thousands'
END
AS timeFrame
// 第二个例子
MATCH (m:Movie)<-[:ACTED_IN]-(p:Person)
WHERE m.title CONTAINS 'Toy Story'
RETURN m.title AS movie,
p.name AS actor,
p.born AS dob,
CASE
WHEN p.died IS NULL THEN date().year - p.born.year WHEN p.died IS NOT NULL THEN "Died" END
AS ageThisYear
公式 | 含义 |
---|---|
MAX() |
最大值 |
min() |
最小值 |
avg() |
均值 |
stddev() |
标准差 |
sum() |
求和 |
MATCH (a:Person)-[:ACTED_IN]->(m:Movie)<-[:DIRECTED]-(d:Person)
RETURN a.name, d.name,
count(*) AS numMovies
ORDER BY numMovies DESC
MATCH (p:Person)
RETURN p.name, [p.born, p.died] AS lifeTime
LIMIT 10
collect
将值聚合到列表中。该值可以是任何表达式,例如属性值、节点或函数或操作的结果
MATCH (a:Person)-[:ACTED_IN]->(m:Movie)
RETURN a.name AS actor,
count(*) AS total,
collect(m.title) AS movies
ORDER BY total DESC LIMIT 10
MATCH (a:Person)-[:ACTED_IN]->(m:Movie)
WHERE m.year = 1920
RETURN collect( DISTINCT m.title) AS movies,
collect( a.name) AS actors
head()
tail
// 返回列表中元素的第一个值
MATCH (a:Person)-[:ACTED_IN]->(m:Movie)
RETURN m.title AS movie,
collect(a.name)[0] AS castMember,
size(collect(a.name)) as castSize
// 从第二个返回列表元素
MATCH (a:Person)-[:ACTED_IN]->(m:Movie)
RETURN m.title AS movie,
collect(a.name)[2..] AS castMember,
size(collect(a.name)) as castSize
size()
//Query 1 profile 测试查询性能
PROFILE MATCH (g:Genre)<-[:IN_GENRE]-(m)
RETURN g.name AS genre,
size(collect(m)) AS numMovies
ORDER BY size(collect(m)) DESC
MATCH (m:Movie)
RETURN m.title as movie,
[x IN m.countries WHERE x = 'USA' OR x = 'Germany']
AS country LIMIT 500
指定带有方括号的列表,以包含图案后跟管道字符,然后指定将在模式列表中放置的值。
MATCH (m:Movie)
WHERE m.year = 2015
RETURN m.title,
[(dir:Person)-[:DIRECTED]->(m) | dir.name] AS directors,
[(actor:Person)-[:ACTED_IN]->(m) | actor.name] AS actors
// 电影的标题与电影年份连接,作为要添加的值作为返回的列表的元素。
MATCH (a:Person {name: 'Tom Hanks'})
RETURN
[(a)-->(b:Movie) WHERE b.title CONTAINS "Toy" | b.title + ": " + b.year] AS movies
// 返回一个持续时间值,该值表示两个值之间的天数和月数以及时间
MATCH (x:Test {id: 1})
RETURN duration.between(x.date1,x.date2)
// 返回两个日期时间值之间的持续时间(以天为单位)
MATCH (x:Test {id: 1})
RETURN duration.inDays(x.datetime1,x.datetime2).days
// 添加6个月的持续时间
MATCH (x:Test {id: 1})
RETURN x.date1 + duration({months: 6})
MATCH (x:Test {id: 1})
RETURN x.datetime as Datetime,
apoc.temporal.format( x.datetime, 'HH:mm:ss.SSSS')
AS formattedDateTime
// 结果
"2022-08-22T08:13:37.150000000Z"
"08:13:37.1500"
MATCH (x:Test {id: 1})
RETURN apoc.date.toISO8601(x.datetime.epochMillis, "ms")
AS iso8601
使用格式字符串分析任意格式化时态值。apoc.temporal.toZonedTemporal()
图形遍历,最重要的是:让查询更具性能优化
锚点通常由存储在图形中的元数据或内联或子句中提供的筛选器确定。
查询的定位点将基于需要检索到内存中的最少节点数。
默认情况下,一组锚点节点由与查询路径和用于筛选查询的子句相关的元数据确定。在某些情况下,您可能有多个锚点节点集
PROFILE
MATCH (p1:Person)-[:ACTED_IN]->(m1)
MATCH (m2)<-[:ACTED_IN]-(p2:Person)
WHERE p1.name = 'Tom Hanks'
AND p2.name = 'Meg Ryan'
AND m1 = m2
RETURN m1.title
// 在此查询中,将检索所有 p1 节点以及所有 p2 节点。此查询有两组锚点节点。它在应用相等性筛选器之前检索锚点节点。查询计划程序会尝试尽早应用筛选器以减少基数(行数)。
作为定位点集一部分的已加载的初始节点具有指向关系的指针,这些关系指向关系另一端的节点。
在查询时添加路径,可加快查询效率
相同查询下,第一种查询比第二种查询更快
MATCH (p:Person)-[:ACTED_IN]->(m:Movie)
WHERE p.name = 'Eminem'
RETURN m.title AS movies
查询性能以及过程演示
MATCH (p:Person)-[:ACTED_IN]->(m:Movie)
WHERE p.name = 'Eminem'
MATCH (allActors:Person)-[:ACTED_IN]->(m)
RETURN m.title AS movie, collect(allActors.name) AS allActors
查询性能以及过程演示
减少查询模式中使用的标签
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
以下两种返回结果一致,但在性能和优化上第二种更优
MATCH (person:Person)-[]->(movie)
WHERE person.name = 'Walt Disney'
RETURN person, movie
MATCH p = ((person:Person)-[]->(movie))
WHERE person.name = 'Walt Disney'
RETURN p
length(p)
返回路径的长度。nodes(p)
返回包含路径节点的列表。relationships(p)
返回一个列表,其中包含路径的关系。在Neo4j中,始终坚持关系的独特性。也就是说,两个节点之间永远不会有两个相同类型和方向的关系。这使得Neo4j能够避免图形遍历中的循环或无限循环。
// 没有限制关系
MATCH p = shortestPath((p1:Person)-[*]-(p2:Person))
WHERE p1.name = "Eminem"
AND p2.name = "Charlton Heston"
RETURN p
//关系类型限制为特定关系
MATCH p = shortestPath((p1:Person)-[:ACTED_IN*]-(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
// 检索埃米纳姆人节点。
// 然后,两个ACTED_IN关系通过“电影”节点遍历 8 英里,以返回三个“人”节点。
// 然后,两个ACTED_IN关系通过Hip Hop Witch,Da的电影节点遍历,以返回三个Person节点。
MATCH (p:Person {name: 'Eminem'})-[:ACTED_IN*4]-(others:Person)
RETURN others.name
// 检索埃米纳姆人节点。
// 然后,通过 Movie 节点遍历ACTED_IN关系,在该节点中检索 8 英里的布列塔尼·墨菲和 Little Black Book 以返回两个 Person 节点。
// 然后,四个ACTED_IN关系通过“8英里”和“预言II”的电影节点遍历,以返回两个“人”节点。
// 仅返回正好来自 Eminem 的 4 个跃点的人员节点。
这种深度优先的遍历和检索将一直持续到检索到两个跃点和四个跃点之外的所有 Person 节点为止。
MATCH (p:Person {name: 'Eminem'})-[:ACTED_IN*1..4]-(others:Person)
RETURN others.name
// 检索埃米纳姆人节点。
// 然后,ACTED_IN关系通过“电影”节点遍历 8 英里和“小黑皮书”。
// 在此遍历期间,我们检索两个跃点外的 Person 节点和四个跃点外的两个 Person 节点。
// 然后,通过电影节点遍历 8 英里和预言 II 的ACTED_IN关系。
// 我们已经检索到了四个跳跃之外的布列塔尼·墨菲,但我们又添加了两个Person 节点。
with
子句定义作用域变量使用子句定义和初始化要在查询中使用的变量。
WITH 'Tom Hanks' AS actorName
MATCH (p:Person)-[:ACTED_IN]->(m:Movie)
WHERE p.name = actorName
RETURN m.title AS movies
with
重新定义范围WITH 'toy story' AS mt, 'Tom Hanks' AS actorName
MATCH (p:Person)-[:ACTED_IN]->(m:Movie)
WHERE p.name = actorName
AND toLower(m.title) CONTAINS mt
RETURN m.title AS movies
WITH 'toy story' AS mt, 'Tom Hanks' AS actorName
MATCH (p:Person)-[:ACTED_IN]->(m:Movie)
WITH m, toLower(m.title) AS movieTitle
WHERE p.name = actorName
AND movieTitle CONTAINS mt
RETURN m.title AS movies, movieTitle
// 变量 mt 和 actorName 可用于子句和子句,就像上一个查询一样。但是,这里的不同之处在于,我们必须将 m 添加到第二个子句中,以便可以使用该节点返回节点的标题。子句用于定义或重新定义变量的作用域。因为我们想要重新定义用于子句的内容,所以我们添加了一个新子句。这将为查询的其余部分创建一个新作用域,以便可以使用 m 和 movieTitle 返回值。如果要删除第二个子句中的 m,则查询将不会编译。
定义表达式(例如,toLower(m.title))时,必须指定使用关键字定义的别名。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
RETURN m.title AS movies
// 使用此查询,将检索两个影片节点。此处的不同之处在于,您可以使用来限制稍后在查询中使用的 m 个节点数。将节点传递到下一个子句称为流水线
对限制结果进一步处理,进行排序
WITH 'Tom Hanks' AS theActor
MATCH (p:Person)-[:ACTED_IN]->(m:Movie)
WHERE p.name = theActor
WITH m ORDER BY m.year LIMIT 5
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)
此查询返回“影片”节点中的数据子集。它返回评分最高的四部电影。由于我们已指定 4 个限制,因此只有 4 个具有指定属性的对象添加到列表中。
虽然这非常适合在客户端进行处理,但它会在服务器上占用更多内存,因为记录无法流式传输到客户端,而是收集到服务器上的列表结构中。
with
流水线查询使我们能够将第一个查询的结果管道化到第二个查询中。通过这个简单的查询,m 不需要重新定义或限定范围。
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
MATCH (u:User {name: "Misty Williams"})-[r:RATED]->(:Movie)
WITH u, avg(r.rating) AS average
MATCH (u)-[r:RATED]->(m:Movie)
WHERE r.rating > average
RETURN average , m.title AS movie,
r.rating as rating
ORDER BY rating 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
LIMIT
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
unwind
展开列表UNWIND
为列表的每个元素返回一行。
如:某节点有个属性为列表,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
// 一部汤姆·汉克斯(Tom Hanks)出演的电影被检索到。
// 语言属性(即列表)被解包,并且每个值都被引用为 lang。
// 返回的行将是电影标题和为多行重复的语言属性以及 lang 值。
// 检索所有影片节点。
// 对于每个“电影”节点,它会展开语言列表以创建一个名为 lang 的列表。请注意,我们使用 trim() 函数来确保语言名称中没有多余的空格字符。
// 然后,我们使用列表的元素来查找使用该语言的所有电影。
// 最后,我们返回一行,其中包含每种语言名称以及该语言的最多 10 个电影标题的列表。
MATCH (m:Movie)
UNWIND m.languages AS lang
WITH m, trim(lang) AS language
WITH language, collect(m.title) AS movies
RETURN language, movies[0..10]
trim() 函数来确保语言名称中没有多余的空格字符。
执行一系列子句时,检索到的所有节点和关系都在内存中。如果一组子句的内存要求超过配置的 VM,则查询将失败。
子查询是一组在其自己的范围内执行的 Cypher 语句。子查询通常从外部封闭查询调用。使用子查询,可以限制需要处理的行数。
以下是有关子查询的一些重要事项:
CALL
执行子查询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
// 第一个为每部电影返回一行,图中的 m。MATCH
// 它将 Movie 节点 m 传递给子查询。
// 然后在子查询中,执行查询以查找给予该电影 5 分级的所有用户,并对其进行计数。
// 子查询返回计数。
// 返回到封闭查询中,将返回标题,并从子查询返回的行数计数。
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
// 这两个查询都返回一个名为 Data 的变量,因此我们可以使用 union all 来组合结果。
UNION ALL
返回所有结果,这些结果在内存上更有效,但可能导致重复。 返回不同的结果
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)
// 检索 100 个人员节点并将其传递到子查询。
// 如果该人在电影中表演过,则返回带有 Actor 后缀的标题。
// 子查询的第二部分对定向关系执行相同的操作。
// 合并并收集工作结果。
// 结果是该人的姓名及其演员或导演头衔。
optional match 用于搜索模式中描述的匹配项, 对于找不到的项用null代替。类似于match, 区别在于如果没有匹配到, optional match会用null来作为未匹配到的部分的值
单个参数
:param actorName: 'Tom Hanks'
多个参数
:params
{
"actorName": 'Tom Hanks'
}
由于 JavaScript 和 Neo4j 类型系统中的整数之间存在差异,因此在设置参数时,任何整数都将转换为浮点值。
:param number: 10
// 会默认将数字转换为浮点数
:param number=> 10
// 将数字强转为整数
:params {actorName: 'Tom Cruise', movieName: 'Top Gun'}
查询
MATCH (p:Person)-[:ACTED_IN]->(m:Movie)
WHERE p.name = $actorName
AND m.title = $movieName
RETURN p, m
:params
:params {}
def get_actors(tx, movieTitle):
result = tx.run("""
MATCH (p:Person)-[:ACTED_IN]->(m:Movie)
WHERE m.title = $title
RETURN p
""", title=movieTitle)
return [ record["p"] for record in result ]
with driver.session() as session:
result = session.read_transaction(get_actors, movieTitle="Toy Story")
以上是我个人在学习过程中的记录所学,希望对正在一起学习的小伙伴有所帮助!!!
cypher
更多具体用法可,跳转至官方文档查看用例
参考链接:
The Neo4j Cypher Manual
【知识图谱】Neo4j Cypher查询语言详解
[知识图谱] 3.3-Cypher语言及语法使用