欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

图数据库查询语言Cypher

程序员文章站 2022-06-12 10:11:48
...

1. Cypher 简介

Cypher 是 Neo4j 提出的图查询语言,是一种声明式的图数据库查询语言,它拥有精简的语法和强大的表现力,能够精准且高效地对图数据进行查询和更新。它是一种受 SQL 启发的语言,用于使用 ASCII-Art 语法描述图中的可视模式。它允许声明想要从图数据库中选择、插入、更新或删除什么,而不需要精确地描述如何做到这一点。通过 Cypher,用户可以构建表达性强且高效的查询,处理所需的创建、读取、更新和删除功能。

1.1 Cypher 设计理念

Cypher 的设计理念是:无论是开发工程师,数据库管理员,还是运维工程师,甚至非技术人员都可以轻松读懂 Cypher。这使得图数据库的使用者可以专注于自身业务需求,而不必花费很多时间去理解图数据库的底层实现原理。

(1)人性化设计
Cypher 在语法设计上十分人性化,它提供了一个直观方式来匹配图中的节点和关系。例如想要查询一个一跳路径,()代表节点,[] 代表关系,看起来就像两个点一条关系组成的一跳路径。

MATCH (n)-[r]-(m) RETURN *

(2)博采众长
Cypher 集思广益,借鉴和吸收了已有数据库查询语言的习惯写法。例如 WHERE、ORDER BY、SKIP 和 LIMIT 就来源于关系数据库查询语言 SQL,例如可视化模式匹配的语法设计来源于 RDF 查询语言 SPARQL 等。

(3)语句组合
Cypher 类似于 SQL 语言,一条完整的查询可由多个语句(Clause)组合而成,每条语句的执行结果将保存为中间结果,并往下一条语句传递,值得一提的是,Cypher 执行采用火山模型(Vocalno),所以除了聚合(Eager)操作,当前语句完全执行完之前就会把部分已完成的结果往下一条语句传递。

1.2 Cypher 事务操作

在Galaxybase 中,用户可以自行选择是否开启事务,对于一个事务内的 Cypher 更新操作来说,要不全部执行失败,要不全部执行成功。

•若业务需求不需开启事务,执行 Cypher 会获得更佳的性能 。

•若业务需求需开启事务,开启事务后,Cypher 执行就运行在该事务中,直到事务成功提交事务,执行
造成的数据改动才会持久化到磁盘中去。

此外可以在一个事务中执行多个 Cypher 查询:通过 Bolt Driver 提供的 API 创建并开启一个事务;执行多个 Cypher 查询;提交并关闭这个事务。

1.3 Cypher 唯一性

当进行多跳路径查找时,Galaxybase 将自动对每个路径扩展起始点和它的二跳邻居点进行去重。
例如:查找一个用户同学的同学,将不会在返回结果返回该用户。

1.4 Cypher兼容性

Cypher 是目前图数据库领域属性图的主流查询语言。为了迎合用户的使用习惯,避免重复的学习成本,Galaxybase选择兼容了 Cypher 查询语言。而 Galaxybase 与 Neo4j 在底层结构上有一定差异,所以 Galaxybase 实现的 Cypher 与 Neo4j 的 Cypher 标准也有一定的差异性,本书将在语法讲解过程中标注出 Galaxybase 与 Neo4j 的使用差异。

2. Cypher 使用场景

本节将从一个用户的视角,使用Cypher完成一个场景下各种查询任务的语句编写,使读者上手一步一步学习和体验如何使用图查询语言来解决实际问题。本节将以MovieDemo图做样例,在Galaxybase上执行Cypher语句。

MovieDemo是一个简单的电影关系。其中包含了3种点类型(人物、电影、电视剧)和4种关系类型(出演电影、出演电视剧、导演电影、导演电视剧)。数据文件下载链接

MovieDemo 图模型:

点类型
a. 人物:外部唯一标识姓名,拥有出生年份(STRING),籍贯(STRING)属性
b. 电影:外部唯一标识电影名,拥有上映年份(STRING),语言(STRING),类型(STRING),评分(INT),票房(INT)属性
c. 电视剧:外部唯一标识电视剧名,拥有首播日期(STRING),语言(STRING),类型(STRING),评分(INT),集数(INT)属性

关系类型
a. 出演电影:起始点类型人物,终止点类型电影,拥有角色名称(STRING)属性,是否主演(STRING)属性,片酬(INT)属性
b. 出演电视剧:起始点类型人物,终止点类型电视剧,拥有角色名称(STRING)属性,是否主演(STRING)属性,片酬(INT)属性
c. 导演电影:起始点类型人物,终止点类型电影,拥有工资(INT)属性
d. 导演电视剧:起始点类型人物,终止点类型电视剧,拥有工资(INT)属性

若想了解 Cypher更多使用细节,可以访问 Galaxybase Cypher官方使用文档。Cypher文档地址

2.1 MATCH

(1)简介
MATCH 用于检索图数据库中的节点和关系。

(2)基本节点查找
1)获取图中所有节点
如果指定模式为不带类型的节点,返回结果则为图中的所有节点。示例如下:

MATCH (n)
RETURN n
//返回数据库中的所有节点

2)获取所有电影类型的节点
如果单节点模式(pattern)中节点带类型,返回结果为所有此类型的节点。示例如下:

MATCH (movie:电影)
RETURN movie
//返回数据库中的所有指定类型的节点

(3)基本关系查找
1)查找与人物“绫野刚”所有存在一跳关系的实体
当需要说明节点间关系的方向时,可以用 -→ 或 ←- 表示。示例如下:

MATCH (n:人物 { 姓名: '绫野刚' })-->(movie)
RETURN movie
//返回一跳关系的终止节点

2)查找人物“绫野刚”所有一跳关系的类型
如果需要为关系添加属性过滤,或者需将关系返回时,需为关系命名。示例如下:

MATCH (n:人物 { 姓名: '绫野刚' })-[r]->(movie)
RETURN type(r)
//返回关系的类型

3)查找所有出演“老炮儿”电影的人
可以通过冒号和关系类型对其加以指定。示例如下:

MATCH (n)-[:出演电影]->(movie:电影 {电影名: '老炮儿'})
RETURN n
//返回指定类型关系的起始节点

2.2 OPTIONAL MATCH

(1)简介
OPTIONAL MATCH 和 MATCH 一样用于对图数据进行检索。两者的区别在于,对于找不到的匹配项 OPTIONAL MATCH 会用 null 代替。注意,与不加 OPTIOANAL 关键字时的查询做比较会更容易理解 OPTIONAL 关键字的作用。

(2)可选模式
查找人物“安圣基”和与其有关系的话剧,若话剧不存在就用 null 替代。示例如下:

MATCH (a {姓名: '安圣基'}) 
OPTIONAL MATCH (a)-->(x:话剧)
RETURN *
//可选查找一跳点,并返回它

(3)可选元素的属性
查找人物“安圣基”和与其有关系的话剧,并返回“安圣基”和话剧的名字,若话剧名不存在,则用 null替代。示例如下:

MATCH (a {姓名: '安圣基'})
OPTIONAL MATCH (a)-->(x:话剧)
RETURN x, x.话剧名
//返回可选元素的属性,可选节点或关系为null,返回属性值也为null

(4)可选关系类型和命名
查找人物“安圣基”和与其存在“出演话剧”关系的实体,并返回“安圣基”和该关系 ,若“出演话剧”关系不存在,则使用 null 替代。示例如下:

MATCH (a {姓名: '安圣基'})
OPTIONAL MATCH (a)-[r:出演话剧]->(x)
RETURN a,r
//返回可选指定类型的关系

2.3 RETURN

(1)简介
RETURN用于指定查询结果返回的部分。

(2)返回节点
查找并返回“安圣基”。示例如下:

MATCH (n { 姓名: '安圣基' })
RETURN n
//返回RETURN 语句中列出的节点

(3)返回关系
查找并返回“安圣基”的一跳“出演电影”关系。示例如下:

MATCH (n { 姓名: '安圣基' })-[r:出演电影]->(:电影)
RETURN r
//返回RETURN 语句中列出的关系

(4)返回属性
查找并返回“安圣基”的姓名属性。示例如下:

MATCH (n { 姓名: '安圣基' })
RETURN n.姓名
//返回 RETURN 语句中列出的属性

(5)返回所有元素
查找“安圣基”存在关系一跳路径,并返回所有实体。示例如下:

MATCH p =(a { 姓名: '安圣基' })-[r]->(b) 
RETURN *
//返回所有实体

2.4 WITH

(1)简介
WITH 用于向后面的语句传递指定结果,并可以改变结果集中实体的形式和数量。注意,WITH 会影响查询结果集里的变量,WITH 语句外的变量不会传递到后续查询中。

(2)处理查询结果并往后传递
1)查询五部评分最高的电影,并查找与这五部电影有关系的人物
示例如下:

MATCH (m:电影)
WITH m order by m.评分 DESC LIMIT 5
MATCH (m:电影)-[r]-(n:人物)
RETURN n
//向后面的语句传递结果并改变了结果的数量

2)查找所有电影的平均评分,并查询评分为该平均分的电影
示例如下:

MATCH (n:电影)
WITH AVG(n.评分) as score
MATCH (h:电影 {评分:score}) 
RETURN h
//向后面的语句传递结果并改变了结果的形式

2.5 UNWIND

(1)简介
UNWIND用于将任何列表变回单独的行。这些列表可以是传入的参数,先前编辑的 collect 结果或其他列表表达式。注意UNWIND 需要指定一个新的名称。

(2)展开列表
将文字列表转换为以x命名的行并返回。示例如下:

UNWIND [1, 2, 3, NULL ] AS x
RETURN x, 'val' AS y
//原始列表的每个值(包括null )都作为单独的行返回

2.6 WHERE

(1)简介
WHERE 用于为 MATCH,OPTIONAL MATCH 和 WITH 语句添加过滤条件。

(2)基本用法
1)查找电影“头文字D”,并返回它
示例如下:

MATCH (n)
WHERE n.电影名 = '头文字D'
RETURN n
//查找指定条件的节点

2)可选查找人物类型的点,并返回它
示例如下:

OPTIONAL MATCH (n)
WHERE n:人物
RETURN n.姓名, n.籍贯
//可选查找节点,并指定查找条件

3)查找评分大于8的电影,并返回它们的名字和评分
示例如下:

MATCH (movie:电影)
WITH movie
WHERE movie.评分 > 8
RETURN movie.电影名, movie.评分
//使用 WITH 和 WHERE 对MATCH 结果进行筛选

2.7 ORDER BY

(1)简介
ORDER BY用于对结果进行排序。

(2)根据属性排序
查找所有人物节点,并按人物姓名属性字典序顺序返回人物姓名,出生年份。示例如下:

MATCH (n:人物)
RETURN n.姓名, n.出生年份
ORDER BY n.姓名
//ORDER BY 用于对输出进行排序

2.8 SKIP

(1)简介
SKIP用于跳过指定行数RETURN、WITH的结果。

(2)跳过前三行
查找所有人物节点,跳过前三个人,从第四个人开始返回其姓名。示例如下:

MATCH (n:人物)
RETURN n.姓名
SKIP 3 
//跳过指定数量的结果,然后返回结果的子集

2.9 LIMIT

(1)简介
LIMIT用于保留指定行数RETURN、WITH的结果。

(2)返回部分結果
查找所有人物节点,并返回年龄最大的前三个人。示例如下:

MATCH (n:人物)
RETURN n
ORDER BY n.出生年份
LIMIT 3
//从顶部开始返回结果的子集

2.10 CREATE

(1)简介
CREATE 用于创建节点,关系或者路径。

(2)创建节点
1)创建一个人物类型节点
示例如下:

CREATE (n:人物)
//创建带有类型的节点

2)创建一个姓名为“香取慎吾”的人物节点
示例如下:

CREATE (n:人物 { 姓名: '香取慎吾' }) 
//创建一个带类型的新节点时,可以同时为该节点添加属性

3)创建一个姓名为“香取慎吾”的人物节点,并返回它
示例如下:

CREATE (n:人物 { 姓名: '香取慎吾' }) 
RETURN n
//通过下列查询创建单个节点并返回

(3)创建关系
1)在人物“安圣基”和电影“甲方乙方”间创建一个关系“出演电影”,并返回该关系
示例如下:

MATCH (a:人物),(b:电影)
WHERE a.姓名 = '安圣基' AND b.电影名 = '甲方乙方'
CREATE (a)-[r:出演电影]->(b)
RETURN r
//在两节点间创建关系前,首先要获取这两个节点,获取节点后,即可在两点间创建关系

(4)创建完整路径
1)创建人物“长妻树里”出演电影“十二生肖”,人物“香取慎吾”导演电影“十二生肖”的路径
单独使用 CREATE 创建整个模式时,模式中所有的节点和关系都会被创建。示例如下:

CREATE p=(n:人物 {姓名: '长妻树里'})-[r: 出演电影 { 角色名称: '医生' }]->(m:电影 {电影名: '十二生肖'})<-[:导演电影]-(h: 人物 {姓名: '香取慎吾'})
return *  
//该查询一次创建三个节点和两个关系,将其赋值给路径变量后,返回变量值

(5)创建新类型
1)创建新的点类型
示例如下:

CREATE VERTEXTYPE 动物 (PRIMARY_ID id String, 种类 String, 性别 String)
CREATE VERTEXTYPE 书籍 (PRIMARY_ID id String, 书名 String, 售价 Int)

2)创建新的关系类型
示例如下:

// 创建允许重复的有向关系
CREATE DIRECTED ALLOWREPEAT EDGETYPE 兄弟 (FROM 人物,  TO 人物,  哥哥 STRING, 弟弟 STRING)
// 创建不允许重复的有向关系
CREATE DIRECTED NOTALLOWREPEAT EDGETYPE 兄弟 (FROM 人物,  TO 人物,  哥哥 STRING, 弟弟 STRING)                        
// 创建允许重复的无向关系
CREATE UNDIRECTED ALLOWREPEAT EDGETYPE 饲养 (FROM 人物,  TO 动物)
// 创建不允许重复的无向关系
CREATE UNDIRECTED NOTALLOWREPEAT EDGETYPE 饲养 (FROM 人物,  TO 动物)
//区别于 Neo4j,Galaxybase 需要通过以上特定的语法创建新的节点类型,关系类型和属性名

2.11 DELETE

(1)简介
DELETE用于删除节点和关系。注意,删除节点前需先删除与该节点有关联的所有边。

(2)删除单个节点
查找到姓名为’香取慎吾’的人物节点并删除。示例如下:

MATCH (n:人物 { 姓名: '香取慎吾' }) 
DELETE n

(3)删除节点及其所有关系
删除人物“香取慎吾”及其所有相关联的关系。示例如下:

MATCH (n { 姓名: '香取慎吾' }) 
DETACH DELETE n
//使用 DETACH DELETE删除节点及与之相连的所有一跳关系

(4)仅删除关系
删除所有以香取慎吾为起始节点的“出演电影”关系。示例如下:

MATCH (n { 姓名: '香取慎吾' })-[r:出演电影]->() 
DELETE r
//只删除关系,保留相关节点

2.12 SET

(1)简介
SET 用于设置节点和关系的属性。注意,在Galaxybase中,主键属性(外部唯一标识)在创建以后不能修改和删除。

(2)设置属性
查找人物“矢本悠马”,将其籍贯属性改成日本,并返回他的籍贯。示例如下:

MATCH (n { 姓名: '矢本悠马' })
SET n.籍贯 = '日本' 
RETURN n.籍贯
在节点上设置属性

(3)删除属性
查找人物“品冠”,移除他的籍贯属性。示例如下:

MATCH (n { 姓名: '品冠' }) 
SET n.籍贯 = NULL
RETURN n.籍贯
//虽然一般情况下会使用 REMOVE 删除属性,但有时使用 SET 更为方便

2.13 REMOVE

(1)简介
REMOVE用于移除节点和关系的属性。

(2)删除属性
查找人物“温峥嵘”,删除他的籍贯属性。示例如下:

MATCH (a { 姓名: '温峥嵘' }) 
REMOVE a.籍贯
RETURN a

2.14 FOREACH

(1)简介
FOREACH用于遍历并操作集合元素。

(2)标记路径上的所有节点
查找5个人物节点,将其放入集合,然后将集合内所有人物点出生年份设置为“1979”。示例如下:

MATCH (n:人物) 
WITH n limit 5 
WITH collect(n) as list
FOREACH (n IN list | SET n.出生年份 = "1979") 
//此查询未返回任何内容,但设置了五个节点的属性

2.15 MERGE

(1)简介
MERGE用于保证元素一定存在,作用为查询节点和边,若查不到就创建该节点和边。

(2)合并(MERGE)使用
1)查找人物“安圣基”,若未找到就创建人物“安圣基”,并返回他的姓名
示例如下:

MERGE (n:人物 {姓名:'安圣基'})
RETURN n.姓名  as val

2)查找人物“安圣基”和电影“无间道”,再查找他们之间“出演电影”的关系,若找不到该关系,则创建该关系,并返回它们
示例如下:

MATCH  (n:人物 {姓名:'安圣基'}), (m:电影 {电影名:"无间道"}) 
MERGE (n)-[r:出演电影]->(m) 
RETURN n.姓名,r,m.电影名

3)查找电影“无间道”,若未找到就创建电影“无间道”,并设置它的评分属性为7,然后返回该电影
示例如下:

MERGE (n:电影 {电影名:"无间道"}) 
ON CREATE SET n.评分 = 7
RETURN n  as val

4)查找电影“无间道”,若找到该电影就设置它的评分属性为7,若未找到就创建电影“无间道”,然后返回该电影
示例如下:

MERGE (n:电影 {电影名:"无间道"}) 
ON MATCH SET n.评分 = 8
RETURN n  as val

5)查找电影“无间道”,若找到该电影就设置它的评分属性为7,若未找到就创建电影“无间道”,并设置它的评分属性为8,然后返回该电影
示例如下:

MERGE (n:电影 {电影名:"无间道"}) 
ON CREATE SET n.评分 = 7
ON MATCH SET n.评分 = 8
RETURN n  as val

2.16 CALL[…YIELD]

(1)简介
CALL 语句用于调用数据库中的内置过程(Procedure),内置过程类似于关系型数据库中的存储过程,是一组完成特定功能的方法。

(2)使用 CALL 调用内置过程
1)调用数据库内置过程查询数据库中所有的点类型
示例如下:

CALL db.nodeTypes() 

也可以使用命名空间和名字调用过程,示例如下:

CALL `db`.`nodeTypes()`

2)使用字面值作为参数调用内置过程查询两点间最短路径
示例如下:

Call ShortestPath(0, 1, 'BOTH', 10)

3)使用查询参数作为参数调用内置过程查询两点间最短路径
示例如下:

// 参数
{	
“startNodeId”: 0,
	“endNodeId”:  1
}
// 使用 $ 符号引用参数
Call ShortestPath($startNodeId, $ endNodeId, 'BOTH', 10)

4)调用内置过程并将结果绑定变量
示例如下:

CALL db.nodeTypes() YIELD type RETURN count(type) as numTypes

5)调用内置过程并过滤结果
示例如下:

CALL db.nodeTypes() YIELD type 
WHERE type STARTS WITH “人” 
RETURN count(type) as numTypes

2.17 UNION

(1)简介
UNION 用于将两个或多个查询的结果合并为一个结果集,该结果集中包含所有进行合并操作的行。

(2)合并两个查询并保留重复项
查找人物“安圣基”和人物“楚源”,并将结果合并后返回他们的姓名。示例如下:

MATCH (n:人物{姓名:'安圣基'})
RETURN n.姓名
UNION All MATCH (n:人物{姓名:'楚原'})
RETURN n.姓名
//返回合并的结果,包括重复项

(3)合并两个查询并删除重复项
查找人物“安圣基”和人物“楚源”,并将结果合并后返回他们的姓名。示例如下:

MATCH (n:人物{姓名:'安圣基'})
RETURN n.姓名
UNION MATCH (n:人物{姓名:'楚原'})
RETURN n.姓名
//返回合并结果,没有重复项,不包含ALL,union 将删除合并结果集中的重复项

2.18 LOAD CSV

(1)简介
LOAD CSV用于将CSV文件加载至Cypher语句中。
company.csv 文件如下:

 Id,Name,Year
1,Alibaba,1999
2,Tencent,1998
3,Yahoo,1979
4,Google,1998

(2)LOAD CSV支持四种URL:http, https, ftp, file
注意:在 Galaxybase 数据库中 LOAD CSV 文件并使用 CREATE 语句创建的节点和关系类型必须已经在 schema 预设过(Galaxybase 不允许使用 CREATE 语句直接创建新的节点和关系实体)
通过以下四种方式加载 CSV 文件:

// http
LOAD CSV FROM 'http://www.chuanglintech.com/public/company.csv' AS line RETURN *
//含首行标题
LOAD CSV WITH HEADERS FROM 'http://www.chuanglintech.com/public/company.csv' AS line
CREATE (:company { name: line.Name, year: toInteger(line.Year)})
//不含首行标题
LOAD CSV FROM 'http://www.chuanglintech.com/public/company.csv' AS line 
CREATE (:company { name: line[1], year: line[2]})                                           
// https
LOAD CSV FROM 'https://www.chuanglintech.com/public/company.csv' AS line RETURN *                                              
// ftp ftp服务器必须设置允许匿名登录                                            
LOAD CSV FROM 'ftp://192.168.2.32/home/ftp/company.csv' AS line RETURN *                                          
// file Galaxybase 中 file 文件的相对路径在 Galaxybase 根目录下的 data 文件夹下                                            
LOAD CSV FROM 'file:///company.csv' AS line RETURN *                  

3. Cypher 高级特性

本节会对Cypher高级特性索引,查询调优,执行计划做一个基本阐述,若想深入了解Cypher高级特性,可以访问 Galaxybase Cypher官方使用文档。Cypher文档地址

3.1 索引

数据库索引是数据库中某些数据的冗余副本,以增加存储空间和较慢的写入为代价的,使相关数据的搜索更加高效。因此决定要建立索引的内容和不建立索引的内容是一项重要且通常不容易的任务。

想要快速查找评分为某一值或为某范围值内的电影,可以对电影类型的评分属性建立索引,建立完成后可以使用评分属性快速查找电影类型点,图数据库会自动应用创建的索引。

(1)创建电影类型评分属性的索引
请注意,该索引不是立即可用的,而是将在后台创建。示例如下:

// 方式1
CREATE INDEX ON :电影(评分)
// 方式2
CALL db.createIndex(":电影(评分)", "native-lucene-1.0")       

(2)查看索引
使用 Call 方法查看所有索引,索引的状态为 online 表示索引创建成功,可以使用。

CALL db.indexes 

(3)使用索引进行查找
使用索引需使用建立索引的类型和属性进行查找,示例如下:

// 方式1
MATCH (n:电影{评分:85}) RETURN n.电影名,n.评分
// 方式2
MATCH (n:电影) WHERE n.评分>90 AND n.评分<100 RETURN n.电影名,n.评分

3.2 查询调优

(1)查询分析模式
当希望通过查看查询的执行计划来分析查询时,有两种模式可供选择:
EXPLAIN
如果想查看执行计划但不运行该语句,请在 Cypher 语句前加上 EXPLAIN。 该语句将始终返回空结果,并且不对数据库进行任何更改。

PROFILE
如果要运行该语句并查看那些执行计划执行的细节,请在 Cypher 语句前加上 PROFILE。 这将运行语句,并跟踪每个执行计划输入输出的行数,以及每个执行计划与存储层进行了多少次交互以获取必要的数据。 请注意,使用 PROFILE 对查询进行性能分析会占用更多资源。因此,除非正在做查询性能分析,否则最好不要使用。

注意:在查询中明确地指出节点和关系的类型,将会使 Galaxybase 使用更准确的统计信息,从而制定性能更好的执行计划。也就是说,当知道节点或者关系只能是某种类型时,应该将其添加到查询中。在关系的开始节点和结束节点上声明类型可以帮助 Galaxybase 找到执行语句的最佳方式。

(2)调优示例
假设我们想要查找籍贯为“中国香港”的人物节点,性能不理想的 Cypher 是这样写:

MATCH (p { 籍贯:'中国香港'}})
RETURN p 

该查询将找到所有籍贯为“中国香港”的节点,但是随着数据库中节点数量地增加,它会变得越来越慢。通过分析查询,可以找到性能不佳原因。

使用PROFILE模式查看此次执行细节,自底向上开始阅读。

Operator Rows Variables
+ProduceResults 366 [p]
+Filter 366 [p]
+AllNodesScan 10025 [p]

从最后一行开始,我们注意到的第一件事是, “Rows” 列中的值似乎很高。 如果查看 “Operator” 列,我们将看到已使用 AllNodesScan,这意味着查询计划程序已扫描数据库中的所有节点。

因此,我们查找到了许多非人物类型的节点,这些都不是我们想要的点的类型,因此上诉方法似乎不是一个高效的查找方法。解决此问题的方法是,每当我们寻找节点时,都应指定类型以帮助查询计划器缩小搜索空间。 对于此查询,我们需要添加一个人物类型。

PROFILE
MATCH (p:人物 {籍贯:'中国香港'})
RETURN p
Operator Rows Variables
+ProduceResults 366 [p]
+Filter 366 [p]
+NodeByLabelScan 6826 [p]

可以看到,此次查询的执行细节中,最后一行的 “Rows” 值减小了,是因为此次查询我们不再扫描那些非人物类型的节点。NodeByLabelScan 运算符表明,我们首先通过对数据库中的所有人物类型节点进行线性扫描来实现此目的。完成之后,我们再次使用 Filter 运算符过滤这些人物类型节点,比较每个节点的籍贯属性。

在某些情况下,这可能是可以接受的,但是如果我们要经常按籍贯查找人,那么,如果在人物类型的籍贯属性上创建索引,就会看到更好的效果:

CREATE INDEX ON :人物(籍贯)

待索引创建完成后,再次运行查询

PROFILE
MATCH (p:人物 { 籍贯: '中国香港' })
RETURN p
Operator Rows Variables
+ProduceResults 366 [p]
+NodeIndexSeek 366 [p]

可以看到执行计划 NodeIndexSeek 运算符的 “Rows” 值变成了366,通过执行索引快速的找到了籍贯为“中国香港”的人物节点。

3.3 执行计划

(1)概述
执行计划定义
执行查询的任务被分解为多个操作,每个操作都执行特定的工作。 操作组合成一个树状结构,称为执行计划。 执行计划中的每个操作都表示为树中的一个节点。 每个操作将零个或多个行作为输入,并产生零个或多个行作为输出。 这意味着一个操作的输出将成为下一个操作的输入。执行计划中两个分支的操作将来自两个传入流的输入合并,产生单个输出。

执行模型
执行计划的评估从树的叶节点开始。叶节点没有输入行,通常由诸如扫描和查找之类的操作符组成。这些操作符直接从存储引擎获取数据,从而导致数据库命中。叶子节点生成的任何行都将被管道输送到它的父节点,然后父节点又将自身输出行输送到它们的父节点,以此类推,一直到根节点。根节点生成查询的最终结果。

操作类型
执行计划操作分为两种类型,一种是流式操作,另一种是全量操作。查询操作通常是流式的:大多数操作开始产生部分行结果后,都会立刻将其通过管道传递给其父操作。这意味着子操作在父操作开始使用由子操作产生的输入行时可能还未全完执行完毕。

但是,某些操作(例如用于聚合和排序的操作)需要先汇总所有行,然后才能向父操作传递。在将任何行发送给其父级作为输入之前,此类操作需要完整地完成执行。这些操作称为“全量操作”,全量可能导致较高的内存使用率,从而导致查询性能问题。

统计信息
每个操作都带有统计信息。

•Rows
操作生成的行数。只有使用 PROFILE 模式执行 Cypher 时才有意义。

•EstimatedRows
这是操作符预期生成的估计行数。这个估计值是基于现有统计信息的近似数字。编译器使用此估计来选择合适的执行计划。

•DbHits
每个操作符都会要求 Galaxybase 存储引擎执行检索或更新数据之类的工作。数据库命中是存储引擎工作的一个抽象单元。触发数据库命中的操作在数据库命中 (DbHits) 中列出。

(2)数据库命中
每个操作将向存储引擎发送一个请求,以执行检索或更新数据等工作。数据库命中是存储引擎工作的一个抽象单元。以下列出能触发一个或多个数据库命中的所有操作:
•创建操作
创建一个节点;创建一个关系;创建一个新的点类型;创建一个新的关系类型;创建一个新的属性。

•删除操作
删除一个节点;删除一个关系。

•节点特定操作
使用 elementId 获取一个节点;获取节点的度;获取节点的类型;获取节点的属性。

•关系特定操作
使用 elementId 获取一个关系;获取关系的属性;获取关系的类型。

•通用操作
通过ID获取属性名称,或者通过键名获取属性键的ID;通过索引查找或索引扫描找到节点或关系;使用可变路径方式查询路径;找到最短路径;查询节点和关系的统计数量。

•Schema 操作
添加索引;丢弃索引;获取索引的引用。

•调用 procedure 方法

(3)执行计划操作
本节列举了部分执行计划操作,若想了解执行计划的更多使用细节,可以访问 Galaxybase Cypher官方使用文档。Cypher文档地址

1)All Nodes Scan
All Nodes Scan 操作符从存储层读取所有节点。
查询:

MATCH (n)
RETURN n
Operator Estimated Rows Rows DB Hits Page Cache Hits Page Cache Misses Page Cache Hit Ratio Variables
+ProduceResults 10025 10025 0 0 0 0.0 [n]
+AllNodesScan 10025 10025 10026 0 0 0.0 [n]

2)Directed Relationship By Id Seek
Directed Relationship By Id Seek 操作符通过 elementId 从存储层读取一个或多个关系。
查询:

MATCH (n1)-[r]->()
WHERE id(r)= '00000000000000030001000000000A09'
RETURN r, n1
Operator Estimated Rows Rows DB Hits Page Cache Hits Page Cache Misses Page Cache Hit Ratio Variables
+ProduceResults 1 1 0 0 0 0 anon[17], n1, r]
+DirectedRelationshipByIdSeek 1 1 1 0 0 0 anon[17], n1, r

3)Node By Id Seek
Node By Id Seek 操作符通过 elementId 从存储层读取一个或多个节点。
查询:

MATCH (n)
WHERE id(n)= 0
RETURN n
Operator Estimated Rows Rows DB Hits Page Cache Hits Page Cache Misses Page Cache Hit Ratio Variables
+ProduceResults 1 1 0 0 0 0.0 [n]
+NodeByIdSeek 1 1 1 0 0 0.0 [n]

4)Node By Label Scan
Node By Label Scan 操作从存储层读取具有特定类型的所有节点。
查询:

MATCH (person:人物)
RETURN person
Operator Estimated Rows Rows DB Hits Page Cache Hits Page Cache Misses Page Cache Hit Ratio Variables
+ProduceResults 6825 6825 0 0 0 0.0 [person]
+NodeByLabelScan 6825 6825 6826 0 0 0.0 [person]

5)Node Index Seek
Node Index Seek 操作使用索引查找节点。
查询:

// 先对人物类型籍贯属性创建索引
CREATE INDEX ON :人物(籍贯);
// 再对籍贯属性做查询
MATCH (person:人物 {籍贯: '中国北京'})
RETURN person
Operator Estimated Rows Rows DB Hits Page Cache Hits Page Cache Misses Page Cache Hit Ratio Variables
+ProduceResults 0 170 0 0 0 0.0 [person]
+NodeIndexSeek 0 170 172 0 0 0.0 [person]

-END

想了解更多图数据库相关知识请在微信搜索【创邻科技】关注该公众号!