neo4j cypher语法详细记录

创建

简单创建一个节点

1
2
3
4
5
6
7
create(
dis:Disease{
oid: "xxx1",
code: "111",
name: "2hh"
}
)

注意:create也可以和return搭配,直接返回结果。

当然,create也可以同时创建多个关系:

1
create (m),(s)

简单的创建一个关系线

比如已经有了两个节点,我们想关联上关系:

1
2
3
4
5
6
7
8
match(
a: Disease
), (
b: Disease
)
where a.code="111" and b.code="d1"
create (a)-[r:type]->(b)
return *

如果我们想给这个线设置一些属性,也没什么问题:

1
2
3
4
5
6
7
8
9
10
11
match(
a: Disease{
code: '111'
}
), (
b: Disease{
code: 'd1'
}
)
create(a) -[r:relation{name : a.name + '--' + b.name}]->(b)
return type(r), r.name

这样查出来的结果是:
Alt text
可以看到,我们成功的设置了r.name.

当然,刚才我们是假设节点已经存在的情况,当然也可以假设不存在节点,直接创建节点+关系了。

1
2
create p=(reba:Person{name: 'reba'})-[:WORK_AT]->(nazha:Person{name:'nazha'})<-[:WORK_AT]-(jt:Person{name: 'jt'})
return p

成功图如下:
Alt text

删除

清空数据库(慎用)

对于节点比较少的可以使用:

1
match (n) detach delete n

对于节点比较多的:
1、停掉服务;
2、删除 graph.db 目录;
3、重启服务。

delete 删除节点和关系

首先不删除干净连接的节点和关系是不能删除一个节点的。

比如首先有这个图:
Alt text

可见它是没有清除干净关系的,现在我们想删除它:

1
match(p:Person{name:'jt'}) delete p

执行的时候发现报Neo.ClientError.Schema.ConstraintValidationFailed的错。

我们找一个没有任何连接关系的进行删除,就没毛病了:

1
match(p:Person{id:124}) delete p

还有一种方式,就是删除一个节点,连带着它的关系全部删除,刚刚上面这种执行错误的,加一个detach关键字即可解决。

1
2
3
4
match(p:Person{
name: 'jt'
})
detach delete p

还有一种情况,我们只想删除关系,该怎么办?当然也是能够解决的:
我们现在有这样一个关系图:
Alt text

想把他们之间的关系删除掉,该怎么办?

1
match(p:Person{name: "reba"})-[r:WORK_AT]->() delete r

这样就能够把两者之间的关系给删除掉了!

remove 删除节点和关系中的属性字段

首先我们有这样一个节点:
Alt text

属性为:

1
2
3
4
5
{
"name": "诊断",
"code": "d1",
"oid": "xxx1"
}

用下面这段代码直接进行删除属性:

1
2
3
match(d:Disease{name:"诊断"})
remove d.code
return d.name, d.oid, d.code

结果变成:
Alt text

除了删除属性,我们还能删除label,什么是label呢?其实就是类名,比如用下面这段话,把刚刚诊断的类删掉:

1
2
3
match(d:Disease{name:"诊断"})
remove d:Disease
return d.name, d.oid, d.code

改(SET)

修改/添加属性

最简单的进行修改一个属性:

1
2
3
match(n {name: 'Andy'})
set n.surname = 'Taylor'
return n.name, n.surname

这里有一点需要注意的是,如果本身没有surname 这个属性,会自动增加属性。

还有一个高级写法,用到了case when

1
2
3
4
5
6
7
match(n{name: 'Andy'})
set(
case
when n.age = 36
then n End
).wordIn = 'Malmo'
return n.name, n.wordIn

这里 如果不满足age=36 便不会执行下面的语句,这样的话,返回的就是一个null,比如:
Alt text

一次修改/添加多个属性

1
2
3
match(n{name: 'Peter'})
set n.a1= '1' , n.a2 = '3'
return n

删除一个属性

将这个属性置为null,就是删除一个属性,如下:

1
2
MATCH (n { name: 'Andy' })
SET n.name = NULL RETURN n.name, n.age

删除所有的属性使用一个空的map和等号

这样即可删除节点所有的属性了。

1
2
3
MATCH (p { name: 'Peter' })
SET p = { }
RETURN p.name, p.age

完全copy一个节点或者关系

SET可用于将所有属性从一个节点或关系复制到另一个节点。这将删除复制到的节点或关系上的所有其他属性。

1
2
3
MATCH (at { name: 'Andy' }),(pn { name: 'Peter' })
set at = pn
return at.name, at.age, at.hungry, pn.name, pn.age

结果是:
Alt text

注意看两个参数!

设置一个节点属性所有从map中

1
2
3
MATCH (p { name: 'Peter' })
SET p = { name: 'Peter Smith', position: 'Entrepreneur' }
RETURN p.name, p.age, p.position

请注意,这个操作会删除原来所有的节点!那有没有不删除所有的做法呢?当然有:

设置一个节点所有属性,但是不覆盖原来的属性

其实和上面几乎一样,就是把=变成+=

1
2
3
match (p{name: 'Peter'})
SET p += { age: 38, hungry: TRUE , position: 'Entrepreneur' }
RETURN p.name, p.age, p.hungry, p.position

修改类(label)

修改一个:

1
2
3
MATCH (n { name: 'Stefan' })
SET n:German
RETURN n.name, labels(n) AS labels

修改多个:

1
2
3
match(n{name: 'Peter'})
set n:Swedish:Bossman
return n.name, labels(n) as labels

查询

简单的查询一个节点

1
2
3
4
match(
d: Disease
)
return d

简单进行条件类型的查询

1
2
3
4
5
6
match(
d: Disease{
oid: "xxx1"
}
)
return d

Return

return 的作用是在match匹配上了以后,选择哪些返回,如果能确定返回的属性,尽量不要全部返回!

  • Return的语法很简单,需要什么就返回什么,*表示返回所有的。

比如:

1
2
MATCH p =(a { name: 'A' })-[r]->(b)
RETURN *

返回的结果就是所有的可能性:
Alt text

  • return中,还可以像传统sql那样,通过as来改名:
1
2
MATCH (a { name: 'A' })
RETURN a.age AS SomethingTotallyDifferent
  • 对于没有属性的会返回一个null,比如:
    1
    2
    MATCH (n)
    RETURN n.age

如果返回的两个结果,一个由age这个属性,一个没有,那么有的正常返回,没有的返回null

  • 还可以像Java一样返回一个布尔运算,并且返回的是多个元素也支持:
    1
    2
    MATCH (a { name: 'A' })
    RETURN a.age > 30, "I'm a literal",(a)-->()

返回的是:
Alt text

  • 当然你也可以和DISTINCT连着用
    1
    2
    MATCH (a { name: 'A' })-->(b)
    RETURN DISTINCT b

OPTIONAL MATCH

这个和match差不多,区别在于当没有查到东西的时候,会返回一个null

比如下面这句话:

1
2
3
4
5
match(p:Person{
name: 'reba'
})
optional match (p) -->(x)
return x

结果是:
Alt text

可见是有值的,但是如果我们把指向方向换一下,变成:

1
2
3
4
5
match(p:Person{
name: 'reba'
})
optional match (p) <--(x)
return x

结果是:
Alt text

同样的,如果我们把optional去掉,结果是:
Alt text

MERGE关键字

这个关键字基本上是把create和match合并到一起,先看一个最简单的用法:

1
2
merge (robert:Critic{name: '111'})
return robert, labels(robert)

本身是不存在这个节点的,所以会直接创建,那么把这句话再执行一遍,发现此时的作用只是相当于match了。

当然也可以从已经存在的节点中获取值,比如:

1
2
3
MATCH (person:Person)
MERGE (city:City { name: person.bornIn })
RETURN person.name, person.bornIn, city

从已经存在的节点中,获取一些属性值,然后进行复制。当然,这个操作可以是批量的!

比如此处的结果是:
Alt text

on create

这个实际上是一个限定条件,表达的是当创建的时候,才执行,不创建就不执行,比如:

1
2
3
merge (c:Critic{name:'1112'})
on create set c.create = timestamp()
return c.name, c.create

这个语句中,如果数据库中已经存在了一个1112那么就不会set值,同样,如果不存在,那么就会执行set后面的部分。

on match

这个命令和上述表达差不多,不同的是它是匹配上了就进行set

1
2
MERGE (person:Person)
ON MATCH SET person.found = TRUE RETURN person.name, person.found

当然也可以同时设置多个属性值:

1
2
3
MERGE (person:Person)
ON MATCH SET person.found = TRUE , person.lastAccessed = timestamp()
RETURN person.name, person.found, person.lastAccessed

on create 和on match 合并

1
2
3
4
MERGE (keanu:Person { name: 'Keanu Reeves' })
ON CREATE SET keanu.created = timestamp()
ON MATCH SET keanu.lastSeen = timestamp()
RETURN keanu.name, keanu.created, keanu.lastSeen

现在数据库中是没有这个节点的,也就是说会进行创建,那么我们看看最后的结果:
Alt text

很明显MATCH后面的没有执行!

Merge relationships

MERGE 同样也能被用来match或者create关系。

比如已经存在两个节点,想给他们MERGE一下关系:

1
2
3
MATCH (charlie:Person { name: 'Charlie Sheen' }),(wallStreet:Movie { title: 'Wall Street' })
MERGE (charlie)-[r:ACTED_IN]->(wallStreet)
RETURN charlie.name, type(r), wallStreet.title

结果是:
Alt text

当然,我们也能够一下子处理多个关系,比如:

1
2
3
MATCH (oliver:Person { name: 'Oliver Stone' }),(reiner:Person { name: 'Rob Reiner' })
MERGE (oliver)-[:DIRECTED]->(movie:Movie)<-[:ACTED_IN]-(reiner)
RETURN movie

最后结果是:
Alt text

还可以创建一个无向的连接:

1
2
3
MATCH (charlie:Person { name: 'Charlie Sheen' }),(oliver:Person { name: 'Oliver Stone' })
MERGE (charlie)-[r:KNOWS]-(oliver)
RETURN r

批量操作

有一些批量操作的写法,能够帮助我们快速创建大量节点和关系,比如:

1
2
3
4
MATCH (person:Person)
MERGE (city:City { name: person.bornIn })
MERGE (person)-[r:BORN_IN]->(city)
RETURN person.name, person.bornIn, city

将所有Person中出生地和实际的城市直接挂钩!

最后结果是:Alt text

上面这句话,我们还可以改写下:

1
2
3
MATCH (person:Person)
MERGE (person)-[r:BORN_IN]->(city:City { name: person.bornIn })
RETURN person.name, person.bornIn, city

限定条件WHERE

首先我们有这样一张图:

对于where,没什么好讲的,和传统的sql中差不多:
比如下面这句话:

1
2
3
MATCH (n)
WHERE n.name = 'Peter' XOR (n.age < 30 AND n.name = 'Timothy') OR NOT (n.name = 'Timothy' OR n.name = 'Peter')
RETURN n.name, n.age

对label进行过滤

下面这句话可以直接对label进行过滤:

1
2
3
MATCH (n)
WHERE n:Swedish
RETURN n.name, n.age

不固定属性的过滤

1
2
3
4
with 'AGE' as propname
match (n)
where n[toLower(propname)] < 30
return n.name, n.age

结果是:
Alt text

属性存在性校验

1
2
3
MATCH (n)
WHERE exists(n.belt)
RETURN n.name, n.belt

Alt text

以xx字符串开头的写法

1
2
3
MATCH (n)
WHERE n.name STARTS WITH 'Pet'
RETURN n.name, n.age

结果是:
Alt text

以xx字符串结尾

1
2
3
MATCH (n)
WHERE n.name ENDS WITH 'ter'
RETURN n.name, n.age

字符串包含

1
2
3
MATCH (n)
WHERE n.name CONTAINS 'ete'
RETURN n.name, n.age

Alt text

NOT 的使用

1
2
3
MATCH (n)
WHERE NOT n.name ENDS WITH 'y'
RETURN n.name, n.age

正则表达式的使用

1
2
3
MATCH (n)
WHERE n.name =~ 'Tim.*'
RETURN n.name, n.age

还有一种不区分大小写的写法:

1
2
3
MATCH (n)
WHERE n.name =~ '(?i)AND.*'
RETURN n.name, n.age

结果是:
Alt text

根据null过滤

1
2
MATCH (person)
WHERE person.name = 'Peter' AND person.belt IS NULL RETURN person.name, person.age, person.belt

结果是:

Alt text

ORDER BY

首先记住,不能根据关系或者节点进行排序,只能根据属性!

然后看一个简单的写法:

1
2
3
MATCH (n)
RETURN n.name, n.age
ORDER BY n.age, n.name

一般order by都是放在return后面

SKIP

从头开始跳过几个数据,一般在Order by 的后面,如果没有order by 就放在return后面:

来看一个稍微高级点的写法:

1
2
3
4
MATCH (n)
RETURN n.name
ORDER BY n.name
SKIP toInteger(3*rand())+ 1

LIMIT

limit 一般是在最后了,控制展示的个数:

1
2
3
4
MATCH (n)
RETURN n.name
ORDER BY n.name
LIMIT toInteger(3 * rand())+ 1

WITH

假设现在有这样一张图:
Alt text

对聚合函数结果进行筛选

我们用这样一段话,来查询

1
2
3
4
match(David{name: 'David'}) --(otherPerson)-->()
with otherPerson, count(*) as cnt
where cnt > 1
return otherPerson.name

结果是:
Alt text

这句话表达的是David连接过去的节点,它向外连接关系大于1的那个节点。
所以很明显,就是Anders

同理,

1
2
3
4
match(Anders{name: 'Anders'}) --(otherPerson)-->()
with otherPerson, count(*) as cnt
where cnt > 1
return otherPerson.name

这个结果很明显就是Bossman了。

在使用collect之前对结果进行排序

1
2
3
4
match(n)
with n
where n.name = 'David' or n.name = 'Bossman'
return collect(n.name)

结果是:Alt text

限制搜索路径分支

1
2
3
4
5
MATCH (n { name: 'Anders' })--(m)
WITH m
ORDER BY m.name DESC LIMIT 1
MATCH (m)--(o)
RETURN o.name

最后结果是:
Alt text

总结一下,With关键字是连接多个查询的结果,即将上一个查询的结果用作下一个查询的开始。

unwind

unwinding a list

我们先看这样一句话,初步了解下unwind 的用法:

1
2
unwind [1, 2, 3, NULL] as x
return x, 'val' as y

结果是:
Alt text

creating a distinct list

1
2
3
4
with [1, 1, 2, 2] as coll
unwind coll as x
with distinct x
return collect(x) as setOfVals

结果是:
Alt text

Using UNWIND with any expression returning a list

其实是在合并列表

1
2
3
with [1, 2] as a, [3, 4] as b
unwind (a + b) as x
return x

Alt text

Using UNWIND with a list of lists

1
2
3
4
with [[1, 2], [3, 4], 5] as nested
unwind nested as x
unwind x as y
return x, y

Alt text

Using UNWIND with an empty list

这是一个用法,只要unwind的是一个[],那么不管一起返回的是什么,都会返回一个0rows

1
2
unwind [] as empty
return empty, 'literal_that_is_not_returned'

Alt text

Using UNWIND with an expression that is not a list

unwind可以被用来检测是不是一个list

1
2
unwind null as x
return x, 'some_itearl'

Alt text

如果我们直接unwind一个数字,会报错:

1
2
unwind 5 as x
return x, '11'

Alt text

FOREACH

对于这样关系的一张图:
Alt text

下面这句话,可以批量的进行修改属性

1
2
3
match p=(begin)-[*]->(END)
where begin.name = 'A' And END.name = 'D'
foreach(n in nodes(p)| set n.marked = TRUE)

结果是:

Alt text

CALL

使用CALL可以调用一些函数,比如来个最简单的调用一个库函数:

1
CALL `db`.`labels`

这样子可以把我们的所有类名全部列出来:
Alt text

但这一部分一般用到的应当不多,不作详细介绍。

UNION

union 就是把两个结果合并起来。

比如:

1
2
3
4
MATCH (n:Actor)
RETURN n.name AS name
UNION ALL MATCH (n:Movie)
RETURN n.title AS name

这样子查出来的结果是:
Alt text

如果不用union all改直接用union呢?

1
2
3
4
5
MATCH (n:Actor)
RETURN n.name AS name
UNION
MATCH (n:Movie)
RETURN n.title AS name

结果是:

Alt text

相当于取了个重