Neo4j入门(一)节点、关系的增删改查
本文作为笔者入门Neo4j的第一篇文章,后续将会更多地介绍Neo4j及知识图谱相关的内容。
Neo4j是图数据库中的佼佼者,采用Java编写,社区版已开源,商业版需收费。Neo4j是一个高性能的NoSQL图形数据库(Graph Database),它将结构化数据存储在网络上而不是表中。它是一个嵌入式的、基于磁盘的、具备完全的事务特性的Java持久化引擎,但是它将结构化数据存储在网络(从数学角度叫做图)上而不是表中。Neo4j也可以被看作是一个高性能的图引擎,该引擎具有成熟数据库的所有特性。Neo4j拥有十分酷炫的可视化界面,这是一般图数据库所不具备的。
本文将通过一个简单的例子来介绍Neo4j中的基本操作:对节点和边实现增删改查。我们想要实现的图(作为例子,简单易懂)如下:
在上述例子中,共有两类节点:City和Company,一类关系:(Company) -[belongTo]-> (City)。
CQL
Neo4j 的 CQL 是非常重要的命令,类似于 SQL 语句,具体的用法可以参考:https://www.w3cschool.cn/neo4j/neo4j_cql_introduction.html。我们使用CQL实现节点、关系的增删改查。
ADD
City的节点可以通过使用如下命令使用:
create (city: City{name: "上海市", area: "6340.5平方千米", population: "2487.09万人", alias: ["沪", "申"]});
create (city: City{name: "北京市", area: "16410平方千米", population: "2189.31万人", alias: ["京", "帝都"]});
create (city: City{name: "深圳市", area: "1997.49平方千米", population: "1756万人", alias: ["深", "鹏城"]});
create (city: City{name: "杭州市", area: "16850平方千米", population: "1193万人", alias: ["杭"]});
在上述语句中,create表示新建,小括号内是节点信息,节点的类型(label)是City,city是其别名,花括号内是该节点的属性,共有name、area、population、alias四个属性。注意:Neo4j支持列表这个数据类型,但不支持时间日期这个数据类型。
Company的节点可以通过使用如下命令使用:
create (company: Company{name: "阿里"});
create (company: Company{name: "网易"});
create (company: Company{name: "百度"});
create (company: Company{name: "字节跳动"});
create (company: Company{name: "新浪"});
create (company: Company{name: "拼多多"});
create (company: Company{name: "B站"});
create (company: Company{name: "小红书"});
create (company: Company{name: "华为"});
create (company: Company{name: "腾讯"});
create (company: Company{name: "招商银行"});
创建关系的命令如下:
match (city: City{name: "杭州市"}), (company: Company{name: "阿里"}) create (company) -[r:belongTo{name: "所在城市"}]-> (city);
match (city: City{name: "杭州市"}), (company: Company{name: "网易"}) create (company) -[r:belongTo{name: "所在城市"}]-> (city);
match (city: City{name: "上海市"}), (company: Company{name: "拼多多"}) create (company) -[r:belongTo{name: "所在城市"}]-> (city);
match (city: City{name: "上海市"}), (company: Company{name: "B站"}) create (company) -[r:belongTo{name: "所在城市"}]-> (city);
match (city: City{name: "上海市"}), (company: Company{name: "小红书"}) create (company) -[r:belongTo{name: "所在城市"}]-> (city);
match (city: City{name: "北京市"}), (company: Company{name: "新浪"}) create (company) -[r:belongTo{name: "所在城市"}]-> (city);
match (city: City{name: "北京市"}), (company: Company{name: "百度"}) create (company) -[r:belongTo{name: "所在城市"}]-> (city);
match (city: City{name: "北京市"}), (company: Company{name: "字节跳动"}) create (company) -[r:belongTo{name: "所在城市"}]-> (city);
match (city: City{name: "深圳市"}), (company: Company{name: "华为"}) create (company) -[r:belongTo{name: "所在城市"}]-> (city);
match (city: City{name: "深圳市"}), (company: Company{name: "腾讯"}) create (company) -[r:belongTo{name: "所在城市"}]-> (city);
match (city: City{name: "深圳市"}), (company: Company{name: "招商银行"}) create (company) -[r:belongTo{name: "所在城市"}]-> (city);
事实上,我们还可以用如下命令来实现关系的新建:
create (company: Company{name: "阿里"}) -[r:belongTo{name: "所在城市"}]-> (city:City{name: "杭州市"});
update
如果需要更新某个节点的属性,命令如下(比如将上海市节点的简称新增“魔都”这个名字):
MATCH (city:City)
WHERE city.name="上海市"
SET city.alias=["沪", "申", "魔都"]
RETURN city;
上述命令也涉及到了查询(select),只是CQL的查询与SQL接近但又不相同。当然,我们也可以修改某条关系。
delete
CQL的删除命令有delete和remove,两者的区别为:
delete命令为删除节点、删除节点及相关节点和关系;
remove命令为删除节点或关系的标签、删除节点或关系的属性
我们以删除上海市节点的alias属性为例,命令如下:
MATCH (city: City{name: "上海市"})
REMOVE city.alias
RETURN city;
注意,当我们在删除某个节点的时候,需要确定该节点无其他节点无关系相连,即该节点为孤立节点,否则,就会报错。比如,我们想删除上海市这个节点,命令如下:
MATCH (city: City{name: "上海市"})
DELETE city;
报错如下:
select
CQL的查询语句是灵活、功能强大的。我们仅介绍简单的几个命令。
查询所有的节点:
match (n) return (n);
查询所有节点的数量
match (n) return count(n);
查询所有的Company节点
match (n:Company) return (n);
查询名字为北京市的City节点
match (n:City{name: "北京市"}) return (n);
查询招商银行与深圳市的关系
match (a:Company{name: "招商银行"}) -[r]-> (b:City{name: "深圳市"}) return type(r);
查询该图谱中杭州市下面的Company
match (a:Company) -[r:belongTo]-> (b:City{name: "杭州市"}) return (a);
CQL还支持更高级的查询,如最短路径查询等。
py2neo
py2neo是用来对接 Neo4j 的 Python 库,官方文档地址为http://py2neo.org/v3/index.html,
GitHub地址为https://github.com/technige/py2neo。
下面将介绍如何使用py2neo实现对Neo4j的简单操作。
neo4j的连接
py2neo连接Neo4j需要Neo4j的服务网址,用户名和密码,示例连接Python代码如下:
# -*- coding: utf-8 -*-
from py2neo import Graph
url = "http://localhost:7474"
username = "neo4j"
password = "password"
graph = Graph(url, auth=(username, password))
print("neo4j info: {}".format(str(graph)))
输出结果如下:
neo4j info: Graph('http://neo4j@localhost:7474')
ADD
我们来创建4个City节点和11个Company节点,代码如下:
from py2neo import Node, Relationship, Subgraph
city_list = [{"name": "上海市", "area": "6340.5平方千米", "population": "2487.09万人", "alias": ["沪", "申"]},
{"name": "北京市", "area": "16410平方千米", "population": "2189.31万人", "alias": ["京", "帝都"]},
{"name": "深圳市", "area": "1997.49平方千米", "population": "1756万人", "alias": ["深", "鹏城"]},
{"name": "杭州市", "area": "16850平方千米", "population": "1193万人", "alias": ["杭"]}]
company_list = [{"name": "阿里"},
{"name": "网易"},
{"name": "百度"},
{"name": "新浪"},
{"name": "字节跳动"},
{"name": "小红书"},
{"name": "B站"},
{"name": "拼多多"},
{"name": "华为"},
{"name": "招商银行"},
{"name": "腾讯"}
]
for city in city_list:
node = Node("City", **city)
graph.create(node)
for company in company_list:
node = Node("Company", **company)
graph.create(node)
或者可以通过子图(Subgraph)来创建,这样创建效率更高,使用子图的示例代码如下:
from py2neo import Node, Relationship, Subgraph
city_list = [{"name": "上海市", "area": "6340.5平方千米", "population": "2487.09万人", "alias": ["沪", "申"]},
{"name": "北京市", "area": "16410平方千米", "population": "2189.31万人", "alias": ["京", "帝都"]},
{"name": "深圳市", "area": "1997.49平方千米", "population": "1756万人", "alias": ["深", "鹏城"]},
{"name": "杭州市", "area": "16850平方千米", "population": "1193万人", "alias": ["杭"]}]
company_list = [{"name": "阿里"},
{"name": "网易"},
{"name": "百度"},
{"name": "新浪"},
{"name": "字节跳动"},
{"name": "小红书"},
{"name": "B站"},
{"name": "拼多多"},
{"name": "华为"},
{"name": "招商银行"},
{"name": "腾讯"}
]
node_list = []
for city in city_list:
node_list.append(Node("City", **city))
for company in company_list:
node_list.append(Node("Company", **company))
new_sub = Subgraph(node_list)
graph.create(new_sub)
创建City节点与Company节点之间的关系的示例代码如下:
# 创建关系
from py2neo import NodeMatcher
rel_dict = {"上海市": ["小红书", "B站", "拼多多"],
"北京市": ["百度", "新浪", "字节跳动"],
"深圳市": ["华为", "招商银行", "腾讯"],
"杭州市": ["阿里", "网易"]}
node_matcher = NodeMatcher(graph=graph)
for key, vals in rel_dict.items():
for val in vals:
start_node = node_matcher.match("Company", name=val).first()
end_end = node_matcher.match("City", name=key).first()
rel = Relationship(start_node, "belongTo", end_end, name="所在城市")
graph.create(rel)
这样创建的图谱就是一开始演示的例子。当然,我们也可以用Subgraph来创建,效率会更高。
UPDATE
如果需要更新某个节点的属性,示例Python代码如下(比如将上海市节点的简称新增“魔都”这个名字):
# before update
node_matcher = NodeMatcher(graph=graph)
node = node_matcher.match("City", name="上海市").first()
print(node.__repr__())
# update node
node["alias"] = ['沪', '申', '魔都']
graph.push(node)
# after update
node = node_matcher.match("City", name="上海市").first()
print(node.__repr__())
输出结果如下:
Node('City', alias=['沪', '申'], area='6340.5平方千米', name='上海市', population='2487.09万人')
Node('City', alias=['沪', '申', '魔都'], area='6340.5平方千米', name='上海市', population='2487.09万人')
当然,我们也可以更新关系。
DELETE
我们以删除上海市节点的alias属性为例,示例Python代码如下:
# before delete
node_matcher = NodeMatcher(graph=graph)
node = node_matcher.match("City", name="上海市").first()
print(node.__repr__())
# delete node property
del node["alias"]
graph.push(node)
# after update
node = node_matcher.match("City", name="上海市").first()
print(node.__repr__())
输出结果如下:
Node('City', alias=['沪', '申', '魔都'], area='6340.5平方千米', name='上海市', population='2487.09万人')
Node('City', area='6340.5平方千米', name='上海市', population='2487.09万人')
当然,我们也可以删除节点。
SELECT
py2neo也能够很好地支持查询语句,示例代码如下:
查询所有的节点:
graph.nodes.match().all()
查询所有节点的数量
len(graph.nodes.match().all())
查询所有的Company节点
graph.nodes.match("Company").all()
查询名字为北京市的City节点
graph.nodes.match("City", name="北京市").all()
查询招商银行与深圳市的关系
node_matcher = NodeMatcher(graph=graph)
end_node = node_matcher.match("City", name="深圳市").first()
start_node = node_matcher.match("Company", name="招商银行").first()
print(graph.match([start_node, end_node]).first())
查询该图谱中杭州市下面的Company
from py2neo import RelationshipMatcher
node_matcher = NodeMatcher(graph=graph)
node = node_matcher.match("City", name="杭州市").first()
rel_matcher = RelationshipMatcher(graph=graph)
find_rels = rel_matcher.match([None, node], r_type="belongTo").all()
print([_.start_node for _ in find_rels if _.start_node.has_label("Company")])
py2neo还支持CQL语句,我们以最后的查询语句为例,示例代码如下:
cql = 'match (a:Company) -[r:belongTo]-> (b:City{name: "杭州市"}) return (a); '
print(graph.run(cql).data())
输出结果如下:
[{'a': Node('Company', name='网易')}, {'a': Node('Company', name='阿里')}]
总结
综上,我们可以了解到,neo4j的CQL以及py2neo各有各的好处,CQL在某些查询场合比较便利,而py2neo可以方便我们使用Python进行对Neo4j的操作,有些操作实现起来也很方便。
本文简单介绍了Neo4j节点、关系的增删改查,以及CQL、py2neo的使用方法。后续会持续更新Neo4j相关内容,欢迎大家关注~
2021.7.13-7.14深夜,最近上海酷暑~