|
@@ -0,0 +1,190 @@
|
|
|
+from typing import List, Dict, Optional
|
|
|
+from sqlalchemy.orm import Session
|
|
|
+from sqlalchemy import text
|
|
|
+from ..db.session import get_db
|
|
|
+
|
|
|
+class KgGraphService:
|
|
|
+ def __init__(self, db: Session):
|
|
|
+ self.db = db
|
|
|
+
|
|
|
+ def get_node_properties(self, node_id: int) -> Dict[str, str]:
|
|
|
+ """查询节点属性"""
|
|
|
+ prop_sql = "SELECT prop_title, prop_value FROM kg_props WHERE ref_id = :node_id"
|
|
|
+ result = self.db.execute(text(prop_sql), {'node_id': node_id}).fetchall()
|
|
|
+ return {row[0]: row[1] for row in result}
|
|
|
+
|
|
|
+ def get_graph(self, label_name: str, input_str: str) -> List[Dict]:
|
|
|
+ """查询知识图谱关系"""
|
|
|
+ # 1. 查询中心节点
|
|
|
+ node_sql = """
|
|
|
+ SELECT n.id, n.name, n.category
|
|
|
+ FROM kg_nodes n
|
|
|
+ WHERE n.category = :label_name AND n.name = :input_str
|
|
|
+ AND n.status = '0'"""
|
|
|
+
|
|
|
+ center_node = self.db.execute(
|
|
|
+ text(node_sql),
|
|
|
+ {'label_name': label_name, 'input_str': input_str}
|
|
|
+ ).fetchone()
|
|
|
+
|
|
|
+ if not center_node:
|
|
|
+ return []
|
|
|
+
|
|
|
+ # 获取中心节点属性
|
|
|
+ s_id, s_name, s_label = center_node
|
|
|
+ s_prop = self.get_node_properties(s_id)
|
|
|
+ s_prop['name'] = s_name
|
|
|
+
|
|
|
+ # 2. 查询关联的边和目标节点
|
|
|
+ relation_sql = """
|
|
|
+ WITH RankedRelations AS (
|
|
|
+ SELECT e.name as rType, m.id as target_id, m.name as target_name,
|
|
|
+ m.category as target_label,
|
|
|
+ (SELECT COUNT(*) FROM kg_edges WHERE src_id = m.id) as pCount,
|
|
|
+ ROW_NUMBER() OVER(PARTITION BY e.name ORDER BY m.id) as rn
|
|
|
+ FROM kg_nodes n
|
|
|
+ JOIN kg_edges e ON n.id = e.src_id
|
|
|
+ JOIN kg_nodes m ON e.dest_id = m.id
|
|
|
+ WHERE n.category = :label_name AND n.name = :input_str
|
|
|
+ AND n.status = '0'
|
|
|
+ )
|
|
|
+ SELECT rType, target_id, target_name, target_label, pCount
|
|
|
+ FROM RankedRelations
|
|
|
+ WHERE rn <= 50
|
|
|
+ ORDER BY rType"""
|
|
|
+
|
|
|
+ relations = self.db.execute(
|
|
|
+ text(relation_sql),
|
|
|
+ {'label_name': label_name, 'input_str': input_str}
|
|
|
+ ).fetchall()
|
|
|
+
|
|
|
+ # 3. 组装返回结果
|
|
|
+ graph_data = {
|
|
|
+ 'name': s_name,
|
|
|
+ 'label': s_label,
|
|
|
+ 'id': s_id,
|
|
|
+ 'properties': s_prop,
|
|
|
+ 'relations': []
|
|
|
+ }
|
|
|
+
|
|
|
+ for r in relations:
|
|
|
+ r_type, e_id, e_name, e_label, p_count = r
|
|
|
+ e_prop = self.get_node_properties(e_id)
|
|
|
+ e_prop['name'] = e_name
|
|
|
+
|
|
|
+ relation_data = {
|
|
|
+ 'rType': r_type,
|
|
|
+ 'target': {
|
|
|
+ 'id': e_id,
|
|
|
+ 'name': e_name,
|
|
|
+ 'label': e_label,
|
|
|
+ 'pCount': p_count,
|
|
|
+ 'properties': e_prop
|
|
|
+ }
|
|
|
+ }
|
|
|
+ graph_data['relations'].append(relation_data)
|
|
|
+
|
|
|
+ return [graph_data]
|
|
|
+
|
|
|
+ def get_graph_fac(self, label_name: str, input_str: str) -> Dict:
|
|
|
+ """获取知识图谱前端展示数据"""
|
|
|
+ graph_data = {
|
|
|
+ 'categories': [],
|
|
|
+ 'node': [],
|
|
|
+ 'links': []
|
|
|
+ }
|
|
|
+
|
|
|
+ # 获取原始图谱数据
|
|
|
+ res = self.get_graph(label_name, input_str)
|
|
|
+ if not res:
|
|
|
+ return graph_data
|
|
|
+
|
|
|
+ item_style_map = {"display": True}
|
|
|
+ node_id = 0
|
|
|
+
|
|
|
+ # 添加分类
|
|
|
+ graph_data['categories'].append({"name": "中心词"})
|
|
|
+ graph_data['categories'].append({"name": "关系"})
|
|
|
+ c_map = {"中心词": 0, "关系": 1}
|
|
|
+
|
|
|
+ # 处理中心节点
|
|
|
+ graph_dto = res[0]
|
|
|
+ g_node_dto = {
|
|
|
+ 'name': graph_dto['name'],
|
|
|
+ 'category': 0,
|
|
|
+ 'label': graph_dto['label'],
|
|
|
+ 'id': node_id,
|
|
|
+ 'symbol': 'circle',
|
|
|
+ 'size': 50,
|
|
|
+ 'properties': graph_dto['properties'],
|
|
|
+ 'kg_id': graph_dto['id'],
|
|
|
+ 'itemStyle': item_style_map
|
|
|
+ }
|
|
|
+ node_id += 1
|
|
|
+ graph_data['node'].append(g_node_dto)
|
|
|
+
|
|
|
+ # 处理关系节点
|
|
|
+ if graph_dto['relations']:
|
|
|
+ rs_id = 2
|
|
|
+ for relation in graph_dto['relations']:
|
|
|
+ r_type = relation['rType']
|
|
|
+ if r_type not in c_map:
|
|
|
+ c_map[r_type] = rs_id
|
|
|
+ graph_data['categories'].append({"name": r_type})
|
|
|
+ rs_id += 1
|
|
|
+
|
|
|
+ # 添加关系节点
|
|
|
+ n_node_dto = {
|
|
|
+ 'name': "",
|
|
|
+ 'category': 1,
|
|
|
+ 'label': graph_dto['label'],
|
|
|
+ 'id': node_id,
|
|
|
+ 'symbol': 'diamond',
|
|
|
+ 'size': 10,
|
|
|
+ 'properties': graph_dto['properties'],
|
|
|
+ 'kg_id': graph_dto['id'],
|
|
|
+ 'itemStyle': item_style_map
|
|
|
+ }
|
|
|
+ graph_data['node'].append(n_node_dto)
|
|
|
+
|
|
|
+ # 添加关系链接
|
|
|
+ graph_data['links'].append({
|
|
|
+ 'source': g_node_dto['name'],
|
|
|
+ 'target': n_node_dto['name'],
|
|
|
+ 'label': r_type,
|
|
|
+ 'category': r_type
|
|
|
+ })
|
|
|
+
|
|
|
+ node_id += 1
|
|
|
+
|
|
|
+ # 处理目标节点
|
|
|
+ target = relation['target']
|
|
|
+ children_item_style_map = {"display": True}
|
|
|
+ symbol = "circle"
|
|
|
+
|
|
|
+ if target['pCount'] == 0:
|
|
|
+ children_item_style_map["display"] = False
|
|
|
+
|
|
|
+ e_node_dto = {
|
|
|
+ 'name': target['name'],
|
|
|
+ 'category': c_map[r_type],
|
|
|
+ 'label': target['label'],
|
|
|
+ 'id': node_id,
|
|
|
+ 'symbol': symbol,
|
|
|
+ 'size': 28,
|
|
|
+ 'properties': target['properties'],
|
|
|
+ 'kg_id': target['id'],
|
|
|
+ 'itemStyle': children_item_style_map
|
|
|
+ }
|
|
|
+ node_id += 1
|
|
|
+ graph_data['node'].append(e_node_dto)
|
|
|
+
|
|
|
+ # 添加目标节点链接
|
|
|
+ graph_data['links'].append({
|
|
|
+ 'source': n_node_dto['name'],
|
|
|
+ 'target': e_node_dto['name'],
|
|
|
+ 'label': "",
|
|
|
+ 'category': r_type
|
|
|
+ })
|
|
|
+
|
|
|
+ return graph_data
|