|
|
import logging
|
|
|
from typing import List
|
|
|
|
|
|
from fastapi import HTTPException
|
|
|
|
|
|
from app.database import neo4j_conn
|
|
|
from app.schemas.user import ChapterRelationCreate, ChapterRelationResponse
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
async def get_graph_relations():
|
|
|
"""
|
|
|
查询知识图谱中的所有节点及关系的详细信息
|
|
|
:return: 列表,每一项为 ChapterRelationResponse 对象
|
|
|
"""
|
|
|
query = """
|
|
|
MATCH (start)-[r]->(end)
|
|
|
RETURN
|
|
|
properties(start) AS start_properties,
|
|
|
labels(start) AS start_labels,
|
|
|
TYPE(r) AS relation,
|
|
|
properties(end) AS end_properties,
|
|
|
labels(end) AS end_labels
|
|
|
"""
|
|
|
async with neo4j_conn.get_session() as session:
|
|
|
result = await session.run(query)
|
|
|
records = await result.data()
|
|
|
|
|
|
responses = []
|
|
|
for record in records:
|
|
|
response = ChapterRelationResponse(
|
|
|
# start_id=record["start_id"],
|
|
|
start_labels=record["start_labels"],
|
|
|
start_properties=record["start_properties"],
|
|
|
relation=record["relation"],
|
|
|
# relation_properties=record["relation_properties"],
|
|
|
#
|
|
|
# end_id=record["end_id"],
|
|
|
end_labels=record["end_labels"],
|
|
|
end_properties=record["end_properties"]
|
|
|
)
|
|
|
responses.append(response)
|
|
|
|
|
|
return responses
|
|
|
|
|
|
|
|
|
async def search_chapters(search_term: str) -> List[ChapterRelationResponse]:
|
|
|
"""
|
|
|
根据搜索关键字模糊查询章节及其相关章节
|
|
|
:param search_term: 搜索关键字
|
|
|
:return: 列表,每一项为 ChapterRelationResponse 对象
|
|
|
"""
|
|
|
# 使用参数化查询以防止Cypher注入
|
|
|
query = """
|
|
|
MATCH (start)-[r]->(end)
|
|
|
WHERE toLower(start.name) CONTAINS toLower($search_term)
|
|
|
or toLower(end.name) CONTAINS toLower($search_term)
|
|
|
RETURN
|
|
|
properties(start) AS start_properties,
|
|
|
labels(start) AS start_labels,
|
|
|
TYPE(r) AS relation,
|
|
|
properties(end) AS end_properties,
|
|
|
labels(end) AS end_labels
|
|
|
"""
|
|
|
try:
|
|
|
async with neo4j_conn.get_session() as session:
|
|
|
result = await session.run(query, parameters={"search_term": search_term})
|
|
|
records = await result.data()
|
|
|
|
|
|
responses = [
|
|
|
ChapterRelationResponse(
|
|
|
start_labels=record.get("start_labels", []),
|
|
|
start_properties=record.get("start_properties", {}),
|
|
|
relation=record.get("relation", ""),
|
|
|
end_labels=record.get("end_labels", []),
|
|
|
end_properties=record.get("end_properties", {}),
|
|
|
)
|
|
|
for record in records
|
|
|
]
|
|
|
|
|
|
return responses
|
|
|
except Exception as e:
|
|
|
logging.error(f"Error during search_chapters: {e}")
|
|
|
return []
|
|
|
|
|
|
|
|
|
async def get_relations_to_level_simple(level: int) -> List[ChapterRelationResponse]:
|
|
|
"""
|
|
|
简化版:根据目标层级查询从 Root 到目标层级的所有节点和关系
|
|
|
:param level: 目标层级(1 -> Root & Subject, 2 -> Root, Subject, Topic, ..., 5 -> Problem)
|
|
|
:return: 列表,每一项为 ChapterRelationResponse 对象
|
|
|
"""
|
|
|
hierarchy = ["Root", "Subject", "Topic", "Section", "SubSection", "Problem"]
|
|
|
|
|
|
if level < 0 or level > len(hierarchy):
|
|
|
raise ValueError("层级参数必须在 0 到 5 之间")
|
|
|
# 特殊处理 level=0 的情况
|
|
|
if level == 0:
|
|
|
query = """
|
|
|
MATCH (start)
|
|
|
WHERE ANY(label IN labels(start) WHERE label = 'Root')
|
|
|
RETURN
|
|
|
properties(start) AS start_properties,
|
|
|
labels(start) AS start_labels
|
|
|
"""
|
|
|
|
|
|
try:
|
|
|
async with neo4j_conn.get_session() as session:
|
|
|
result = await session.run(query)
|
|
|
records = await result.data()
|
|
|
|
|
|
return [
|
|
|
ChapterRelationResponse(
|
|
|
start_labels=record.get("start_labels", []),
|
|
|
start_properties=record.get("start_properties", {}),
|
|
|
relation="", # 没有关系信息
|
|
|
end_labels=[], # 没有终点节点
|
|
|
end_properties={}, # 没有终点节点属性
|
|
|
)
|
|
|
for record in records
|
|
|
]
|
|
|
except Exception as e:
|
|
|
logging.error(f"Error during get_relations_to_level_simple (level=0): {e}")
|
|
|
return []
|
|
|
# 获取从 Root 到目标层级的所有标签
|
|
|
target_labels = hierarchy[:level + 1]
|
|
|
|
|
|
query = """
|
|
|
MATCH (start)-[r]->(end)
|
|
|
WHERE ANY(label IN labels(start) WHERE label IN $target_labels)
|
|
|
AND ANY(label IN labels(end) WHERE label IN $target_labels)
|
|
|
RETURN
|
|
|
properties(start) AS start_properties,
|
|
|
labels(start) AS start_labels,
|
|
|
TYPE(r) AS relation,
|
|
|
properties(end) AS end_properties,
|
|
|
labels(end) AS end_labels
|
|
|
"""
|
|
|
|
|
|
try:
|
|
|
async with neo4j_conn.get_session() as session:
|
|
|
result = await session.run(query, parameters={"target_labels": target_labels})
|
|
|
records = await result.data()
|
|
|
|
|
|
return [
|
|
|
ChapterRelationResponse(
|
|
|
start_labels=record.get("start_labels", []),
|
|
|
start_properties=record.get("start_properties", {}),
|
|
|
relation=record.get("relation", ""),
|
|
|
end_labels=record.get("end_labels", []),
|
|
|
end_properties=record.get("end_properties", {}),
|
|
|
)
|
|
|
for record in records
|
|
|
]
|
|
|
except Exception as e:
|
|
|
logging.error(f"Error during get_relations_to_level_simple: {e}")
|
|
|
return []
|
|
|
async def get_chapter_relations(chapter: str) -> List[ChapterRelationResponse]:
|
|
|
"""
|
|
|
根据章节名称查找该章节及其相关节点和关系
|
|
|
:param chapter_name: 章节名称
|
|
|
:return: 列表,每一项为 ChapterRelationResponse 对象
|
|
|
"""
|
|
|
query = """
|
|
|
MATCH (start)-[r]->(end)
|
|
|
WHERE start.chapter = $chapter and end.chapter = $chapter
|
|
|
RETURN
|
|
|
properties(start) AS start_properties,
|
|
|
labels(start) AS start_labels,
|
|
|
TYPE(r) AS relation,
|
|
|
properties(end) AS end_properties,
|
|
|
labels(end) AS end_labels
|
|
|
"""
|
|
|
try:
|
|
|
async with neo4j_conn.get_session() as session:
|
|
|
result = await session.run(query, parameters={"chapter": chapter})
|
|
|
records = await result.data()
|
|
|
return [
|
|
|
ChapterRelationResponse(
|
|
|
start_labels=record.get("start_labels", []),
|
|
|
start_properties=record.get("start_properties", {}),
|
|
|
relation=record.get("relation", ""),
|
|
|
end_labels=record.get("end_labels", []),
|
|
|
end_properties=record.get("end_properties", {}),
|
|
|
)
|
|
|
for record in records
|
|
|
]
|
|
|
except Exception as e:
|
|
|
logging.error(f"Error during get_chapter_relations: {e}")
|
|
|
return [] |