ITPub博客

首页 > > > 微学堂第72期:Cypher语句 第三节(共三节)

微学堂第72期:Cypher语句 第三节(共三节)

作者:cynthia 时间:2019-03-01 18:18:28 0 删除 编辑

微学堂第72期:Cypher语句 第三节(共三节)

                                   

Neo4j是全球领先的图数据库,它是原生的图数据库、并通过Cypher查询语言实现对图数据的查询、更新和管理。Neo4j的图数据模型叫做Label Property Graph (LPG), LPG可以有很多意思,例如“液化石油气”(Liquefied Petroleum Gas”,这里Neo4j的图模型叫做标签属性图模型,叫做“Label Property Graph”标签属性图Property Gragh(LPG)。它关键的内容就是在图模型当中,Neo4j的图数据库是以点和边来保存数据,在点和边上也可以定义属性,属性的概念和关系型数据库表的字段是一样的,节点是有标签,标签我们可以暂且当做是关系型数据库的表的表名,关系可以理解为主键、外键、连接表(或者称作关系表)的记录。这就是组成图数据库的基本元素,基本数据模式。

举个例子,我们这有两个人,还有一辆车。两个人他们有属性,他们的属性可以分别是姓名,出生年月日,或社交网络号。不同的节点他们可以有相同的标签,这里的标签都是“person”,有相同标签的节点他们也可以有不同的属性,这个跟关系数据库不太一样,关系数据库当中如果表里有字段,那么要求所有的表中所有的记录都应该有相同的字段,在图数据库当中,这个并不是强制的,可以有的节点有三个属性,有的节点有四个属性,这都是允许的,这个更适合现在大数据环境下的非结构化数据和实时数据对灵活性的要求。

节点和节点之间有关系,比如说这有“LOVES”“LIVES WITH”这种关系。关系是用来连接节点的,那么在Neo4j当中,在定义关系的时候,关系必须是有方向的,比如LOVES它的关系,方向是从左向右或者从右向左。如果两个节点之间的关系是双向的,要保存这样的关系就需要建立两个关系,从左到右和从右到左。但是在查询的时候关系可以指定方向,也可以不指定方向,那么不指定方向的情况下就是从左向右或者从右向左。现实生活当中有些关系的确是无向的,比如亲戚关系,还有就是同事关系;有些关系是有向的,比如是这里的“LOVES”“DRIVES”或者“OWNS”,这个完全是根据业务模型的需求来决定,还有一个重要的事情是在节点可以有多个标签,也可以有一个标签,或者没有标签,比如说一个节点这里是“Dan”它可以使用“person”,它也可以是父母,也可以是员工,也可以是任何其他的标签,但是它也可以没有标签。这跟关系数据库中不一样就是,关系数据库的记录必须存在一个表当中,而且只能存在一个表当中。在图数据库当中,一个节点可以没有标签,也可以有一个标签或者多个标签。属性可以定义在节点上面,也可以定义在关系上面,这就给节点和关系提供更加丰富的语义。

                                 

这就是Neo4j原生图数据库的基本数据模式,就是它有节点,关系,来保存数据。在节点和关系上面都可以有属性,节点的类型或者节点的类别、标签,就是用标签的方式,进行分类分组。关系有关系的名称,关系的名称也是关系的类型。但是关系只能有一个类型。

我们回到前面那个例子当中,如果两个人之间有多种关系就必须创建多个关系的数据。

Cypher是Neo4j的图查询语言,它是模式匹配,重要的一点需要理解的是和关系数据库的SQL不同,它是一个路径查询语言,我们后面会看到越来越多的例子—实际上什么叫模式匹配,什么叫路径。我们查一个路径,根据你的需求,需要深入理解这个含义。Cypher是声明型的,就像SQL,它不是过程型的。也就是说,只要在Cypher里面写好你的查询逻辑,要开始的节点和要结束的节点,要匹配的关系类型,怎么执行这是数据库引擎的事情。

我们来看一个Cypher的例子,比如说在图数据库当中,两个节点中有一个关系。

比如说这样的节点,两个都是“person”代表人的节点,他们都各自有个属性“name”,一个值是“Dan”,一个是“Ann”, 如果我们要代表“Dan”LOVES “Ann”,我们创建两个节点,圆括号用来包括这个节点的,“:Person”代表了这个节点所拥有的标签,{name:},“name”代表的是属性,这个属性有一个值是个字符串,是“Dan”;类似的,可以与“Ann”,代表“Ann”这个人的节点的也是一种类似的标签“Person”。属性是“name”用圆括号括起来,定义它们之间的关系用“LOVES”-[:LOVES]->后面跟的名称就是关系的类型,那么关系的方向是用“<”表示从左向右或者“>”从右向左,用字符的模式来表示一个关系。

如果我们要创建这样的关系就在前面加“CREATE”,创建节点、关系,如果我们在Neo4j中运行这么一条命令,它会做三件事情:第一件它先创建一个标签为“Person”的节点,它有一个name属性,它的属性值是“Dan”。第二步它又创建了另一个有“Person”标签的节点,它有一个属性,它的属性值是“Ann”,第三步它创建一个关系,来连接这两个节点,关系的类型叫“LOVES”。这是在Neo4j当中怎么用Cypher来创建关系所连接的节点。

如果我们要查询,比如说我们要知道一个叫“Dan”的喜欢的人是谁,我们把“CREATE”换成“MATCH”,我们不用“SELECT”,我们用“MATCH”,那么从 “MATCH(:Person{name: “Dan”})”其实相当于“select * from Person where name = ‘Dan’”。相当于我们从所有的节点当中找一个根据它的name的属性值,找到这个节点以后,我们沿着它所有的“LOVES”关系,来找到所有的其他的“Person”节点,并把找到的节点放到这个“whom”这个变量里面,实际上这个变量是一个“列表”,因为它有可能是很多人,也有可能是空、空的一个列表。那么它最好找到的节点是一个“Ann”。这是一个我们进行搜索的命令就是“MATCH”,从一些节点出发,沿着某些关系找到其他节点,这是最常用的Cypher中查询数据查询路径的方法。

Cypher的基本语法中,节点必须用圆括号标起来,标签名前面必须用冒号,

这里每一个节点都可以用多个标签,这里有两个标签。那么标签与标签之间用冒号分隔,这里n这个节点有“Label1” “Label2”两个标签、也可以没有标签 ,节点可以有属性,属性是用{prop: ‘value’}属性名:属性值。

关系就是用-[],如果用--,那说明我们不限定关系的名称,任何关系都可以,那么用“<,>”来代表关系的方向。

 

有的时候我也可以给关系指定一个变量,比如说我们需要返回这个关系,我们可以加一个“ [r:TYPE]-> ”表示关系类型,那么这个关系查询出来之后,会把这个关系保存在“r”这个变量中。关系也可以有属性,关系在创建的时候必须指定方向,而在查询的时候也可以不指定方向。

模式就是我们所要查询的表达式,其实在图数据库当中,很多时候,你要想象一个查询怎么编写,可以想象一个GPS导航:就是我们指定从哪里出发、然后我们要去哪里;中间走什么路,完全是GPS决定的,可以是最短的、最快的,或者是最便宜的、是不收费的。不同的查询模式,基本的概念其实是很类似的,都是说一个路径的查询。

这里有些例子,比如说我们认识一个人叫“Alice”,通过她所认识的人“KNOWS”这个关系找到一个叫“Bob”的人,那么这个“Alice”在数据库当中认识“Bob”这个人,那么这个模式匹配就会返回一条路径。那么如果是“Alice”和“Bob”他们之间互相认识有多条路径的话,也会返回多个路径,如果不认识的话,就返回空。这是在图数据库当中查询的一个最基本的方法。

“MATCH”开始一直查询(m:Movie),其中()代表节点,m是个变量,Movie是它的标签,这是我们返回所有有“Movie”标签的节点,相当于关系里的数据库表名。从Movie这个表里返回所有的记录,但我们这里是返回数据库当中所有的有Movie标签的节点。

 

再稍微复杂一点,从“Person”出发,如果我们要想知道谁曾经在哪些电影中有担任过角色,那么从“Person”出发,沿着[ACTED_IN]这么一个关系,找到所有的“Movie”,再返回“p,r,m”p代表人的节点,r代表“ACTED_IN”的关系,代表m电影节点的这些变量的节点。

有时候对复杂的路径,或者我们不想写对于每一个节点指定一个变量,我们可以用“path”,就是MATCH path = 然后写一个很长很长,不管怎么复杂的一个路径,它会返回这个“path”。实际上呢,它会返回节点-关系-节点-节点关系-节点这么一个数组、一个列表,通过当中的节点-关系-节点这么一个三元组我们会知道,这是一个路径的完整结构。

我们可以返回一个节点,这里是m,m代表包含所有Movie标签节点的一个列表。

有的时候我们可以只返回属性,返回一个类似于表状的结构,这里我们返回电影名和它这个发布年份。有的时候我们需要在其他的传统BI报表工具当中来处理数据或显示数据,显示从Neo4j图数据库当中读取的数据,已返回节点关系恐怕比较难显示,可以通过返回的属性值得到一个类似于列表状的结构,那么饼图、线图这种传统的报表分析就可以使用了。

所有的关键字大小写都不敏感,对于我们自定义的节点标签、关系类型、属性名、属性值都是大小写敏感的。

这个比较好理解,比如这里大小写不敏感“Match,return”就是关键字,那么对于我们自己定义的名称是大小写敏感的。

 

有一些简单的命名规范,比如说关键字—全部大写,标签名—首字母大写。根据各个不同的企业、不同的组织,内部的一些开发规范可以去选择。

创建、删除和更新节点,这里我们举一个比较熟悉的例子,比如说一个简单的三国人物关系图,刘关张,再加上一个赵云曹操,那么为了简单起见呢,在人物和人物之间只有一个关系,它的关系类型就是“关系”。

那么来看下,我们可以使用一条 “CREATE”语句创建这么一个关系出来,比如说我们从“CREATE”(n1人物)人物,我在外面加了一个符号,这个符号是PC键盘数字“1”,数字“1”左边那个符号,这个(`)符号通常是不需要的,那么在什么情况下需要呢?如果你的标签,或者属性,名称当中有空格,或者有一些特殊字符,有时候写Cypher查询时它不认识它,出错,那么你可以用这个小符号(`)把它包括起来,这样Cypher的解释器就知道两个(`)中间就代表它的标签名或者关系。

我们来看这一长串做了什么事情,我们先创建了一个n1节点,n1是个变量,它的标签是人物,它有一个属性,属性值是“刘备”,那么当这个节点创建完成之后呢,这个节点的一个引用被保存在n1这个变量当中。同样我们又创建了一个人物叫关羽,把它的这个节点保存到n2当中,然后在他们之间创建一个关系,关系的名称叫“关系”我们用一个属性“relationship  ”来代表关系的不同类型、叫兄长。然后我们创建类似的n3是张飞,那么n2代表的是关羽,我们前面已经创建过了,创建成功之后呢,n2就包含了指向关羽这个节点的引用。n2关羽和张飞之间我们创建一个兄长关系,当然,兄长的兄长也是你的兄长,所以我们在刘备n1和张飞n3之间再创建一个兄长关系,在刘备和赵云之间我们创建的是主公关系,刘备和曹操之间创造一个对手关系,这么一长串就是创建了刚才所看到的这么一个简单基本的三国人物关系图。

有几件事情需要提示一下,就是关系在创建的时候必须有方向,在创建过程中创建完成后,如果我们给节点或者变量,创建成功之后,变量就会包含对节点的引用或者指针。有人会问到为什么这里会用关系来表示他们之间的关系,而不是用“兄长关系,主公关系,对手”,这实际上是一个建模的考虑,具体是我们是用一个很通用的名称作为关系的类型名、要把具体的关系类别作为属性,还是说具体的关系类别作为关系的名称,这个根据建模的需要,各有优劣,这里主要是作为简单的演示使用。

 

Cypher语句完了之后,我们可以看到使用CREATE同时可以创建多个节点,属性和关系,那么节点可以有一个或多个标签,关系必须指定关系类型,节点可以没有标签。

简单的查询比如说“MATCH(n)RETURN n”就相当于是从数据库当中返回所有的节点、从所有的表中返回所有的记录;还有一种就是“MATCH path = ()”这种不指定节点的标签也不指定变量 ,我们就把返回的所有可能的路径放到path里面,这里“--”是指指定关系的类别,也没有指定关系的层数,可以说从任何节点开始通过一层关系到达了任何节点,我们都把它返回出来,那么都会得到下面这张图。

但是有区别,第一个查询我们只返回了节点,没有去查标签,也没有查关系,这实际上是Neo4j浏览器自动将节点之间的关系从数据库中读取出来,这个设置是可以取消掉的,因为有的时候如果不注意,会觉得奇怪:为什么我的查询在屏幕上显示都有关系,但是我把它查询拷贝粘贴到应用程序客户端去执行,却看不到相应的关系?这是因为浏览器自动的帮你把关系加进去了。第二个返回的是路径,前面说过路径是节点-关系-节点-的三元组,这里是会返回所有的节点和关系的。在浏览器里,如果我们不想让浏览器自动的读取关系,可以通过设置,这里有一个连接结果节点,把这个选项去掉,那么,浏览器就不会再自动连接节点了。

更多的些例子,比如我们说寻找刘备节点,它的节点类型是用标签是人物,姓名是刘备,找到以后返回,相当于“MATCH(n:人物) WHERE n.name = ‘刘备’”使用一个WHERE查询过滤条件,根据名称进行过滤。我们要找到刘备的兄弟是谁,我们可以用“MATCH”从刘备出发先找到“name”的与刘备的人物节点,沿着兄长关系,找到其他的人物节点。当然我们知道刘备是大哥,如果我们想要找到刘备的哥哥怎么办,这里我们指定了一个关系方向,刘备是其他人的兄长,这里我们只能找到关羽张飞,如果我们要找刘备的兄长,或者说是我们甚至于其他关系的朝上的“祖宗八代”和朝下“祖宗八代”,我们只要把这个“<”去掉,就是两个方向都会寻找,沿着“亲属关系,兄长关系。弟弟或者小弟”很容易找到他的哥哥或者大哥,那么还有比如说寻找刘备的小弟(兄弟的兄弟),在关系的名称上面加上一个“*2”,表示2层关系之外,这里只会返回张飞一个人。还有比如我们寻找刘备的人物关系,社会关系,找到刘备之后呢,沿着关系,或指定关系的类型,找到所有其他的节点,返回这些节点。在Cypher中查询关系实际上有很多方法。

 

如果说“兄弟的敌人是我的敌人”,那么能不能找到谁是张飞的敌人?

敌人关系就是对手关系,我们的数据库当中目前只有对手这个关系,那么我找到张飞的敌人是谁呢,当然张飞也许有自己的对手,有敌人,如果存在一个关系从张飞出发,关系“relationship”的属性值是对手,那么可以找到。另外还有就是“兄弟的敌人”,那么先要找到张飞,沿着张飞的兄弟关系找到所有其他节点,然后从这些节点出发,再去寻找和他们有对手关系的节点,这就是找到“兄弟的敌人是我的敌人”的所有相关节点。

我们也可以对一个节点增加多个标签,先找到这个节点,然后使用“SET”标签,然后要显示一个节点的标签和属性,找到这个节点以后呢,“Labels”会显示这个节点的所有标签,因为节点可以有多个标签,所以返回结果是一个列表。还有就是“keys”返回节点所有属性,如果删除一个标签,我们用“REMOVE”,把这个标签去掉。然后返回表状的结构,只要返回这些属性值就可以了。

更改属性我们用“SET”,在Neo4j当中,我们没有属性值为空的属性,如果你“SET n.genre = NULL”实际上在数据库当中会把这个属性删除,我们是不保存空值的属性值的,这个是跟关系数据库不一样。

Data Architect或者说是这个架构师最喜欢看的,在数据库中做的事情就是看元数据,看数据库的模式,在Neo4j当中,我们可以通过叫“CALL db.schema”去显示数据库当前的元数据元模型,返回所有的标签类型以及他们之间的关系。

“db.schema”,如果想得到更详细的信息,可以使用“apoc.meta,graph”或者“apoc.meta.data”,可以找到更详细的比如说关于属性值,甚至于多少“同值”,多少关系的类型,更详细的情形,那么这个可以搜索相关的文档。

删除节点我们就 “delete”就可以了,找到节点,然后删除。但是,如果这个节点有关系存在,那么你不能直接“delete”,因为数据库中不能够存在只有关系、没有关系所连接的节点这种情况,这种数据会破坏引用完整性。如果要删除包含关系的节点,那么用“DETACH DELETE”,你也可以只删除关系,保留节点,找到一个节点,从它出发了所有关系,那么删除这些关系。

如果删除全库,最简单的方法就是删除Neo4j安装目录下的data/databases/graph.db文件夹,当然这需要先停止数据库服务,然后当Neo4j启动时候,如果这个文件夹不存在,他也会创建一个空的数据库。

 

Neo4j在创建数据,包括节点、关系、属性的时候会给每一个类型的数据增加一个id(n),这个id(n)是数据库内部的一个标识,外部是无法控制的,它实际上在存储当中代表着真正存储这个节点或者关系的一个物理位置的一个序列号。

因为Neo4j的存储模式是以图的模式存储,知道了它的id,就要通过越简单的数学运算,可以迅速的定位到这个存储,这个节点数据在物理硬盘上的一个位置,所以说,使用id访问节点是最快速的方法。如果要知道一个节点的id,我们需要用id函数,id(节点)。节点也可以有自己的id属性,这个id属性是你们的业务id,业务属性这个和数据库的属性是不一样的,数据库的id 是不一样的,他们的访问方法是“node.id”的方式访问。如果要使用数据库的内部id,就需要使用id函数来访问,这是一个很重要的区别。

 

Neo4j是一个叫“Schema-lite”的一个轻型模式数据库,它对这个数据的模式,没有像关系数据库要求那么严格,你可以创建、不创建、不定义任何标签或者关系类型,你只要开始加入数据,那么数据库会自动的根据你加入的数据来得到它的元数据。实际上你不用定义标签,你就可以创建,但是创建第一次创建节点时被定义,关系类型也是在第一次创建关系时被定义,这个有它的灵活性。但也有需要注意的地方,你在查询的时候,比如说这里,你写了一个“人物”,结果不小心,多打了个1在人物的后面,那么“人物1”这个标签在数据库当中是不存在的,但是当你运行到查询的时候,它不会告诉你“人物1”这个标签不存在,不像关系数据库如果表名不存在或者字段名不存在,它是会返回错误的,说到这个不存在,你没法执行的。在Neo4j当中它是可以执行的,但是你得不到任何结果,所以这个是需要注意的地方。在你们使用浏览器的时候,当你输入的查询当中有不存在的标签或者说是属性,或者是关系的时候,它会在这个输入框左边会有一个小小的三角形的黄色的一个警告,点开看一看,在你看到的时候你去点一点,然后它会提示你这个标签不存在。

因为图数据库其实最擅长是分析连接的数据,所以,通常图数据库中的节点和关系规模都很大,有的时候不知道数据库规模的时候,写查询想看一看有些什么东西的时候呢,建议你们都加上“LIMIT”,“MATCH”之后都加上“LIMIT”,尽量完全避免不要使用“MATCH (n) RETURN n”,相当于SELECT * FROM *”。当你知道标签,关系,类型的时候一定要加上标签名和关系做限定,这样的话能够提高查询的性能,也不会对数据库造成严重影响。

在图数据库当中,关系和数据实际上都是被保存下来的。这就是为什么当我们说要把不同类型的数据连接起来的时候,在关系数据库当中,我们通常是使用关系表或主键外键做连接的方式来寻找关联数据。在原生的图数据库当中存储模式也保存关系,所以说我们不用做连接。当我们知道一个人的朋友的朋友是谁的时候,我们先要找到他的朋友,那么他所拥有的朋友关系本身也是保存在数据库当中,只要数据库把这个关系,即指针读取出来,沿着这些关系找到其他的朋友就可以迅速的得到一个结果。所以在图数据库当中是不作连接的,我们称作“无需索引的邻接跳转”,因为原先在关系数据库当中主键外键都需要建立大量的索引,需要匹配主键外键的值,这也是为什么Neo4j 在处理复杂查询,还有深度关系遍历的时候,因为不需要做连接,在大量数据情况、多次跳转和遍历情况下,它的查询性能远远高于关系型数据库。

但是,图数据库也是需要索引的。需要索引的唯一目的就是当我们对节点或关系的属性建立了索引,可以根据索引的值找到相关的节点和关系。比如说,最简单的,我们前面看到的例子,如果我们对人物的name姓名属性建立了索引,就可以很快找到某一个名字代表那个人的节点,如果不建索引,它就必须做全库扫描,就是所有有person的标签的节点,然后标签里的每一个节点去扫描,看看属性值是不是我们所需要的值。这是它做索引的主要的目的。

这个是3.4版本之前的,目前来说所有的基本数据类型,包括数组等等都已经是本地数据库索引的,甚至对于大文本Neo4j整合Apache Lucene可以创建全文索引,这个全文索引在3.5版本之后也已经是数据库本地索引,就是你在做自动维护更新的时候也会把索引引起备份。

比如说,数据库模式索引你可以定义在节点上面,然后你要看当前所有的索引可以通过两种方法,一种是“CALL db.indexes”,或者使用“:schema”在这个浏览器当中输入就可以看到当前的索引。

 

 

也可以支持复合索引,在多个属性上面做索引,如果定义的索引时候,当我们使用查询,查询的分析器先会去寻找这个相关的索引存在不存在,如果存在的话,它就会使用索引去查找,速度就会快很多;如果不存在的话,才会做节点扫描。

有的时候,如果我们已经有大量的数据了,然后我们新定义一个索引,当定义完索引之后,这个定义索引的命令马上返回,但是索引的创建是一个后台的异步过程。也就是说,它会花点时间,如果你数据量很大的时候。这个时候,如果你想运行一个查询,而且这个查询涉及到了刚刚在这个属性上定义的索引,有可能会得到一个错误,告诉你索引还没有创建完成,那这种情况只能通过“CALL db.awaitlndex”可以等它这个索引创建完成之后,再运行查询。

前面提到Neo4j整合了Apache Lucene的全文索引包,我们不需要安装Apache Lucene服务器,就直接通过相应的过程和函数,就可以创建全文索引。

全文索引之后可以进行一些Apache Lucene相关的文本模式匹配的一些语法,来对属性和内容进行检索。这个时候是需要指明使用哪一个全文索引来做查询,这个在在线文档中都有提到。现在3.5版本已经不是APOC的过程,已经变成数据库本身的过程。

 

多数情况下数据库会自动寻找匹配的索引来查询。有的时候,出于查询优化的考虑也可以指定数据库来使用某一个查询或某几个查询,在运行Cypher的时候,这是一个性能调优的方法。

 

 

遗留索引是3.0以前版本的索引,现在也不推荐使用。那么如果有的话,在升级到新版本的时候,应该把同时把这些索引重新创建,这里我们就不讲了。

 

Neo4j的数据库模式其实就是两件事情, 一个是索引,一个是限制。那么虽然说“轻型模式数据库”,但是它还是有一些数据库模式的。存在三种限制,一个是属性的唯一限制,还有属性的存在性限制,还有复合键唯一性限制。前面忘提了一个,在索引当中,其实关系的属性中也是有索引的,就是节点和关系的属性都有索引。

在限制当中,如果我们用人的节点为例,我们要求人的姓名是必须存在的,就是存在性限制,我的人的身份证号码必须是唯一的,这是唯一性限制。

要创建一个限制,这个语法就用“CREATE CONSTRAINT ”然后在节点标签,然后确保它节点的某一个属性是 “UNIQUE”唯一的。这很类似关系型数据库中的主键。另外存在性限制,比如说对所有的人物节点,它的姓名属性必须存在,如果我们已经定义了限制,数据库会自动创建索引,所以不需要再创建索引了,这个就跟主键的概念一样的。标签和关系类型上面你是不需要再创建索引了,数据库会自动创建索引,那么还有一点就是说,限制和索引都是可以在数据库不存在数据的时候就事先编译好了,那么定义好之后,每次有新的数据插入、修改的时候,相应的索引和限制都会被运行,被更新。

关系性限制也是一样,就是我们确保关系上的某一个属性必须存在,有的时候也是比较重要的。

 

实际上,关于Neo4j图数据库的数据库模式是非常简单的,非常灵活的,它是原生的图数据库,它的查询展示处理全部是按照图的方式。它在做跨数据类型跳转,图的路径搜索等等,是不需要去做连接的,因为这些相关的连接,相关的跳转,数据也已经保存在数据库当中。Neo4j支持的模式,包括索引和限制,索引的唯一目的不是用来做连接,而是为了快速的沿着索引寻找具有某些值的属性,然后通过这些属性来定义到相关的节点。关系上也可以定义索引,通常来讲不通过属性值来搜索关系,通常是先找到节点,找到节点之后呢,再沿着节点某些关系,来找到更多的节点和更多的关系。外部索引就是全文索引,现在3.5的版本之后已经变成内部索引,主要是可以实现文本内的全文检索,也不需要再用APOC创建,主要用db相关的过程就可以创建。

关于这个创建索引,创建限制,我前面提到过会在Cypher查询当中会使用“USING INDEX”来指定要使用的索引,Cypher是解释型的,它会自动生成查询计划,但是并不是所有时候它的查询计划都是最优化的,最聪明的。有的时候我们需使用来告诉我们Neo4j怎么来运行一个查询。那么在后续查询优化的部分,我会进一步的讲解。

 

最后我们在看一看怎么在数据库当中避免重复创建节点和节点与节点之间的关系。

比如说我们使用“CREATE”,它每次都会创建相同的节点。如果我们在同一个“CREATE”语句,如果执行多次,它就会创建多个属性值一样的节点,除非我们在节点属性上面已经定义了限制,唯一性限制,比如说在这里“CREATE(n1 :‘人物’{name: ‘刘备’})”节点呢,如果没有定义限制,那么第二个刘备节点会被创建;如果我们定义了限制,那么就会出错。如果我们确定了姓名必须是唯一的,那么我们再次运行这个“CREATE(n1 :‘人物’{name: ‘刘备’})”,它就会报出错误,这就是为什么我们需要这个。有的时候用于导入的原数据会不可避免的重复,为了避免重复创建,我们是用MERGE。MERGE这个名称其实和合并节点没有任何关系,它的意思就是说我通过MERGE,如果有MERGE“(n1 :‘人物’{name: ‘刘备’})”它会根据标签名、属性名和属性值,但如果你定义了唯一性限制,所以你会更快去寻找这个数据库当中是不是已经存在一个叫刘备的、标签为人物的这么一个节点。如果存在的话,它就是什么都不做,它就不再创建;它如果不存在,它就会创建。那么第一句话 “MERGE(n1 :‘人物’{name: ‘刘备’})”,因为我们已经创建过刘备了,所以第一句话什么都不做,但也不会出错,它反正就是不做任何事情。那么第二句话,我们在创建一个“MERGE(n2 :‘人物’{name: ‘刘禅’})”,那么这个刘禅节点的在数据库当中还不存在,那么它就会创建这个节点,节点的标签是人物,属性是name,属性值是刘禅。第三步,我们“MERGE(n1) –[:‘关系’{relationship: ‘父亲’}]->(n2)”我们在刘备和刘禅之间创建一个父子关系,因为两个节点都存在了,之前刘禅这个节点是不存在的,这回创建一个关系,如果我们把这三句Cypher命令再执行一遍,那么什么都不会做了,因为刘备节点已经存在,刘禅节点也已经存在,他们之间的父子关系也已经存在。那么Neo4j怎么判断这个关系怎么存在?存在不存在是根据关系的类型,关系的属性,属性的值来判断是不是存在,还有关系的方向。那么这就是在使用MERGE语句在导入数据的时候是非常有效的,就是我们为了避免这个创建重复的关系和节点。

再举个比较复杂的例子,比如说有的时候我们想如果找到了这个节点,在数据库当中我们做一些事情:如果不存在,我们创建它,创建完了之后,我们再做其他的事情,这个也是经常碰到的。比如说雇员关系,公司和雇员之间的雇佣关系,那么在第一次加入这个公司呢,创建一个人员节点,我们MERGE一下,第一次创建。我们有一个关系的属性,比如说关系的类型叫做加入,它的属性有一个加入时间、加入次数。加入时间就是当前系统时间,如果这个员工离开了,后来又来了,那么我们在MERGE的时候,员工节点以前曾经创建过,因此不会再被创建一次,他们之间的雇佣关系也存在过,这个时候我们可以做的事情就是把它的属性值更新一下。比如说,把这个雇佣关系,加入时间更新为当前时间,加入次数+1,这样的话就很灵活。那就是我们可以使用 “MERGE ON CREATE”就是说第一句话如果Movie不存在呢我们就会执行先把节点创建,然后ON CREATE,做一些对属性复制的事情,“ON MATCH”就是说如果节点已经存在,那么做些其他的事情,做一些其他的属性复制。那么这可以实现非常非常复杂灵活和强大的功能。关于MERGE就讲这么多。

今天的课程就到这里,感谢各位。


来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/69900308/viewspace-3166083/ ,如需转载,请注明出处,否则将追究法律责任。

请登录后发表评论 登录
全部评论

注册时间:2018-11-27

  • 博文量
    43
  • 访问量
    1010