WHCSRL 技术网

Pyhton操作Neo4j图数据库实践(南北朝隋唐历史北朝主要人物知识图谱)

独孤信“第一岳父”之称很形象,他有三个女儿做了三个朝代皇后,以及杨坚、杨广、李渊等人关系。本文试着使用图数据库(neo4j)表达这段南北朝隋唐历史北朝主要人物知识图谱。
在这里插入图片描述

1. Neo4j与py2neo

neo4j目前是图数据库的主流,neo4j的Cypher语法简单直观,但是不便于流程化。如果习惯在python环境下处理数据,那么还是要用到python的neo4j库,即py2neo.

py2neo本身并不复杂,但要先适应它的思考模式。另一个问题是py2neo文档的示例较少,而且不同版本的py2neo挺不相同,容易弄混。本文基于Neo4j3.5.x及py2neo 2021.2.3。

创建neo4j图数据库有常用数据库接口(py2neo )和数据库语言(Cypher)两种方法,本文重点采用python程序py2neo 方法。

安装Pyhton操作Neo4j图数据库驱动接口
pip install py2neo

第一步:连接图数据库。

from py2neo import Graph,Node,Relationship
##连接neo4j数据库,输入地址、用户名、密码
graph = Graph('http://192.168.19.229:7474/',auth=('neo4j','1526') ) #username='neo4j',password='1526')
graph
  • 1
  • 2
  • 3
  • 4
Graph('http://192.168.19.229:7474')
  • 1

2. 创建实体与关系

2.1. 创建节点Node

##创建结点
# 如果使用lable='',则是表示Property Keys
#person_node_1 = Node(label='person',name='李渊',dynasty=['隋朝','唐朝'],post=['唐高祖']) 
person_node_1 = Node('person',name='李渊',dynasty=['隋朝','唐朝'],post='唐高祖') 
person_node_2 = Node('person',name='李世民',dynasty=['隋朝','唐朝'],post='唐太宗')
person_node_3 = Node('person',name='独孤曼陀',dynasty=['隋朝','北周'],post='元贞皇后')
person_node_4 = Node('person',name='杨广',dynasty='隋朝',post='隋炀帝')
person_node_5 = Node('person',name='杨坚',dynasty=['隋朝','北周'],post='隋文帝')
person_node_6 = Node('person',name='独孤伽罗',dynasty=['隋朝','北周'],post='文献皇后')
person_node_7 = Node('person',name='独孤信',dynasty=['西魏','北周'],post='柱国大将军')
person_node_8 = Node('person',name='萧皇后',dynasty=['梁朝','隋朝','唐朝'],post='皇后')
person_node_9 = Node('person',name='独孤皇后',dynasty='北周',post='独孤皇后')
person_node_10 = Node('person',name='宇文毓',dynasty='北周',post='周明帝')
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

2.2. 创建关系Relationship

R_12 = Relationship(person_node_1, '父子', person_node_2)
R_31 = Relationship(person_node_3, '母子', person_node_1)
R_36 = Relationship(person_node_3, '姊妹', person_node_6)
R_64 = Relationship(person_node_6, '母子', person_node_4)
R_56 = Relationship(person_node_5, '夫妻', person_node_6)
R_76 = Relationship(person_node_7, '父女', person_node_6)
R_73 = Relationship(person_node_7, '父女', person_node_3)
R_54 = Relationship(person_node_5, '父子', person_node_4)
R_48 = Relationship(person_node_4, '夫妻', person_node_8)
R_79 = Relationship(person_node_7, '父女', person_node_9)
R_90 = Relationship(person_node_10, '夫妻', person_node_9)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

注,有新的写法,源代码中给出:
The positional arguments passed to the constructor identify the nodes to
relate and the type of the relationship. Keyword arguments describe the
properties of the relationship::

   >>> from py2neo import Node, Relationship
   >>> a = Node("Person", name="Alice")
   >>> b = Node("Person", name="Bob")
   >>> a_knows_b = Relationship(a, "KNOWS", b, since=1999)
  • 1
  • 2
  • 3
  • 4

This class may be extended to allow relationship types names to be
derived from the class name. For example::

  >>> WORKS_WITH = Relationship.type("WORKS_WITH")
   >>> a_works_with_b = WORKS_WITH(a, b)
   >>> a_works_with_b
   (Alice)-[:WORKS_WITH {}]->(Bob)
  • 1
  • 2
  • 3
  • 4

2.3. 按事务管理,写入/删除数据

2.3.1. 写入/创建

# 事务开始
tx = graph.begin()
# 创建节点
tx.create(person_node_1)
tx.create(person_node_2)
tx.create(person_node_4)
tx.create(person_node_5)
tx.create(person_node_6)
tx.create(person_node_3)
tx.create(person_node_7)
tx.create(person_node_8)
tx.create(person_node_9)
tx.create(person_node_10)
# 创建关系
tx.create(R_12)
tx.create(R_31)
tx.create(R_36)
tx.create(R_64)
tx.create(R_56)
tx.create(R_76)
tx.create(R_73)
tx.create(R_54)
tx.create(R_48)
tx.create(R_79)
tx.create(R_90)
# (事务)提交数据
graph.commit(tx)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27

2.3.2. 删除

# 删除节点
#graph.delete(person_node_1)
#graph.delete(person_node_2)
  • 1
  • 2
  • 3

如果使用delete删除关系,则将相关的节点也删除。

3. 增删节点与关系

对于增删操作,与传统数据库一样,最好先判断节点、关系是否存在,本文暂时不具体展开介绍。

3.1. 查询节点补充建立关系

# 增补节点和关系
person_node_18 = Node('person',name='宇文阐',dynasty='北周',post='周静帝')
# nodes = NodeMatcher(graph)
# 增补关系,先把节点查询出来
node_p1 = graph.nodes.match('person',name='宇文泰').first()
node_p2 = graph.nodes.match('person',name='宇文毓').first()
node_p3 = graph.nodes.match('person',name='宇文赟').first()
node_p3
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

Node(‘person’, dynasty=‘北周’, name=‘宇文赟’, post=‘北周宣帝’)

node_d = graph.nodes.match('dynasty',name='北周').first()
node_d
  • 1
  • 2

Node(‘dynasty’, name=‘北周’, region=‘北朝’)

R_1 = Relationship(node_p1, '父子', node_p2)
R_2 = Relationship(node_p3, '父子', person_node_18)
RDP_1 = Relationship(person_node_18, '亡国',node_d )

tx = graph.begin()
tx.create(person_node_18)
tx.create(R_1)
tx.create(R_2)
tx.create(RDP_1)

graph.commit(tx)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

打开浏览器,http://localhost:7474/browser/,将给出如下的图。
在这里插入图片描述
这张图有点问题,宇文泰是西魏权臣掌控者,在其去世后,由其侄子宇文护扶持其子宇文觉建立了北周,修改一下。

3.2. 删除错误的关系并修改

删除宇文泰与北周的关系,新建立与西魏的关系。其中,删除关系是指解除分离的含义,使用separate()断开关系。

#查找北周的臣民
node_d = graph.nodes.match('dynasty',name='北周').first()
node_p1 = graph.nodes.match('person',name='宇文泰').first()
relation_1 = graph.match_one((node_d,node_p1),r_type='国人')
print(relation_1)
#graph.delete(relation_1) #将删除关系及相关的节点(node)
graph.separate(relation_1)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
(北周)-[:国人 {}]->(宇文泰)
  • 1
#补充关系
node_p1 = graph.nodes.match('person',name='宇文泰').first()
node_d1 = graph.nodes.match('dynasty',name='西魏').first()
node_p2 = graph.nodes.match('person',name='独孤信').first()
RDP_1 = Relationship(node_d1, '国人', node_p1)
RDP_2 = Relationship(node_d1, '国人', node_p2)
tx = graph.begin()
tx.create(RDP_1)
tx.create(RDP_2)

graph.commit(tx)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

修改后的效果图如下所示,还是有些问题,欢迎读者补充完善。

在这里插入图片描述

4. 关于基本常用查询

4.1. 节点node查询

node_d = graph.nodes.match('dynasty')
for rel in node_d:
    print(rel['name'],rel['region'])
  • 1
  • 2
  • 3
西魏 北朝
梁朝 南朝
隋朝 中国
唐朝 中国
北周 北朝
陈朝 南朝
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
node_d = graph.nodes.match('dynasty').where("_.region='北朝'")
for rel in node_d:
    print(rel['name'],rel['region'])
  • 1
  • 2
  • 3
西魏 北朝
北周 北朝
  • 1
  • 2

4.2. 关系Relationship查询

#查询关系是’国人‘的所有关系(relationship)
relation_1 = graph.match_one(r_type='国人')
relation_1
  • 1
  • 2
  • 3
国人(Node('person', dynasty='陈朝', name='广德公主', post='隋炀帝嫔妃'), Node('dynasty', name='陈朝', region='南朝'))
  • 1
#查找北周的臣民,match(nodes=None,r_type,**properties)
#node顺序为:开始节点,结束节点(可省略结束节点)
node_d = graph.nodes.match('dynasty',name='北周').first()

relation_1 = graph.match((node_d,),r_type='国人')

for rel in relation_1:
    print(rel)
    print(rel.start_node['name'], rel.end_node['name'], rel.end_node['post'])

relation_3 = graph.match_one((node_d,),r_type='国人')
print(relation_3)
node_p1

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
(北周)-[:国人 {}]->(宇文邕)
北周 宇文邕 北周武帝
(北周)-[:国人 {}]->(宇文泰)
北周 宇文泰 太师
(北周)-[:国人 {}]->(宇文赟)
北周 宇文赟 北周宣帝
(北周)-[:国人 {}]->(宇文毓)
北周 宇文毓 周明帝
(北周)-[:国人 {}]->(独孤皇后)
北周 独孤皇后 独孤皇后
(北周)-[:国人 {}]->(独孤伽罗)
北周 独孤伽罗 文献皇后
(北周)-[:国人 {}]->(独孤曼陀)
北周 独孤曼陀 元贞皇后
(北周)-[:国人 {}]->(独孤信)
北周 独孤信 柱国大将军
(北周)-[:国人 {}]->(宇文邕)

Node('person', dynasty='西魏', name='宇文泰', post='太师')
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

5. 补充数据库语言(Cypher)常用语句

  • 删除所有内容,清空数据库:

MATCH (n)
DETACH DELETE n

  • 删除所有person节点,自动删除关系:

MATCH (n:person)
DETACH DELETE n

  • 删除person和dynasty多种节点:

MATCH (n:person),(m:dynasty)
DETACH DELETE n,m

  • 查询多节点:

MATCH (n:dynasty),(m:person) RETURN n,m

  • 提高查询速度,创建索引:

CREATE INDEX ON :person(name)

6. 小结

在中国的历史长河之中,历史人物及其典故众多,基于图数据库的知识图谱将为我们学习、掌握历史提供新思维,便于整理掌握。

关于知识图谱的使用,后续将逐步展开学习,欢迎一起研究。

参考:
肖永威. Neo4j图数据库入门实践. CSDN博客, 2021.10
The Py2neo Handbook, 2021.1

推荐阅读