您好,登錄后才能下訂單哦!
這篇文章將為大家詳細(xì)講解有關(guān)怎么在Python中使用py2neo操作neo4j數(shù)據(jù)庫(kù),文章內(nèi)容質(zhì)量較高,因此小編分享給大家做個(gè)參考,希望大家閱讀完這篇文章后對(duì)相關(guān)知識(shí)有一定的了解。
圖:數(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é)果。
圖模型:
節(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)行索引可以加速查找
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的可視化界面
py2neo是一個(gè)社區(qū)第三方庫(kù),通過(guò)它可以更為便捷地使用python來(lái)操作neo4j
安裝py2neo:pip install py2neo
,我安裝的版本是4.3.0
創(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')
由節(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)
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))
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'}) '''
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ò),可以把它分享出去讓更多的人看到。
免責(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)容。