9

使用Py2neo在Neo4j中创建节点和关系

 2 years ago
source link: https://segmentfault.com/a/1190000040519140
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Neo4j是一款开源图数据库,使用Python语言访问Neo4j可以使用Py2neo。本文介绍了使用Py2neo访问Neo4j,批量创建节点和关系的方法。

Py2neo提供了直接执行Cypher语句的方法,也提供了诸如Node、Relationship、Path一系列的数据结构,在不同的场景下可以灵活使用。

​本文使用的Py2neo是2021.1之后的版本,手册请戳这里:
The Py2neo Handbook

安装Py2neo

使用pip安装Py2neo,执行:

pip install py2neo

查看已安装的Py2neo是什么版本的:

pip show py2neo

Name: py2neo
Version: 2021.1.5
Summary: Python client library and toolkit for Neo4j
Home-page: https://py2neo.org/

连接Neo4j数据库

本文中会用到多种数据类型,在此一并引用

import numpy as np
import pandas as pd
from py2neo import Node,Relationship,Graph,Path,Subgraph

配置Neo4j数据库的访问地址、用户名和密码

neo4j_url = 'http://localhost:7474/'
user = 'neo4j'
pwd = 'admin'

2021.1之前访问数据库的方式为:

graph = Graph(neo4j_url, username=user, password=pwd)

2021.1之后的版本访问数据库的方式为(就是这么不兼容):

graph = Graph(neo4j_url,  auth=(user, pwd))

1. 使用graph.run执行Cypher语句创建节点

如果熟悉Cypher语句的话,可以通过使用graph.run执行Cypher语句来实现创建节点等操作,方法如下所示:

cypher_ = "CREATE (:Person {name:'王王', age:35, work:'宇宙电子厂'}),\
(:Person {name:'李李', age:20, work:'宇宙电子厂'})"
graph.run(cypher_)

这样就在Neo4j中创建了两个label为Person的节点,第一个节点的name属性为“王王”,age属性为35,work属性为“宇宙电子厂”,第二个节点的name属性为“李李”,age属性为20,work属性为“宇宙电子厂”。
同样,可以通过调用graph.run执行Cypher语句创建关系。

cypher_ = "MATCH (from:Person{name:'王王'}),\
(to:Person{name:'李李'}) MERGE (from)-[r:同事]->(to)"
graph.run(cypher_)

这样在Neo4j中就有了具有同事关系的两个Person节点。

2. 使用Node数据结构创建节点

Py2neo也提供graph.create方法来创建节点和关系

node = Node("Person", name="李李", age=20, work="宇宙电子厂")
graph.create(node)

与执行Cypher语句的效果相同,在Neo4j中创建了一个Person节点。

需要注意的是,这两种创建方法,如果反复执行的话,是会在Neo4j中创建出重复的节点的,即name、age、work属性完全一样,但在Neo4j中的id不一样的多个节点。

3. 使用Node、Relationship和Subgraph数据结构创建节点和关系

上面两种方法都是一次创建一个节点或者一个关系,Py2neo也提供了批量创建节点和关系的方法,而且性能更优。下面就以下图中的图谱为例,使用Py2neo提供Node、Relationship和Subgraph数据结构在Neo4j中创建节点和关系。

首先创建一些label为Person的节点,即Node对象,第一个参数是label,属性按key=value依次作为参数传入。如果节点有多个label,可以用Node.add_label("label_text")来追加label。

node1 = Node("Person", name="王王", age=35, work="宇宙电子厂")
node2 = Node("Person", name="李李", age=20, work="宇宙电子厂")
node3 = Node("Person", name="张张", age=30, work="宇宙电子厂")
node4 = Node("Person", name="赵赵", age=45, work="月亮中学")
node4.add_label("Teacher")
node5 = Node("Person", name="刘刘", age=20, work="地球电子商务公司")

再创建一些label为Location的节点

node6 = Node("Location", name="南京") 
node7 = Node("Location", name="江宁区") 
node8 = Node("Location", name="禄口机场") 

建立一些Person和Person节点之间的关系,Neo4j中的关系是有方向的,所以Relationship第一个参数为起始节点,第三个参数是结束节点,而第二个节点为关系的类型。这里创建的同事、邻居的关系为双向的,老师、学生的关系为单向。

relation1 = Relationship(node1, "同事", node2)
relation2 = Relationship(node2, "同事", node1)

relation3 = Relationship(node2, "同事", node3)
relation4 = Relationship(node3, "同事", node2)

relation5 = Relationship(node3, "邻居", node4)
relation6 = Relationship(node4, "邻居", node3)

relation7 = Relationship(node4, "学生", node5)
relation8 = Relationship(node5, "老师", node4)

创建一些Location和Location节点之间的关系,地域之间的包含关系为单向。

relation9 = Relationship(node6, "包含", node7)
relation10 = Relationship(node7, "包含", node8)

创建Person节点和Location节点之间的关系,这里“到访”的关系是有属性的,date表示到访的日期,stay_hours表示停留的时间。可以使用一个key:value的字典数据结构保存属性,再赋予关系

properties1={'date':'2021-7-16','stay_hours':1}
relation11 = Relationship(node2, "到访", node8, **properties1)

properties2={'date':'2021-7-19','stay_hours':4}
relation12 = Relationship(node5, "到访", node8, **properties2)

然后将以上所有节点和关系组成Subgraph

node_ls = [node1, node2, node3, node4, 
           node5, node6, node7, node8]
relation_ls = [relation1, relation2, relation3, relation4, 
               relation5, relation6, relation7, relation8, 
               relation9, relation10, relation11, relation12]
subgraph = Subgraph(node_ls, relation_ls)

最后通过事务类Transaction提交,批量创建这些节点和关系。这里tx.create并没有真正创建节点和关系,直到graph.commit才一次性提交到Neo4j进行创建。

tx = graph.begin() 
tx.create(subgraph)
graph.commit(tx)

重复执行上面的命令,不会创造出重复的节点和关系。这一点手册中有说明:“subgraph中的已经和数据库绑定的实体将保持不变,那些没有绑定的将在数据库中新建并绑定上。”

create(subgraph) Create remote nodes and relationships that correspond to those in a local subgraph. Any entities in subgraph that are already bound to remote entities will remain unchanged, those which are not will become bound to their newly-created counterparts.

做一个简单的实验粗略地对比逐个创建和批量创建的时间开销。在Neo4j为空数据库的情况下,分别采用逐个创建和批量创建的方法创建10000个节点,每个节点有name和age两个属性,都是随机生成的,使用jupyter notebook的%%time命令计算时间开销。

import random
N = 10000

逐个创建节点

%%time
for i in range(N):
    random_name = "P"+str(round(random.random()*N*2))
    random_age = round(random.random()*15)
    node = Node("Person", name=random_name, age=random_age)
    graph.create(node)

CPU times: user 50.3 s, sys: 4.19 s, total: 54.5 s
Wall time: 5min 16s

批量创建节点

%%time
node_ls = []
for i in range(N):
    random_name = "P"+str(round(random.random()*N*2))
    random_age = round(random.random()*15)
    node = Node("Person", name=random_name, age=random_age)
    node_ls.append(node)

subgraph = Subgraph(node_ls, [])
tx = graph.begin() 
tx.create(subgraph)
graph.commit(tx)

CPU times: user 448 ms, sys: 75.5 ms, total: 523 ms
Wall time: 1.46 s

实验中也发现,只是创建节点的话,批量创建方法的时间开销几乎是线性增长的,当一次性提交10万个节点的创建任务时,时间开销大约在4.5秒。

在使用Py2neo构建图谱时,尽可能使用批量创建方法。先创建节点(Node)对象、关系(Relationship)对象,再构成子图(Subgraph),最后利用事务类一次提交创建。
下一篇将介绍如何运用Py2neo查询节点、关系和路径。


我的Python版本

>>> import sys
>>> print(sys.version)
3.7.6 (default, Jan  8 2020, 13:42:34) 
[Clang 4.0.1 (tags/RELEASE_401/final)]

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK