|
@@ -557,72 +557,126 @@ def model_list(skip_count, page_size, en_name_filter=None, name_filter=None,
|
|
tuple: (数据模型列表, 总数量)
|
|
tuple: (数据模型列表, 总数量)
|
|
"""
|
|
"""
|
|
try:
|
|
try:
|
|
- # 构建where子句
|
|
|
|
- where_clause = []
|
|
|
|
|
|
+ # 构建where子句 - 只针对DataModel节点的过滤条件
|
|
|
|
+ datamodel_where_clause = []
|
|
params = {}
|
|
params = {}
|
|
|
|
|
|
if name_filter is not None:
|
|
if name_filter is not None:
|
|
- where_clause.append("n.name =~ $name")
|
|
|
|
|
|
+ datamodel_where_clause.append("n.name =~ $name")
|
|
params['name'] = f".*{name_filter}.*"
|
|
params['name'] = f".*{name_filter}.*"
|
|
|
|
|
|
if en_name_filter is not None:
|
|
if en_name_filter is not None:
|
|
- where_clause.append("n.en_name =~ $en_name")
|
|
|
|
|
|
+ datamodel_where_clause.append("n.en_name =~ $en_name")
|
|
params['en_name'] = f".*{en_name_filter}.*"
|
|
params['en_name'] = f".*{en_name_filter}.*"
|
|
|
|
|
|
if category is not None:
|
|
if category is not None:
|
|
- where_clause.append("n.category = $category")
|
|
|
|
|
|
+ datamodel_where_clause.append("n.category = $category")
|
|
params['category'] = category
|
|
params['category'] = category
|
|
|
|
|
|
if level is not None:
|
|
if level is not None:
|
|
- where_clause.append("n.level = $level")
|
|
|
|
|
|
+ datamodel_where_clause.append("n.level = $level")
|
|
params['level'] = level
|
|
params['level'] = level
|
|
-
|
|
|
|
|
|
+
|
|
|
|
+ # 处理标签查询
|
|
if tag is not None:
|
|
if tag is not None:
|
|
- where_clause.append("id(t) = $tag")
|
|
|
|
- params['tag'] = tag
|
|
|
|
|
|
+ # 确保tag参数是整数类型
|
|
|
|
+ try:
|
|
|
|
+ tag_id = int(tag)
|
|
|
|
+ params['tag'] = tag_id
|
|
|
|
+ except (ValueError, TypeError):
|
|
|
|
+ logger.warning(f"Invalid tag parameter: {tag}, expected integer")
|
|
|
|
+ return [], 0
|
|
|
|
+
|
|
|
|
+ # 有标签查询条件时,需要确保标签关系存在
|
|
|
|
+ match_clause = "MATCH (n:DataModel)-[:label]->(t)"
|
|
|
|
+ datamodel_where_clause.append("id(t) = $tag")
|
|
|
|
+ else:
|
|
|
|
+ # 没有标签查询条件时,先匹配DataModel,然后可选连接标签
|
|
|
|
+ match_clause = "MATCH (n:DataModel)"
|
|
|
|
|
|
- # At the end of where_clause construction
|
|
|
|
- where_str = " AND ".join(where_clause)
|
|
|
|
- if where_str:
|
|
|
|
- where_str = f"WHERE {where_str}"
|
|
|
|
|
|
+ # 构建DataModel节点的WHERE子句
|
|
|
|
+ datamodel_where_str = " AND ".join(datamodel_where_clause)
|
|
|
|
+ if datamodel_where_str:
|
|
|
|
+ datamodel_where_str = f"WHERE {datamodel_where_str}"
|
|
|
|
|
|
# 构建查询
|
|
# 构建查询
|
|
with connect_graph().session() as session:
|
|
with connect_graph().session() as session:
|
|
# 计算总数量
|
|
# 计算总数量
|
|
- count_query = f"""
|
|
|
|
- MATCH (n:DataModel)
|
|
|
|
- OPTIONAL MATCH (n)-[:label]->(t)
|
|
|
|
- {where_str}
|
|
|
|
- RETURN COUNT(DISTINCT n) AS count
|
|
|
|
- """
|
|
|
|
|
|
+ if tag is not None:
|
|
|
|
+ # 有标签查询时,直接使用标签连接
|
|
|
|
+ count_query = f"""
|
|
|
|
+ {match_clause}
|
|
|
|
+ {datamodel_where_str}
|
|
|
|
+ RETURN COUNT(DISTINCT n) AS count
|
|
|
|
+ """
|
|
|
|
+ else:
|
|
|
|
+ # 无标签查询时,只计算DataModel节点
|
|
|
|
+ count_query = f"""
|
|
|
|
+ MATCH (n:DataModel)
|
|
|
|
+ {datamodel_where_str}
|
|
|
|
+ RETURN COUNT(n) AS count
|
|
|
|
+ """
|
|
|
|
+
|
|
|
|
+ logger.debug(f"Count query: {count_query}")
|
|
|
|
+ logger.debug(f"Query parameters: {params}")
|
|
|
|
+
|
|
count_result = session.run(count_query, **params)
|
|
count_result = session.run(count_query, **params)
|
|
count_record = count_result.single()
|
|
count_record = count_result.single()
|
|
total = count_record['count'] if count_record else 0
|
|
total = count_record['count'] if count_record else 0
|
|
|
|
|
|
- # 查询数据
|
|
|
|
- query = f"""
|
|
|
|
- MATCH (n:DataModel)
|
|
|
|
- OPTIONAL MATCH (n)-[:label]->(t)
|
|
|
|
- {where_str}
|
|
|
|
- RETURN DISTINCT
|
|
|
|
- id(n) as id,
|
|
|
|
- n.name as name,
|
|
|
|
- n.en_name as en_name,
|
|
|
|
- n.time as time,
|
|
|
|
- n.describe as describe,
|
|
|
|
- n.level as level,
|
|
|
|
- n.category as category,
|
|
|
|
- n.status as status,
|
|
|
|
- n.leader as leader,
|
|
|
|
- n.origin as origin,
|
|
|
|
- n.blood_resource as blood_resource,
|
|
|
|
- n.organization as organization,
|
|
|
|
- id(t) as tag_id,
|
|
|
|
- t.name as tag_name
|
|
|
|
- ORDER BY n.time DESC
|
|
|
|
- SKIP $skip
|
|
|
|
- LIMIT $limit
|
|
|
|
- """
|
|
|
|
|
|
+ # 查询数据 - 修复OPTIONAL MATCH的笛卡尔积问题
|
|
|
|
+ if tag is not None:
|
|
|
|
+ # 有标签查询时,直接使用标签连接
|
|
|
|
+ query = f"""
|
|
|
|
+ {match_clause}
|
|
|
|
+ {datamodel_where_str}
|
|
|
|
+ RETURN DISTINCT
|
|
|
|
+ id(n) as id,
|
|
|
|
+ n.name as name,
|
|
|
|
+ n.en_name as en_name,
|
|
|
|
+ n.time as time,
|
|
|
|
+ n.describe as describe,
|
|
|
|
+ n.level as level,
|
|
|
|
+ n.category as category,
|
|
|
|
+ n.status as status,
|
|
|
|
+ n.leader as leader,
|
|
|
|
+ n.origin as origin,
|
|
|
|
+ n.blood_resource as blood_resource,
|
|
|
|
+ n.organization as organization,
|
|
|
|
+ id(t) as tag_id,
|
|
|
|
+ t.name as tag_name
|
|
|
|
+ ORDER BY time DESC
|
|
|
|
+ SKIP $skip
|
|
|
|
+ LIMIT $limit
|
|
|
|
+ """
|
|
|
|
+ else:
|
|
|
|
+ # 无标签查询时,先过滤DataModel节点,然后可选连接标签
|
|
|
|
+ query = f"""
|
|
|
|
+ MATCH (n:DataModel)
|
|
|
|
+ {datamodel_where_str}
|
|
|
|
+ WITH n
|
|
|
|
+ OPTIONAL MATCH (n)-[:label]->(t)
|
|
|
|
+ RETURN
|
|
|
|
+ id(n) as id,
|
|
|
|
+ n.name as name,
|
|
|
|
+ n.en_name as en_name,
|
|
|
|
+ n.time as time,
|
|
|
|
+ n.describe as describe,
|
|
|
|
+ n.level as level,
|
|
|
|
+ n.category as category,
|
|
|
|
+ n.status as status,
|
|
|
|
+ n.leader as leader,
|
|
|
|
+ n.origin as origin,
|
|
|
|
+ n.blood_resource as blood_resource,
|
|
|
|
+ n.organization as organization,
|
|
|
|
+ id(t) as tag_id,
|
|
|
|
+ t.name as tag_name
|
|
|
|
+ ORDER BY n.time DESC
|
|
|
|
+ SKIP $skip
|
|
|
|
+ LIMIT $limit
|
|
|
|
+ """
|
|
|
|
+
|
|
|
|
+ logger.debug(f"Main query: {query}")
|
|
|
|
|
|
result = session.run(query, skip=skip_count, limit=page_size, **params)
|
|
result = session.run(query, skip=skip_count, limit=page_size, **params)
|
|
|
|
|
|
@@ -646,9 +700,11 @@ def model_list(skip_count, page_size, en_name_filter=None, name_filter=None,
|
|
}
|
|
}
|
|
data.append(item)
|
|
data.append(item)
|
|
|
|
|
|
|
|
+ logger.info(f"Query returned {len(data)} items out of {total} total")
|
|
return data, total
|
|
return data, total
|
|
|
|
+
|
|
except Exception as e:
|
|
except Exception as e:
|
|
- print(f"Error in model_list: {str(e)}")
|
|
|
|
|
|
+ logger.error(f"Error in model_list: {str(e)}")
|
|
import traceback
|
|
import traceback
|
|
traceback.print_exc()
|
|
traceback.print_exc()
|
|
return [], 0
|
|
return [], 0
|
|
@@ -1180,4 +1236,178 @@ def data_model_edit(receiver):
|
|
else:
|
|
else:
|
|
logger.info(f"meta_data为空,不需要重新创建INCLUDES关系,DataModel({id})将不关联任何DataMeta节点")
|
|
logger.info(f"meta_data为空,不需要重新创建INCLUDES关系,DataModel({id})将不关联任何DataMeta节点")
|
|
|
|
|
|
- return {"message": "数据模型更新成功"}
|
|
|
|
|
|
+ return {"message": "数据模型更新成功"}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+def model_community(tag=None):
|
|
|
|
+ """
|
|
|
|
+ 查询DataModel的所有节点及DERIVED_FROM关系
|
|
|
|
+
|
|
|
|
+ Args:
|
|
|
|
+ tag: 可选的标签ID,如果指定则只查找有该标签的DataModel节点
|
|
|
|
+
|
|
|
|
+ Returns:
|
|
|
|
+ dict: 包含节点和连线信息的图谱数据,格式与model_kinship_graph相同
|
|
|
|
+ """
|
|
|
|
+ try:
|
|
|
|
+ with connect_graph().session() as session:
|
|
|
|
+ # 构建查询条件
|
|
|
|
+ if tag is not None:
|
|
|
|
+ # 确保tag参数是整数类型
|
|
|
|
+ try:
|
|
|
|
+ tag_id = int(tag)
|
|
|
|
+ except (ValueError, TypeError):
|
|
|
|
+ logger.warning(f"Invalid tag parameter: {tag}, expected integer")
|
|
|
|
+ return {"nodes": [], "lines": []}
|
|
|
|
+
|
|
|
|
+ # 有标签查询条件时,查询有指定标签的DataModel节点及其DERIVED_FROM关系
|
|
|
|
+ cypher = """
|
|
|
|
+ MATCH (dm:DataModel)-[:label]->(t)
|
|
|
|
+ WHERE id(t) = $tag_id
|
|
|
|
+ WITH dm
|
|
|
|
+ MATCH path = (dm)-[:DERIVED_FROM*0..]->(target:DataModel)
|
|
|
|
+ RETURN path
|
|
|
|
+ UNION
|
|
|
|
+ MATCH (dm:DataModel)-[:label]->(t)
|
|
|
|
+ WHERE id(t) = $tag_id
|
|
|
|
+ WITH dm
|
|
|
|
+ MATCH path = (source:DataModel)-[:DERIVED_FROM*0..]->(dm)
|
|
|
|
+ RETURN path
|
|
|
|
+ """
|
|
|
|
+
|
|
|
|
+ result = session.run(cypher, tag_id=tag_id)
|
|
|
|
+ else:
|
|
|
|
+ # 没有标签查询条件时,查询所有DataModel节点及其DERIVED_FROM关系
|
|
|
|
+ cypher = """
|
|
|
|
+ MATCH (dm:DataModel)
|
|
|
|
+ WITH dm
|
|
|
|
+ MATCH path = (dm)-[:DERIVED_FROM*0..]->(target:DataModel)
|
|
|
|
+ RETURN path
|
|
|
|
+ UNION
|
|
|
|
+ MATCH (dm:DataModel)
|
|
|
|
+ WITH dm
|
|
|
|
+ MATCH path = (source:DataModel)-[:DERIVED_FROM*0..]->(dm)
|
|
|
|
+ RETURN path
|
|
|
|
+ """
|
|
|
|
+
|
|
|
|
+ result = session.run(cypher)
|
|
|
|
+
|
|
|
|
+ # 收集节点和关系
|
|
|
|
+ nodes = {}
|
|
|
|
+ lines = {}
|
|
|
|
+
|
|
|
|
+ for record in result:
|
|
|
|
+ # 处理路径
|
|
|
|
+ path = record['path']
|
|
|
|
+ logger.debug(f"处理社区路径,长度: {len(path)}, 节点数: {len(path.nodes)}, 关系数: {len(path.relationships)}")
|
|
|
|
+
|
|
|
|
+ # 处理路径中的所有节点
|
|
|
|
+ for node in path.nodes:
|
|
|
|
+ node_id = int(node.id) # 直接转换为整数
|
|
|
|
+ if node_id not in nodes:
|
|
|
|
+ node_dict = serialize_node_properties(node)
|
|
|
|
+ node_dict["id"] = str(node_id)
|
|
|
|
+ node_dict["node_type"] = list(node.labels)[0] if node.labels else ""
|
|
|
|
+ nodes[node_id] = node_dict
|
|
|
|
+ logger.debug(f"添加社区节点: ID={node_id}, 标签={list(node.labels)}")
|
|
|
|
+
|
|
|
|
+ # 处理路径中的所有关系
|
|
|
|
+ for rel in path.relationships:
|
|
|
|
+ rel_id = int(rel.id) # 直接转换为整数
|
|
|
|
+ if rel_id not in lines:
|
|
|
|
+ rel_dict = {
|
|
|
|
+ "id": str(rel_id),
|
|
|
|
+ "from": str(int(rel.start_node.id)),
|
|
|
|
+ "to": str(int(rel.end_node.id)),
|
|
|
|
+ "text": rel.type
|
|
|
|
+ }
|
|
|
|
+ lines[rel_id] = rel_dict
|
|
|
|
+ logger.debug(f"添加社区关系: ID={rel_id}, 类型={rel.type}, 从{int(rel.start_node.id)}到{int(rel.end_node.id)}")
|
|
|
|
+
|
|
|
|
+ logger.info(f"成功获取数据模型社区图谱,标签ID: {tag}, 节点数: {len(nodes)}, 关系数: {len(lines)}")
|
|
|
|
+ return {
|
|
|
|
+ "nodes": list(nodes.values()),
|
|
|
|
+ "lines": list(lines.values())
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ except Exception as e:
|
|
|
|
+ logger.error(f"获取数据模型社区图谱失败: {str(e)}")
|
|
|
|
+ import traceback
|
|
|
|
+ logger.error(f"错误详情: {traceback.format_exc()}")
|
|
|
|
+ return {"nodes": [], "lines": []}
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+def model_search_list(model_id, page, page_size, en_name_filter=None,
|
|
|
|
+ name_filter=None, category_filter=None, tag_filter=None):
|
|
|
|
+ """获取特定数据模型关联的元数据列表"""
|
|
|
|
+ try:
|
|
|
|
+ with connect_graph().session() as session:
|
|
|
|
+ # 确保model_id为整数
|
|
|
|
+ try:
|
|
|
|
+ model_id_int = int(model_id)
|
|
|
|
+ except (ValueError, TypeError):
|
|
|
|
+ logger.error(f"模型ID不是有效的整数: {model_id}")
|
|
|
|
+ return [], 0
|
|
|
|
+
|
|
|
|
+ # 基本匹配语句 - 支持DataMeta和Metadata标签
|
|
|
|
+ match_clause = """
|
|
|
|
+ MATCH (n:DataModel)-[:INCLUDES]->(m)
|
|
|
|
+ WHERE id(n) = $model_id
|
|
|
|
+ AND (m:DataMeta OR m:Metadata)
|
|
|
|
+ """
|
|
|
|
+
|
|
|
|
+ where_conditions = []
|
|
|
|
+
|
|
|
|
+ if en_name_filter:
|
|
|
|
+ where_conditions.append(f"m.en_name CONTAINS '{en_name_filter}'")
|
|
|
|
+
|
|
|
|
+ if name_filter:
|
|
|
|
+ where_conditions.append(f"m.name CONTAINS '{name_filter}'")
|
|
|
|
+
|
|
|
|
+ if category_filter:
|
|
|
|
+ where_conditions.append(f"m.category = '{category_filter}'")
|
|
|
|
+
|
|
|
|
+ # 标签过滤需要额外的匹配
|
|
|
|
+ tag_match = ""
|
|
|
|
+ if tag_filter:
|
|
|
|
+ tag_match = "MATCH (m)-[:HAS_TAG]->(t:Tag) WHERE t.name = $tag_filter"
|
|
|
|
+
|
|
|
|
+ where_clause = " AND " + " AND ".join(where_conditions) if where_conditions else ""
|
|
|
|
+
|
|
|
|
+ # 计算总数
|
|
|
|
+ count_cypher = f"""
|
|
|
|
+ {match_clause}{where_clause}
|
|
|
|
+ {tag_match}
|
|
|
|
+ RETURN count(m) as count
|
|
|
|
+ """
|
|
|
|
+ count_params = {"model_id": model_id_int}
|
|
|
|
+ if tag_filter:
|
|
|
|
+ count_params["tag_filter"] = tag_filter
|
|
|
|
+
|
|
|
|
+ count_result = session.run(count_cypher, **count_params)
|
|
|
|
+ total_count = count_result.single()["count"]
|
|
|
|
+
|
|
|
|
+ # 分页查询
|
|
|
|
+ skip = (page - 1) * page_size
|
|
|
|
+ cypher = f"""
|
|
|
|
+ {match_clause}{where_clause}
|
|
|
|
+ {tag_match}
|
|
|
|
+ RETURN m
|
|
|
|
+ ORDER BY m.name
|
|
|
|
+ SKIP {skip} LIMIT {page_size}
|
|
|
|
+ """
|
|
|
|
+
|
|
|
|
+ result = session.run(cypher, **count_params)
|
|
|
|
+
|
|
|
|
+ # 格式化结果
|
|
|
|
+ metadata_list = []
|
|
|
|
+ for record in result:
|
|
|
|
+ meta = serialize_node_properties(record["m"])
|
|
|
|
+ meta["id"] = record["m"].id
|
|
|
|
+ metadata_list.append(meta)
|
|
|
|
+
|
|
|
|
+ logger.info(f"成功获取数据模型关联元数据,ID: {model_id_int}, 元数据数量: {total_count}")
|
|
|
|
+ return metadata_list, total_count
|
|
|
|
+ except Exception as e:
|
|
|
|
+ logger.error(f"获取数据模型关联的元数据列表失败: {str(e)}")
|
|
|
|
+ return [], 0
|