乐趣区

关于图数据库:使用Py2neo在Neo4j中创建节点和关系

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)]
退出移动版