neo4j 圖數據庫:Cypher 查詢語言、醫學知識圖譜
- Cypher 查詢語言
- 創建數據
- 查詢數據
- 查詢并返回所有節點
- 查詢并返回所有帶有特定標簽的節點
- 查詢特定屬性的節點及其所有關系和關系的另一端節點
- 查詢從名為“小明”的節點到名為“小紅”的節點的路徑
- 更新數據
- 更新一個節點的屬性
- 添加新屬性
- 更新關系的屬性
- 刪除數據
- 刪除節點
- 刪除關系
- 刪除屬性
- 三元組導入知識圖譜
- 對于 `kg_triples_small.txt`
- 對于 `CPubMed-KGV_1.1.txt`
- 深度級聯查詢 - 獲取全面的癥狀-疾病網絡
- Python 交互 Neo4j
- 步驟 1: 安裝 Neo4j Python 驅動程序
- 步驟 2: 建立連接
- 步驟 3: 執行查詢
- 步驟 4: 處理事務
- 步驟 5: 關閉驅動
- 查詢性能優化
- 使用索引
- 合理使用標簽
- 優化Cypher查詢
- 使用參數化查詢
- 使用更高效的查詢模式
- 監控和分析查詢性能
?
Cypher 查詢語言
Cypher 是 neo4j 查詢語言。
以下列舉一些最少必要知識,能用就行。
其他用法,可以問大模型。
創建數據
CREATE (n:User {name:"小明", age:18}) RETURN n
# 創建一個屬性為 name:"小明" 和 age:18 的 User 類型的節點,并返回這個節點。CREATE (n:User {name:"小明"}) -[r:LOVE{time:"一萬年"}]-> (m:User{name:"小紅"}) return n,r,m
# 創建了兩個用戶節點:小明 和 小紅。它還創建了一個從 小明 指向 小紅 的 LOVE 類型的關系,并設置了屬性 time:"一萬年"。該命令返回這兩個節點以及他們之間的關系。CREATE (n:User {name:"小明"}) <-[r:LOVE{time:"一萬年"}]- (m:User{name:"小紅"}) return n,r,m
# 與第二條命令類似,但關系的方向相反。
在 Cypher 查詢語言中,用于圖數據庫操作的特定符號有特殊意義,這里對您提到的符號進行詳細解釋:
-
{}(花括號):
花括號用于定義節點或關系的屬性。
在創建節點或關系時,花括號內可以包含一系列的鍵值對,鍵與值之間用冒號分隔。例如:
CREATE (n:User {name: "小明", age: 18})
這里
{name: "小明", age: 18}
定義了一個User
類型的節點,具有name
和age
兩個屬性。 -
:(冒號):
冒號在 Cypher 中用于指定節點的標簽或關系的類型。標簽通常用來分類或標識不同的節點,而關系的類型用來描述節點之間的連接方式。
例如:
MATCH (n:User) -[:FRIEND]-> (m:User)
這里
:User
表明n
和m
是User
類型的節點,而:FRIEND
指明兩者之間的關系類型為FRIEND
。 -
->(箭頭):
箭頭用于指明關系的方向。在圖數據庫中,關系可以是有向的,箭頭顯示了從一個節點指向另一個節點的路徑。例如:
CREATE (n) -[:LIKES]-> (m)
這表示從節點
n
到節點m
存在一個LIKES
類型的關系,并且方向是從n
指向m
。 -
()(圓括號):
圓括號用于圍繞節點,并可與其他符號結合表示關系和屬性。
在定義節點時,圓括號內可以包含節點的變量名(可選)、標簽(可選)和屬性(可選)。例如:
MATCH (n) -[:KNOWS]-> (m)
這里
(n)
和(m)
分別代表圖中的兩個節點,通過KNOWS
關系相連。
這些符號的組合使得 Cypher 查詢語言能夠直觀地描述和操作圖數據結構。
查詢數據
這三個 Cypher 查詢示例都是用來從圖數據庫中檢索數據的:
查詢并返回所有節點
MATCH (n)
RETURN n
MATCH (n)
: 匹配圖中的所有節點,這里n
是節點的變量名,可以用來引用任何被匹配到的節點。RETURN n
: 返回所有匹配到的節點。- 注釋:// 匹配并返回圖中的所有節點
查詢并返回所有帶有特定標簽的節點
MATCH (n:User)
RETURN n
MATCH (n:User)
: 匹配圖中所有標記為User
的節點,:User
指定了節點的標簽。RETURN n
: 返回所有帶有User
標簽的節點。- 注釋:// 查詢并返回所有標為“User”的節點
查詢特定屬性的節點及其所有關系和關系的另一端節點
MATCH (n:User {name:"小明"}) -[r]- (m)
RETURN n, r, m
MATCH (n:User {name:"小明"}) -[r]- (m)
: 匹配圖中名為 “小明” 的User
類型節點,并匹配該節點的所有關系r
及關系的另一端節點m
。關系[r]
沒有指定方向,表示可以是任何方向。RETURN n, r, m
: 返回與名為 “小明” 的User
節點有關系的所有節點和這些關系。寫成 return * 等同- 注釋:// 查詢名為“小明”的用戶節點及其所有關系和關聯節點
查詢從名為“小明”的節點到名為“小紅”的節點的路徑
MATCH path = (a:User {name: "小明"})-[*]->(b:User {name: "小紅"})
RETURN path
這條查詢的解釋如下:
MATCH path = (a:User {name: "小明"})-[*]->(b:User {name: "小紅"})
: 這里定義了一個變量path
來保存從“小明”到“小紅”的路徑。(a:User {name: "小明"})
和(b:User {name: "小紅"})
分別指定了起點和終點節點,而[*]
表示路徑中可以包含任意數量和類型的關系,并且是有方向的,從“小明”指向“小紅”。RETURN path
: 返回找到的所有路徑。
如果你想限制路徑的長度,例如,查找最多經過3個關系的路徑,可以修改查詢如下:
MATCH path = (a:User {name: "小明"})-[*..3]->(b:User {name: "小紅"})
RETURN path
這個修改使得查詢只返回最多包含三個關系的路徑。
這樣的限制有助于避免在大圖中執行過于復雜的查詢,從而可能導致性能問題。
此外,如果你對路徑的詳細信息感興趣,比如路徑中每個節點和關系的具體屬性,你可以展開路徑的返回內容:
MATCH path = (a:User {name: "小明"})-[*]->(b:User {name: "小紅"})
RETURN nodes(path) AS nodes, relationships(path) AS relationships
這將返回路徑中的所有節點和關系,使得你能夠獲取更多關于路徑的具體細節。
這些查詢覆蓋了從最基本的節點檢索到更具體的帶有條件和關系的檢索,適合不同的查詢需求。
在實際使用時,根據圖的大小和復雜性,你可能需要考慮查詢的效率和性能。
更新數據
在 Cypher 查詢語言中,更新數據通常涉及修改節點或關系的屬性。這些操作通過 SET
關鍵詞來實現。下面,我會給出一些常見的更新數據的示例:
更新一個節點的屬性
假設你想更新名為“小明”的用戶的年齡。你可以使用以下Cypher命令:
MATCH (n:User {name: "小明"})
SET n.age = 20
RETURN n
這條命令的解釋如下:
MATCH (n:User {name: "小明"})
: 找到所有名為“小明”的User
節點。SET n.age = 20
: 將這些節點的age
屬性設置為20
。RETURN n
: 返回更新后的節點。
添加新屬性
如果你想給“小明”添加一個新的屬性,比如 email
:
MATCH (n:User {name: "小明"})
SET n.email = "xiaoming@example.com"
RETURN n
這將給“小明”節點添加一個 email
屬性,并設其值為 “xiaoming@example.com”。
更新關系的屬性
假設你想更新“小明”和“小紅”之間 LOVE
關系的時間屬性:
MATCH (a:User {name: "小明"})-[r:LOVE]->(b:User {name: "小紅"})
SET r.time = "兩萬年"
RETURN a, r, b
這條命令的解釋如下:
MATCH (a:User {name: "小明"})-[r:LOVE]->(b:User {name: "小紅"})
: 找到從“小明”到“小紅”的LOVE
類型的關系。SET r.time = "兩萬年"
: 更新這個關系的time
屬性。RETURN a, r, b
: 返回更新后的節點和關系。
刪除數據
在 Cypher 查詢語言中,刪除操作涉及到移除節點、關系、屬性或整個圖結構的一部分。
刪除節點
要刪除特定的節點,你需要先匹配到這個節點,然后使用 DELETE
命令。
需要注意的是,如果該節點還有任何關系,直接刪除會失敗,因為圖數據庫要求任何存在的關系都必須有明確的起點和終點。
如果你想刪除一個節點及其所有關系,可以使用 DETACH DELETE
。
示例:刪除名為“小紅”的用戶節點及其所有關系:
MATCH (n:User {name: "小紅"})
DETACH DELETE n
這里,DETACH DELETE n
會刪除匹配到的節點 n
及其所有關系。
刪除關系
如果你想單獨刪除節點之間的關系而保留節點本身,可以匹配到這些關系然后刪除它們。
示例:刪除“小明”和“小紅”之間的所有關系:
MATCH (a:User {name: "小明"})-[r]-(b:User {name: "小紅"})
DELETE r
這條命令不會刪除任何節點,只會刪除兩個用戶之間的所有關系。
刪除屬性
你可以使用 REMOVE
命令來刪除節點或關系上的某個屬性。
示例:刪除“小明”節點的 age
屬性:
MATCH (n:User {name: "小明"})
REMOVE n.age
RETURN n
執行后,“小明”的節點將不再有 age
屬性。
三元組導入知識圖譜
對于 kg_triples_small.txt
-
檢查數據格式:
- 確認每行是不是標準的三元組形式,例如
"主體, 關系, 賓體"
。
- 確認每行是不是標準的三元組形式,例如
-
導入到Neo4j:
- 如果文件已經是CSV格式,直接使用如下Cypher命令導入:
LOAD CSV FROM 'file:///path_to_kg_triples_small.csv' AS line MERGE (subject:Entity {name: line[0]}) MERGE (object:Entity {name: line[2]}) MERGE (subject)-[r:RELATION {type: line[1]}]->(object)
- 如果文件已經是CSV格式,直接使用如下Cypher命令導入:
對于 CPubMed-KGV_1.1.txt
CPubMed-KGV_1.1.txt 格式有點特別。
是@@形式,這種和三元祖形式有差異,要怎么處理呢?
這種數據是人工做的標識文本,是分類,特有的設計模式,直接導入就可以了。
可以用llamaindex,可以直接導入,因為AI幫你處理了數據,就不用你手動寫代碼轉csv。
深度級聯查詢 - 獲取全面的癥狀-疾病網絡
在醫學領域,構建和查詢癥狀與疾病之間的網絡可以極大地幫助醫生和研究人員理解疾病的復雜關系,從而改進診斷和治療方法。
在這樣的應用場景中,深度級聯查詢可以用來探索癥狀和疾病之間的多級關聯,比如通過中間狀態或其他相關癥狀/疾病來鏈接起始癥狀和目標疾病。
首先,我們需要構建一個圖模型,其中包含兩種基本類型的節點:Symptom
(癥狀)和Disease
(疾病)。
這些節點通過關系如INDICATES
(表征)、LEADS_TO
(導致)或ASSOCIATED_WITH
(相關聯)相連。
例如:
- 癥狀A
INDICATES
疾病B - 疾病B
LEADS_TO
疾病C - 癥狀A
ASSOCIATED_WITH
癥狀D
假設我們想要探索某個特定癥狀如何通過一系列其他癥狀和疾病最終可能導致某個具體疾病。
這種查詢不僅有助于診斷,還能揭示潛在的病理路徑。
MATCH path = (s:Symptom {name: "Persistent Cough"})-[:INDICATES|ASSOCIATED_WITH*1..4]->(d:Disease {name: "Lung Cancer"})
RETURN path
這里的查詢做了以下幾點:
- 起始節點:癥狀節點,名為“Persistent Cough”(持續性咳嗽)。
- 目標節點:疾病節點,名為“Lung Cancer”(肺癌)。
- 關系類型和深度:通過
INDICATES
(表征)或ASSOCIATED_WITH
(相關聯)關系,探索從起始癥狀到目標疾病的所有可能路徑,路徑的深度從1到4級不等。 - 返回:返回從“持續性咳嗽”可能導致“肺癌”的所有路徑。
為了進一步探索可能的中間疾病和癥狀,可以擴展查詢來包含更多的關系類型和節點:
MATCH path = (s:Symptom {name: "Persistent Cough"})-[:INDICATES|ASSOCIATED_WITH|LEADS_TO*1..6]->(d:Disease {name: "Lung Cancer"})
RETURN path
在這個查詢中,添加了LEADS_TO
關系,這表示疾病到疾病的直接影響,允許探索更長的病理路徑。
Python 交互 Neo4j
在項目中,一般是Python中執行Cypher查詢語句,從而操作Neo4j數據庫中的數據,而不是直接對著 neo4j 操作。
步驟 1: 安裝 Neo4j Python 驅動程序
首先,確保你已經安裝了neo4j
包。如果還未安裝,可以通過pip安裝:
pip install neo4j
步驟 2: 建立連接
你需要創建一個與Neo4j數據庫的連接。通常這涉及到指定數據庫的URL、用戶名和密碼。
from neo4j import GraphDatabaseuri = "bolt://localhost:7687" # Neo4j實例的Bolt URL
username = "neo4j"
password = "your_password"driver = GraphDatabase.driver(uri, auth=(username, password))
步驟 3: 執行查詢
通過定義一個函數來執行查詢。這個函數使用一個驅動實例來運行Cypher查詢并返回結果。
def get_user(driver, name):with driver.session() as session:result = session.run("MATCH (u:User {name: $name}) RETURN u", name=name)return [record["u"] for record in result]# 調用函數
user_data = get_user(driver, "小明")
print(user_data)
步驟 4: 處理事務
為了處理更復雜的業務邏輯,可以在會話中執行多個操作作為一個事務。
def create_friendship(driver, name1, name2):with driver.session() as session:session.write_transaction(lambda tx: tx.run("MATCH (a:User), (b:User) ""WHERE a.name = $name1 AND b.name = $name2 ""CREATE (a)-[:FRIENDS_WITH]->(b)", name1=name1, name2=name2))create_friendship(driver, "小明", "小紅")
步驟 5: 關閉驅動
操作完成后,確保關閉驅動連接。
driver.close()
查詢性能優化
在醫學問診的場景中,使用Neo4j來存儲和查詢有關疾病、癥狀、治療方法及其相互關系的數據時,可以采取以下優化策略來提升查詢性能。每個策略下將給出一個具體的例子:
使用索引
應用場景:快速查找特定癥狀或疾病的記錄。
示例:為疾病的名稱創建索引,以便快速啟動診斷過程。
CREATE INDEX ON :Disease(name)
合理使用標簽
應用場景:確保查詢只涉及相關類型的節點,以減少查詢范圍。
示例:在查詢涉及特定類型的節點時,使用合適的標簽。
MATCH (s:Symptom)-[:INDICATES]->(d:Disease)
WHERE s.name = 'Headache'
RETURN d.name
在此例中,Symptom
和 Disease
標簽幫助數據庫引擎快速定位和查詢相關節點。
優化Cypher查詢
應用場景:避免返回不必要的數據,減少數據傳輸。
示例:只返回需要的屬性,而不是整個節點。
MATCH (p:Patient)-[:HAS_SYMPTOM]->(s:Symptom)
RETURN s.name
這里,只返回癥狀名稱而非整個癥狀節點的所有屬性。
使用參數化查詢
應用場景:提高查詢的重用性和安全性。
示例:使用參數化的方式來查詢具有特定癥狀的所有患者。
MATCH (p:Patient)-[:HAS_SYMPTOM]->(s:Symptom {name: $symptomName})
RETURN p.name
參數 $symptomName
可以在運行時提供,這樣同一查詢可以用于不同的癥狀。
使用更高效的查詢模式
應用場景:避免過于復雜的匹配模式,特別是在大型圖中。
示例:使用較短的路徑和限制結果集的大小來優化查詢。
MATCH path = (s:Symptom {name: 'Fever'})-[:INDICATES*1..2]->(d:Disease)
RETURN d.name LIMIT 10
這里,路徑長度限制為1到2跳,查詢結果也限制為最多10個。
監控和分析查詢性能
應用場景:識別和優化慢查詢。
示例:使用PROFILE
來分析查詢并找出性能瓶頸。
PROFILE MATCH (p:Patient)-[:HAS_DISEASE]->(d:Disease)
WHERE d.name = 'Diabetes'
RETURN p.name
PROFILE
提供了關于查詢如何執行的詳細信息,包括每個操作的數據庫命中次數和資源消耗。
通過應用這些策略,醫學問診系統的數據庫查詢可以更加快速和有效,從而提升系統的整體性能和用戶體驗。