模式及模式匹配(Pattern and Pattern matching)此乃Cypher的核心,描述了我们想要查找、创建或更新的数据的形状。不理解模式和模式匹配,就写不出既有效果又有效率的查询。
一、数据准备
首先,输入如下命令清空当前数据库:
- match (n)-[r]-(n1) delete n,r,n1;
- match (n) delete n
然后,创建一堆男人和女人:
-
CREATE (bradley:MALE:TEACHER {name:'Bradley', surname:'Green',age:24, country:'US'})
CREATE (matthew:MALE:STUDENT {name:'Matthew', surname:'Cooper',age:36, country:'US'})
CREATE (lisa:FEMALE {name:'Lisa', surname:'Adams', age:15,country:'Canada'})
CREATE (john:MALE {name:'John', surname:'Goodman', age:24,country:'Mexico'})
CREATE (annie:FEMALE {name:'Annie', surname:'Behr', age:25,country:'Canada'})
CREATE (ripley:MALE {name:'Ripley', surname:'Aniston',country:'US'})
- MATCH (bradley:MALE{name:"Bradley"}),(matthew:MALE{name:"Matthew"})WITH bradley, matthew CREATE (bradley)-[:FRIEND]->(matthew) , (bradley)-[:TEACHES]->(matthew);
- MATCH (bradley:MALE{name:"Bradley"}),(matthew:MALE{name:"Matthew"})WITH bradley,matthew CREATE (matthew)-[:FRIEND]->(bradley);
- MATCH (bradley:MALE{name:"Bradley"}),(lisa:FEMALE{name:"Lisa"})WITH bradley,lisa CREATE (bradley)-[:FRIEND]->(lisa);
- MATCH (lisa:FEMALE{name:"Lisa"}),(john:MALE{name:"John"})WITH lisa,john CREATE (lisa)-[:FRIEND]->(john);
- MATCH (annie:FEMALE{name:"Annie"}),(ripley:MALE{name:"Ripley"})WITH annie,ripley CREATE (annie)-[:FRIEND]->(ripley);
- MATCH (ripley:MALE{name:"Ripley"}),(lisa:FEMALE{name:"Lisa"})WITH ripley,lisa CREATE (ripley)-[:FRIEND]->(lisa);
二、模式简介
(1)Pattern for Nodes
匹配节点是最基本也是最简单的一种,使用括号进行描述。但是要注意,如果不额外使用属性或标签,那么括号可以省略:
- MATCH (a) return a
- 等价于:
- MATCH a return a;
(2)Pattern for Labels
就是增加“:标签”进行限定,需要说明的是,可以同时使用多标签,起到交集的作用,如下第二句就使用了多标签:
- MATCH (n:MALE) return n;
- MATCH (n:MALE:TEACHER) return n;
(3)Pattern for Relationships
联系就是两个给定节点之间的连接,既可以是单向的,也可以是双向的,由[]和命名组成。
看一个单向的例子:
- match (a:TEACHER)-[b:TEACHES]->(c:STUDENT) return a,b,c
双向就是不需要箭头标识了,如下:
- match (a:MALE)-[b:FRIEND]-(c:FEMALE) return a,b,c
(4)Pattern for property
属性的匹配使用的是花括号和键值对,其间使用都好分隔,如下:
- match (a:MALE{ name:"John", age:24} return a
三、使用Where从句
(1)Where
如果仅仅使用Pattern并不能充分地满足要求,别懵逼,还有Where在。可以使用Where进一步过滤数据,但是要注意Where条件句本身并不能单独使用,只能用在match、optionalmatch、start或with的后面。比如:
- match (x)
- where x.age < 30 and x.country = "US"
- return x
(2)Where从句中使用Pattern
对于一个集合而言,如果是空集,那么就代表false,非空则表示true。可以使用in这个关键词来进一步限定:
- match (x)
- where x.name in ["John", "Andrew"] and x.age is not NULL
- return x
- match (x)
- where x.name =~ "J.*"
- return x
好了,以下要介绍一些此书上没有技巧:
[1] 使用别名:
- with 'AGE' as haha
- match (x)
- where x[toLower(haha)] < 30
- return x
但是,“.”并不等同于“[]”,比如如下写法就是错误的:
Why?我终于发现,并非“.”不等同于“[]”,而是二者确实相等,但用法有讲究。对于[]而言,其间必须是常量,所以当我把x[age]写成x['age']后,就顺利通过了,而且返回结果与x.age一样:
[2] 使用exists()函数进行属性检验:
- match (x)
- where exists(x.age)
- return x.name
[3] 字符匹配:
这绝对是一把利器,使用starts with、ends with或contains,匹配字符串以何种模式开始,以何种模式结束或者其中包含什么。非常便利!比如:
- match (x)
- where x.name starts with "B"
- return x.name
- match (x)
- where x.name contains "a"
- return x.name
(3)其他从句
[1] order来排序(默认是升序,支持混排)
[2] limit来限定返回数,skip则表示忽略最前面的。从而使用limit和skip的组配,可以取到中间的值:
- match (x)
- return x
- order by x.age skip 3 limit 2
(4)with从句
with也是非常有用的一种从句,在介绍with之前,需要先研究一下“,”。比如在如下语句中,逗号是作为并列出现的,结果返回x和y两个人的信息,包括return语句中的x,y之间的逗号也都是这种用法。
- match (x{name:"John"}),(y{name:'Annie'})
- return x,y
执行以下的语句会有什么结果?
- match (x{name:'Lisa'})<--(y)--()
- return count(y)
为什么会是这样?也就意味着把两个间接的联系也算上了?好吧,自己再试试,这次用双向试试。结果大跌眼镜,依旧是4:
- match (x{name:'Lisa'})--(y)--()
- return count(y)
OK,话说回来,让逗号出现在with中,书中的例子如下:
- match (x{name:'Bradley'})--(y)-->()
- with y, count(*) AS cnt
- where cnt > 1
- return y
- match (x{name:'Bradley'})--(y)-->()
- with count(*) AS cnt
- where cnt > 1
- return cnt
- match (x{name:'Bradley'})--(y)-->()
- with y, count(*) AS cnt
- where cnt > 1
- return cnt
- match (x{name:'Bradley'})--(y)-->()
- return count(*)
- match (x{name:'Bradley'})--(y)-->(z)
- return count(z)
- match (x{name:'Bradley'})--(y)-->(z)
- return count(distinct z)
也就是说,有向联系的结果加入distinct是正确的,没有问题,不加就是4和2。无向联系就见了鬼了:
更加奇怪的是,一下结果并不返回John:
天啊,神啊。我终于发现是怎么回事了,都是我的错!怪我并没有理解的很深。
细细讲一下,我一直把(x)--(y)--()当成了一组联系,实际上这是错误的,因为联系并不是用()表示,而是用[]表示。看看,不论有方向还是没有方向,这下全对了:
好吧,现在主要来看()--()--()三个括号的连用。那么我先试试-而不是--,如:
很显然,结果是错误的。也就是说match (x{name:'Lisa'})-(y)-(z),这种写法就是不对的。当然,换成--就没有语法错误了:match (x{name:'Lisa'})--(y)--(z) return count(*),但结果是4。现在我明白,因为(a)--(b)--(c)表示节点之间的连接关系,就是说要满足a是Lisa,而且b和a是连接的,并且c还要和b相连。通俗地讲,就是以a为中心,向外扩2层。如果我理解的是对的,那么match (x{name:'Lisa'})--(y)--(z) return z,结果就应该是最外层的两个节点了。而且match (x{name:'Lisa'})--(y) return count(*),就应该与Lisa直接相连的3个节点罗。试试,检验一下:
恭喜自己,虽然费了番周折,但终于搞定!反过来说,对于联系也可以这么玩:[a]-(b)-[c],我没试,但我相信是可以的。Cypher真的很灵活!
接来下,使用with x增加限定条件,进行创建。但我发现不论有没有这个with x,都是一样的,创建了三个kai:
(5)union和union all从句
union的用法与SQL一样,用于连接两个Match,返回结果中剔除了重复记录。但union all功能一样,但不剔除重复记录。
- match (x:MALE)-[:FRIEND]->() return x.name, labels(x)
- union
- match (x:FEMALE)-[:FRIEND]->() return x.name, labels(x)
但是,要注意:没有label()这个返回一个标签的函数。
这一篇博文更长,终于告一段落了。接下来会开启第三篇。
五岳之巅
2017年5月23日(孟菲斯时间)
20:37
终稿于Dorsey