Announcement: All data comes from the book "Building Web Applications with Python and Neo4j", just for study & not for commerce.  模式及模式匹配(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  在这个例子中,系统首先搜寻TEACHER和STUDENT标签的节点,然后在这些节点中再找寻符合TEACHES联系的。  双向就是不需要箭头标识了,如下: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.agereturn x  此时,只有一个人符合要求:(2)Where从句中使用Pattern  对于一个集合而言,如果是空集,那么就代表false,非空则表示true。可以使用in这个关键词来进一步限定:match (x)where x.name in ["John", "Andrew"] and x.age is not NULLreturn x  当然,也可以使用not和正则表达式进行过滤:match (x)where x.name =~ "J.*"return x  在这种情况下,不是= ~"xxx",而是 =~ "xxx",也就是说=~这是一个符号,千万别写错了。 好了,以下要介绍一些此书上没有技巧:  [1] 使用别名:with 'AGE' as hahamatch (x)where x[toLower(haha)]return x  注意,Neo4j中属性名是大小写敏感的,如果写成x.AGE,则系统会提示并没有该属性:  但是,“.”并不等同于“[]”,比如如下写法就是错误的:  Why?我终于发现,并非“.”不等同于“[]”,而是二者确实相等,但用法有讲究。对于[]而言,其间必须是常量,所以当我把x[age]写成x['age']后,就顺利通过了,而且返回结果与x.age一样:  [2] 使用exists()函数进行属性检验:match (x)where exists(x.age)return x.name  以前使用过has(),但现在被exists()代替而移除了。  [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 xorder by x.age skip 3 limit 2  返回的就是“不要前3个,只要第4和第5”。确实很灵活!(4)with从句  with也是非常有用的一种从句,在介绍with之前,需要先研究一下“,”。比如在如下语句中,逗号是作为并列出现的,结果返回x和y两个人的信息,包括return语句中的x,y之间的逗号也都是这种用法。match (x{name:"John"}),(y{name:'Annie'})return x,y  好的,继续,对于如下的初始情况:  执行以下的语句会有什么结果?match (x{name:'Lisa'})return count(y)  我觉得应该是2,但我错了,结果是4。如下图:  为什么会是这样?也就意味着把两个间接的联系也算上了?好吧,自己再试试,这次用双向试试。结果大跌眼镜,依旧是4:match (x{name:'Lisa'})--(y)--()return count(y)  用Brandley的结果竟然是8,使用有方向的话是3。我又重新核对了一遍预设的所有联系,确实不应该是3。难道是BUG?(需要换个版本试试)  OK,话说回来,让逗号出现在with中,书中的例子如下:match (x{name:'Bradley'})--(y)-->()with y, count(*) AS cntwhere cnt > 1return y  返回值是Matthew,这没问题。因为符合模式的所有记录中,只有Matthew超过两条联系。问题来了,必须写with y才能限定吗?如果我去掉y呢?match (x{name:'Bradley'})--(y)-->()with count(*) AS cntwhere cnt > 1return cnt  cnt的值就变成了3,好的,我再加上y,看看cnt值:match (x{name:'Bradley'})--(y)-->()with y, count(*) AS cntwhere cnt > 1return cnt  这时,返回的cnt就成了2了。上述的尝试充分说明了with y, xxx这个模式中,逗号前的y起到限定的作用,如果不加y,那么:match (x{name:'Bradley'})--(y)-->()return count(*)  记录数就是3,表示命中的自Bradley发出的3条联系及其节点,如果推论正确,那么添加一个z表示目标节点,那么count(z)就应该是2个:match (x{name:'Bradley'})--(y)-->(z)return count(z)  结果是3个,但是如果我加上distinct,则变成了2个: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)unionmatch (x:FEMALE)-[:FRIEND]->() return x.name, labels(x)  labels()返回的是全部的标签,有几个返回几个,如下图所示:  但是,要注意:没有label()这个返回一个标签的函数。 这一篇博文更长,终于告一段落了。接下来会开启第三篇。                                               五岳之巅                                   2017年5月23日(孟菲斯时间)                                                  20:37                                             终稿于Dorsey
12-17 16:03