model.py 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787
  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. from app.services.package_function import create_or_get_node, relationship_exists, get_node
  18. from app.core.graph.graph_operations import connect_graph
  19. from app.services.neo4j_driver import neo4j_driver
  20. from app.core.meta_data import get_formatted_time, handle_id_unstructured
  21. from app.core.common import delete_relationships, update_or_create_node, get_node_by_id_no_label
  22. from app.core.data_resource.resource import get_node_by_id
  23. # 根据child关系计算数据模型当前的level自动保存
  24. def calculate_model_level(id):
  25. """
  26. 根据child关系计算数据模型当前的level并自动保存
  27. Args:
  28. id: 数据模型的节点ID
  29. Returns:
  30. None
  31. """
  32. cql = """
  33. MATCH (start_node:data_model {id: $nodeId})
  34. CALL {
  35. WITH start_node
  36. OPTIONAL MATCH path = (start_node)-[:child*]->(end_node)
  37. RETURN length(path) AS level
  38. }
  39. WITH coalesce(max(level), 0) AS max_level
  40. RETURN max_level
  41. """
  42. data = connect_graph.run(cql, nodeId=id).evaluate()
  43. # 更新level属性
  44. update_query = """
  45. MATCH (n:data_model {id: $nodeId})
  46. SET n.level = $level
  47. RETURN n
  48. """
  49. connect_graph.run(update_query, nodeId=id, level=data)
  50. # 处理数据模型血缘关系
  51. def handle_model_relation(resource_ids):
  52. """
  53. 处理数据模型血缘关系
  54. Args:
  55. resource_ids: 数据资源ID
  56. Returns:
  57. 血缘关系数据
  58. """
  59. query = """
  60. MATCH (search:data_resource)-[:connection]->(common_node:meta_node)<-[:connection]-(connect:data_resource)
  61. WHERE id(search) = $resource_Ids
  62. WITH search, connect, common_node
  63. MATCH (search)-[:connection]->(search_node:meta_node)
  64. WITH search, connect, common_node, collect(DISTINCT id(search_node)) AS search_nodes
  65. MATCH (connect)-[:connection]->(connect_node:meta_node)
  66. WITH search, connect, common_node, search_nodes, collect(DISTINCT id(connect_node)) AS connect_nodes
  67. WITH search, connect, search_nodes, connect_nodes, collect(DISTINCT id(common_node)) AS common_nodes
  68. // 剔除 search_nodes 和 connect_nodes 中包含在 common_nodes 中的内容
  69. WITH search, connect, common_nodes,
  70. [node IN search_nodes WHERE NOT node IN common_nodes] AS filtered_search_nodes,
  71. [node IN connect_nodes WHERE NOT node IN common_nodes] AS filtered_connect_nodes
  72. RETURN id(connect) as blood_resources, common_nodes,
  73. filtered_search_nodes as origin_nodes, filtered_connect_nodes as blood_nodes
  74. """
  75. result = connect_graph.run(query, resource_Ids=resource_ids)
  76. return result.data()
  77. # 创建一个数据模型节点
  78. def handle_data_model(data_model, result_list, result, receiver):
  79. """
  80. 创建一个数据模型节点
  81. Args:
  82. data_model: 数据模型名称
  83. result_list: 数据模型英文名列表
  84. result: 序列化的ID列表
  85. receiver: 接收到的请求参数
  86. Returns:
  87. tuple: (id, data_model_node)
  88. """
  89. # 添加数据资源 血缘关系的字段 blood_resource
  90. data_model_en = result_list[0]
  91. receiver['id_list'] = result
  92. add_attribute = {
  93. 'time': get_formatted_time(),
  94. 'en_name': data_model_en
  95. }
  96. receiver.update(add_attribute)
  97. data_model_node = get_node('data_model', name=data_model) or create_or_get_node('data_model', **receiver)
  98. child_list = receiver['childrenId']
  99. for child_id in child_list:
  100. child = get_node_by_id_no_label(child_id)
  101. # 建立关系:当前节点的childrenId指向,以及关系child
  102. res = relationship_exists(data_model_node, 'child', child)
  103. if child and not res:
  104. connect_graph.create(Relationship(data_model_node, 'child', child))
  105. # 根据传入参数id,和数据标签建立关系
  106. if receiver['tag']:
  107. # 使用 Cypher 查询通过 id 查找节点
  108. tag = get_node_by_id('data_label', receiver['tag'])
  109. if tag and not relationship_exists(data_model_node, 'label', tag):
  110. connection = Relationship(data_model_node, 'label', tag)
  111. connect_graph.create(connection)
  112. id = data_model_node.identity
  113. return id, data_model_node
  114. # (从数据资源中选取)
  115. def resource_handle_meta_data_model(id_lists, data_model_node_id):
  116. """
  117. 处理从数据资源中选取的数据模型与元数据的关系
  118. Args:
  119. id_lists: ID列表
  120. data_model_node_id: 数据模型节点ID
  121. Returns:
  122. None
  123. """
  124. # 构建meta_id和resouce_id的列表
  125. resouce_ids = [record['resource_id'] for record in id_lists]
  126. meta_ids = [record['id'] for id_list in id_lists for record in id_list['metaData']]
  127. metaData = [record['data_standard'] for id_list in id_lists for record in id_list['metaData']]
  128. # 创建与meta_node的关系 组成关系
  129. if meta_ids:
  130. query = """
  131. MATCH (source:data_model), (target:meta_node)
  132. WHERE id(source)=$source_id AND id(target) IN $target_ids
  133. MERGE (source)-[:component]->(target)
  134. """
  135. with neo4j_driver.get_session() as session:
  136. session.run(query, source_id=data_model_node_id, target_ids=meta_ids)
  137. # 创建与data_resource的关系 资源关系
  138. if resouce_ids:
  139. query = """
  140. MATCH (source:data_model), (target:data_resource)
  141. WHERE id(source)=$source_id AND id(target) IN $target_ids
  142. MERGE (source)-[:resource]->(target)
  143. """
  144. with neo4j_driver.get_session() as session:
  145. session.run(query, source_id=data_model_node_id, target_ids=resouce_ids)
  146. # (从数据模型中选取)
  147. def model_handle_meta_data_model(id_lists, data_model_node_id):
  148. """
  149. 处理从数据模型中选取的数据模型与元数据的关系
  150. Args:
  151. id_lists: ID列表
  152. data_model_node_id: 数据模型节点ID
  153. Returns:
  154. None
  155. """
  156. # 构建meta_id和model_id的列表
  157. model_ids = [record['model_id'] for record in id_lists]
  158. meta_ids = [record['id'] for id_list in id_lists for record in id_list['metaData']]
  159. # 创建与meta_node的关系 组成关系
  160. if meta_ids:
  161. query = """
  162. MATCH (source:data_model), (target:meta_node)
  163. WHERE id(source)=$source_id AND id(target) IN $target_ids
  164. MERGE (source)-[:component]->(target)
  165. """
  166. with neo4j_driver.get_session() as session:
  167. session.run(query, source_id=data_model_node_id, target_ids=meta_ids)
  168. # 创建与data_model的关系 模型关系
  169. if model_ids:
  170. query = """
  171. MATCH (source:data_model), (target:data_model)
  172. WHERE id(source)=$source_id AND id(target) IN $target_ids
  173. MERGE (source)-[:use]->(target)
  174. """
  175. with neo4j_driver.get_session() as session:
  176. session.run(query, source_id=data_model_node_id, target_ids=model_ids)
  177. # (从DDL中选取)
  178. def handle_no_meta_data_model(id_lists, receiver, data_model_node):
  179. """
  180. 处理从DDL中选取的没有元数据的数据模型
  181. Args:
  182. id_lists: ID列表
  183. receiver: 接收到的请求参数
  184. data_model_node: 数据模型节点
  185. Returns:
  186. None
  187. """
  188. # 构建meta_id和resouce_id的列表
  189. resouce_ids = [record['resource_id'] for record in id_lists]
  190. meta_ids = [record['id'] for id_list in id_lists for record in id_list['metaData']]
  191. # 创建与data_resource的关系 资源关系
  192. if resouce_ids:
  193. query = """
  194. MATCH (source:data_model), (target:data_resource)
  195. WHERE id(source)=$source_id AND id(target) IN $target_ids
  196. MERGE (source)-[:resource]->(target)
  197. """
  198. with neo4j_driver.get_session() as session:
  199. session.run(query, source_id=data_model_node.identity, target_ids=resouce_ids)
  200. if meta_ids:
  201. meta_node_list = []
  202. for id in meta_ids:
  203. query = """
  204. MATCH (n)
  205. WHERE id(n) = $node_id
  206. RETURN n
  207. """
  208. result = connect_graph.run(query, node_id=id)
  209. if result:
  210. record = result.data()
  211. if record:
  212. meta_node_list.append(record[0]['n'])
  213. # 提取接收到的数据并创建meta_node节点
  214. meta_node = None
  215. resource_ids = []
  216. for item in id_lists:
  217. resource_id = item['resource_id']
  218. resource_ids.append(resource_id)
  219. for meta_item in item['metaData']:
  220. meta_id = meta_item['id']
  221. data_standard = meta_item.get('data_standard', '')
  222. en_name_zh = meta_item.get('en_name_zh', '')
  223. data_name = meta_item.get('data_name', '')
  224. # 使用传递的参数创建meta_node节点
  225. meta_params = {
  226. 'name': data_name,
  227. 'cn_name': en_name_zh,
  228. 'standard': data_standard,
  229. 'time': get_formatted_time()
  230. }
  231. # 创建meta_node节点
  232. meta_node = create_or_get_node('meta_node', **meta_params)
  233. # 创建与data_model的关系
  234. if meta_node and not relationship_exists(data_model_node, 'component', meta_node):
  235. connection = Relationship(data_model_node, 'component', meta_node)
  236. connect_graph.create(connection)
  237. # 数据模型详情
  238. def handle_id_model(model_id):
  239. """
  240. 获取数据模型详情
  241. Args:
  242. model_id: 数据模型ID
  243. Returns:
  244. 数据模型详情
  245. """
  246. model_detail_query = """
  247. MATCH (n:data_model) WHERE id(n) = $model_id
  248. RETURN n
  249. """
  250. model_detail_result = connect_graph.run(model_detail_query, model_id=model_id).data()
  251. if not model_detail_result:
  252. return None
  253. model_detail = model_detail_result[0]['n']
  254. model_info = dict(model_detail)
  255. model_info['id'] = model_id
  256. # 获取data_model节点连接的resource节点
  257. resource_query = """
  258. MATCH (n:data_model)-[:resource]->(r:data_resource) WHERE id(n) = $model_id
  259. RETURN r
  260. """
  261. resource_result = connect_graph.run(resource_query, model_id=model_id).data()
  262. resources = []
  263. for item in resource_result:
  264. resource = dict(item['r'])
  265. resource['id'] = item['r'].identity
  266. resources.append(resource)
  267. model_info['resources'] = resources
  268. # 获取data_model节点连接的component节点
  269. component_query = """
  270. MATCH (n:data_model)-[:component]->(m:meta_node) WHERE id(n) = $model_id
  271. RETURN m
  272. """
  273. component_result = connect_graph.run(component_query, model_id=model_id).data()
  274. components = []
  275. for item in component_result:
  276. component = dict(item['m'])
  277. component['id'] = item['m'].identity
  278. components.append(component)
  279. model_info['components'] = components
  280. # 获取data_model节点连接的use节点
  281. use_query = """
  282. MATCH (n:data_model)-[:use]->(u:data_model) WHERE id(n) = $model_id
  283. RETURN u
  284. """
  285. use_result = connect_graph.run(use_query, model_id=model_id).data()
  286. uses = []
  287. for item in use_result:
  288. use = dict(item['u'])
  289. use['id'] = item['u'].identity
  290. uses.append(use)
  291. model_info['uses'] = uses
  292. # 获取data_model节点连接的标签
  293. tag_query = """
  294. MATCH (n:data_model)-[:label]->(t:data_label) WHERE id(n) = $model_id
  295. RETURN t
  296. """
  297. tag_result = connect_graph.run(tag_query, model_id=model_id).data()
  298. if tag_result:
  299. tag = dict(tag_result[0]['t'])
  300. tag['id'] = tag_result[0]['t'].identity
  301. model_info['tag'] = tag
  302. return model_info
  303. # 数据模型列表
  304. def model_list(skip_count, page_size, en_name_filter=None, name_filter=None,
  305. category=None, tag=None, level=None):
  306. """
  307. 获取数据模型列表
  308. Args:
  309. skip_count: 跳过的记录数量
  310. page_size: 每页记录数量
  311. en_name_filter: 英文名称过滤条件
  312. name_filter: 名称过滤条件
  313. category: 分类过滤条件
  314. tag: 标签过滤条件
  315. level: 级别过滤条件
  316. Returns:
  317. tuple: (数据列表, 总记录数)
  318. """
  319. # 构建查询条件
  320. params = {}
  321. where_clause = []
  322. if name_filter:
  323. where_clause.append("n.name =~ $name_filter")
  324. params['name_filter'] = f"(?i).*{name_filter}.*"
  325. if en_name_filter:
  326. where_clause.append("n.en_name =~ $en_name_filter")
  327. params['en_name_filter'] = f"(?i).*{en_name_filter}.*"
  328. if level:
  329. where_clause.append("n.level = $level")
  330. params['level'] = level
  331. if category:
  332. where_clause.append("n.category = $category")
  333. params['category'] = category
  334. # 添加tag标签查询逻辑
  335. if tag:
  336. match_clause = "MATCH (n:data_model)"
  337. if tag:
  338. match_clause += "\nMATCH (n)-[:label]->(t:data_label) WHERE id(t) = $tag_id"
  339. params['tag_id'] = tag
  340. else:
  341. match_clause = "MATCH (n:data_model)"
  342. # 转换为字符串形式
  343. where_str = " AND ".join(where_clause)
  344. if where_str:
  345. where_str = "WHERE " + where_str
  346. # 获取数据总数
  347. count_query = f"""
  348. {match_clause}
  349. {where_str}
  350. RETURN count(n) as count
  351. """
  352. count = connect_graph.run(count_query, **params).evaluate()
  353. # 获取分页数据
  354. params['skip'] = skip_count
  355. params['limit'] = page_size
  356. data_query = f"""
  357. {match_clause}
  358. {where_str}
  359. OPTIONAL MATCH (n)-[:label]->(t:data_label)
  360. WITH n, t
  361. OPTIONAL MATCH (n)-[:component]->(m:meta_node)
  362. RETURN
  363. id(n) as id,
  364. n.name as name,
  365. n.en_name as en_name,
  366. n.category as category,
  367. n.description as description,
  368. n.time as time,
  369. n.level as level,
  370. t.name as tag_name,
  371. id(t) as tag_id,
  372. count(m) as component_count
  373. ORDER BY n.time DESC
  374. SKIP $skip
  375. LIMIT $limit
  376. """
  377. result = connect_graph.run(data_query, **params).data()
  378. return result, count
  379. # 有血缘关系的数据资源列表
  380. def model_resource_list(skip_count, page_size, name_filter=None, id=None,
  381. category=None, time=None):
  382. """
  383. 获取有血缘关系的数据资源列表
  384. Args:
  385. skip_count: 跳过的记录数量
  386. page_size: 每页记录数量
  387. name_filter: 名称过滤条件
  388. id: 数据资源ID
  389. category: 分类过滤条件
  390. time: 时间过滤条件
  391. Returns:
  392. tuple: (数据列表, 总记录数)
  393. """
  394. # 构建查询条件
  395. params = {'id': id}
  396. where_clause = []
  397. if name_filter:
  398. where_clause.append("n.name =~ $name_filter")
  399. params['name_filter'] = f"(?i).*{name_filter}.*"
  400. if category:
  401. where_clause.append("n.category = $category")
  402. params['category'] = category
  403. if time:
  404. where_clause.append("n.time >= $time")
  405. params['time'] = time
  406. # 转换为字符串形式
  407. where_str = " AND ".join(where_clause)
  408. if where_str:
  409. where_str = "WHERE " + where_str
  410. # 获取数据总数
  411. count_query = f"""
  412. MATCH (search:data_resource) WHERE id(search) = $id
  413. MATCH (search)-[:connection]->(mn:meta_node)<-[:connection]-(n:data_resource)
  414. {where_str}
  415. RETURN count(DISTINCT n) as count
  416. """
  417. count = connect_graph.run(count_query, **params).evaluate()
  418. # 获取分页数据
  419. params['skip'] = skip_count
  420. params['limit'] = page_size
  421. data_query = f"""
  422. MATCH (search:data_resource) WHERE id(search) = $id
  423. MATCH (search)-[:connection]->(mn:meta_node)<-[:connection]-(n:data_resource)
  424. {where_str}
  425. WITH DISTINCT n, mn
  426. RETURN
  427. id(n) as id,
  428. n.name as name,
  429. n.en_name as en_name,
  430. n.category as category,
  431. n.description as description,
  432. n.time as time,
  433. collect({{id: id(mn), name: mn.name}}) as common_meta
  434. ORDER BY n.time DESC
  435. SKIP $skip
  436. LIMIT $limit
  437. """
  438. result = connect_graph.run(data_query, **params).data()
  439. return result, count
  440. # 数据模型血缘图谱
  441. def model_kinship_graph(nodeid, meta=False):
  442. """
  443. 获取数据模型血缘图谱
  444. Args:
  445. nodeid: 节点ID
  446. meta: 是否返回元数据
  447. Returns:
  448. 图谱数据
  449. """
  450. if meta:
  451. query = """
  452. MATCH p = (n:data_model)-[r:component|resource*..3]-(m)
  453. WHERE id(n) = $nodeId
  454. WITH p, relationships(p) as rels
  455. RETURN p
  456. limit 300
  457. """
  458. else:
  459. query = """
  460. MATCH p = (n:data_model)-[r:resource*..3]-(m)
  461. WHERE id(n) = $nodeId and labels(m) <> ['meta_node']
  462. WITH p, relationships(p) as rels
  463. RETURN p
  464. limit 300
  465. """
  466. result = connect_graph.run(query, nodeId=nodeid)
  467. nodes = set()
  468. relationships = set()
  469. nodes_by_id = {}
  470. for record in result:
  471. path = record["p"]
  472. for node in path.nodes:
  473. if node.identity not in nodes:
  474. node_id = str(node.identity)
  475. node_type = list(node.labels)[0].split('_')[1]
  476. node_data = {
  477. "id": node_id,
  478. "text": node.get("name", ""),
  479. "type": node_type
  480. }
  481. nodes.add(node.identity)
  482. nodes_by_id[node.identity] = node_data
  483. for rel in path.relationships:
  484. relationship_id = f"{rel.start_node.identity}-{rel.end_node.identity}"
  485. if relationship_id not in relationships:
  486. relationship_data = {
  487. "from": str(rel.start_node.identity),
  488. "to": str(rel.end_node.identity),
  489. "text": type(rel).__name__
  490. }
  491. relationships.add(relationship_id)
  492. # 转换为所需格式
  493. return {
  494. "nodes": list(nodes_by_id.values()),
  495. "edges": [{"from": rel.split("-")[0], "to": rel.split("-")[1], "text": ""}
  496. for rel in relationships]
  497. }
  498. # 数据模型影响图谱
  499. def model_impact_graph(nodeid, meta=False):
  500. """
  501. 获取数据模型影响图谱
  502. Args:
  503. nodeid: 节点ID
  504. meta: 是否返回元数据
  505. Returns:
  506. 图谱数据
  507. """
  508. if meta:
  509. query = """
  510. MATCH p = (n:data_model)-[r:use*..3]-(m)
  511. WHERE id(n) = $nodeId
  512. WITH p, relationships(p) as rels
  513. RETURN p
  514. limit 300
  515. """
  516. else:
  517. query = """
  518. MATCH p = (n:data_model)-[r:use*..3]-(m)
  519. WHERE id(n) = $nodeId
  520. WITH p, relationships(p) as rels
  521. RETURN p
  522. limit 300
  523. """
  524. result = connect_graph.run(query, nodeId=nodeid)
  525. nodes = set()
  526. relationships = set()
  527. nodes_by_id = {}
  528. for record in result:
  529. path = record["p"]
  530. for node in path.nodes:
  531. if node.identity not in nodes:
  532. node_id = str(node.identity)
  533. node_type = list(node.labels)[0].split('_')[1]
  534. node_data = {
  535. "id": node_id,
  536. "text": node.get("name", ""),
  537. "type": node_type
  538. }
  539. nodes.add(node.identity)
  540. nodes_by_id[node.identity] = node_data
  541. for rel in path.relationships:
  542. relationship_id = f"{rel.start_node.identity}-{rel.end_node.identity}"
  543. if relationship_id not in relationships:
  544. relationship_data = {
  545. "from": str(rel.start_node.identity),
  546. "to": str(rel.end_node.identity),
  547. "text": type(rel).__name__
  548. }
  549. relationships.add(relationship_id)
  550. # 转换为所需格式
  551. return {
  552. "nodes": list(nodes_by_id.values()),
  553. "edges": [{"from": rel.split("-")[0], "to": rel.split("-")[1], "text": ""}
  554. for rel in relationships]
  555. }
  556. # 数据模型全部图谱
  557. def model_all_graph(nodeid, meta=False):
  558. """
  559. 获取数据模型全部图谱
  560. Args:
  561. nodeid: 节点ID
  562. meta: 是否返回元数据
  563. Returns:
  564. 图谱数据
  565. """
  566. if meta:
  567. query = """
  568. MATCH p = (n:data_model)-[r*..3]-(m)
  569. WHERE id(n) = $nodeId
  570. WITH p, relationships(p) as rels
  571. RETURN p
  572. limit 300
  573. """
  574. else:
  575. query = """
  576. MATCH p = (n:data_model)-[r*..3]-(m)
  577. WHERE id(n) = $nodeId and labels(m) <> ['meta_node']
  578. WITH p, relationships(p) as rels
  579. RETURN p
  580. limit 300
  581. """
  582. result = connect_graph.run(query, nodeId=nodeid)
  583. nodes = set()
  584. relationships = set()
  585. nodes_by_id = {}
  586. for record in result:
  587. path = record["p"]
  588. for node in path.nodes:
  589. if node.identity not in nodes:
  590. node_id = str(node.identity)
  591. node_type = list(node.labels)[0].split('_')[1]
  592. node_data = {
  593. "id": node_id,
  594. "text": node.get("name", ""),
  595. "type": node_type
  596. }
  597. nodes.add(node.identity)
  598. nodes_by_id[node.identity] = node_data
  599. for rel in path.relationships:
  600. relationship_id = f"{rel.start_node.identity}-{rel.end_node.identity}"
  601. if relationship_id not in relationships:
  602. relationship_data = {
  603. "from": str(rel.start_node.identity),
  604. "to": str(rel.end_node.identity),
  605. "text": type(rel).__name__
  606. }
  607. relationships.add(relationship_id)
  608. # 转换为所需格式
  609. return {
  610. "nodes": list(nodes_by_id.values()),
  611. "edges": [{"from": rel.split("-")[0], "to": rel.split("-")[1], "text": ""}
  612. for rel in relationships]
  613. }
  614. # 更新数据模型
  615. def data_model_edit(receiver):
  616. """
  617. 更新数据模型
  618. Args:
  619. receiver: 接收到的请求参数
  620. Returns:
  621. 更新结果
  622. """
  623. id = receiver.get('id')
  624. name = receiver.get('name')
  625. en_name = receiver.get('en_name')
  626. category = receiver.get('category')
  627. description = receiver.get('description')
  628. tag = receiver.get('tag')
  629. # 更新数据模型节点
  630. query = """
  631. MATCH (n:data_model) WHERE id(n) = $id
  632. SET n.name = $name, n.en_name = $en_name, n.category = $category, n.description = $description
  633. RETURN n
  634. """
  635. result = connect_graph.run(query, id=id, name=name, en_name=en_name,
  636. category=category, description=description).data()
  637. # 处理标签关系
  638. if tag:
  639. # 先删除所有标签关系
  640. delete_query = """
  641. MATCH (n:data_model)-[r:label]->() WHERE id(n) = $id
  642. DELETE r
  643. """
  644. connect_graph.run(delete_query, id=id)
  645. # 再创建新的标签关系
  646. tag_node = get_node_by_id('data_label', tag)
  647. if tag_node:
  648. model_node = get_node_by_id_no_label(id)
  649. if model_node and not relationship_exists(model_node, 'label', tag_node):
  650. connection = Relationship(model_node, 'label', tag_node)
  651. connect_graph.create(connection)
  652. return {"message": "数据模型更新成功"}