model.py 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792
  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. match_clause = "MATCH (n:data_model)"
  322. where_clause = []
  323. if tag:
  324. match_clause = "MATCH (n:data_model)-[:label]->(t:data_label)"
  325. where_clause.append("id(t) = $tag")
  326. params['tag'] = int(tag)
  327. if name_filter:
  328. where_clause.append("n.name =~ $name_filter")
  329. params['name_filter'] = f"(?i).*{name_filter}.*"
  330. if en_name_filter:
  331. where_clause.append("n.en_name =~ $en_name_filter")
  332. params['en_name_filter'] = f"(?i).*{en_name_filter}.*"
  333. if category:
  334. where_clause.append("n.category = $category")
  335. params['category'] = category
  336. if level:
  337. where_clause.append("n.level = $level")
  338. params['level'] = level
  339. # 转换为字符串形式
  340. where_str = " AND ".join(where_clause)
  341. if where_str:
  342. where_str = "WHERE " + where_str
  343. # 获取数据总数
  344. count_query = f"""
  345. {match_clause}
  346. {where_str}
  347. RETURN count(n) as count
  348. """
  349. # 使用正确的session方式执行查询
  350. driver = connect_graph()
  351. if not driver:
  352. return [], 0
  353. with driver.session() as session:
  354. count_result = session.run(count_query, **params)
  355. count = count_result.single()["count"]
  356. # 获取分页数据
  357. params['skip'] = skip_count
  358. params['limit'] = page_size
  359. data_query = f"""
  360. {match_clause}
  361. {where_str}
  362. OPTIONAL MATCH (n)-[:label]->(t:data_label)
  363. WITH n, t
  364. OPTIONAL MATCH (n)-[:component]->(m:meta_node)
  365. RETURN
  366. id(n) as id,
  367. n.name as name,
  368. n.en_name as en_name,
  369. n.category as category,
  370. n.description as description,
  371. n.time as time,
  372. n.level as level,
  373. t.name as tag_name,
  374. id(t) as tag_id,
  375. count(m) as component_count
  376. ORDER BY n.time DESC
  377. SKIP $skip
  378. LIMIT $limit
  379. """
  380. result = session.run(data_query, **params)
  381. data = result.data()
  382. return data, count
  383. # 有血缘关系的数据资源列表
  384. def model_resource_list(skip_count, page_size, name_filter=None, id=None,
  385. category=None, time=None):
  386. """
  387. 获取有血缘关系的数据资源列表
  388. Args:
  389. skip_count: 跳过的记录数量
  390. page_size: 每页记录数量
  391. name_filter: 名称过滤条件
  392. id: 数据资源ID
  393. category: 分类过滤条件
  394. time: 时间过滤条件
  395. Returns:
  396. tuple: (数据列表, 总记录数)
  397. """
  398. # 构建查询条件
  399. params = {'id': id}
  400. where_clause = []
  401. if name_filter:
  402. where_clause.append("n.name =~ $name_filter")
  403. params['name_filter'] = f"(?i).*{name_filter}.*"
  404. if category:
  405. where_clause.append("n.category = $category")
  406. params['category'] = category
  407. if time:
  408. where_clause.append("n.time >= $time")
  409. params['time'] = time
  410. # 转换为字符串形式
  411. where_str = " AND ".join(where_clause)
  412. if where_str:
  413. where_str = "WHERE " + where_str
  414. # 获取数据总数
  415. count_query = f"""
  416. MATCH (search:data_resource) WHERE id(search) = $id
  417. MATCH (search)-[:connection]->(mn:meta_node)<-[:connection]-(n:data_resource)
  418. {where_str}
  419. RETURN count(DISTINCT n) as count
  420. """
  421. count = connect_graph.run(count_query, **params).evaluate()
  422. # 获取分页数据
  423. params['skip'] = skip_count
  424. params['limit'] = page_size
  425. data_query = f"""
  426. MATCH (search:data_resource) WHERE id(search) = $id
  427. MATCH (search)-[:connection]->(mn:meta_node)<-[:connection]-(n:data_resource)
  428. {where_str}
  429. WITH DISTINCT n, mn
  430. RETURN
  431. id(n) as id,
  432. n.name as name,
  433. n.en_name as en_name,
  434. n.category as category,
  435. n.description as description,
  436. n.time as time,
  437. collect({{id: id(mn), name: mn.name}}) as common_meta
  438. ORDER BY n.time DESC
  439. SKIP $skip
  440. LIMIT $limit
  441. """
  442. result = connect_graph.run(data_query, **params).data()
  443. return result, count
  444. # 数据模型血缘图谱
  445. def model_kinship_graph(nodeid, meta=False):
  446. """
  447. 获取数据模型血缘图谱
  448. Args:
  449. nodeid: 节点ID
  450. meta: 是否返回元数据
  451. Returns:
  452. 图谱数据
  453. """
  454. if meta:
  455. query = """
  456. MATCH p = (n:data_model)-[r:component|resource*..3]-(m)
  457. WHERE id(n) = $nodeId
  458. WITH p, relationships(p) as rels
  459. RETURN p
  460. limit 300
  461. """
  462. else:
  463. query = """
  464. MATCH p = (n:data_model)-[r:resource*..3]-(m)
  465. WHERE id(n) = $nodeId and labels(m) <> ['meta_node']
  466. WITH p, relationships(p) as rels
  467. RETURN p
  468. limit 300
  469. """
  470. result = connect_graph.run(query, nodeId=nodeid)
  471. nodes = set()
  472. relationships = set()
  473. nodes_by_id = {}
  474. for record in result:
  475. path = record["p"]
  476. for node in path.nodes:
  477. if node.identity not in nodes:
  478. node_id = str(node.identity)
  479. node_type = list(node.labels)[0].split('_')[1]
  480. node_data = {
  481. "id": node_id,
  482. "text": node.get("name", ""),
  483. "type": node_type
  484. }
  485. nodes.add(node.identity)
  486. nodes_by_id[node.identity] = node_data
  487. for rel in path.relationships:
  488. relationship_id = f"{rel.start_node.identity}-{rel.end_node.identity}"
  489. if relationship_id not in relationships:
  490. relationship_data = {
  491. "from": str(rel.start_node.identity),
  492. "to": str(rel.end_node.identity),
  493. "text": type(rel).__name__
  494. }
  495. relationships.add(relationship_id)
  496. # 转换为所需格式
  497. return {
  498. "nodes": list(nodes_by_id.values()),
  499. "edges": [{"from": rel.split("-")[0], "to": rel.split("-")[1], "text": ""}
  500. for rel in relationships]
  501. }
  502. # 数据模型影响图谱
  503. def model_impact_graph(nodeid, meta=False):
  504. """
  505. 获取数据模型影响图谱
  506. Args:
  507. nodeid: 节点ID
  508. meta: 是否返回元数据
  509. Returns:
  510. 图谱数据
  511. """
  512. if meta:
  513. query = """
  514. MATCH p = (n:data_model)-[r:use*..3]-(m)
  515. WHERE id(n) = $nodeId
  516. WITH p, relationships(p) as rels
  517. RETURN p
  518. limit 300
  519. """
  520. else:
  521. query = """
  522. MATCH p = (n:data_model)-[r:use*..3]-(m)
  523. WHERE id(n) = $nodeId
  524. WITH p, relationships(p) as rels
  525. RETURN p
  526. limit 300
  527. """
  528. result = connect_graph.run(query, nodeId=nodeid)
  529. nodes = set()
  530. relationships = set()
  531. nodes_by_id = {}
  532. for record in result:
  533. path = record["p"]
  534. for node in path.nodes:
  535. if node.identity not in nodes:
  536. node_id = str(node.identity)
  537. node_type = list(node.labels)[0].split('_')[1]
  538. node_data = {
  539. "id": node_id,
  540. "text": node.get("name", ""),
  541. "type": node_type
  542. }
  543. nodes.add(node.identity)
  544. nodes_by_id[node.identity] = node_data
  545. for rel in path.relationships:
  546. relationship_id = f"{rel.start_node.identity}-{rel.end_node.identity}"
  547. if relationship_id not in relationships:
  548. relationship_data = {
  549. "from": str(rel.start_node.identity),
  550. "to": str(rel.end_node.identity),
  551. "text": type(rel).__name__
  552. }
  553. relationships.add(relationship_id)
  554. # 转换为所需格式
  555. return {
  556. "nodes": list(nodes_by_id.values()),
  557. "edges": [{"from": rel.split("-")[0], "to": rel.split("-")[1], "text": ""}
  558. for rel in relationships]
  559. }
  560. # 数据模型全部图谱
  561. def model_all_graph(nodeid, meta=False):
  562. """
  563. 获取数据模型全部图谱
  564. Args:
  565. nodeid: 节点ID
  566. meta: 是否返回元数据
  567. Returns:
  568. 图谱数据
  569. """
  570. if meta:
  571. query = """
  572. MATCH p = (n:data_model)-[r*..3]-(m)
  573. WHERE id(n) = $nodeId
  574. WITH p, relationships(p) as rels
  575. RETURN p
  576. limit 300
  577. """
  578. else:
  579. query = """
  580. MATCH p = (n:data_model)-[r*..3]-(m)
  581. WHERE id(n) = $nodeId and labels(m) <> ['meta_node']
  582. WITH p, relationships(p) as rels
  583. RETURN p
  584. limit 300
  585. """
  586. result = connect_graph.run(query, nodeId=nodeid)
  587. nodes = set()
  588. relationships = set()
  589. nodes_by_id = {}
  590. for record in result:
  591. path = record["p"]
  592. for node in path.nodes:
  593. if node.identity not in nodes:
  594. node_id = str(node.identity)
  595. node_type = list(node.labels)[0].split('_')[1]
  596. node_data = {
  597. "id": node_id,
  598. "text": node.get("name", ""),
  599. "type": node_type
  600. }
  601. nodes.add(node.identity)
  602. nodes_by_id[node.identity] = node_data
  603. for rel in path.relationships:
  604. relationship_id = f"{rel.start_node.identity}-{rel.end_node.identity}"
  605. if relationship_id not in relationships:
  606. relationship_data = {
  607. "from": str(rel.start_node.identity),
  608. "to": str(rel.end_node.identity),
  609. "text": type(rel).__name__
  610. }
  611. relationships.add(relationship_id)
  612. # 转换为所需格式
  613. return {
  614. "nodes": list(nodes_by_id.values()),
  615. "edges": [{"from": rel.split("-")[0], "to": rel.split("-")[1], "text": ""}
  616. for rel in relationships]
  617. }
  618. # 更新数据模型
  619. def data_model_edit(receiver):
  620. """
  621. 更新数据模型
  622. Args:
  623. receiver: 接收到的请求参数
  624. Returns:
  625. 更新结果
  626. """
  627. id = receiver.get('id')
  628. name = receiver.get('name')
  629. en_name = receiver.get('en_name')
  630. category = receiver.get('category')
  631. description = receiver.get('description')
  632. tag = receiver.get('tag')
  633. # 更新数据模型节点
  634. query = """
  635. MATCH (n:data_model) WHERE id(n) = $id
  636. SET n.name = $name, n.en_name = $en_name, n.category = $category, n.description = $description
  637. RETURN n
  638. """
  639. result = connect_graph.run(query, id=id, name=name, en_name=en_name,
  640. category=category, description=description).data()
  641. # 处理标签关系
  642. if tag:
  643. # 先删除所有标签关系
  644. delete_query = """
  645. MATCH (n:data_model)-[r:label]->() WHERE id(n) = $id
  646. DELETE r
  647. """
  648. connect_graph.run(delete_query, id=id)
  649. # 再创建新的标签关系
  650. tag_node = get_node_by_id('data_label', tag)
  651. if tag_node:
  652. model_node = get_node_by_id_no_label(id)
  653. if model_node and not relationship_exists(model_node, 'label', tag_node):
  654. connection = Relationship(model_node, 'label', tag_node)
  655. connect_graph.create(connection)
  656. return {"message": "数据模型更新成功"}