|
|
@@ -29,10 +29,10 @@ def metric_list(skip_count, page_size, name_en_filter=None,
|
|
|
skip_count: 跳过的记录数量
|
|
|
page_size: 每页记录数量
|
|
|
name_en_filter: 英文名称过滤条件
|
|
|
- name_zh_filter: 名称过滤条件
|
|
|
+ name_zh_filter: 中文名称过滤条件
|
|
|
category_filter: 分类过滤条件
|
|
|
- create_time_filter: 时间过滤条件
|
|
|
- tag_filter: 标签过滤条件
|
|
|
+ create_time_filter: 创建时间过滤条件
|
|
|
+ tag_filter: 标签ID过滤条件
|
|
|
|
|
|
Returns:
|
|
|
tuple: (数据列表, 总记录数)
|
|
|
@@ -42,6 +42,8 @@ def metric_list(skip_count, page_size, name_en_filter=None,
|
|
|
# 构建查询条件
|
|
|
where_clause = []
|
|
|
params = {}
|
|
|
+
|
|
|
+ # 基础节点条件
|
|
|
if name_zh_filter:
|
|
|
where_clause.append("n.name_zh CONTAINS $name_zh_filter")
|
|
|
params['name_zh_filter'] = name_zh_filter
|
|
|
@@ -54,57 +56,71 @@ def metric_list(skip_count, page_size, name_en_filter=None,
|
|
|
if create_time_filter:
|
|
|
where_clause.append("n.create_time CONTAINS $create_time_filter")
|
|
|
params['create_time_filter'] = create_time_filter
|
|
|
- # 添加tag标签查询逻辑
|
|
|
+
|
|
|
+ # 标签过滤条件
|
|
|
if tag_filter:
|
|
|
where_clause.append("id(la) = $tag_filter")
|
|
|
params['tag_filter'] = tag_filter
|
|
|
|
|
|
- where_str = " AND ".join(where_clause)
|
|
|
- if where_str == "":
|
|
|
- where_str = "TRUE"
|
|
|
+ # 构建WHERE子句
|
|
|
+ where_str = " AND ".join(where_clause) if where_clause else "TRUE"
|
|
|
|
|
|
- # 构建完整的查询语句
|
|
|
+ # 构建查询语句 - 移除DataModel相关查询
|
|
|
cql = f"""
|
|
|
- MATCH (n:DataMetric)-[:LABEL]->(la:DataLabel)
|
|
|
+ MATCH (n:DataMetric)
|
|
|
+ OPTIONAL MATCH (n)-[:LABEL]->(la:DataLabel)
|
|
|
WHERE {where_str}
|
|
|
- OPTIONAL MATCH (n)-[:origin]->(m:DataModel)
|
|
|
- WITH n, la, CASE WHEN m IS NULL THEN null ELSE {{id: id(m), name_zh: m.name_zh}}
|
|
|
- END AS data_model,properties(n) as properties,
|
|
|
- n.create_time as time,id(n) as nodeid,{{id:id(la),name_zh:la.name_zh}} as tag
|
|
|
- return properties,time,nodeid,data_model,tag
|
|
|
- ORDER BY time desc
|
|
|
+ WITH n, la,
|
|
|
+ properties(n) AS properties,
|
|
|
+ n.create_time AS create_time,
|
|
|
+ id(n) AS nodeid,
|
|
|
+ CASE WHEN la IS NOT NULL
|
|
|
+ THEN {{id: id(la), name_zh: la.name_zh}}
|
|
|
+ ELSE null
|
|
|
+ END AS tag
|
|
|
+ RETURN properties, create_time, nodeid, tag
|
|
|
+ ORDER BY create_time DESC
|
|
|
SKIP $skip_count
|
|
|
LIMIT $page_size
|
|
|
"""
|
|
|
+
|
|
|
params['skip_count'] = skip_count
|
|
|
params['page_size'] = page_size
|
|
|
|
|
|
- # 修复:使用正确的session方式执行查询
|
|
|
+ # 使用session方式执行查询
|
|
|
driver = connect_graph()
|
|
|
if not driver:
|
|
|
logger.error("无法连接到数据库")
|
|
|
return [], 0
|
|
|
|
|
|
with driver.session() as session:
|
|
|
+ # 执行主查询
|
|
|
result = session.run(cql, **params)
|
|
|
for record in result:
|
|
|
properties = record['properties']
|
|
|
- properties['data_model'] = record['data_model']
|
|
|
+ properties['id'] = record['nodeid']
|
|
|
properties['tag'] = record['tag']
|
|
|
- new_attr = {
|
|
|
- 'id': record['nodeid']
|
|
|
- }
|
|
|
- if "id_list" in properties:
|
|
|
- properties['id_list'] = json.loads(properties['id_list'])
|
|
|
- if "describe" not in properties:
|
|
|
+
|
|
|
+ # 解析JSON字段
|
|
|
+ if "id_list" in properties and properties['id_list']:
|
|
|
+ try:
|
|
|
+ properties['id_list'] = json.loads(properties['id_list'])
|
|
|
+ except (json.JSONDecodeError, TypeError):
|
|
|
+ properties['id_list'] = []
|
|
|
+
|
|
|
+ # 设置默认值
|
|
|
+ if "describe" not in properties or properties["describe"] is None:
|
|
|
properties["describe"] = None
|
|
|
-
|
|
|
- properties.update(new_attr)
|
|
|
+
|
|
|
data.append(properties)
|
|
|
|
|
|
- # 获取总量
|
|
|
- total_query = f"MATCH (n:DataMetric) " \
|
|
|
- f"WHERE {where_str} RETURN COUNT(n) AS total"
|
|
|
+ # 获取总数 - 使用相同的过滤条件
|
|
|
+ total_query = f"""
|
|
|
+ MATCH (n:DataMetric)
|
|
|
+ OPTIONAL MATCH (n)-[:LABEL]->(la:DataLabel)
|
|
|
+ WHERE {where_str}
|
|
|
+ RETURN COUNT(DISTINCT n) AS total
|
|
|
+ """
|
|
|
total_result = session.run(total_query, **params).single()["total"]
|
|
|
|
|
|
return data, total_result
|
|
|
@@ -139,8 +155,15 @@ def handle_metric_relation(model_ids):
|
|
|
filtered_search_nodes as origin_nodes, filtered_connect_nodes as blood_nodes
|
|
|
"""
|
|
|
|
|
|
- result = connect_graph.run(query, model_Ids=model_ids)
|
|
|
- return result.data()
|
|
|
+ # 修复:使用正确的session方式执行查询
|
|
|
+ driver = connect_graph()
|
|
|
+ if not driver:
|
|
|
+ logger.error("无法连接到数据库")
|
|
|
+ return []
|
|
|
+
|
|
|
+ with driver.session() as session:
|
|
|
+ result = session.run(query, model_Ids=model_ids)
|
|
|
+ return result.data()
|
|
|
|
|
|
|
|
|
def id_mertic_graph(id):
|
|
|
@@ -178,15 +201,23 @@ def id_mertic_graph(id):
|
|
|
toString(id(n)) as res
|
|
|
RETURN lines,nodes,res
|
|
|
"""
|
|
|
- data = connect_graph.run(query, nodeId=id)
|
|
|
-
|
|
|
- res = {}
|
|
|
- for item in data:
|
|
|
- res = {
|
|
|
- "nodes": [record for record in item['nodes'] if record['id']],
|
|
|
- "lines": [record for record in item['lines'] if record['from'] and record['to']],
|
|
|
- "rootId": item['res'],
|
|
|
- }
|
|
|
+
|
|
|
+ # 修复:使用正确的session方式执行查询
|
|
|
+ driver = connect_graph()
|
|
|
+ if not driver:
|
|
|
+ logger.error("无法连接到数据库")
|
|
|
+ return {}
|
|
|
+
|
|
|
+ with driver.session() as session:
|
|
|
+ data = session.run(query, nodeId=id)
|
|
|
+
|
|
|
+ res = {}
|
|
|
+ for item in data:
|
|
|
+ res = {
|
|
|
+ "nodes": [record for record in item['nodes'] if record['id']],
|
|
|
+ "lines": [record for record in item['lines'] if record['from'] and record['to']],
|
|
|
+ "rootId": item['res'],
|
|
|
+ }
|
|
|
|
|
|
logger.info(res) # 记录 'res' 变量
|
|
|
return res
|
|
|
@@ -213,31 +244,40 @@ def handle_data_metric(metric_name, result_list, receiver):
|
|
|
'name_en': data_metric_en
|
|
|
})
|
|
|
|
|
|
- data_metric_node = get_node('DataMetric', name=metric_name) or create_or_get_node('DataMetric', **receiver)
|
|
|
+ # get_node 和 create_or_get_node 都返回节点ID(整数)
|
|
|
+ data_metric_node_id = get_node('DataMetric', name_zh=metric_name) or create_or_get_node('DataMetric', **receiver)
|
|
|
|
|
|
+ # 处理子节点关系
|
|
|
child_list = receiver['childrenId']
|
|
|
- for child_id in child_list:
|
|
|
- child = get_node_by_id_no_label(child_id)
|
|
|
- # 建立关系:当前节点的childrenId指向,以及关系child
|
|
|
- if child:
|
|
|
- # 获取节点ID
|
|
|
- dm_id = data_metric_node.id if hasattr(data_metric_node, 'id') else data_metric_node.identity if hasattr(data_metric_node, 'identity') else None
|
|
|
- child_node_id = child.id if hasattr(child, 'id') else child.identity if hasattr(child, 'identity') else child_id
|
|
|
-
|
|
|
- if dm_id and child_node_id and not relationship_exists(dm_id, 'child', child_node_id):
|
|
|
- connect_graph.create(Relationship(data_metric_node, 'child', child))
|
|
|
-
|
|
|
- if receiver.get('tag'):
|
|
|
- tag = get_node_by_id('DataLabel', receiver['tag'])
|
|
|
- if tag:
|
|
|
- # 获取节点ID
|
|
|
- dm_id = data_metric_node.id if hasattr(data_metric_node, 'id') else data_metric_node.identity if hasattr(data_metric_node, 'identity') else None
|
|
|
- tag_node_id = tag.id if hasattr(tag, 'id') else tag.identity if hasattr(tag, 'identity') else receiver['tag']
|
|
|
-
|
|
|
- if dm_id and tag_node_id and not relationship_exists(dm_id, 'LABEL', tag_node_id):
|
|
|
- connect_graph.create(Relationship(data_metric_node, 'LABEL', tag))
|
|
|
-
|
|
|
- return data_metric_node.id, id_list
|
|
|
+ driver = connect_graph()
|
|
|
+ if driver:
|
|
|
+ with driver.session() as session:
|
|
|
+ for child_id in child_list:
|
|
|
+ # 检查关系是否已存在
|
|
|
+ if not relationship_exists(data_metric_node_id, 'child', child_id):
|
|
|
+ # 创建关系
|
|
|
+ create_rel_query = """
|
|
|
+ MATCH (parent:DataMetric), (child)
|
|
|
+ WHERE id(parent) = $parent_id AND id(child) = $child_id
|
|
|
+ MERGE (parent)-[r:child]->(child)
|
|
|
+ RETURN r
|
|
|
+ """
|
|
|
+ session.run(create_rel_query, parent_id=data_metric_node_id, child_id=child_id)
|
|
|
+
|
|
|
+ # 处理标签关系
|
|
|
+ if receiver.get('tag'):
|
|
|
+ tag_id = receiver['tag']
|
|
|
+ if not relationship_exists(data_metric_node_id, 'LABEL', tag_id):
|
|
|
+ # 创建标签关系
|
|
|
+ create_label_query = """
|
|
|
+ MATCH (metric:DataMetric), (label:DataLabel)
|
|
|
+ WHERE id(metric) = $metric_id AND id(label) = $label_id
|
|
|
+ MERGE (metric)-[r:LABEL]->(label)
|
|
|
+ RETURN r
|
|
|
+ """
|
|
|
+ session.run(create_label_query, metric_id=data_metric_node_id, label_id=tag_id)
|
|
|
+
|
|
|
+ return data_metric_node_id, id_list
|
|
|
|
|
|
|
|
|
def handle_meta_data_metric(data_metric_node_id, id_list):
|
|
|
@@ -304,34 +344,60 @@ def handle_id_metric(id):
|
|
|
query = """
|
|
|
MATCH (n:DataMetric)
|
|
|
WHERE id(n) = $nodeId
|
|
|
- WITH apoc.convert.fromJsonList(n.id_list) AS info, n
|
|
|
- UNWIND info AS item
|
|
|
- WITH n, item.id AS model_or_metric_id, item.metaData AS meta_ids, item.type AS type
|
|
|
-
|
|
|
- // 数据模型或者数据指标
|
|
|
- OPTIONAL MATCH (n)-[:origin]->(m1:DataModel)
|
|
|
- WHERE type = 'model' AND id(m1) = model_or_metric_id
|
|
|
- WITH n, model_or_metric_id, meta_ids, type, m1
|
|
|
- OPTIONAL MATCH (n)-[:origin]->(m2:DataMetric)
|
|
|
- WHERE type = 'metric' AND id(m2) = model_or_metric_id
|
|
|
- WITH n, model_or_metric_id, meta_ids, type, m1, m2
|
|
|
- // 元数据
|
|
|
- OPTIONAL MATCH (n)-[:connection]-(meta:DataMeta)
|
|
|
- // 数据标签
|
|
|
- OPTIONAL MATCH (n)-[:LABEL]-(la:DataLabel)
|
|
|
- OPTIONAL MATCH (parent)-[:child]-(n)
|
|
|
- WITH properties(n) AS properties,collect(DISTINCT id(meta)) AS meta_list,parent,
|
|
|
- {id: id(la), name_zh: la.name_zh} AS tag,
|
|
|
- CASE
|
|
|
- WHEN type = 'model' THEN m1
|
|
|
- WHEN type = 'metric' THEN m2
|
|
|
- ELSE NULL
|
|
|
- END AS m
|
|
|
- WITH {model_name: m.name_zh, model_id: id(m), meta: meta_list} AS result, properties,
|
|
|
- tag,{id:id(parent),name_zh:parent.name_zh} as parentId
|
|
|
- RETURN collect(result) AS id_list, properties, tag,collect(parentId)as parentId
|
|
|
+
|
|
|
+ // 查找第一层关系 - 来源关系(DataModel 和 DataMetric)
|
|
|
+ OPTIONAL MATCH (n)-[:origin]->(origin)
|
|
|
+ WHERE origin:DataModel OR origin:DataMetric
|
|
|
+
|
|
|
+ // 查找第一层关系 - 元数据连接
|
|
|
+ OPTIONAL MATCH (n)-[:connection]->(meta:DataMeta)
|
|
|
+
|
|
|
+ // 查找第一层关系 - 数据标签
|
|
|
+ OPTIONAL MATCH (n)-[:LABEL]->(label:DataLabel)
|
|
|
+
|
|
|
+ // 查找第一层关系 - 父节点
|
|
|
+ OPTIONAL MATCH (parent:DataMetric)-[:child]->(n)
|
|
|
+
|
|
|
+ // 聚合数据
|
|
|
+ WITH n,
|
|
|
+ collect(DISTINCT label) AS labels,
|
|
|
+ collect(DISTINCT parent) AS parents,
|
|
|
+ collect(DISTINCT origin) AS origins,
|
|
|
+ collect(DISTINCT meta) AS metas
|
|
|
+
|
|
|
+ // 构建 id_list(来源信息和元数据)
|
|
|
+ WITH n, labels, parents,
|
|
|
+ [origin IN origins | {
|
|
|
+ model_name: origin.name_zh,
|
|
|
+ model_id: id(origin),
|
|
|
+ meta: [m IN metas | id(m)],
|
|
|
+ type: CASE
|
|
|
+ WHEN 'DataModel' IN labels(origin) THEN 'model'
|
|
|
+ WHEN 'DataMetric' IN labels(origin) THEN 'metric'
|
|
|
+ ELSE null
|
|
|
+ END
|
|
|
+ }] AS id_list_data
|
|
|
+
|
|
|
+ // 返回结果
|
|
|
+ RETURN
|
|
|
+ properties(n) AS properties,
|
|
|
+ id_list_data AS id_list,
|
|
|
+ CASE WHEN size(labels) > 0
|
|
|
+ THEN {id: id(labels[0]), name_zh: labels[0].name_zh}
|
|
|
+ ELSE null
|
|
|
+ END AS tag,
|
|
|
+ [p IN parents | {id: id(p), name_zh: p.name_zh}] AS parentId
|
|
|
"""
|
|
|
- data_ = connect_graph.run(query, nodeId=id).data()
|
|
|
+
|
|
|
+ # 修复:使用正确的session方式执行查询
|
|
|
+ driver = connect_graph()
|
|
|
+ if not driver:
|
|
|
+ logger.error("无法连接到数据库")
|
|
|
+ return {"data_metric": {}}
|
|
|
+
|
|
|
+ with driver.session() as session:
|
|
|
+ result = session.run(query, nodeId=id)
|
|
|
+ data_ = result.data()
|
|
|
|
|
|
if not data_:
|
|
|
return {"data_metric": {}}
|
|
|
@@ -403,11 +469,19 @@ def metric_kinship_graph(nodeid, meta):
|
|
|
toString($nodeId) as rootId
|
|
|
"""
|
|
|
|
|
|
- data = connect_graph.run(cql, nodeId=nodeid)
|
|
|
- res = {}
|
|
|
- for item in data:
|
|
|
- res = {
|
|
|
- "nodes": [record for record in item['nodes'] if record['id']],
|
|
|
+ # 修复:使用正确的session方式执行查询
|
|
|
+ driver = connect_graph()
|
|
|
+ if not driver:
|
|
|
+ logger.error("无法连接到数据库")
|
|
|
+ return {}
|
|
|
+
|
|
|
+ with driver.session() as session:
|
|
|
+ data = session.run(cql, nodeId=nodeid)
|
|
|
+
|
|
|
+ res = {}
|
|
|
+ for item in data:
|
|
|
+ res = {
|
|
|
+ "nodes": [record for record in item['nodes'] if record['id']],
|
|
|
"lines": [record for record in item['lines'] if record['from'] and record['to']],
|
|
|
"rootId": str(nodeid)
|
|
|
}
|
|
|
@@ -473,14 +547,24 @@ def metric_impact_graph(nodeid, meta):
|
|
|
apoc.coll.toSet(nodes) as nodes
|
|
|
RETURN nodes,lines,rootId
|
|
|
"""
|
|
|
- data = connect_graph.run(cql, nodeId=nodeid)
|
|
|
- res = {}
|
|
|
- for item in data:
|
|
|
- res = {
|
|
|
- "nodes": [record for record in item['nodes'] if record['id']],
|
|
|
- "lines": [record for record in item['lines'] if record['from'] and record['to']],
|
|
|
- "rootId": item['rootId']
|
|
|
- }
|
|
|
+
|
|
|
+ # 修复:使用正确的session方式执行查询
|
|
|
+ driver = connect_graph()
|
|
|
+ if not driver:
|
|
|
+ logger.error("无法连接到数据库")
|
|
|
+ return {}
|
|
|
+
|
|
|
+ with driver.session() as session:
|
|
|
+ data = session.run(cql, nodeId=nodeid)
|
|
|
+
|
|
|
+ res = {}
|
|
|
+ for item in data:
|
|
|
+ res = {
|
|
|
+ "nodes": [record for record in item['nodes'] if record['id']],
|
|
|
+ "lines": [record for record in item['lines'] if record['from'] and record['to']],
|
|
|
+ "rootId": item['rootId']
|
|
|
+ }
|
|
|
+
|
|
|
logger.info(res) # 记录 'res' 变量
|
|
|
return res
|
|
|
|
|
|
@@ -555,14 +639,24 @@ def metric_all_graph(nodeid, meta):
|
|
|
apoc.coll.toSet(nodes) as nodes
|
|
|
RETURN nodes,lines,rootId
|
|
|
"""
|
|
|
- data = connect_graph.run(cql, nodeId=nodeid)
|
|
|
- res = {}
|
|
|
- for item in data:
|
|
|
- res = {
|
|
|
- "nodes": [record for record in item['nodes'] if record['id']],
|
|
|
- "lines": [record for record in item['lines'] if record['from'] and record['to']],
|
|
|
- "rootId": item['rootId']
|
|
|
- }
|
|
|
+
|
|
|
+ # 修复:使用正确的session方式执行查询
|
|
|
+ driver = connect_graph()
|
|
|
+ if not driver:
|
|
|
+ logger.error("无法连接到数据库")
|
|
|
+ return {}
|
|
|
+
|
|
|
+ with driver.session() as session:
|
|
|
+ data = session.run(cql, nodeId=nodeid)
|
|
|
+
|
|
|
+ res = {}
|
|
|
+ for item in data:
|
|
|
+ res = {
|
|
|
+ "nodes": [record for record in item['nodes'] if record['id']],
|
|
|
+ "lines": [record for record in item['lines'] if record['from'] and record['to']],
|
|
|
+ "rootId": item['rootId']
|
|
|
+ }
|
|
|
+
|
|
|
logger.info(res) # 记录 'res' 变量
|
|
|
return res
|
|
|
|
|
|
@@ -574,45 +668,137 @@ def data_metric_edit(data):
|
|
|
Args:
|
|
|
data: 数据指标数据
|
|
|
"""
|
|
|
- node_a = get_node_by_id('DataMetric', data["id"])
|
|
|
- if node_a:
|
|
|
- delete_relationships(data["id"])
|
|
|
+ metric_id = data.get("id")
|
|
|
+ if not metric_id:
|
|
|
+ logger.error("数据指标ID不能为空")
|
|
|
+ raise ValueError("数据指标ID不能为空")
|
|
|
+
|
|
|
+ # 验证节点是否存在
|
|
|
+ node_a = get_node_by_id('DataMetric', metric_id)
|
|
|
+ if not node_a:
|
|
|
+ logger.error(f"数据指标节点不存在: ID={metric_id}")
|
|
|
+ raise ValueError(f"数据指标节点不存在: ID={metric_id}")
|
|
|
+
|
|
|
+ # 删除旧关系
|
|
|
+ delete_relationships(metric_id)
|
|
|
|
|
|
- # 更新或创建数据指标节点的属性
|
|
|
+ # 准备需要更新的属性(排除特殊字段和复杂类型)
|
|
|
+ excluded_keys = {'id', 'model_selected', 'childrenId', 'tag'}
|
|
|
+
|
|
|
+ # 过滤函数:只保留 Neo4j 支持的基本类型
|
|
|
+ def is_valid_neo4j_property(value):
|
|
|
+ """检查值是否为 Neo4j 支持的属性类型"""
|
|
|
+ if value is None:
|
|
|
+ return False
|
|
|
+ # 基本类型:str, int, float, bool
|
|
|
+ if isinstance(value, (str, int, float, bool)):
|
|
|
+ return True
|
|
|
+ # 列表类型:但列表中的元素必须是基本类型
|
|
|
+ if isinstance(value, list):
|
|
|
+ # 空列表是允许的
|
|
|
+ if not value:
|
|
|
+ return True
|
|
|
+ # 检查列表中所有元素是否为基本类型
|
|
|
+ return all(isinstance(item, (str, int, float, bool)) for item in value)
|
|
|
+ # 其他类型(dict, object等)不支持
|
|
|
+ return False
|
|
|
+
|
|
|
+ # 准备更新属性,只保留有效类型
|
|
|
+ update_props = {}
|
|
|
for key, value in data.items():
|
|
|
- if value is not None and key != "model_selected":
|
|
|
- node_a[key] = value
|
|
|
+ if key in excluded_keys:
|
|
|
+ continue
|
|
|
+ if not is_valid_neo4j_property(value):
|
|
|
+ # 如果是复杂类型,尝试转换为 JSON 字符串
|
|
|
+ if isinstance(value, (dict, list)):
|
|
|
+ try:
|
|
|
+ import json
|
|
|
+ update_props[key] = json.dumps(value, ensure_ascii=False)
|
|
|
+ logger.info(f"属性 {key} 从复杂类型转换为JSON字符串")
|
|
|
+ except Exception as e:
|
|
|
+ logger.warning(f"跳过无法序列化的属性 {key}: {type(value)}, 错误: {str(e)}")
|
|
|
+ else:
|
|
|
+ logger.warning(f"跳过不支持的属性类型 {key}: {type(value)}")
|
|
|
+ else:
|
|
|
+ update_props[key] = value
|
|
|
|
|
|
- with connect_graph().session() as session:
|
|
|
- session.push(node_a)
|
|
|
-
|
|
|
- child_list = data.get('childrenId', [])
|
|
|
- for child_id in child_list:
|
|
|
- child = get_node_by_id_no_label(child_id)
|
|
|
- # 建立关系:当前节点的childrenId指向,以及关系child
|
|
|
- if child:
|
|
|
- # 获取节点ID
|
|
|
- dm_id = node_a.id if hasattr(node_a, 'id') else node_a.identity if hasattr(node_a, 'identity') else None
|
|
|
- child_node_id = child.id if hasattr(child, 'id') else child.identity if hasattr(child, 'identity') else child_id
|
|
|
+ # 使用 Cypher 更新节点属性
|
|
|
+ driver = connect_graph()
|
|
|
+ if not driver:
|
|
|
+ logger.error("无法连接到数据库")
|
|
|
+ raise ConnectionError("无法连接到数据库")
|
|
|
+
|
|
|
+ with driver.session() as session:
|
|
|
+ # 更新节点属性
|
|
|
+ if update_props:
|
|
|
+ # 构建SET子句
|
|
|
+ set_clauses = []
|
|
|
+ for key in update_props.keys():
|
|
|
+ set_clauses.append(f"n.{key} = ${key}")
|
|
|
+ set_clause = ", ".join(set_clauses)
|
|
|
|
|
|
- if dm_id and child_node_id and not relationship_exists(dm_id, 'child', child_node_id):
|
|
|
- connection = Relationship(node_a, 'child', child)
|
|
|
- connect_graph.create(connection)
|
|
|
-
|
|
|
- # 处理数据标签及其关系
|
|
|
- if data.get("tag"):
|
|
|
- tag_node = get_node_by_id('DataLabel', data["tag"])
|
|
|
- if tag_node:
|
|
|
- relationship_label = Relationship(node_a, "LABEL", tag_node)
|
|
|
- connect_graph.merge(relationship_label)
|
|
|
-
|
|
|
- # 处理元数据节点及其关系(此处只调整关系,不修改对应属性)
|
|
|
- for record in data.get('model_selected', []):
|
|
|
- for parsed_item in record.get("meta", []):
|
|
|
- metadata_node = update_or_create_node(parsed_item["id"])
|
|
|
- if metadata_node:
|
|
|
- relationship_connection = Relationship(node_a, "connection", metadata_node)
|
|
|
- connect_graph.merge(relationship_connection)
|
|
|
+ update_query = f"""
|
|
|
+ MATCH (n:DataMetric)
|
|
|
+ WHERE id(n) = $metric_id
|
|
|
+ SET {set_clause}
|
|
|
+ RETURN n
|
|
|
+ """
|
|
|
+ session.run(update_query, metric_id=metric_id, **update_props)
|
|
|
+ logger.info(f"成功更新数据指标节点属性: ID={metric_id}, 更新字段: {list(update_props.keys())}")
|
|
|
+
|
|
|
+ # 处理子节点关系
|
|
|
+ child_list = data.get('childrenId', [])
|
|
|
+ for child_id in child_list:
|
|
|
+ try:
|
|
|
+ child_id_int = int(child_id)
|
|
|
+ # 创建child关系
|
|
|
+ child_query = """
|
|
|
+ MATCH (parent:DataMetric), (child)
|
|
|
+ WHERE id(parent) = $parent_id AND id(child) = $child_id
|
|
|
+ MERGE (parent)-[:child]->(child)
|
|
|
+ """
|
|
|
+ session.run(child_query, parent_id=metric_id, child_id=child_id_int)
|
|
|
+ logger.info(f"成功创建child关系: {metric_id} -> {child_id_int}")
|
|
|
+ except (ValueError, TypeError) as e:
|
|
|
+ logger.warning(f"无效的子节点ID: {child_id}, 错误: {str(e)}")
|
|
|
+ continue
|
|
|
+
|
|
|
+ # 处理数据标签关系
|
|
|
+ tag_id = data.get("tag")
|
|
|
+ if tag_id:
|
|
|
+ try:
|
|
|
+ tag_id_int = int(tag_id)
|
|
|
+ tag_query = """
|
|
|
+ MATCH (metric:DataMetric), (tag:DataLabel)
|
|
|
+ WHERE id(metric) = $metric_id AND id(tag) = $tag_id
|
|
|
+ MERGE (metric)-[:LABEL]->(tag)
|
|
|
+ """
|
|
|
+ session.run(tag_query, metric_id=metric_id, tag_id=tag_id_int)
|
|
|
+ logger.info(f"成功创建LABEL关系: {metric_id} -> {tag_id_int}")
|
|
|
+ except (ValueError, TypeError) as e:
|
|
|
+ logger.warning(f"无效的标签ID: {tag_id}, 错误: {str(e)}")
|
|
|
+
|
|
|
+ # 处理元数据节点关系
|
|
|
+ model_selected = data.get('model_selected', [])
|
|
|
+ for record in model_selected:
|
|
|
+ meta_list = record.get("meta", [])
|
|
|
+ for parsed_item in meta_list:
|
|
|
+ meta_id = parsed_item.get("id")
|
|
|
+ if meta_id:
|
|
|
+ try:
|
|
|
+ meta_id_int = int(meta_id)
|
|
|
+ connection_query = """
|
|
|
+ MATCH (metric:DataMetric), (meta)
|
|
|
+ WHERE id(metric) = $metric_id AND id(meta) = $meta_id
|
|
|
+ MERGE (metric)-[:connection]->(meta)
|
|
|
+ """
|
|
|
+ session.run(connection_query, metric_id=metric_id, meta_id=meta_id_int)
|
|
|
+ logger.info(f"成功创建connection关系: {metric_id} -> {meta_id_int}")
|
|
|
+ except (ValueError, TypeError) as e:
|
|
|
+ logger.warning(f"无效的元数据ID: {meta_id}, 错误: {str(e)}")
|
|
|
+ continue
|
|
|
+
|
|
|
+ logger.info(f"数据指标编辑完成: ID={metric_id}")
|
|
|
|
|
|
|
|
|
def create_metric_node(name, description, category, id_list):
|
|
|
@@ -745,4 +931,72 @@ def metric_check(formula_text):
|
|
|
|
|
|
except Exception as e:
|
|
|
logger.error(f"公式解析失败: {str(e)}")
|
|
|
- return []
|
|
|
+ return []
|
|
|
+
|
|
|
+
|
|
|
+def metric_delete(metric_node_id):
|
|
|
+ """
|
|
|
+ 删除数据指标节点及其所有关联关系
|
|
|
+
|
|
|
+ Args:
|
|
|
+ metric_node_id: 指标节点ID
|
|
|
+
|
|
|
+ Returns:
|
|
|
+ dict: 删除结果,包含 success 状态和 message 信息
|
|
|
+ """
|
|
|
+ try:
|
|
|
+ # 修复:使用正确的session方式执行查询
|
|
|
+ driver = connect_graph()
|
|
|
+ if not driver:
|
|
|
+ logger.error("无法连接到数据库")
|
|
|
+ return {
|
|
|
+ "success": False,
|
|
|
+ "message": "无法连接到数据库"
|
|
|
+ }
|
|
|
+
|
|
|
+ with driver.session() as session:
|
|
|
+ # 首先检查节点是否存在
|
|
|
+ check_query = """
|
|
|
+ MATCH (n:DataMetric)
|
|
|
+ WHERE id(n) = $nodeId
|
|
|
+ RETURN n
|
|
|
+ """
|
|
|
+ check_result = session.run(check_query, nodeId=metric_node_id).single()
|
|
|
+
|
|
|
+ if not check_result:
|
|
|
+ logger.warning(f"数据指标节点不存在: ID={metric_node_id}")
|
|
|
+ return {
|
|
|
+ "success": False,
|
|
|
+ "message": f"数据指标节点不存在 (ID: {metric_node_id})"
|
|
|
+ }
|
|
|
+
|
|
|
+ # 删除节点及其所有关联关系
|
|
|
+ # DETACH DELETE 会自动删除节点的所有关系
|
|
|
+ delete_query = """
|
|
|
+ MATCH (n:DataMetric)
|
|
|
+ WHERE id(n) = $nodeId
|
|
|
+ DETACH DELETE n
|
|
|
+ RETURN count(n) as deleted_count
|
|
|
+ """
|
|
|
+ delete_result = session.run(delete_query, nodeId=metric_node_id).single()
|
|
|
+ deleted_count = delete_result["deleted_count"]
|
|
|
+
|
|
|
+ if deleted_count > 0:
|
|
|
+ logger.info(f"成功删除数据指标节点: ID={metric_node_id}")
|
|
|
+ return {
|
|
|
+ "success": True,
|
|
|
+ "message": f"成功删除数据指标节点 (ID: {metric_node_id})"
|
|
|
+ }
|
|
|
+ else:
|
|
|
+ logger.warning(f"删除失败,节点可能已被删除: ID={metric_node_id}")
|
|
|
+ return {
|
|
|
+ "success": False,
|
|
|
+ "message": "删除失败,节点可能已被删除"
|
|
|
+ }
|
|
|
+
|
|
|
+ except Exception as e:
|
|
|
+ logger.error(f"删除数据指标节点失败: {str(e)}")
|
|
|
+ return {
|
|
|
+ "success": False,
|
|
|
+ "message": f"删除失败: {str(e)}"
|
|
|
+ }
|