model.py 39 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117
  1. """
  2. 数据模型核心业务逻辑模块
  3. 本模块包含了数据模型相关的所有核心业务逻辑函数,包括:
  4. - 数据模型的创建、更新、删除
  5. - 数据模型与数据资源、元数据之间的关系处理
  6. - 数据模型血缘关系管理
  7. - 数据模型图谱生成
  8. - 数据模型层级计算等功能
  9. """
  10. import math
  11. import threading
  12. from concurrent.futures import ThreadPoolExecutor
  13. import pandas as pd
  14. from py2neo import Relationship
  15. import logging
  16. import json
  17. # Configure logger
  18. logger = logging.getLogger(__name__)
  19. from app.core.graph.graph_operations import relationship_exists
  20. from app.core.graph.graph_operations import connect_graph,create_or_get_node,get_node
  21. from app.services.neo4j_driver import neo4j_driver
  22. from app.core.meta_data import get_formatted_time, handle_id_unstructured
  23. from app.core.common import delete_relationships, update_or_create_node, get_node_by_id_no_label
  24. from app.core.data_resource.resource import get_node_by_id
  25. # 根据child关系计算数据模型当前的level自动保存
  26. def calculate_model_level(id):
  27. """
  28. 根据child关系计算数据模型当前的level并自动保存
  29. Args:
  30. id: 数据模型的节点ID(整数)
  31. Returns:
  32. None
  33. """
  34. # 确保id是整数类型
  35. node_id = int(id) if id is not None else None
  36. cql = """
  37. MATCH (start_node:data_model)
  38. WHERE id(start_node) = $nodeId
  39. CALL {
  40. WITH start_node
  41. OPTIONAL MATCH path = (start_node)-[:child*]->(end_node)
  42. RETURN length(path) AS level
  43. }
  44. WITH coalesce(max(level), 0) AS max_level
  45. RETURN max_level
  46. """
  47. with connect_graph().session() as session:
  48. result = session.run(cql, nodeId=node_id)
  49. record = result.single()
  50. data = record["max_level"] if record and "max_level" in record else 0
  51. # 更新level属性
  52. update_query = """
  53. MATCH (n:data_model)
  54. WHERE id(n) = $nodeId
  55. SET n.level = $level
  56. RETURN n
  57. """
  58. with connect_graph().session() as session:
  59. session.run(update_query, nodeId=node_id, level=data)
  60. # 处理数据模型血缘关系
  61. def handle_model_relation(resource_ids):
  62. """
  63. 处理数据模型血缘关系
  64. Args:
  65. resource_ids: 数据资源ID
  66. Returns:
  67. 血缘关系数据
  68. """
  69. query = """
  70. MATCH (search:data_resource)-[:connection]->(common_node:meta_node)<-[:connection]-(connect:data_resource)
  71. WHERE id(search) = $resource_Ids
  72. WITH search, connect, common_node
  73. MATCH (search)-[:connection]->(search_node:meta_node)
  74. WITH search, connect, common_node, collect(DISTINCT id(search_node)) AS search_nodes
  75. MATCH (connect)-[:connection]->(connect_node:meta_node)
  76. WITH search, connect, common_node, search_nodes, collect(DISTINCT id(connect_node)) AS connect_nodes
  77. WITH search, connect, search_nodes, connect_nodes, collect(DISTINCT id(common_node)) AS common_nodes
  78. // 剔除 search_nodes 和 connect_nodes 中包含在 common_nodes 中的内容
  79. WITH search, connect, common_nodes,
  80. [node IN search_nodes WHERE NOT node IN common_nodes] AS filtered_search_nodes,
  81. [node IN connect_nodes WHERE NOT node IN common_nodes] AS filtered_connect_nodes
  82. RETURN id(connect) as blood_resources, common_nodes,
  83. filtered_search_nodes as origin_nodes, filtered_connect_nodes as blood_nodes
  84. """
  85. with connect_graph().session() as session:
  86. result = session.run(query, resource_Ids=resource_ids)
  87. return result.data()
  88. # 创建一个数据模型节点
  89. def handle_data_model(data_model, result_list, result, receiver):
  90. """
  91. 创建一个数据模型节点
  92. Args:
  93. data_model: 数据模型名称
  94. result_list: 数据模型英文名列表
  95. result: 序列化的ID列表
  96. receiver: 接收到的请求参数
  97. Returns:
  98. tuple: (id, data_model_node)
  99. """
  100. try:
  101. # 添加数据资源 血缘关系的字段 blood_resource
  102. data_model_en = result_list[0] if result_list and len(result_list) > 0 else ""
  103. receiver['id_list'] = result
  104. add_attribute = {
  105. 'time': get_formatted_time(),
  106. 'en_name': data_model_en
  107. }
  108. receiver.update(add_attribute)
  109. data_model_node = get_node('data_model', name=data_model) or create_or_get_node('data_model', **receiver)
  110. logger.info(f"通过查询或创建节点获得节点ID111,data_model_node: {data_model_node}")
  111. # 获取节点ID,确保我们能安全地访问节点ID
  112. node_id = data_model_node
  113. if hasattr(data_model_node, 'id'):
  114. logger.info(f"通过节点ID获取节点ID222,data_model_node: {data_model_node}")
  115. node_id = data_model_node.id
  116. else:
  117. logger.info(f"通过查询节点名称获取节点ID333,data_model_node: {data_model_node}")
  118. # 如果节点没有id属性,尝试通过查询获取
  119. query = """
  120. MATCH (n:data_model {name: $name})
  121. RETURN id(n) as node_id
  122. """
  123. with connect_graph().session() as session:
  124. result = session.run(query, name=data_model)
  125. record = result.single()
  126. logger.info(f"通过查询节点名称获取节点ID444,record: {record}")
  127. if record and "node_id" in record:
  128. logger.info(f"通过查询节点名称获取节点ID555,record: {record}")
  129. node_id = record["node_id"]
  130. # 安全地处理子节点关系
  131. child_list = receiver.get('childrenId', [])
  132. for child_id in child_list:
  133. child_node = get_node_by_id_no_label(child_id)
  134. if child_node:
  135. # 直接使用Cypher查询检查关系是否存在
  136. with connect_graph().session() as session:
  137. rel_query = """
  138. MATCH (a)-[r:child]->(b)
  139. WHERE id(a) = $start_id AND id(b) = $end_id
  140. RETURN count(r) > 0 as exists
  141. """
  142. rel_result = session.run(rel_query,
  143. start_id=int(node_id),
  144. end_id=int(child_node.id)).single()
  145. # 如果关系不存在,则创建关系
  146. if not (rel_result and rel_result["exists"]):
  147. session.execute_write(
  148. lambda tx: tx.run(
  149. "MATCH (a), (b) WHERE id(a) = $a_id AND id(b) = $b_id CREATE (a)-[:child]->(b)",
  150. a_id=int(node_id), b_id=int(child_node.id)
  151. )
  152. )
  153. # 根据传入参数id,和数据标签建立关系
  154. if receiver.get('tag'):
  155. tag = get_node_by_id('data_label', receiver['tag'])
  156. if tag:
  157. # 直接使用Cypher查询检查关系是否存在
  158. with connect_graph().session() as session:
  159. rel_query = """
  160. MATCH (a)-[r:label]->(b)
  161. WHERE id(a) = $start_id AND id(b) = $end_id
  162. RETURN count(r) > 0 as exists
  163. """
  164. rel_result = session.run(rel_query,
  165. start_id=int(node_id),
  166. end_id=int(tag.id)).single()
  167. # 如果关系不存在,则创建关系
  168. if not (rel_result and rel_result["exists"]):
  169. session.execute_write(
  170. lambda tx: tx.run(
  171. "MATCH (a), (b) WHERE id(a) = $a_id AND id(b) = $b_id CREATE (a)-[:label]->(b)",
  172. a_id=int(node_id), b_id=int(tag.id)
  173. )
  174. )
  175. return node_id, data_model_node
  176. except Exception as e:
  177. logging.error(f"Error in handle_data_model: {str(e)}")
  178. raise
  179. # (从数据资源中选取)
  180. def resource_handle_meta_data_model(id_lists, data_model_node_id):
  181. """
  182. 处理从数据资源中选取的数据模型与元数据的关系
  183. Args:
  184. id_lists: ID列表
  185. data_model_node_id: 数据模型节点ID
  186. Returns:
  187. None
  188. """
  189. try:
  190. logger.info(f"开始处理数据模型与元数据的关系,数据模型ID: {data_model_node_id}")
  191. # 构建meta_id和resouce_id的列表
  192. resouce_ids = [record['resource_id'] for record in id_lists]
  193. meta_ids = [record['id'] for id_list in id_lists for record in id_list['metaData']]
  194. logger.info(f"资源ID列表: {resouce_ids}")
  195. logger.info(f"元数据ID列表: {meta_ids}")
  196. # 创建与meta_node的关系 组成关系
  197. if meta_ids:
  198. logger.info("开始创建数据模型与元数据的关系")
  199. query = """
  200. MATCH (source:data_model), (target:meta_data)
  201. WHERE id(source)=$source_id AND id(target) IN $target_ids
  202. MERGE (source)-[:INCLUDE]->(target)
  203. RETURN count(*) as count
  204. """
  205. with connect_graph().session() as session:
  206. result = session.run(query, source_id=data_model_node_id, target_ids=meta_ids)
  207. count = result.single()["count"]
  208. logger.info(f"成功创建 {count} 个数据模型与元数据的关系")
  209. # 创建与data_resource的关系 资源关系
  210. if resouce_ids:
  211. logger.info("开始创建数据模型与数据资源的关系")
  212. query = """
  213. MATCH (source:data_model), (target:data_resource)
  214. WHERE id(source)=$source_id AND id(target) IN $target_ids
  215. MERGE (source)-[:DERIVES_FROM]->(target)
  216. RETURN count(*) as count
  217. """
  218. with connect_graph().session() as session:
  219. result = session.run(query, source_id=data_model_node_id, target_ids=resouce_ids)
  220. count = result.single()["count"]
  221. logger.info(f"成功创建 {count} 个数据模型与数据资源的关系")
  222. except Exception as e:
  223. logger.error(f"处理数据模型与元数据的关系时发生错误: {str(e)}")
  224. raise
  225. # (从数据模型中选取)
  226. def model_handle_meta_data_model(id_lists, data_model_node_id):
  227. """
  228. 处理从数据模型中选取的数据模型与元数据的关系
  229. Args:
  230. id_lists: ID列表
  231. data_model_node_id: 数据模型节点ID
  232. Returns:
  233. None
  234. """
  235. # 构建meta_id和model_id的列表
  236. model_ids = [record['model_id'] for record in id_lists]
  237. meta_ids = [record['id'] for id_list in id_lists for record in id_list['metaData']]
  238. # 创建与meta_node的关系 组成关系
  239. if meta_ids:
  240. query = """
  241. MATCH (source:data_model), (target:meta_node)
  242. WHERE id(source)=$source_id AND id(target) IN $target_ids
  243. MERGE (source)-[:component]->(target)
  244. """
  245. with neo4j_driver.get_session() as session:
  246. session.run(query, source_id=data_model_node_id, target_ids=meta_ids)
  247. # 创建与data_model的关系 模型关系
  248. if model_ids:
  249. query = """
  250. MATCH (source:data_model), (target:data_model)
  251. WHERE id(source)=$source_id AND id(target) IN $target_ids
  252. MERGE (source)-[:use]->(target)
  253. """
  254. with neo4j_driver.get_session() as session:
  255. session.run(query, source_id=data_model_node_id, target_ids=model_ids)
  256. # (从DDL中选取)
  257. def handle_no_meta_data_model(id_lists, receiver, data_model_node):
  258. """
  259. 处理从DDL中选取的没有元数据的数据模型
  260. Args:
  261. id_lists: ID列表
  262. receiver: 接收到的请求参数
  263. data_model_node: 数据模型节点
  264. Returns:
  265. None
  266. """
  267. # 构建meta_id和resouce_id的列表
  268. resouce_ids = [record['resource_id'] for record in id_lists]
  269. meta_ids = [record['id'] for id_list in id_lists for record in id_list['metaData']]
  270. # 获取数据模型节点ID
  271. data_model_node_id = None
  272. if hasattr(data_model_node, 'id'):
  273. data_model_node_id = data_model_node.id
  274. else:
  275. # 如果节点没有id属性,尝试通过查询获取
  276. query = """
  277. MATCH (n:data_model {name: $name})
  278. RETURN id(n) as node_id
  279. """
  280. with connect_graph().session() as session:
  281. result = session.run(query, name=data_model_node.get('name'))
  282. record = result.single()
  283. if record:
  284. data_model_node_id = record["node_id"]
  285. if not data_model_node_id:
  286. return
  287. # 创建与data_resource的关系 资源关系
  288. if resouce_ids:
  289. query = """
  290. MATCH (source:data_model), (target:data_resource)
  291. WHERE id(source)=$source_id AND id(target) IN $target_ids
  292. MERGE (source)-[:resource]->(target)
  293. """
  294. with connect_graph().session() as session:
  295. session.run(query, source_id=data_model_node_id, target_ids=resouce_ids)
  296. if meta_ids:
  297. meta_node_list = []
  298. for id in meta_ids:
  299. query = """
  300. MATCH (n)
  301. WHERE id(n) = $node_id
  302. RETURN n
  303. """
  304. with connect_graph().session() as session:
  305. result = session.run(query, node_id=id)
  306. if result:
  307. record = result.data()
  308. if record:
  309. meta_node_list.append(record[0]['n'])
  310. # 提取接收到的数据并创建meta_node节点
  311. meta_node = None
  312. resource_ids = []
  313. for item in id_lists:
  314. resource_id = item['resource_id']
  315. resource_ids.append(resource_id)
  316. for meta_item in item['metaData']:
  317. meta_id = meta_item['id']
  318. data_standard = meta_item.get('data_standard', '')
  319. en_name_zh = meta_item.get('en_name_zh', '')
  320. data_name = meta_item.get('data_name', '')
  321. # 使用传递的参数创建meta_node节点
  322. meta_params = {
  323. 'name': data_name,
  324. 'cn_name': en_name_zh,
  325. 'standard': data_standard,
  326. 'time': get_formatted_time()
  327. }
  328. # 创建meta_node节点
  329. meta_node = create_or_get_node('meta_node', **meta_params)
  330. # 获取数据模型节点ID
  331. dm_id = data_model_node_id if data_model_node_id is not None else data_model_node
  332. if meta_node:
  333. # 直接使用Cypher查询检查关系是否存在
  334. with connect_graph().session() as session:
  335. rel_query = """
  336. MATCH (a)-[r:component]->(b)
  337. WHERE id(a) = $start_id AND id(b) = $end_id
  338. RETURN count(r) > 0 as exists
  339. """
  340. rel_result = session.run(rel_query,
  341. start_id=int(dm_id),
  342. end_id=int(meta_node)).single()
  343. # 如果关系不存在,则创建关系
  344. if not (rel_result and rel_result["exists"]):
  345. session.execute_write(
  346. lambda tx: tx.run(
  347. "MATCH (a), (b) WHERE id(a) = $a_id AND id(b) = $b_id CREATE (a)-[:component]->(b)",
  348. a_id=int(dm_id), b_id=int(meta_node)
  349. )
  350. )
  351. # 数据模型-详情接口
  352. def handle_id_model(id):
  353. """
  354. 获取数据模型详情
  355. Args:
  356. id: 数据模型的节点ID
  357. Returns:
  358. dict: 包含数据模型详情的字典,格式为:
  359. {"data_model": {
  360. "resource_selected": [...],
  361. "leader": ...,
  362. "origin": ...,
  363. "frequency": ...,
  364. "childrenId": [...],
  365. "organization": ...,
  366. "name": ...,
  367. "en_name": ...,
  368. "data_sensitivity": ...,
  369. "describe": ...,
  370. "tag": ...,
  371. "time": ...,
  372. "category": ...,
  373. "status": ...
  374. }}
  375. """
  376. node_id = id
  377. cql = """
  378. MATCH (n:data_model) WHERE id(n) = $nodeId
  379. OPTIONAL MATCH (n)-[:INCLUDE]->(meta:meta_data)
  380. OPTIONAL MATCH (n)-[:DERIVES_FROM]->(resource:data_resource)
  381. OPTIONAL MATCH (n)-[:label]->(tag:data_label)
  382. OPTIONAL MATCH (uses:model_use)-[:use]->(n)
  383. OPTIONAL MATCH (n)-[:has_component]->(component)
  384. WITH n,
  385. collect(DISTINCT meta) as meta_nodes,
  386. collect(DISTINCT resource) as resources,
  387. collect(DISTINCT component) as components,
  388. collect(DISTINCT uses) as uses,
  389. collect(DISTINCT tag) as tags,
  390. CASE WHEN n.childrenId IS NOT NULL THEN n.childrenId ELSE [] END as children
  391. RETURN {
  392. // 基本信息
  393. id: id(n),
  394. name: n.name,
  395. en_name: n.en_name,
  396. time: n.time,
  397. describe: n.describe,
  398. category: n.category,
  399. level: n.level,
  400. tag: CASE WHEN size(tags) > 0 AND tags[0] IS NOT NULL THEN {id: id(tags[0]), name: tags[0].name} ELSE null END,
  401. // 添加其他必需字段
  402. leader: n.leader,
  403. origin: n.origin,
  404. blood_resource: n.blood_resource,
  405. frequency: n.frequency,
  406. organization: n.organization,
  407. data_sensitivity: n.data_sensitivity,
  408. status: n.status,
  409. // 子节点列表
  410. childrenId: children
  411. } AS result,
  412. // 资源列表
  413. [{
  414. data_resource: [resource IN resources WHERE resource IS NOT NULL | {
  415. id: id(resource),
  416. name: resource.name,
  417. en_name: resource.en_name,
  418. description: resource.description
  419. }],
  420. resource_id: [resource IN resources WHERE resource IS NOT NULL | id(resource)],
  421. meta_ids: [meta IN meta_nodes WHERE meta IS NOT NULL | {
  422. id: id(meta),
  423. name: meta.name,
  424. en_name: meta.en_name,
  425. data_type: meta.data_type
  426. }]
  427. }] AS resource_selected
  428. """
  429. with connect_graph().session() as session:
  430. result = session.run(cql, nodeId=node_id)
  431. # 处理查询结果
  432. record = result.single()
  433. logging.info(f"获得查询结果---------->>>{record}")
  434. if record:
  435. # 获取基本属性和资源选择列表
  436. properties = record["result"]
  437. resource_selected = record["resource_selected"]
  438. # 确保所有必需字段都有默认值,避免空值
  439. required_fields = ['tag', 'leader', 'origin', 'blood_resource',
  440. 'frequency', 'describe', 'organization', 'name', 'en_name',
  441. 'data_sensitivity', 'time', 'category', 'status', 'childrenId']
  442. for field in required_fields:
  443. if field not in properties or properties[field] is None:
  444. if field == 'tag':
  445. properties[field] = {}
  446. elif field == 'childrenId':
  447. properties[field] = []
  448. else:
  449. properties[field] = ""
  450. # 构建最终返回格式
  451. final_data = {
  452. "resource_selected": resource_selected,
  453. **properties
  454. }
  455. return {"data_model": final_data}
  456. else:
  457. # 如果没有查询到结果,返回空的结构
  458. return {"data_model": {
  459. "resource_selected": [{"meta_ids": [], "data_resource": None, "resource_id": None}],
  460. "leader": None, "origin": None, "frequency": None, "childrenId": [],
  461. "organization": None, "name": None, "en_name": None, "data_sensitivity": None,
  462. "describe": None, "tag": {}, "time": None, "category": None, "status": None
  463. }}
  464. # 数据模型列表
  465. def model_list(skip_count, page_size, en_name_filter=None, name_filter=None,
  466. category=None, tag=None, level=None):
  467. """
  468. 获取数据模型列表
  469. Args:
  470. skip_count: 跳过的数量
  471. page_size: 页面大小
  472. en_name_filter: 英文名称过滤条件
  473. name_filter: 名称过滤条件
  474. category: 类别过滤条件
  475. tag: 标签过滤条件
  476. level: 层级过滤条件
  477. Returns:
  478. tuple: (数据模型列表, 总数量)
  479. """
  480. try:
  481. # 构建where子句
  482. where_clause = []
  483. params = {}
  484. if name_filter is not None:
  485. where_clause.append("n.name =~ $name")
  486. params['name'] = f".*{name_filter}.*"
  487. if en_name_filter is not None:
  488. where_clause.append("n.en_name =~ $en_name")
  489. params['en_name'] = f".*{en_name_filter}.*"
  490. if category is not None:
  491. where_clause.append("n.category = $category")
  492. params['category'] = category
  493. if level is not None:
  494. where_clause.append("n.level = $level")
  495. params['level'] = level
  496. if tag is not None:
  497. where_clause.append("id(t) = $tag")
  498. params['tag'] = tag
  499. # At the end of where_clause construction
  500. where_str = " AND ".join(where_clause)
  501. if where_str:
  502. where_str = f"WHERE {where_str}"
  503. # 构建查询
  504. with connect_graph().session() as session:
  505. # 计算总数量
  506. count_query = f"""
  507. MATCH (n:data_model)
  508. OPTIONAL MATCH (n)-[:label]->(t)
  509. {where_str}
  510. RETURN COUNT(DISTINCT n) AS count
  511. """
  512. count_result = session.run(count_query, **params)
  513. count_record = count_result.single()
  514. total = count_record['count'] if count_record else 0
  515. # 查询数据
  516. query = f"""
  517. MATCH (n:data_model)
  518. OPTIONAL MATCH (n)-[:label]->(t)
  519. {where_str}
  520. RETURN DISTINCT
  521. id(n) as id,
  522. n.name as name,
  523. n.en_name as en_name,
  524. n.time as time,
  525. n.describe as describe,
  526. n.level as level,
  527. n.category as category,
  528. n.status as status,
  529. n.leader as leader,
  530. n.origin as origin,
  531. n.blood_resource as blood_resource,
  532. n.organization as organization,
  533. id(t) as tag_id,
  534. t.name as tag_name
  535. ORDER BY n.time DESC
  536. SKIP $skip
  537. LIMIT $limit
  538. """
  539. result = session.run(query, skip=skip_count, limit=page_size, **params)
  540. # 处理结果
  541. data = []
  542. for record in result:
  543. item = {
  544. "id": record['id'],
  545. "name": record['name'],
  546. "en_name": record['en_name'],
  547. "time": record['time'],
  548. "describe": record['describe'],
  549. "category": record['category'],
  550. "status": record['status'],
  551. "leader": record['leader'],
  552. "origin": record['origin'],
  553. "blood_resource": record['blood_resource'],
  554. "organization": record['organization'],
  555. "level": record['level'],
  556. "tag": {"id": record['tag_id'], "name": record['tag_name']} if record['tag_id'] is not None else None
  557. }
  558. data.append(item)
  559. return data, total
  560. except Exception as e:
  561. print(f"Error in model_list: {str(e)}")
  562. import traceback
  563. traceback.print_exc()
  564. return [], 0
  565. # 有血缘关系的数据资源列表
  566. def model_resource_list(skip_count, page_size, name_filter=None, id=None,
  567. category=None, time=None):
  568. """
  569. 获取数据模型相关的数据资源列表
  570. Args:
  571. skip_count: 跳过的数量
  572. page_size: 页面大小
  573. name_filter: 名称过滤条件
  574. id: 数据模型ID
  575. category: 类别过滤条件
  576. time: 时间过滤条件
  577. Returns:
  578. tuple: (数据资源列表, 总数量)
  579. """
  580. try:
  581. # 构建基础查询
  582. base_query = """
  583. MATCH (n:data_model)
  584. WHERE id(n) = $nodeId
  585. MATCH (n)-[:children]->(m:data_resource)
  586. """
  587. # 计算总数量
  588. count_query = base_query + """
  589. RETURN COUNT(m) as count
  590. """
  591. with connect_graph().session() as session:
  592. # 执行计数查询
  593. count_result = session.run(count_query, nodeId=id)
  594. count_record = count_result.single()
  595. total = count_record['count'] if count_record else 0
  596. # 使用分页和筛选条件构建主查询
  597. main_query = base_query + """
  598. MATCH (m)-[:label]->(l)
  599. WHERE id(n) = $nodeId and labels(m) <> ['meta_node']
  600. RETURN m.name as name,
  601. m.en_name as en_name,
  602. id(m) as id,
  603. l.name as label,
  604. m.time as time,
  605. m.description as description,
  606. m.category as category
  607. ORDER BY m.time DESC
  608. SKIP $skip LIMIT $limit
  609. """
  610. # 执行主查询
  611. result = session.run(main_query, nodeId=id, skip=skip_count, limit=page_size)
  612. # 处理结果
  613. data = []
  614. for record in result:
  615. item = {
  616. "name": record['name'],
  617. "en_name": record['en_name'],
  618. "id": record['id'],
  619. "label": record['label'],
  620. "time": record['time'],
  621. "description": record['description'],
  622. "category": record['category']
  623. }
  624. data.append(item)
  625. return data, total
  626. except Exception as e:
  627. print(f"Error in model_resource_list: {str(e)}")
  628. import traceback
  629. traceback.print_exc()
  630. return [], 0
  631. # 数据模型血缘图谱
  632. def model_kinship_graph(nodeid, meta=False):
  633. """
  634. 生成数据模型的血缘关系图谱
  635. Args:
  636. nodeid: 节点ID
  637. meta: 是否包含元数据
  638. Returns:
  639. dict: 包含节点和连线信息的图谱数据
  640. """
  641. result = {}
  642. with connect_graph().session() as session:
  643. # 查询起始模型节点
  644. start_node_query = """
  645. MATCH (n:data_model)
  646. WHERE id(n) = $nodeId
  647. RETURN n.name as name, n.en_name as en_name
  648. """
  649. start_result = session.run(start_node_query, nodeId=nodeid)
  650. start_record = start_result.single()
  651. if not start_record:
  652. return {"nodes": [], "lines": []}
  653. # 查询与模型关联的数据资源
  654. resource_query = """
  655. MATCH (n:data_model)
  656. WHERE id(n) = $nodeId
  657. MATCH p = (n)-[:children]->(resource:data_resource)
  658. RETURN resource
  659. """
  660. resource_result = session.run(resource_query, nodeId=nodeid)
  661. nodes = [{"id": str(nodeid), "text": start_record['name'], "type": "model"}]
  662. lines = []
  663. # 处理资源节点
  664. for record in resource_result:
  665. if 'resource' in record:
  666. resource = record['resource']
  667. resource_id = str(resource.id)
  668. resource_name = resource.get('name', '')
  669. resource_en_name = resource.get('en_name', '')
  670. # 创建资源节点
  671. resource_node = {
  672. "id": resource_id,
  673. "text": resource_name,
  674. "type": "resource"
  675. }
  676. nodes.append(resource_node)
  677. # 创建资源到模型的关系
  678. line = {
  679. "from": str(nodeid),
  680. "to": resource_id,
  681. "text": "resource"
  682. }
  683. lines.append(line)
  684. # 处理元数据节点
  685. if meta:
  686. meta_query = """
  687. MATCH (n:data_model)
  688. WHERE id(n) = $nodeId and labels(m) <> ['meta_node']
  689. MATCH p = (n)-[:meta]->(meta:meta_node)
  690. RETURN meta
  691. """
  692. meta_result = session.run(meta_query, nodeId=nodeid)
  693. for record in meta_result:
  694. if 'meta' in record:
  695. meta_node = record['meta']
  696. meta_id = str(meta.id)
  697. meta_name = meta.get('name', '')
  698. meta_en_name = meta.get('en_name', '')
  699. # 创建元数据节点
  700. meta_node = {
  701. "id": meta_id,
  702. "text": meta_name,
  703. "type": "meta"
  704. }
  705. nodes.append(meta_node)
  706. # 创建模型到元数据的标签关系
  707. tag_line = {
  708. "from": str(nodeid),
  709. "to": meta_id,
  710. "text": "component"
  711. }
  712. lines.append(tag_line)
  713. # 构建结果
  714. result = {
  715. "nodes": nodes,
  716. "lines": lines
  717. }
  718. return result
  719. # 数据模型影响图谱
  720. def model_impact_graph(nodeid, meta=False):
  721. """
  722. 生成数据模型的影响关系图谱
  723. Args:
  724. nodeid: 节点ID
  725. meta: 是否包含元数据
  726. Returns:
  727. dict: 包含节点和连线信息的图谱数据
  728. """
  729. result = {}
  730. with connect_graph().session() as session:
  731. # 查询起始模型节点
  732. start_node_query = """
  733. MATCH (n:data_model)
  734. WHERE id(n) = $nodeId
  735. RETURN n.name as name, n.en_name as en_name
  736. """
  737. start_result = session.run(start_node_query, nodeId=nodeid)
  738. start_record = start_result.single()
  739. if not start_record:
  740. return {"nodes": [], "lines": []}
  741. # 查询影响模型的数据资源
  742. resource_query = """
  743. MATCH (n:data_model)
  744. WHERE id(n) = $nodeId
  745. MATCH p = (n)-[:children]->(resource:data_resource)
  746. RETURN resource
  747. """
  748. resource_result = session.run(resource_query, nodeId=nodeid)
  749. nodes = [{"id": str(nodeid), "text": start_record['name'], "type": "model"}]
  750. lines = []
  751. # 处理资源节点
  752. for record in resource_result:
  753. if 'resource' in record:
  754. resource = record['resource']
  755. resource_id = str(resource.id)
  756. resource_name = resource.get('name', '')
  757. resource_en_name = resource.get('en_name', '')
  758. # 创建资源节点
  759. resource_node = {
  760. "id": resource_id,
  761. "text": resource_name,
  762. "type": "resource"
  763. }
  764. nodes.append(resource_node)
  765. # 创建资源到模型的关系
  766. line = {
  767. "from": str(nodeid),
  768. "to": resource_id,
  769. "text": "resource"
  770. }
  771. lines.append(line)
  772. # 处理元数据节点
  773. if meta:
  774. meta_query = """
  775. MATCH (n:data_model)
  776. WHERE id(n) = $nodeId and labels(m) <> ['meta_node']
  777. MATCH p = (n)-[:meta]->(meta:meta_node)
  778. RETURN meta
  779. """
  780. meta_result = session.run(meta_query, nodeId=nodeid)
  781. for record in meta_result:
  782. if 'meta' in record:
  783. meta_node = record['meta']
  784. meta_id = str(meta.id)
  785. meta_name = meta.get('name', '')
  786. meta_en_name = meta.get('en_name', '')
  787. # 创建元数据节点
  788. meta_node = {
  789. "id": meta_id,
  790. "text": meta_name,
  791. "type": "meta"
  792. }
  793. nodes.append(meta_node)
  794. # 创建模型到元数据的标签关系
  795. tag_line = {
  796. "from": str(nodeid),
  797. "to": meta_id,
  798. "text": "component"
  799. }
  800. lines.append(tag_line)
  801. # 构建结果
  802. result = {
  803. "nodes": nodes,
  804. "lines": lines
  805. }
  806. return result
  807. # 数据模型全部图谱
  808. def model_all_graph(nodeid, meta=False):
  809. """
  810. 生成数据模型的所有关系图谱
  811. Args:
  812. nodeid: 节点ID
  813. meta: 是否包含元数据
  814. Returns:
  815. dict: 包含节点和连线信息的图谱数据
  816. """
  817. result = {}
  818. with connect_graph().session() as session:
  819. # 查询起始模型节点
  820. start_node_query = """
  821. MATCH (n:data_model)
  822. WHERE id(n) = $nodeId
  823. RETURN n.name as name, n.en_name as en_name
  824. """
  825. start_result = session.run(start_node_query, nodeId=nodeid)
  826. start_record = start_result.single()
  827. if not start_record:
  828. return {"nodes": [], "lines": []}
  829. # 查询与模型关联的数据资源
  830. resource_query = """
  831. MATCH (n:data_model)
  832. WHERE id(n) = $nodeId
  833. MATCH p = (n)-[:children]->(resource:data_resource)
  834. RETURN resource
  835. """
  836. resource_result = session.run(resource_query, nodeId=nodeid)
  837. # 查询与模型关联的元数据
  838. meta_query = """
  839. MATCH (n:data_model)
  840. WHERE id(n) = $nodeId and labels(m) <> ['meta_node']
  841. MATCH p = (n)-[:meta]->(meta:meta_node)
  842. RETURN meta
  843. """
  844. nodes = [{"id": str(nodeid), "text": start_record['name'], "type": "model"}]
  845. lines = []
  846. # 处理资源节点
  847. for record in resource_result:
  848. if 'resource' in record:
  849. resource = record['resource']
  850. resource_id = str(resource.id)
  851. resource_name = resource.get('name', '')
  852. resource_en_name = resource.get('en_name', '')
  853. # 创建资源节点
  854. resource_node = {
  855. "id": resource_id,
  856. "text": resource_name,
  857. "type": "resource"
  858. }
  859. nodes.append(resource_node)
  860. # 创建资源到模型的关系
  861. line = {
  862. "from": str(nodeid),
  863. "to": resource_id,
  864. "text": "resource"
  865. }
  866. lines.append(line)
  867. # 处理元数据节点
  868. if meta:
  869. meta_result = session.run(meta_query, nodeId=nodeid)
  870. for record in meta_result:
  871. if 'meta' in record:
  872. meta_node = record['meta']
  873. meta_id = str(meta.id)
  874. meta_name = meta.get('name', '')
  875. meta_en_name = meta.get('en_name', '')
  876. # 创建元数据节点
  877. meta_node = {
  878. "id": meta_id,
  879. "text": meta_name,
  880. "type": "meta"
  881. }
  882. nodes.append(meta_node)
  883. # 创建模型到元数据的标签关系
  884. tag_line = {
  885. "from": str(nodeid),
  886. "to": meta_id,
  887. "text": "component"
  888. }
  889. lines.append(tag_line)
  890. # 构建结果
  891. result = {
  892. "nodes": nodes,
  893. "lines": lines
  894. }
  895. return result
  896. # 更新数据模型
  897. def data_model_edit(receiver):
  898. """
  899. 更新数据模型
  900. Args:
  901. receiver: 接收到的请求参数
  902. Returns:
  903. 更新结果
  904. """
  905. id = receiver.get('id')
  906. name = receiver.get('name')
  907. en_name = receiver.get('en_name')
  908. category = receiver.get('category')
  909. describe = receiver.get('describe')
  910. tag = receiver.get('tag')
  911. # 更新数据模型节点
  912. query = """
  913. MATCH (n:data_model) WHERE id(n) = $id
  914. SET n.name = $name, n.en_name = $en_name, n.category = $category, n.describe = $describe
  915. RETURN n
  916. """
  917. with connect_graph().session() as session:
  918. result = session.run(query, id=id, name=name, en_name=en_name,
  919. category=category, describe=describe).data()
  920. # 处理标签关系
  921. if tag:
  922. # 先删除所有标签关系
  923. delete_query = """
  924. MATCH (n:data_model)-[r:label]->() WHERE id(n) = $id
  925. DELETE r
  926. """
  927. with connect_graph().session() as session:
  928. session.run(delete_query, id=id)
  929. # 再创建新的标签关系
  930. tag_node = get_node_by_id('data_label', tag)
  931. if tag_node:
  932. model_node = get_node_by_id_no_label(id)
  933. if model_node:
  934. # 获取节点ID
  935. model_id = model_node.id if hasattr(model_node, 'id') else model_node
  936. tag_id = tag_node.id if hasattr(tag_node, 'id') else tag_node
  937. # 直接使用Cypher查询检查关系是否存在
  938. with connect_graph().session() as session:
  939. rel_query = """
  940. MATCH (a)-[r:label]->(b)
  941. WHERE id(a) = $start_id AND id(b) = $end_id
  942. RETURN count(r) > 0 as exists
  943. """
  944. rel_result = session.run(rel_query,
  945. start_id=int(model_id),
  946. end_id=int(tag_id)).single()
  947. # 如果关系不存在,则创建关系
  948. if not (rel_result and rel_result["exists"]):
  949. session.execute_write(
  950. lambda tx: tx.run(
  951. "MATCH (a), (b) WHERE id(a) = $a_id AND id(b) = $b_id CREATE (a)-[:label]->(b)",
  952. a_id=int(model_id), b_id=int(tag_id)
  953. )
  954. )
  955. return {"message": "数据模型更新成功"}