Neo4j 图数据库使用

我们在使用 Neo4j 图数据库时,经常会接触到结点、关系、属性、标签等概念。结点是描述对象的实体,类似于面向对象中一个实例化出来的对象。对象内部的实际数据我们用属性来描述。

我们都知道对象是有类型的,类型可以用标签来描述,在 Neo4j 图数据库中,一个结点对象 C 可以有多个标签,即多个类型,即:我们可以定义一个对象既属于 A 类型,也属于 B 类型 … 当对象有多个标签时,我们更倾向于把标签理解为分类。比如在查询时,可以查询某个分类下的所有结点。当查询 A 类或者 B 类下所有结点时,结点 C 都会出现。

假设我们有两个结点 C、D,这两个结点之间存在某种关系,例如:C 是 D 的男朋友,D 是 C 的女朋友,这个结点间的信息就可以用关系来描述。注意,关系是有方向的。也需要注意,有时,我们为了更进一步描述关系,可以给关系添加一些属性。

标签一般用大驼峰法表示,关系一般用全大写表示,属性一般用条驼峰法表示。

# Mac 安装 Neo4j
brew install neo4j

# 启动 Neo4j 服务
neo4j start

# 关闭 Neo4j 服务
neo4j stop

# 重启 Neo4j 服务
neo4j restart

Cypher 是 Neo4j 图数据库中的 SQL 语句,可以用来方便的创建结点、关系,以及根据条件进行查询相关信息。

1. 结点操作

结点操作主要有 CREATE、DELETE、MERGE 等关键字。我们先使用 CREATE 关键字来创建几个用于演示的结点:

create (:Test{name: 'aaa'})
create (:Test{name: 'bbb'})
create (:Test{name: 'ccc'})

# 查看添加的结点
match (n:Test) return n.name

接下来,使用 DELETE 来删除 name = ‘bbb’ 和 name = ‘ccc’ 的结点:

# 删除 name = 'bbb' 的结点
match (n:Test{name: 'bbb'}) delete n

# 删除 name = 'ccc' 的结点
match (n:Test) where n.name = 'ccc' delete n

# 删除所有 Test 类型的结点
match (n:Test) delete n

再回到创建结点的操作,有时我们想查询某个结点,当该结点不存在时,我们再新建结点。此时,可以使用 MERGE 关键字。MERGE 关键字不单单对于结点操作,也可以用于对结点关系的操作。

merge (n:Test{name: 'aaa'})
on create set n.age = 100
return n.name, n.age

2. 属性操作

属性操作可以使用 SET、REMOVE 关键字,注意结点和关系都有属性,所以这些关键字对属性的操作适用于关系操作和结点操作。

我们先使用 CREATE 新增几个结点:

create (:Person{name: '张三', age: 18})
create (:Person{name: '赵六', age: 19})

SET 关键字可用于新增、修改结点属性值。我们先把赵六的年龄修改为 20,把张三的年龄修改为 15。可以使用下面两种方法:

# 赵六的年龄修改为 20
match (n:Person{name: '赵六'}) set n.age = 20 return n.name, n.age

# 张三的年龄修改为 15
match (n:Person) where n.name = '张三' set n.age = 15 return n.name, n.age

新增结点属性也较为容器,直接查询到相关的结点,set 属性即可。例如:我们要给赵六增加一个性别的属性:

match (p:Person{name:'赵六'}) set p.sex = '男' return p.name, p.age, p.sex

2.2 REMOVE 属性

删除结点使用 DELETE,删除结点属性使用 REMOVE。

match (n:Test{name: 'aaa'}) remove n.age return n.name, n.age

3. 关系操作

创建关系时,主要分为给已存在结点创建关系,还是在创建新结点时创建关系。我们先看后者,创建两个结点,并指定两个结点为 FRIEND 关系。注意:关系是具有方向性的。

我们先创建两个 Student 结点,并指定 Obama 是 Trump 的朋友,这并不代表 Trump 是 Obama 的朋友。

create (a:Student{name: 'Obama'})-[r:FRIEND]->(b:Student{name: 'Trump'}) return a, b

我们现在指定 Trump 也是 Obama 的朋友,此时两个结点已经存在。基本思路是,先 match 两个结点,然后使用 merge 来创建结点之间的关系,merge 的优点是如果关系存在则不创建,否则则创建关系。

match (a:Student{name:'Obama'}), (b:Student{name:'Trump'}) merge (b)-[r:FRIEND]->(a) return a, b

假设,我们要删除 Obama 对 Trump 的朋友关系,这里要使用 DELETE 关键字。删除结点和关系必须使用该关键字。先查询下,Obama 结点对 Trump 结点的关系:

match (a:Student{name:'Obama'})-[r]->(b:Student{name:'Trump'}) 
return a.name, b.name, type(r)

接下来,使用 DELETE 删除这层关系:

match (a:Student{name:'Obama'})-[r]->(b:Student{name:'Trump'}) delete r

# 查看两个结点之间的关系
match (a:Student{name:'Obama'}), (b:Student{name:'Trump'}) return a, b

我们会发现双向的 FRIEND 关系,现在只剩下单向的 FRIEND 关系了。

4. 查询操作

先把演示数据库中所有的结点删除,我们构建一批新的结点,以及结点之间的关系。

match (n) detach delete n

4.1 构建结点关系

创建 Person 类型 5 个结点,Country 类型 2 个结点,如下所示:

# 插入 Peron 类型的 5 个结点
create (:Person{name: '张三', age: 18})
create (:Person{name: '李四', age: 18})
create (:Person{name: '王五', age: 16})
create (:Person{name: '赵六', age: 19})
create (:Person{name: '邓七', age: 17})


# 插入 Country 类型的 2 个结点
create (:Country{name: 'china'})
create (:Country{name: 'american'})

接下来,查看下所有结点的信息:

# 查询国家结点信息
match (n:Country) return n.name as 国家

# 根据年龄降序查看所有 Person 信息
match (n:Person) return n.name as 姓名, n.age as 年龄  order by 年龄 desc

我们开始构建结点之间的关系:

  1. 张三和李四是王五的朋友
  2. 王五是赵六、邓七的朋友
  3. 李四和邓七生活在中国
  4. 张三、王五、赵六生活在美国
  5. 美国是中国的附庸国
  6. 张三、王五、赵六增加新的 VIP 标签

我们接下来,使用 cypher 来构建出这些结点的关系

# 张三和李四是王五的朋友

match (p1:Person{name:'张三'}),(p2:Person{name:'李四'}),(p3:Person{name:'王五'}) 
merge (p1)-[:FRIEND]->(p3)
merge (p2)-[:FRIEND]->(p3)
return p1, p2, p3

执行结果如下图所示:

# 王五是赵六、邓七的朋友

match (p1:Person{name:'王五'}),(p2:Person{name:'赵六'}),(p3:Person{name:'邓七'}) 
merge (p1)-[:FRIEND]->(p2)
merge (p1)-[:FRIEND]->(p3)
return p1, p2, p3

执行结果如下图所示:

# 李四和邓七生活在中国

match (p1:Person{name:'李四'}),(p2:Person{name:'邓七'}),(c:Country{name:'china'}) 
merge (p1)-[:LIVE_IN]->(c)
merge (p2)-[:LIVE_IN]->(c)
return p1, p2, c

执行结果如下图所示:

# 张三、王五、赵六生活在美国

match (p1:Person{name:'张三'}),(p2:Person{name:'王五'}),(p3:Person{name:'赵六'}),(c:Country{name:'american'}) 
merge (p1)-[:LIVE_IN]->(c)
merge (p2)-[:LIVE_IN]->(c)
merge (p3)-[:LIVE_IN]->(c)
return p1, p2, p3, c

执行结果如下图所示:

# 美国是中国的附庸国

match (c1:Country{name:'china'}), (c2:Country{name:'american'}) 
merge (c2)-[:BELONG_TO]->(c1)
return c1, c2

执行结果如下图所示:

# 张三、王五、赵六增加新的 VIP 标签

match (p1:Person{name:'张三'}),(p2:Person{name:'王五'}),(p3:Person{name:'赵六'}) set p1:VIP, p2:VIP, p3:VIP return p1, p2, p3

# 查询 VIP 分类下所有的结点
match (n:VIP) return n.name, n.age, labels(n)

到目前为止,我们已经构建了一个较为复杂的对象关系网。查看所有结点之间的关系:

match (n) return n

执行结果如下图所示:

4.2 查询结点数据

# 1. 查询所有的 Person 类型结点
match (p:Person) return p.name as 姓名, p.age as 年龄

# 2. 查询所有的 Country 类型的结点
match (c:Country) return c.name as 国家

# 3. 查询所有王五的朋友,注意: 王五指向的朋友
match (:Person{name:'王五'})-[:FRIEND]->(p:Person) return p.name, p.age

# 4. 查询张三朋友的朋友
match (:Person{name:'张三'})-[:FRIEND]->(:Person)-[:FRIEND]->(p:Person) return p.name, p.age

# 5. 查询王五朋友生活的国家
match (:Person{name:'王五'})-[:FRIEND]->(p:Person)-[:LIVE_IN]->(c:Country) return c.name, collect(p.name)

# 6. 查询所有 Person 之间的关系
match (p1:Person)-[r]-(p2:Person) return p1.name, type(r), p2.name
match (p1)-[r:FRIEND]-(p2) return p1.name, type(r), p2.name

# 7. 查询具有 LIVE_IN 关系的结点
match (p)-[r:LIVE_IN]->(c) return p.name, type(r), c.name

5. 约束操作

# 创建索引
create index on:Student(name)

# 删除索引
drop index on:Student(name)

# 创建唯一索引
create constraint on (s:Teacher) assert s.name is unique

# 删除唯一索引
drop constraint on (s:Teacher) assert s.name is unique
未经允许不得转载:一亩三分地 » Neo4j 图数据库使用