fix: auto-fix code issues (cron)
- 修复重复导入/字段 - 修复异常处理 - 修复PEP8格式问题 - 添加类型注解
This commit is contained in:
@@ -10,20 +10,20 @@ import logging
|
||||
import os
|
||||
from dataclasses import dataclass
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Neo4j 连接配置
|
||||
NEO4J_URI = os.getenv("NEO4J_URI", "bolt://localhost:7687")
|
||||
NEO4J_USER = os.getenv("NEO4J_USER", "neo4j")
|
||||
NEO4J_PASSWORD = os.getenv("NEO4J_PASSWORD", "password")
|
||||
NEO4J_URI = os.getenv("NEO4J_URI", "bolt://localhost:7687")
|
||||
NEO4J_USER = os.getenv("NEO4J_USER", "neo4j")
|
||||
NEO4J_PASSWORD = os.getenv("NEO4J_PASSWORD", "password")
|
||||
|
||||
# 延迟导入,避免未安装时出错
|
||||
try:
|
||||
from neo4j import Driver, GraphDatabase
|
||||
|
||||
NEO4J_AVAILABLE = True
|
||||
NEO4J_AVAILABLE = True
|
||||
except ImportError:
|
||||
NEO4J_AVAILABLE = False
|
||||
NEO4J_AVAILABLE = False
|
||||
logger.warning("Neo4j driver not installed. Neo4j features will be disabled.")
|
||||
|
||||
|
||||
@@ -35,15 +35,15 @@ class GraphEntity:
|
||||
project_id: str
|
||||
name: str
|
||||
type: str
|
||||
definition: str = ""
|
||||
aliases: list[str] = None
|
||||
properties: dict = None
|
||||
definition: str = ""
|
||||
aliases: list[str] = None
|
||||
properties: dict = None
|
||||
|
||||
def __post_init__(self) -> None:
|
||||
if self.aliases is None:
|
||||
self.aliases = []
|
||||
self.aliases = []
|
||||
if self.properties is None:
|
||||
self.properties = {}
|
||||
self.properties = {}
|
||||
|
||||
|
||||
@dataclass
|
||||
@@ -54,12 +54,12 @@ class GraphRelation:
|
||||
source_id: str
|
||||
target_id: str
|
||||
relation_type: str
|
||||
evidence: str = ""
|
||||
properties: dict = None
|
||||
evidence: str = ""
|
||||
properties: dict = None
|
||||
|
||||
def __post_init__(self) -> None:
|
||||
if self.properties is None:
|
||||
self.properties = {}
|
||||
self.properties = {}
|
||||
|
||||
|
||||
@dataclass
|
||||
@@ -69,7 +69,7 @@ class PathResult:
|
||||
nodes: list[dict]
|
||||
relationships: list[dict]
|
||||
length: int
|
||||
total_weight: float = 0.0
|
||||
total_weight: float = 0.0
|
||||
|
||||
|
||||
@dataclass
|
||||
@@ -79,7 +79,7 @@ class CommunityResult:
|
||||
community_id: int
|
||||
nodes: list[dict]
|
||||
size: int
|
||||
density: float = 0.0
|
||||
density: float = 0.0
|
||||
|
||||
|
||||
@dataclass
|
||||
@@ -89,17 +89,17 @@ class CentralityResult:
|
||||
entity_id: str
|
||||
entity_name: str
|
||||
score: float
|
||||
rank: int = 0
|
||||
rank: int = 0
|
||||
|
||||
|
||||
class Neo4jManager:
|
||||
"""Neo4j 图数据库管理器"""
|
||||
|
||||
def __init__(self, uri: str = None, user: str = None, password: str = None) -> None:
|
||||
self.uri = uri or NEO4J_URI
|
||||
self.user = user or NEO4J_USER
|
||||
self.password = password or NEO4J_PASSWORD
|
||||
self._driver: Driver | None = None
|
||||
def __init__(self, uri: str = None, user: str = None, password: str = None) -> None:
|
||||
self.uri = uri or NEO4J_URI
|
||||
self.user = user or NEO4J_USER
|
||||
self.password = password or NEO4J_PASSWORD
|
||||
self._driver: Driver | None = None
|
||||
|
||||
if not NEO4J_AVAILABLE:
|
||||
logger.error("Neo4j driver not available. Please install: pip install neo4j")
|
||||
@@ -113,13 +113,13 @@ class Neo4jManager:
|
||||
return
|
||||
|
||||
try:
|
||||
self._driver = GraphDatabase.driver(self.uri, auth = (self.user, self.password))
|
||||
self._driver = GraphDatabase.driver(self.uri, auth=(self.user, self.password))
|
||||
# 验证连接
|
||||
self._driver.verify_connectivity()
|
||||
logger.info(f"Connected to Neo4j at {self.uri}")
|
||||
except (RuntimeError, ValueError, TypeError) as e:
|
||||
logger.error(f"Failed to connect to Neo4j: {e}")
|
||||
self._driver = None
|
||||
self._driver = None
|
||||
|
||||
def close(self) -> None:
|
||||
"""关闭连接"""
|
||||
@@ -179,7 +179,7 @@ class Neo4jManager:
|
||||
# ==================== 数据同步 ====================
|
||||
|
||||
def sync_project(
|
||||
self, project_id: str, project_name: str, project_description: str = ""
|
||||
self, project_id: str, project_name: str, project_description: str = ""
|
||||
) -> None:
|
||||
"""同步项目节点到 Neo4j"""
|
||||
if not self._driver:
|
||||
@@ -193,9 +193,9 @@ class Neo4jManager:
|
||||
p.description = $description,
|
||||
p.updated_at = datetime()
|
||||
""",
|
||||
project_id = project_id,
|
||||
name = project_name,
|
||||
description = project_description,
|
||||
project_id=project_id,
|
||||
name=project_name,
|
||||
description=project_description,
|
||||
)
|
||||
|
||||
def sync_entity(self, entity: GraphEntity) -> None:
|
||||
@@ -218,13 +218,13 @@ class Neo4jManager:
|
||||
MATCH (p:Project {id: $project_id})
|
||||
MERGE (e)-[:BELONGS_TO]->(p)
|
||||
""",
|
||||
id = entity.id,
|
||||
project_id = entity.project_id,
|
||||
name = entity.name,
|
||||
type = entity.type,
|
||||
definition = entity.definition,
|
||||
aliases = json.dumps(entity.aliases),
|
||||
properties = json.dumps(entity.properties),
|
||||
id=entity.id,
|
||||
project_id=entity.project_id,
|
||||
name=entity.name,
|
||||
type=entity.type,
|
||||
definition=entity.definition,
|
||||
aliases=json.dumps(entity.aliases),
|
||||
properties=json.dumps(entity.properties),
|
||||
)
|
||||
|
||||
def sync_entities_batch(self, entities: list[GraphEntity]) -> None:
|
||||
@@ -234,7 +234,7 @@ class Neo4jManager:
|
||||
|
||||
with self._driver.session() as session:
|
||||
# 使用 UNWIND 批量处理
|
||||
entities_data = [
|
||||
entities_data = [
|
||||
{
|
||||
"id": e.id,
|
||||
"project_id": e.project_id,
|
||||
@@ -261,7 +261,7 @@ class Neo4jManager:
|
||||
MATCH (p:Project {id: entity.project_id})
|
||||
MERGE (e)-[:BELONGS_TO]->(p)
|
||||
""",
|
||||
entities = entities_data,
|
||||
entities=entities_data,
|
||||
)
|
||||
|
||||
def sync_relation(self, relation: GraphRelation) -> None:
|
||||
@@ -280,12 +280,12 @@ class Neo4jManager:
|
||||
r.properties = $properties,
|
||||
r.updated_at = datetime()
|
||||
""",
|
||||
id = relation.id,
|
||||
source_id = relation.source_id,
|
||||
target_id = relation.target_id,
|
||||
relation_type = relation.relation_type,
|
||||
evidence = relation.evidence,
|
||||
properties = json.dumps(relation.properties),
|
||||
id=relation.id,
|
||||
source_id=relation.source_id,
|
||||
target_id=relation.target_id,
|
||||
relation_type=relation.relation_type,
|
||||
evidence=relation.evidence,
|
||||
properties=json.dumps(relation.properties),
|
||||
)
|
||||
|
||||
def sync_relations_batch(self, relations: list[GraphRelation]) -> None:
|
||||
@@ -294,7 +294,7 @@ class Neo4jManager:
|
||||
return
|
||||
|
||||
with self._driver.session() as session:
|
||||
relations_data = [
|
||||
relations_data = [
|
||||
{
|
||||
"id": r.id,
|
||||
"source_id": r.source_id,
|
||||
@@ -317,7 +317,7 @@ class Neo4jManager:
|
||||
r.properties = rel.properties,
|
||||
r.updated_at = datetime()
|
||||
""",
|
||||
relations = relations_data,
|
||||
relations=relations_data,
|
||||
)
|
||||
|
||||
def delete_entity(self, entity_id: str) -> None:
|
||||
@@ -331,7 +331,7 @@ class Neo4jManager:
|
||||
MATCH (e:Entity {id: $id})
|
||||
DETACH DELETE e
|
||||
""",
|
||||
id = entity_id,
|
||||
id=entity_id,
|
||||
)
|
||||
|
||||
def delete_project(self, project_id: str) -> None:
|
||||
@@ -346,13 +346,13 @@ class Neo4jManager:
|
||||
OPTIONAL MATCH (e:Entity)-[:BELONGS_TO]->(p)
|
||||
DETACH DELETE e, p
|
||||
""",
|
||||
id = project_id,
|
||||
id=project_id,
|
||||
)
|
||||
|
||||
# ==================== 复杂图查询 ====================
|
||||
|
||||
def find_shortest_path(
|
||||
self, source_id: str, target_id: str, max_depth: int = 10
|
||||
self, source_id: str, target_id: str, max_depth: int = 10
|
||||
) -> PathResult | None:
|
||||
"""
|
||||
查找两个实体之间的最短路径
|
||||
@@ -369,31 +369,31 @@ class Neo4jManager:
|
||||
return None
|
||||
|
||||
with self._driver.session() as session:
|
||||
result = session.run(
|
||||
result = session.run(
|
||||
"""
|
||||
MATCH path = shortestPath(
|
||||
(source:Entity {id: $source_id})-[*1..$max_depth]-(target:Entity {id: $target_id})
|
||||
)
|
||||
RETURN path
|
||||
""",
|
||||
source_id = source_id,
|
||||
target_id = target_id,
|
||||
max_depth = max_depth,
|
||||
source_id=source_id,
|
||||
target_id=target_id,
|
||||
max_depth=max_depth,
|
||||
)
|
||||
|
||||
record = result.single()
|
||||
record = result.single()
|
||||
if not record:
|
||||
return None
|
||||
|
||||
path = record["path"]
|
||||
path = record["path"]
|
||||
|
||||
# 提取节点和关系
|
||||
nodes = [
|
||||
nodes = [
|
||||
{"id": node["id"], "name": node["name"], "type": node["type"]}
|
||||
for node in path.nodes
|
||||
]
|
||||
|
||||
relationships = [
|
||||
relationships = [
|
||||
{
|
||||
"source": rel.start_node["id"],
|
||||
"target": rel.end_node["id"],
|
||||
@@ -404,11 +404,11 @@ class Neo4jManager:
|
||||
]
|
||||
|
||||
return PathResult(
|
||||
nodes = nodes, relationships = relationships, length = len(path.relationships)
|
||||
nodes=nodes, relationships=relationships, length=len(path.relationships)
|
||||
)
|
||||
|
||||
def find_all_paths(
|
||||
self, source_id: str, target_id: str, max_depth: int = 5, limit: int = 10
|
||||
self, source_id: str, target_id: str, max_depth: int = 5, limit: int = 10
|
||||
) -> list[PathResult]:
|
||||
"""
|
||||
查找两个实体之间的所有路径
|
||||
@@ -426,29 +426,29 @@ class Neo4jManager:
|
||||
return []
|
||||
|
||||
with self._driver.session() as session:
|
||||
result = session.run(
|
||||
result = session.run(
|
||||
"""
|
||||
MATCH path = (source:Entity {id: $source_id})-[*1..$max_depth]-(target:Entity {id: $target_id})
|
||||
WHERE source <> target
|
||||
RETURN path
|
||||
LIMIT $limit
|
||||
""",
|
||||
source_id = source_id,
|
||||
target_id = target_id,
|
||||
max_depth = max_depth,
|
||||
limit = limit,
|
||||
source_id=source_id,
|
||||
target_id=target_id,
|
||||
max_depth=max_depth,
|
||||
limit=limit,
|
||||
)
|
||||
|
||||
paths = []
|
||||
paths = []
|
||||
for record in result:
|
||||
path = record["path"]
|
||||
path = record["path"]
|
||||
|
||||
nodes = [
|
||||
nodes = [
|
||||
{"id": node["id"], "name": node["name"], "type": node["type"]}
|
||||
for node in path.nodes
|
||||
]
|
||||
|
||||
relationships = [
|
||||
relationships = [
|
||||
{
|
||||
"source": rel.start_node["id"],
|
||||
"target": rel.end_node["id"],
|
||||
@@ -460,14 +460,14 @@ class Neo4jManager:
|
||||
|
||||
paths.append(
|
||||
PathResult(
|
||||
nodes = nodes, relationships = relationships, length = len(path.relationships)
|
||||
nodes=nodes, relationships=relationships, length=len(path.relationships)
|
||||
)
|
||||
)
|
||||
|
||||
return paths
|
||||
|
||||
def find_neighbors(
|
||||
self, entity_id: str, relation_type: str = None, limit: int = 50
|
||||
self, entity_id: str, relation_type: str = None, limit: int = 50
|
||||
) -> list[dict]:
|
||||
"""
|
||||
查找实体的邻居节点
|
||||
@@ -485,30 +485,30 @@ class Neo4jManager:
|
||||
|
||||
with self._driver.session() as session:
|
||||
if relation_type:
|
||||
result = session.run(
|
||||
result = session.run(
|
||||
"""
|
||||
MATCH (e:Entity {id: $entity_id})-[r:RELATES_TO {relation_type: $relation_type}]-(neighbor:Entity)
|
||||
RETURN neighbor, r.relation_type as rel_type, r.evidence as evidence
|
||||
LIMIT $limit
|
||||
""",
|
||||
entity_id = entity_id,
|
||||
relation_type = relation_type,
|
||||
limit = limit,
|
||||
entity_id=entity_id,
|
||||
relation_type=relation_type,
|
||||
limit=limit,
|
||||
)
|
||||
else:
|
||||
result = session.run(
|
||||
result = session.run(
|
||||
"""
|
||||
MATCH (e:Entity {id: $entity_id})-[r:RELATES_TO]-(neighbor:Entity)
|
||||
RETURN neighbor, r.relation_type as rel_type, r.evidence as evidence
|
||||
LIMIT $limit
|
||||
""",
|
||||
entity_id = entity_id,
|
||||
limit = limit,
|
||||
entity_id=entity_id,
|
||||
limit=limit,
|
||||
)
|
||||
|
||||
neighbors = []
|
||||
neighbors = []
|
||||
for record in result:
|
||||
node = record["neighbor"]
|
||||
node = record["neighbor"]
|
||||
neighbors.append(
|
||||
{
|
||||
"id": node["id"],
|
||||
@@ -536,13 +536,13 @@ class Neo4jManager:
|
||||
return []
|
||||
|
||||
with self._driver.session() as session:
|
||||
result = session.run(
|
||||
result = session.run(
|
||||
"""
|
||||
MATCH (e1:Entity {id: $id1})-[:RELATES_TO]-(common:Entity)-[:RELATES_TO]-(e2:Entity {id: $id2})
|
||||
RETURN DISTINCT common
|
||||
""",
|
||||
id1 = entity_id1,
|
||||
id2 = entity_id2,
|
||||
id1=entity_id1,
|
||||
id2=entity_id2,
|
||||
)
|
||||
|
||||
return [
|
||||
@@ -556,7 +556,7 @@ class Neo4jManager:
|
||||
|
||||
# ==================== 图算法分析 ====================
|
||||
|
||||
def calculate_pagerank(self, project_id: str, top_n: int = 20) -> list[CentralityResult]:
|
||||
def calculate_pagerank(self, project_id: str, top_n: int = 20) -> list[CentralityResult]:
|
||||
"""
|
||||
计算 PageRank 中心性
|
||||
|
||||
@@ -571,7 +571,7 @@ class Neo4jManager:
|
||||
return []
|
||||
|
||||
with self._driver.session() as session:
|
||||
result = session.run(
|
||||
result = session.run(
|
||||
"""
|
||||
CALL gds.graph.exists('project-graph-$project_id') YIELD exists
|
||||
WITH exists
|
||||
@@ -581,7 +581,7 @@ class Neo4jManager:
|
||||
{}
|
||||
) YIELD value RETURN value
|
||||
""",
|
||||
project_id = project_id,
|
||||
project_id=project_id,
|
||||
)
|
||||
|
||||
# 创建临时图
|
||||
@@ -601,11 +601,11 @@ class Neo4jManager:
|
||||
}
|
||||
)
|
||||
""",
|
||||
project_id = project_id,
|
||||
project_id=project_id,
|
||||
)
|
||||
|
||||
# 运行 PageRank
|
||||
result = session.run(
|
||||
result = session.run(
|
||||
"""
|
||||
CALL gds.pageRank.stream('project-graph-$project_id')
|
||||
YIELD nodeId, score
|
||||
@@ -615,19 +615,19 @@ class Neo4jManager:
|
||||
ORDER BY score DESC
|
||||
LIMIT $top_n
|
||||
""",
|
||||
project_id = project_id,
|
||||
top_n = top_n,
|
||||
project_id=project_id,
|
||||
top_n=top_n,
|
||||
)
|
||||
|
||||
rankings = []
|
||||
rank = 1
|
||||
rankings = []
|
||||
rank = 1
|
||||
for record in result:
|
||||
rankings.append(
|
||||
CentralityResult(
|
||||
entity_id = record["entity_id"],
|
||||
entity_name = record["entity_name"],
|
||||
score = record["score"],
|
||||
rank = rank,
|
||||
entity_id=record["entity_id"],
|
||||
entity_name=record["entity_name"],
|
||||
score=record["score"],
|
||||
rank=rank,
|
||||
)
|
||||
)
|
||||
rank += 1
|
||||
@@ -637,12 +637,12 @@ class Neo4jManager:
|
||||
"""
|
||||
CALL gds.graph.drop('project-graph-$project_id')
|
||||
""",
|
||||
project_id = project_id,
|
||||
project_id=project_id,
|
||||
)
|
||||
|
||||
return rankings
|
||||
|
||||
def calculate_betweenness(self, project_id: str, top_n: int = 20) -> list[CentralityResult]:
|
||||
def calculate_betweenness(self, project_id: str, top_n: int = 20) -> list[CentralityResult]:
|
||||
"""
|
||||
计算 Betweenness 中心性(桥梁作用)
|
||||
|
||||
@@ -658,7 +658,7 @@ class Neo4jManager:
|
||||
|
||||
with self._driver.session() as session:
|
||||
# 使用 APOC 的 betweenness 计算(如果没有 GDS)
|
||||
result = session.run(
|
||||
result = session.run(
|
||||
"""
|
||||
MATCH (e:Entity)-[:BELONGS_TO]->(p:Project {id: $project_id})
|
||||
OPTIONAL MATCH (e)-[:RELATES_TO]-(other:Entity)
|
||||
@@ -667,19 +667,19 @@ class Neo4jManager:
|
||||
LIMIT $top_n
|
||||
RETURN e.id as entity_id, e.name as entity_name, degree as score
|
||||
""",
|
||||
project_id = project_id,
|
||||
top_n = top_n,
|
||||
project_id=project_id,
|
||||
top_n=top_n,
|
||||
)
|
||||
|
||||
rankings = []
|
||||
rank = 1
|
||||
rankings = []
|
||||
rank = 1
|
||||
for record in result:
|
||||
rankings.append(
|
||||
CentralityResult(
|
||||
entity_id = record["entity_id"],
|
||||
entity_name = record["entity_name"],
|
||||
score = float(record["score"]),
|
||||
rank = rank,
|
||||
entity_id=record["entity_id"],
|
||||
entity_name=record["entity_name"],
|
||||
score=float(record["score"]),
|
||||
rank=rank,
|
||||
)
|
||||
)
|
||||
rank += 1
|
||||
@@ -701,7 +701,7 @@ class Neo4jManager:
|
||||
|
||||
with self._driver.session() as session:
|
||||
# 简单的社区检测:基于连通分量
|
||||
result = session.run(
|
||||
result = session.run(
|
||||
"""
|
||||
MATCH (e:Entity)-[:BELONGS_TO]->(p:Project {id: $project_id})
|
||||
OPTIONAL MATCH (e)-[:RELATES_TO]-(other:Entity)-[:BELONGS_TO]->(p)
|
||||
@@ -710,25 +710,25 @@ class Neo4jManager:
|
||||
connections, size(connections) as connection_count
|
||||
ORDER BY connection_count DESC
|
||||
""",
|
||||
project_id = project_id,
|
||||
project_id=project_id,
|
||||
)
|
||||
|
||||
# 手动分组(基于连通性)
|
||||
communities = {}
|
||||
communities = {}
|
||||
for record in result:
|
||||
entity_id = record["entity_id"]
|
||||
connections = record["connections"]
|
||||
entity_id = record["entity_id"]
|
||||
connections = record["connections"]
|
||||
|
||||
# 找到所属的社区
|
||||
found_community = None
|
||||
found_community = None
|
||||
for comm_id, comm_data in communities.items():
|
||||
if any(conn in comm_data["member_ids"] for conn in connections):
|
||||
found_community = comm_id
|
||||
found_community = comm_id
|
||||
break
|
||||
|
||||
if found_community is None:
|
||||
found_community = len(communities)
|
||||
communities[found_community] = {"member_ids": set(), "nodes": []}
|
||||
found_community = len(communities)
|
||||
communities[found_community] = {"member_ids": set(), "nodes": []}
|
||||
|
||||
communities[found_community]["member_ids"].add(entity_id)
|
||||
communities[found_community]["nodes"].append(
|
||||
@@ -741,27 +741,27 @@ class Neo4jManager:
|
||||
)
|
||||
|
||||
# 构建结果
|
||||
results = []
|
||||
results = []
|
||||
for comm_id, comm_data in communities.items():
|
||||
nodes = comm_data["nodes"]
|
||||
size = len(nodes)
|
||||
nodes = comm_data["nodes"]
|
||||
size = len(nodes)
|
||||
# 计算密度(简化版)
|
||||
max_edges = size * (size - 1) / 2 if size > 1 else 1
|
||||
actual_edges = sum(n["connections"] for n in nodes) / 2
|
||||
density = actual_edges / max_edges if max_edges > 0 else 0
|
||||
max_edges = size * (size - 1) / 2 if size > 1 else 1
|
||||
actual_edges = sum(n["connections"] for n in nodes) / 2
|
||||
density = actual_edges / max_edges if max_edges > 0 else 0
|
||||
|
||||
results.append(
|
||||
CommunityResult(
|
||||
community_id = comm_id, nodes = nodes, size = size, density = min(density, 1.0)
|
||||
community_id=comm_id, nodes=nodes, size=size, density=min(density, 1.0)
|
||||
)
|
||||
)
|
||||
|
||||
# 按大小排序
|
||||
results.sort(key = lambda x: x.size, reverse = True)
|
||||
results.sort(key=lambda x: x.size, reverse=True)
|
||||
return results
|
||||
|
||||
def find_central_entities(
|
||||
self, project_id: str, metric: str = "degree"
|
||||
self, project_id: str, metric: str = "degree"
|
||||
) -> list[CentralityResult]:
|
||||
"""
|
||||
查找中心实体
|
||||
@@ -778,7 +778,7 @@ class Neo4jManager:
|
||||
|
||||
with self._driver.session() as session:
|
||||
if metric == "degree":
|
||||
result = session.run(
|
||||
result = session.run(
|
||||
"""
|
||||
MATCH (e:Entity)-[:BELONGS_TO]->(p:Project {id: $project_id})
|
||||
OPTIONAL MATCH (e)-[:RELATES_TO]-(other:Entity)
|
||||
@@ -787,11 +787,11 @@ class Neo4jManager:
|
||||
ORDER BY degree DESC
|
||||
LIMIT 20
|
||||
""",
|
||||
project_id = project_id,
|
||||
project_id=project_id,
|
||||
)
|
||||
else:
|
||||
# 默认使用度中心性
|
||||
result = session.run(
|
||||
result = session.run(
|
||||
"""
|
||||
MATCH (e:Entity)-[:BELONGS_TO]->(p:Project {id: $project_id})
|
||||
OPTIONAL MATCH (e)-[:RELATES_TO]-(other:Entity)
|
||||
@@ -800,18 +800,18 @@ class Neo4jManager:
|
||||
ORDER BY degree DESC
|
||||
LIMIT 20
|
||||
""",
|
||||
project_id = project_id,
|
||||
project_id=project_id,
|
||||
)
|
||||
|
||||
rankings = []
|
||||
rank = 1
|
||||
rankings = []
|
||||
rank = 1
|
||||
for record in result:
|
||||
rankings.append(
|
||||
CentralityResult(
|
||||
entity_id = record["entity_id"],
|
||||
entity_name = record["entity_name"],
|
||||
score = float(record["score"]),
|
||||
rank = rank,
|
||||
entity_id=record["entity_id"],
|
||||
entity_name=record["entity_name"],
|
||||
score=float(record["score"]),
|
||||
rank=rank,
|
||||
)
|
||||
)
|
||||
rank += 1
|
||||
@@ -835,49 +835,49 @@ class Neo4jManager:
|
||||
|
||||
with self._driver.session() as session:
|
||||
# 实体数量
|
||||
entity_count = session.run(
|
||||
entity_count = session.run(
|
||||
"""
|
||||
MATCH (e:Entity)-[:BELONGS_TO]->(p:Project {id: $project_id})
|
||||
RETURN count(e) as count
|
||||
""",
|
||||
project_id = project_id,
|
||||
project_id=project_id,
|
||||
).single()["count"]
|
||||
|
||||
# 关系数量
|
||||
relation_count = session.run(
|
||||
relation_count = session.run(
|
||||
"""
|
||||
MATCH (e:Entity)-[:BELONGS_TO]->(p:Project {id: $project_id})
|
||||
MATCH (e)-[r:RELATES_TO]-()
|
||||
RETURN count(r) as count
|
||||
""",
|
||||
project_id = project_id,
|
||||
project_id=project_id,
|
||||
).single()["count"]
|
||||
|
||||
# 实体类型分布
|
||||
type_distribution = session.run(
|
||||
type_distribution = session.run(
|
||||
"""
|
||||
MATCH (e:Entity)-[:BELONGS_TO]->(p:Project {id: $project_id})
|
||||
RETURN e.type as type, count(e) as count
|
||||
ORDER BY count DESC
|
||||
""",
|
||||
project_id = project_id,
|
||||
project_id=project_id,
|
||||
)
|
||||
|
||||
types = {record["type"]: record["count"] for record in type_distribution}
|
||||
types = {record["type"]: record["count"] for record in type_distribution}
|
||||
|
||||
# 平均度
|
||||
avg_degree = session.run(
|
||||
avg_degree = session.run(
|
||||
"""
|
||||
MATCH (e:Entity)-[:BELONGS_TO]->(p:Project {id: $project_id})
|
||||
OPTIONAL MATCH (e)-[:RELATES_TO]-(other)
|
||||
WITH e, count(other) as degree
|
||||
RETURN avg(degree) as avg_degree
|
||||
""",
|
||||
project_id = project_id,
|
||||
project_id=project_id,
|
||||
).single()["avg_degree"]
|
||||
|
||||
# 关系类型分布
|
||||
rel_types = session.run(
|
||||
rel_types = session.run(
|
||||
"""
|
||||
MATCH (e:Entity)-[:BELONGS_TO]->(p:Project {id: $project_id})
|
||||
MATCH (e)-[r:RELATES_TO]-()
|
||||
@@ -885,10 +885,10 @@ class Neo4jManager:
|
||||
ORDER BY count DESC
|
||||
LIMIT 10
|
||||
""",
|
||||
project_id = project_id,
|
||||
project_id=project_id,
|
||||
)
|
||||
|
||||
relation_types = {record["type"]: record["count"] for record in rel_types}
|
||||
relation_types = {record["type"]: record["count"] for record in rel_types}
|
||||
|
||||
return {
|
||||
"entity_count": entity_count,
|
||||
@@ -901,7 +901,7 @@ class Neo4jManager:
|
||||
else 0,
|
||||
}
|
||||
|
||||
def get_subgraph(self, entity_ids: list[str], depth: int = 1) -> dict:
|
||||
def get_subgraph(self, entity_ids: list[str], depth: int = 1) -> dict:
|
||||
"""
|
||||
获取指定实体的子图
|
||||
|
||||
@@ -916,7 +916,7 @@ class Neo4jManager:
|
||||
return {"nodes": [], "relationships": []}
|
||||
|
||||
with self._driver.session() as session:
|
||||
result = session.run(
|
||||
result = session.run(
|
||||
"""
|
||||
MATCH (e:Entity)
|
||||
WHERE e.id IN $entity_ids
|
||||
@@ -927,14 +927,14 @@ class Neo4jManager:
|
||||
}) YIELD node
|
||||
RETURN DISTINCT node
|
||||
""",
|
||||
entity_ids = entity_ids,
|
||||
depth = depth,
|
||||
entity_ids=entity_ids,
|
||||
depth=depth,
|
||||
)
|
||||
|
||||
nodes = []
|
||||
node_ids = set()
|
||||
nodes = []
|
||||
node_ids = set()
|
||||
for record in result:
|
||||
node = record["node"]
|
||||
node = record["node"]
|
||||
node_ids.add(node["id"])
|
||||
nodes.append(
|
||||
{
|
||||
@@ -946,17 +946,17 @@ class Neo4jManager:
|
||||
)
|
||||
|
||||
# 获取这些节点之间的关系
|
||||
result = session.run(
|
||||
result = session.run(
|
||||
"""
|
||||
MATCH (source:Entity)-[r:RELATES_TO]->(target:Entity)
|
||||
WHERE source.id IN $node_ids AND target.id IN $node_ids
|
||||
RETURN source.id as source_id, target.id as target_id,
|
||||
r.relation_type as type, r.evidence as evidence
|
||||
""",
|
||||
node_ids = list(node_ids),
|
||||
node_ids=list(node_ids),
|
||||
)
|
||||
|
||||
relationships = [
|
||||
relationships = [
|
||||
{
|
||||
"source": record["source_id"],
|
||||
"target": record["target_id"],
|
||||
@@ -970,14 +970,14 @@ class Neo4jManager:
|
||||
|
||||
|
||||
# 全局单例
|
||||
_neo4j_manager = None
|
||||
_neo4j_manager = None
|
||||
|
||||
|
||||
def get_neo4j_manager() -> Neo4jManager:
|
||||
"""获取 Neo4j 管理器单例"""
|
||||
global _neo4j_manager
|
||||
if _neo4j_manager is None:
|
||||
_neo4j_manager = Neo4jManager()
|
||||
_neo4j_manager = Neo4jManager()
|
||||
return _neo4j_manager
|
||||
|
||||
|
||||
@@ -986,7 +986,7 @@ def close_neo4j_manager() -> None:
|
||||
global _neo4j_manager
|
||||
if _neo4j_manager:
|
||||
_neo4j_manager.close()
|
||||
_neo4j_manager = None
|
||||
_neo4j_manager = None
|
||||
|
||||
|
||||
# 便捷函数
|
||||
@@ -1004,7 +1004,7 @@ def sync_project_to_neo4j(
|
||||
entities: 实体列表(字典格式)
|
||||
relations: 关系列表(字典格式)
|
||||
"""
|
||||
manager = get_neo4j_manager()
|
||||
manager = get_neo4j_manager()
|
||||
if not manager.is_connected():
|
||||
logger.warning("Neo4j not connected, skipping sync")
|
||||
return
|
||||
@@ -1013,29 +1013,29 @@ def sync_project_to_neo4j(
|
||||
manager.sync_project(project_id, project_name)
|
||||
|
||||
# 同步实体
|
||||
graph_entities = [
|
||||
graph_entities = [
|
||||
GraphEntity(
|
||||
id = e["id"],
|
||||
project_id = project_id,
|
||||
name = e["name"],
|
||||
type = e.get("type", "unknown"),
|
||||
definition = e.get("definition", ""),
|
||||
aliases = e.get("aliases", []),
|
||||
properties = e.get("properties", {}),
|
||||
id=e["id"],
|
||||
project_id=project_id,
|
||||
name=e["name"],
|
||||
type=e.get("type", "unknown"),
|
||||
definition=e.get("definition", ""),
|
||||
aliases=e.get("aliases", []),
|
||||
properties=e.get("properties", {}),
|
||||
)
|
||||
for e in entities
|
||||
]
|
||||
manager.sync_entities_batch(graph_entities)
|
||||
|
||||
# 同步关系
|
||||
graph_relations = [
|
||||
graph_relations = [
|
||||
GraphRelation(
|
||||
id = r["id"],
|
||||
source_id = r["source_entity_id"],
|
||||
target_id = r["target_entity_id"],
|
||||
relation_type = r["relation_type"],
|
||||
evidence = r.get("evidence", ""),
|
||||
properties = r.get("properties", {}),
|
||||
id=r["id"],
|
||||
source_id=r["source_entity_id"],
|
||||
target_id=r["target_entity_id"],
|
||||
relation_type=r["relation_type"],
|
||||
evidence=r.get("evidence", ""),
|
||||
properties=r.get("properties", {}),
|
||||
)
|
||||
for r in relations
|
||||
]
|
||||
@@ -1048,9 +1048,9 @@ def sync_project_to_neo4j(
|
||||
|
||||
if __name__ == "__main__":
|
||||
# 测试代码
|
||||
logging.basicConfig(level = logging.INFO)
|
||||
logging.basicConfig(level=logging.INFO)
|
||||
|
||||
manager = Neo4jManager()
|
||||
manager = Neo4jManager()
|
||||
|
||||
if manager.is_connected():
|
||||
print("✅ Connected to Neo4j")
|
||||
@@ -1064,18 +1064,18 @@ if __name__ == "__main__":
|
||||
print("✅ Project synced")
|
||||
|
||||
# 测试实体
|
||||
test_entity = GraphEntity(
|
||||
id = "test-entity-1",
|
||||
project_id = "test-project",
|
||||
name = "Test Entity",
|
||||
type = "Person",
|
||||
definition = "A test entity",
|
||||
test_entity = GraphEntity(
|
||||
id="test-entity-1",
|
||||
project_id="test-project",
|
||||
name="Test Entity",
|
||||
type="Person",
|
||||
definition="A test entity",
|
||||
)
|
||||
manager.sync_entity(test_entity)
|
||||
print("✅ Entity synced")
|
||||
|
||||
# 获取统计
|
||||
stats = manager.get_graph_stats("test-project")
|
||||
stats = manager.get_graph_stats("test-project")
|
||||
print(f"📊 Graph stats: {stats}")
|
||||
|
||||
else:
|
||||
|
||||
Reference in New Issue
Block a user