溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶服務(wù)條款》

怎么在Python中使用py2neo操作neo4j數(shù)據(jù)庫(kù)

發(fā)布時(shí)間:2021-03-22 16:35:41 來(lái)源:億速云 閱讀:504 作者:Leah 欄目:開(kāi)發(fā)技術(shù)

這篇文章將為大家詳細(xì)講解有關(guān)怎么在Python中使用py2neo操作neo4j數(shù)據(jù)庫(kù),文章內(nèi)容質(zhì)量較高,因此小編分享給大家做個(gè)參考,希望大家閱讀完這篇文章后對(duì)相關(guān)知識(shí)有一定的了解。

1、概念

圖:數(shù)據(jù)結(jié)構(gòu)中的圖由節(jié)點(diǎn)和其之間的邊組成。節(jié)點(diǎn)表示一個(gè)實(shí)體,邊表示實(shí)體之間的聯(lián)系。

圖數(shù)據(jù)庫(kù):以圖的結(jié)構(gòu)存儲(chǔ)管理數(shù)據(jù)的數(shù)據(jù)庫(kù)。其中一些數(shù)據(jù)庫(kù)將原生的圖結(jié)構(gòu)經(jīng)過(guò)優(yōu)化后直接存儲(chǔ),即原生圖存儲(chǔ)。還有一些圖數(shù)據(jù)庫(kù)將圖數(shù)據(jù)序列化后保存到關(guān)系型或其他數(shù)據(jù)庫(kù)中。

之所以使用圖數(shù)據(jù)庫(kù)存儲(chǔ)數(shù)據(jù)是因?yàn)樗谔幚韺?shí)體之間存在復(fù)雜關(guān)系的數(shù)據(jù)具有很大的優(yōu)勢(shì)。使用傳統(tǒng)的關(guān)系型數(shù)據(jù)庫(kù)在處理數(shù)據(jù)之間的關(guān)系時(shí)其實(shí)很不方便。例如查詢選修一個(gè)課程的同學(xué)時(shí)需要join兩個(gè)表,查詢選修某個(gè)課程的同學(xué)還選修什么課程,這就需要兩次join操作,當(dāng)涉及到十分復(fù)雜的關(guān)系以及龐大的數(shù)據(jù)量時(shí),關(guān)系型數(shù)據(jù)庫(kù)效率十分低下。而通過(guò)圖存儲(chǔ),可以通過(guò)節(jié)點(diǎn)之間的邊十分便捷地查詢到結(jié)果。

圖模型:

怎么在Python中使用py2neo操作neo4j數(shù)據(jù)庫(kù)

節(jié)點(diǎn)(Node)是主要的數(shù)據(jù)元素,表示一個(gè)實(shí)體。

屬性(Properties)用于描述實(shí)體的特征,以鍵值對(duì)的方式表示,其中鍵是字符串,可以對(duì)屬性創(chuàng)建索引和約束。

關(guān)系(Relationships)表示實(shí)體之間的聯(lián)系,關(guān)系具有方向,實(shí)體之間可以有多個(gè)關(guān)系,關(guān)系也可以具有屬性

標(biāo)簽(Label)用于將實(shí)體分類,一個(gè)實(shí)體可以具有多個(gè)標(biāo)簽,對(duì)標(biāo)簽進(jìn)行索引可以加速查找

2、Neo4j

Neo4j是目前最流行的圖數(shù)據(jù)庫(kù),它采用原生圖存儲(chǔ),在windows中下載安裝訪問(wèn)如下地址https://neo4j.com/download/community-edition/。在Linux下通過(guò)如下命令下載解壓

curl -O http://dist.neo4j.org/neo4j-community-3.4.5-unix.tar.gz
tar -axvf neo4j-community-3.4.5-unix.tar.gz

修改配置文件conf/neo4j.conf

# 修改第22行l(wèi)oad csv時(shí)l路徑,在前面加個(gè)#,可從任意路徑讀取文件
#dbms.directories.import=import
# 修改35行和36行,設(shè)置JVM初始堆內(nèi)存和JVM最大堆內(nèi)存
# 生產(chǎn)環(huán)境給的JVM最大堆內(nèi)存越大越好,但是要小于機(jī)器的物理內(nèi)存
dbms.memory.heap.initial_size=5g
dbms.memory.heap.max_size=10g
# 修改46行,可以認(rèn)為這個(gè)是緩存,如果機(jī)器配置高,這個(gè)越大越好
dbms.memory.pagecache.size=10g
# 修改54行,去掉改行的#,可以遠(yuǎn)程通過(guò)ip訪問(wèn)neo4j數(shù)據(jù)庫(kù)
dbms.connectors.default_listen_address=0.0.0.0
# 默認(rèn) bolt端口是7687,http端口是7474,https關(guān)口是7473,不修改下面3項(xiàng)也可以
# 修改71行,去掉#,設(shè)置http端口為7687,端口可以自定義,只要不和其他端口沖突就行
#dbms.connector.bolt.listen_address=:7687
# 修改75行,去掉#,設(shè)置http端口為7474,端口可以自定義,只要不和其他端口沖突就行
dbms.connector.http.listen_address=:7474
# 修改79行,去掉#,設(shè)置http端口為7473,端口可以自定義,只要不和其他端口沖突就行
dbms.connector.https.listen_address=:7473
# 去掉#,允許從遠(yuǎn)程url來(lái)load csv
dbms.security.allow_csv_import_from_file_urls=true
# 修改250行,去掉#,設(shè)置neo4j-shell端口,端口可以自定義,只要不和其他端口沖突就行
dbms.shell.port=1337
# 修改254行,設(shè)置neo4j可讀可寫(xiě)
dbms.read_only=false

在bin目錄下執(zhí)行 ./neo4j start,啟動(dòng)服務(wù),在瀏覽器http://服務(wù)器ip地址:7474/browser/可以看到neo4j的可視化界面

3、py2neo

py2neo是一個(gè)社區(qū)第三方庫(kù),通過(guò)它可以更為便捷地使用python來(lái)操作neo4j

安裝py2neo:pip install py2neo,我安裝的版本是4.3.0

3.1、Node與Relationship

創(chuàng)建節(jié)點(diǎn)和它們之間的關(guān)系,注意在使用下面的py2neo相關(guān)類之前首先需要import導(dǎo)入:

# 引入庫(kù)
from py2neo import Node, Relationship
# 創(chuàng)建節(jié)點(diǎn)a、b并定義其標(biāo)簽為Person,屬性name
a = Node("Person", name="Alice",height=166)
b = Node("Person", name="Bob")
# 節(jié)點(diǎn)添加標(biāo)簽
a.add_label('Female')
# 創(chuàng)建ab之間的關(guān)系
ab = Relationship(a, "KNOWS", b)
# 輸出節(jié)點(diǎn)之間的關(guān)系:(Alice)-[:KNOWS]->(Bob)
print(ab)

Node 和 Relationship 都繼承了 PropertyDict 類,類似于python的dictionary,可以通過(guò)如下方式對(duì) Node 或 Relationship 進(jìn)行屬性賦值和訪問(wèn)

# 節(jié)點(diǎn)和關(guān)系添加、修改屬性
a['age']=20
ab['time']='2019/09/03'
# 刪除屬性
del a['age']
# 打印屬性
print(a[name])
# 設(shè)置默認(rèn)屬性,如果沒(méi)有賦值,使用默認(rèn)值,否則設(shè)置的新值覆蓋默認(rèn)值
a.setdefault('sex','unknown')
# 更新屬性
a.update(age=22, sex='female')
ab.update(time='2019/09/03')

3.2、Subgraph

由節(jié)點(diǎn)和關(guān)系組成的集合就是子圖,通過(guò)關(guān)系運(yùn)算符求交集&、并集|、差集-、對(duì)稱差集^

subgraph.labels返回子圖中所有標(biāo)簽集合,keys()返回所有屬性集合,nodes返回所有節(jié)點(diǎn)集,relationships返回所有關(guān)系集

# 構(gòu)建一個(gè)子圖
s = a | b | ab
# 對(duì)圖中的所有節(jié)點(diǎn)集合進(jìn)行遍歷
for item in s.nodes:
  print('s的節(jié)點(diǎn):', item)

通常將圖中的所有節(jié)點(diǎn)和關(guān)系構(gòu)成一個(gè)子圖后再統(tǒng)一寫(xiě)入數(shù)據(jù)庫(kù),與多次寫(xiě)入單個(gè)節(jié)點(diǎn)相比效率更高

# 連接neo4j數(shù)據(jù)庫(kù),輸入地址、用戶名、密碼
graph = Graph('http://localhost:7474', username='neo4j', password='123456')
# 將節(jié)點(diǎn)和關(guān)系通過(guò)關(guān)系運(yùn)算符合并為一個(gè)子圖,再寫(xiě)入數(shù)據(jù)庫(kù)
s=a | b | ab
graph.create(s)

3.3、Walkable

walkable是在子圖subgraph的基礎(chǔ)上增加了遍歷信息的對(duì)象,通過(guò)它可以便捷地遍歷圖數(shù)據(jù)庫(kù)。

通過(guò)+號(hào)將關(guān)系連接起來(lái)就構(gòu)成了一個(gè)walkable對(duì)象。通過(guò)walk()函數(shù)對(duì)其進(jìn)行遍歷,可以利用 start_node、end_node、nodes、relationships屬性來(lái)獲取起始 Node、終止 Node、所有 Node 和 Relationship

# 組合成一個(gè)walkable對(duì)象w
w = ab + bc + ac
# 對(duì)w進(jìn)行遍歷
for item in walk(w):
  print(item)
# 訪問(wèn)w的初始、終止節(jié)點(diǎn)
print('起始節(jié)點(diǎn):', w.start_node, ' 終止節(jié)點(diǎn):', w.end_node)
# 訪問(wèn)w的所有節(jié)點(diǎn)、關(guān)系列表
print('節(jié)點(diǎn)列表:', w.nodes)
print('關(guān)系列表:', w.relationships)

運(yùn)行結(jié)果為:

(:Person {age: 20, name: 'Bob'})
(Bob)-[:KNOWS {}]->(Alice)
(:Person {age: 21, name: 'Alice'})
(Alice)-[:LIKES {}]->(Mike)
(:Person {name: 'Mike'})
(Bob)-[:KNOWS {}]->(Mike)
(:Person {age: 20, name: 'Bob'})
起始節(jié)點(diǎn): (:Person {age: 22, name: 'Bob', sex: 'female'})  終止節(jié)點(diǎn): (:Person {age: 22, name: 'Bob', sex: 'female'})
節(jié)點(diǎn)列表: ((:Person {age: 22, name: 'Bob', sex: 'female'}), (:Person {age: 21, name: 'Alice'}), (:Person {name: 'Mike'}), (:Person {age: 22, name: 'Bob', sex: 'female'}))
關(guān)系列表: ((Bob)-[:KNOWS {time: '2019/09/03'}]->(Alice), (Alice)-[:LIKES {}]->(Mike), (Bob)-[:KNOWS {}]->(Mike))

3.4、Graph

py2neo通過(guò)graph對(duì)象操作neo4j數(shù)據(jù)庫(kù),目前的neo4j只支持一個(gè)數(shù)據(jù)庫(kù)定義一張圖

通過(guò)Graph的初始化函數(shù)完成對(duì)數(shù)據(jù)庫(kù)的連接并創(chuàng)建一個(gè)graph對(duì)象

graph.create()可以將子圖寫(xiě)入數(shù)據(jù)庫(kù),也可以一次只寫(xiě)入一個(gè)節(jié)點(diǎn)或關(guān)系

graph.delete()刪除指定子圖,graph.delete_all()刪除所有子圖

graph.seperate()刪除指定關(guān)系

# 初始化連接neo4j數(shù)據(jù)庫(kù),參數(shù)依次為url、用戶名、密碼
graph = Graph('http://localhost:7474', username='neo4j', password='123456')
# 寫(xiě)入子圖w
graph.create(w)
# 刪除子圖w
graph.delete(w)
# 刪除所有圖
graph.delete_all()
# 刪除關(guān)系rel
graph.separate(rel)

graph.match(nodes=None, r_type=None, limit=None)查找符合條件的關(guān)系,第一個(gè)參數(shù)為節(jié)點(diǎn)集合或者集合(起始節(jié)點(diǎn),終止節(jié)點(diǎn)),如果省略代表所有節(jié)點(diǎn)。第二個(gè)參數(shù)為關(guān)系的屬性,第三個(gè)為返回結(jié)果的數(shù)量。也可以使用match_one()代替,返回一條結(jié)果。例如查找所有節(jié)點(diǎn)a認(rèn)識(shí)的人:

# 查找所有以a為起點(diǎn),并且屬性為KNOWS的關(guān)系
res = graph.match((a, ), r_type="KNOWS")
# 打印關(guān)系的終止節(jié)點(diǎn),即為a所有認(rèn)識(shí)的人
for rel in res:
  print(rel.end_node["name"])

使用graph.nodes.match()查找指定節(jié)點(diǎn),可以使用first()、where()、order_by()等函數(shù)對(duì)查找做高級(jí)限制

還可以通過(guò)節(jié)點(diǎn)或關(guān)系的id查找

# 查找標(biāo)簽為Person,屬性name="Alice"的節(jié)點(diǎn),并返回第一個(gè)結(jié)果
graph.nodes.match("Person", name="Alice").first()
# 查找所有標(biāo)簽為Person,name以B開(kāi)頭的節(jié)點(diǎn),并將結(jié)果按照age字段排序
res = graph.nodes.match("Person").where("_.name =~ 'B.*'").order_by('_.age')
for node in res:
  print(node['name'])
# 查找id為4的節(jié)點(diǎn)
t_node = graph.nodes[4]
# 查找id為196的關(guān)系
rel = graph.relationships[196]

通過(guò)Graph對(duì)象進(jìn)行Cypher操作并處理返回結(jié)果

graph.evaluate()執(zhí)行一個(gè)Cypher語(yǔ)句并返回結(jié)果的第一條數(shù)據(jù)

# 執(zhí)行Cypher語(yǔ)句并返回結(jié)果集的第一條數(shù)據(jù)
res = graph.evaluate('MATCH (p:Person) return p')
# 輸出:(_3:Person {age: 20, name: 'Bob'})
print(res)

graph.run()執(zhí)行Cypher語(yǔ)句并返回結(jié)果數(shù)據(jù)流的游標(biāo)Cursor,通過(guò)forward()方法不斷向前移動(dòng)游標(biāo)可以向前切換結(jié)果集的每條記錄Record對(duì)象

# 查詢(p1)-[k]->(p2),并返回所有節(jié)點(diǎn)和關(guān)系
gql="MATCH (p1:Person)-[k:KNOWS]->(p2:Person) RETURN *"
cursor=graph.run(gql)
# 循環(huán)向前移動(dòng)游標(biāo)
while cursor.forward():
  # 獲取并打印當(dāng)前的結(jié)果集
  record=cursor.current
  print(record)

打印的每條Record記錄對(duì)象如下所示,可以看到其中的元素是key=value的集合,通過(guò)方法get(key)可以取出具體元素。通過(guò)方法items(keys)可以將記錄中指定的key按(key,value)元組的形式返回

<Record k=(xiaowang)-[:KNOWS {}]->(xiaozhang) p1=(_96:Person {name: 'xiaowang'}) p2=(_97:Person {name: 'xiaozhang'})>
  record = cursor.current
  print('通過(guò)get返回:', record.get('k'))
  for (key, value) in record.items('p1', 'p2'):
    print('通過(guò)items返回元組:', key, ':', value)
# 運(yùn)行結(jié)果如下
'''
通過(guò)get返回: (xiaowang)-[:KNOWS {}]->(xiaozhang)
通過(guò)items返回元組: p1 : (_92:Person {name: 'xiaowang'})
通過(guò)items返回元組: p2 : (_93:Person {name: 'xiaozhang'})
'''

還可以將graph.run()返回的結(jié)果通過(guò)data()方法轉(zhuǎn)化為字典列表,所有結(jié)果整體上是一個(gè)列表,其中每一條結(jié)果是字典的格式,其查詢與結(jié)果如下,可以通過(guò)訪問(wèn)列表與字典的方式獲取數(shù)據(jù):

# 查詢(p1)-[k]->(p2),并返回所有節(jié)點(diǎn)和關(guān)系
gql = "MATCH (p1:Person)-[k:KNOWS]->(p2:Person) RETURN *"
res = graph.run(gql).data()
print(res)
#結(jié)果如下:
'''
[{'k': (xiaowang)-[:KNOWS {}]->(xiaozhang), 
 'p1': (_196:Person {name: 'xiaowang'}), 
 'p2': (_197:Person {name: 'xiaozhang'})}, 
{'k': (xiaozhang)-[:KNOWS {}]->(xiaozhao), 
 'p1': (_197:Person {name: 'xiaozhang'}),
 'p2': (_198:Person {name: 'xiaozhao'})},
{'k': (xiaozhao)-[:KNOWS {}]->(xiaoli),
 'p1': (_198:Person {name: 'xiaozhao'}),
 'p2': (_199:Person {name: 'xiaoli'})}
]
'''

通過(guò)graph.run().to_subgraph()方法將返回的結(jié)果轉(zhuǎn)化為SubGraph對(duì)象,接著按之前操作SubGraph對(duì)象的方法取得節(jié)點(diǎn)對(duì)象,這里的節(jié)點(diǎn)對(duì)象Node可以直接按照之前的Node操作

# 查詢(p1)-[k]->(p2),并返回所有節(jié)點(diǎn)和關(guān)系
gql = "MATCH (p1:Person)-[k:KNOWS]->(p2:Person) RETURN *"
sub_graph = graph.run(gql).to_subgraph()
# 獲取子圖中所有節(jié)點(diǎn)對(duì)象并打印
nodes=sub_graph.nodes
for node in nodes:
  print(node)
# 輸出的節(jié)點(diǎn)對(duì)象如下:
'''
(_101:Person {name: 'xiaozhang'})
(_100:Person {name: 'xiaowang'})
(_103:Person {name: 'xiaoli'})
(_102:Person {name: 'xiaozhao'})
'''

3.5、OGM

Object-Graph Mapping將圖數(shù)據(jù)庫(kù)中的節(jié)點(diǎn)映射為python對(duì)象,通過(guò)對(duì)象的方式對(duì)節(jié)點(diǎn)進(jìn)行訪問(wèn)和操作。

將圖中的每種標(biāo)簽定義為一個(gè)python類,其繼承自GraphObject,注意使用前先import。在定義時(shí)可以指定數(shù)據(jù)類的主鍵,并定義類的屬性Property()、標(biāo)簽Label()、關(guān)系RelatedTo()/RelatedFrom。

from py2neo.ogm import GraphObject, Property, RelatedTo, RelatedFrom, Label
class Person(GraphObject):
  # 定義主鍵
  __primarykey__ = 'name'
  # 定義類的屬性
  name=Property()
  age=Property()
  # 定義類的標(biāo)簽
  student=Label()
  # 定義Person指向的關(guān)系
  knows=RelatedTo('Person','KNOWS')
  # 定義指向Person的關(guān)系
  known=RelatedFrom('Person','KNOWN')

通過(guò)類方法wrap()可以將一個(gè)普通節(jié)點(diǎn)轉(zhuǎn)化為類的對(duì)象。

類方法match(graph,primary_key)可以在graph中查找主鍵值為primary_key的節(jié)點(diǎn)

可以直接通過(guò)類構(gòu)造方法創(chuàng)建一個(gè)對(duì)象,并直接訪問(wèn)對(duì)象的屬性及方法,并通過(guò)關(guān)系方法add()添加關(guān)系

類的標(biāo)簽是一個(gè)bool值,默認(rèn)為False,將其修改為T(mén)rue,即可為對(duì)象添加標(biāo)簽

# 將節(jié)點(diǎn)c轉(zhuǎn)化為OGM類型
c=Person.wrap(c)
print(c.name)
# 查找Person類中主鍵(name)為Alice的節(jié)點(diǎn)
ali=Person.match(graph,'Alice').first()
# 創(chuàng)建一個(gè)新的Person對(duì)象并對(duì)其屬性賦值
new_person = Person()
new_person.name = 'Durant'
new_person.age = 28
# 標(biāo)簽值默認(rèn)為False
print(new_person.student)
# 修改bool值為T(mén)rue,為對(duì)象添加student標(biāo)簽
new_person.student=True
# 將修改后的圖寫(xiě)入數(shù)據(jù)庫(kù)
graph.push(ali)

在定義節(jié)點(diǎn)類時(shí)還可以定義其相關(guān)的關(guān)系,例如通過(guò)RelatedTo()定義從該節(jié)點(diǎn)指出的關(guān)系,RelatedFrom()定義指向該節(jié)點(diǎn)的關(guān)系。通過(guò)對(duì)象調(diào)用關(guān)系的對(duì)應(yīng)的方法完成節(jié)點(diǎn)周?chē)年P(guān)系操作,例如add()添加關(guān)系,clear()清除節(jié)點(diǎn)所有的關(guān)系,get()獲取關(guān)系屬性,remove()清楚指定的關(guān)系,update()更新關(guān)系

class Person(GraphObject):
  # 定義Person指向的關(guān)系
  knows=RelatedTo('Person','KNOWS')
  # 定義指向Person的關(guān)系
  known=RelatedFrom('Person','KNOWN')
# 新建一個(gè)從ali指向new_person的關(guān)系
ali.knows.add(new_person)
# 清除ali節(jié)點(diǎn)所有的know關(guān)系
ali.knows.clear()
# 清除ali節(jié)點(diǎn)指向new_person的那個(gè)know關(guān)系
ali.knows.remove(new_person)
# 更新ali指向new_person關(guān)系的屬性值
ali.knows.update(new_person,year=5)
# 獲取ali指向new_person關(guān)系的屬性year的值
ali.knows.get(new_person,'year')

通過(guò)圖對(duì)象也可以調(diào)用match方法對(duì)節(jié)點(diǎn)、關(guān)系進(jìn)行匹配

# 獲取第一個(gè)主鍵name名為Alice的Person對(duì)象
ali = Person.match(graph, 'Alice').first()
# 獲取所有name以B開(kāi)頭的Person對(duì)象
Person.match(graph).where("_.name =~ 'B.*'")

也可以通過(guò)圖graph對(duì)節(jié)點(diǎn)對(duì)象進(jìn)行操作:

# 更新圖中ali節(jié)點(diǎn)的相關(guān)數(shù)據(jù)
graph.push(ali)
# 用圖中的信息來(lái)更新ali節(jié)點(diǎn)
graph.pull(ali)
# 刪除圖中的ali對(duì)象節(jié)點(diǎn)
graph.delete(ali)

關(guān)于怎么在Python中使用py2neo操作neo4j數(shù)據(jù)庫(kù)就分享到這里了,希望以上內(nèi)容可以對(duì)大家有一定的幫助,可以學(xué)到更多知識(shí)。如果覺(jué)得文章不錯(cuò),可以把它分享出去讓更多的人看到。

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI