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
提供了关于查询如何执行的详细信息,包括每个操作的数据库命中次数和资源消耗。
通过应用这些策略,医学问诊系统的数据库查询可以更加快速和有效,从而提升系统的整体性能和用户体验。