yangdx commited on
Commit
731e6b1
·
1 Parent(s): d60bf1c

Fix linting

Browse files
lightrag/kg/postgres_impl.py CHANGED
@@ -1037,7 +1037,10 @@ class PGGraphStorage(BaseGraphStorage):
1037
  await self.upsert_node(node1_id, node1_data)
1038
  await self.delete_node(node1_id)
1039
 
1040
- query = """CREATE INDEX entity_id_gin_idxSELECT ON %s."base" USING gin (properties);""" % (self.graph_name)
 
 
 
1041
 
1042
  await self.db.execute(
1043
  query,
@@ -1563,15 +1566,15 @@ class PGGraphStorage(BaseGraphStorage):
1563
 
1564
  out_degrees = {}
1565
  in_degrees = {}
1566
-
1567
  for result in outgoing_results:
1568
  if result["node_id"] is not None:
1569
  out_degrees[result["node_id"]] = int(result["out_degree"])
1570
-
1571
  for result in incoming_results:
1572
  if result["node_id"] is not None:
1573
  in_degrees[result["node_id"]] = int(result["in_degree"])
1574
-
1575
  degrees_dict = {}
1576
  for node_id in node_ids:
1577
  out_degree = out_degrees.get(node_id, 0)
@@ -1659,11 +1662,15 @@ class PGGraphStorage(BaseGraphStorage):
1659
 
1660
  for result in forward_results:
1661
  if result["source"] and result["target"] and result["edge_properties"]:
1662
- edges_dict[(result["source"], result["target"])] = result["edge_properties"]
1663
-
 
 
1664
  for result in backward_results:
1665
  if result["source"] and result["target"] and result["edge_properties"]:
1666
- edges_dict[(result["source"], result["target"])] = result["edge_properties"]
 
 
1667
 
1668
  return edges_dict
1669
 
@@ -1711,13 +1718,13 @@ class PGGraphStorage(BaseGraphStorage):
1711
  incoming_results = await self._query(incoming_query)
1712
 
1713
  nodes_edges_dict = {node_id: [] for node_id in node_ids}
1714
-
1715
  for result in outgoing_results:
1716
  if result["node_id"] and result["connected_id"]:
1717
  nodes_edges_dict[result["node_id"]].append(
1718
  (result["node_id"], result["connected_id"])
1719
  )
1720
-
1721
  for result in incoming_results:
1722
  if result["node_id"] and result["connected_id"]:
1723
  nodes_edges_dict[result["node_id"]].append(
 
1037
  await self.upsert_node(node1_id, node1_data)
1038
  await self.delete_node(node1_id)
1039
 
1040
+ query = (
1041
+ """CREATE INDEX entity_id_gin_idxSELECT ON %s."base" USING gin (properties);"""
1042
+ % (self.graph_name)
1043
+ )
1044
 
1045
  await self.db.execute(
1046
  query,
 
1566
 
1567
  out_degrees = {}
1568
  in_degrees = {}
1569
+
1570
  for result in outgoing_results:
1571
  if result["node_id"] is not None:
1572
  out_degrees[result["node_id"]] = int(result["out_degree"])
1573
+
1574
  for result in incoming_results:
1575
  if result["node_id"] is not None:
1576
  in_degrees[result["node_id"]] = int(result["in_degree"])
1577
+
1578
  degrees_dict = {}
1579
  for node_id in node_ids:
1580
  out_degree = out_degrees.get(node_id, 0)
 
1662
 
1663
  for result in forward_results:
1664
  if result["source"] and result["target"] and result["edge_properties"]:
1665
+ edges_dict[(result["source"], result["target"])] = result[
1666
+ "edge_properties"
1667
+ ]
1668
+
1669
  for result in backward_results:
1670
  if result["source"] and result["target"] and result["edge_properties"]:
1671
+ edges_dict[(result["source"], result["target"])] = result[
1672
+ "edge_properties"
1673
+ ]
1674
 
1675
  return edges_dict
1676
 
 
1718
  incoming_results = await self._query(incoming_query)
1719
 
1720
  nodes_edges_dict = {node_id: [] for node_id in node_ids}
1721
+
1722
  for result in outgoing_results:
1723
  if result["node_id"] and result["connected_id"]:
1724
  nodes_edges_dict[result["node_id"]].append(
1725
  (result["node_id"], result["connected_id"])
1726
  )
1727
+
1728
  for result in incoming_results:
1729
  if result["node_id"] and result["connected_id"]:
1730
  nodes_edges_dict[result["node_id"]].append(
tests/test_graph_storage.py CHANGED
@@ -209,7 +209,7 @@ async def test_graph_basic(storage):
209
  else:
210
  print(f"读取边属性失败: {node1_id} -> {node2_id}")
211
  assert False, f"未能读取边属性: {node1_id} -> {node2_id}"
212
-
213
  # 5.1 验证无向图特性 - 读取反向边属性
214
  print(f"读取反向边属性: {node2_id} -> {node1_id}")
215
  reverse_edge_props = await storage.get_edge(node2_id, node1_id)
@@ -219,11 +219,15 @@ async def test_graph_basic(storage):
219
  print(f"反向边描述: {reverse_edge_props.get('description', '无描述')}")
220
  print(f"反向边权重: {reverse_edge_props.get('weight', '无权重')}")
221
  # 验证正向和反向边属性是否相同
222
- assert edge_props == reverse_edge_props, "正向和反向边属性不一致,无向图特性验证失败"
 
 
223
  print("无向图特性验证成功:正向和反向边属性一致")
224
  else:
225
  print(f"读取反向边属性失败: {node2_id} -> {node1_id}")
226
- assert False, f"未能读取反向边属性: {node2_id} -> {node1_id},无向图特性验证失败"
 
 
227
 
228
  print("基本测试完成,数据已保留在数据库中")
229
  return True
@@ -308,7 +312,7 @@ async def test_graph_advanced(storage):
308
  node1_degree = await storage.node_degree(node1_id)
309
  print(f"节点 {node1_id} 的度数: {node1_degree}")
310
  assert node1_degree == 1, f"节点 {node1_id} 的度数应为1,实际为 {node1_degree}"
311
-
312
  # 2.1 测试所有节点的度数
313
  print("== 测试所有节点的度数")
314
  node2_degree = await storage.node_degree(node2_id)
@@ -325,12 +329,14 @@ async def test_graph_advanced(storage):
325
  assert (
326
  edge_degree == 3
327
  ), f"边 {node1_id} -> {node2_id} 的度数应为3,实际为 {edge_degree}"
328
-
329
  # 3.1 测试反向边的度数 - 验证无向图特性
330
  print(f"== 测试反向边的度数: {node2_id} -> {node1_id}")
331
  reverse_edge_degree = await storage.edge_degree(node2_id, node1_id)
332
  print(f"反向边 {node2_id} -> {node1_id} 的度数: {reverse_edge_degree}")
333
- assert edge_degree == reverse_edge_degree, f"正向边和反向边的度数不一致,无向图特性验证失败"
 
 
334
  print("无向图特性验证成功:正向边和反向边的度数一致")
335
 
336
  # 4. 测试 get_node_edges - 获取节点的所有边
@@ -340,7 +346,7 @@ async def test_graph_advanced(storage):
340
  assert (
341
  len(node2_edges) == 2
342
  ), f"节点 {node2_id} 应有2条边,实际有 {len(node2_edges)}"
343
-
344
  # 4.1 验证节点边的无向图特性
345
  print("== 验证节点边的无向图特性")
346
  # 检查是否包含与node1和node3的连接关系(无论方向)
@@ -348,14 +354,22 @@ async def test_graph_advanced(storage):
348
  has_connection_with_node3 = False
349
  for edge in node2_edges:
350
  # 检查是否有与node1的连接(无论方向)
351
- if (edge[0] == node1_id and edge[1] == node2_id) or (edge[0] == node2_id and edge[1] == node1_id):
 
 
352
  has_connection_with_node1 = True
353
  # 检查是否有与node3的连接(无论方向)
354
- if (edge[0] == node2_id and edge[1] == node3_id) or (edge[0] == node3_id and edge[1] == node2_id):
 
 
355
  has_connection_with_node3 = True
356
-
357
- assert has_connection_with_node1, f"节点 {node2_id} 的边列表中应包含与 {node1_id} 的连接"
358
- assert has_connection_with_node3, f"节点 {node2_id} 的边列表中应包含与 {node3_id} 的连接"
 
 
 
 
359
  print(f"无向图特性验证成功:节点 {node2_id} 的边列表包含所有相关的边")
360
 
361
  # 5. 测试 get_all_labels - 获取所有标签
@@ -393,12 +407,14 @@ async def test_graph_advanced(storage):
393
  edge_props = await storage.get_edge(node2_id, node3_id)
394
  print(f"删除后查询边属性 {node2_id} -> {node3_id}: {edge_props}")
395
  assert edge_props is None, f"�� {node2_id} -> {node3_id} 应已被删除"
396
-
397
  # 8.1 验证删除边的无向图特性
398
  print(f"== 验证删除边的无向图特性: {node3_id} -> {node2_id}")
399
  reverse_edge_props = await storage.get_edge(node3_id, node2_id)
400
  print(f"删除后查询反向边属性 {node3_id} -> {node2_id}: {reverse_edge_props}")
401
- assert reverse_edge_props is None, f"反向边 {node3_id} -> {node2_id} 也应被删除,无向图特性验证失败"
 
 
402
  print("无向图特性验证成功:删除一个方向的边后,反向边也被删除")
403
 
404
  # 9. 测试 remove_nodes - 批量删除节点
@@ -657,20 +673,27 @@ async def test_graph_batch_operations(storage):
657
  edges_dict[(node3_id, node4_id)]["relationship"]
658
  == edge5_data["relationship"]
659
  ), f"边 {node3_id} -> {node4_id} 关系不匹配"
660
-
661
  # 5.1 测试反向边的批量获取 - 验证无向图特性
662
  print("== 测试反向边的批量获取")
663
  # 创建反向边的字典列表
664
  reverse_edge_dicts = [{"src": tgt, "tgt": src} for src, tgt in edges]
665
  reverse_edges_dict = await storage.get_edges_batch(reverse_edge_dicts)
666
  print(f"批量获取反向边属性结果: {reverse_edges_dict.keys()}")
667
- assert len(reverse_edges_dict) == 3, f"应返回3条反向边的属性,实际返回 {len(reverse_edges_dict)} 条"
668
-
 
 
669
  # 验证正向和反向边的属性是否一致
670
  for (src, tgt), props in edges_dict.items():
671
- assert (tgt, src) in reverse_edges_dict, f"反向边 {tgt} -> {src} 应在返回结果中"
672
- assert props == reverse_edges_dict[(tgt, src)], f"边 {src} -> {tgt} 和反向边 {tgt} -> {src} 的属性不一致"
673
-
 
 
 
 
 
674
  print("无向图特性验证成功:批量获取的正向和反向边属性一致")
675
 
676
  # 6. 测试 get_nodes_edges_batch - 批量获取多个节点的所有边
@@ -688,40 +711,50 @@ async def test_graph_batch_operations(storage):
688
  assert (
689
  len(nodes_edges[node3_id]) == 3
690
  ), f"{node3_id} 应有3条边,实际有 {len(nodes_edges[node3_id])} 条"
691
-
692
  # 6.1 验证批量获取节点边的无向图特性
693
  print("== 验证批量获取节点边的无向图特性")
694
-
695
  # 检查节点1的边是否包含所有相关的边(无论方向)
696
- node1_outgoing_edges = [(src, tgt) for src, tgt in nodes_edges[node1_id] if src == node1_id]
697
- node1_incoming_edges = [(src, tgt) for src, tgt in nodes_edges[node1_id] if tgt == node1_id]
 
 
 
 
698
  print(f"节点 {node1_id} 的出边: {node1_outgoing_edges}")
699
  print(f"节点 {node1_id} 的入边: {node1_incoming_edges}")
700
-
701
  # 检查是否包含到机器学习、自然语言处理和计算机视觉的边
702
  has_edge_to_node2 = any(tgt == node2_id for _, tgt in node1_outgoing_edges)
703
  has_edge_to_node4 = any(tgt == node4_id for _, tgt in node1_outgoing_edges)
704
  has_edge_to_node5 = any(tgt == node5_id for _, tgt in node1_outgoing_edges)
705
-
706
  assert has_edge_to_node2, f"节点 {node1_id} 的边列表中应包含到 {node2_id} 的边"
707
  assert has_edge_to_node4, f"节点 {node1_id} 的边列表中应包含到 {node4_id} 的边"
708
  assert has_edge_to_node5, f"节点 {node1_id} 的边列表中应包含到 {node5_id} 的边"
709
-
710
  # 检查节点3的边是否包含所有相关的边(无论方向)
711
- node3_outgoing_edges = [(src, tgt) for src, tgt in nodes_edges[node3_id] if src == node3_id]
712
- node3_incoming_edges = [(src, tgt) for src, tgt in nodes_edges[node3_id] if tgt == node3_id]
 
 
 
 
713
  print(f"节点 {node3_id} 的出边: {node3_outgoing_edges}")
714
  print(f"节点 {node3_id} 的入边: {node3_incoming_edges}")
715
-
716
  # 检查是否包含从机器学习来的边,以及到自然语言处理和计算机视觉的��
717
  has_edge_from_node2 = any(src == node2_id for src, _ in node3_incoming_edges)
718
  has_edge_to_node4 = any(tgt == node4_id for _, tgt in node3_outgoing_edges)
719
  has_edge_to_node5 = any(tgt == node5_id for _, tgt in node3_outgoing_edges)
720
-
721
- assert has_edge_from_node2, f"节点 {node3_id} 的边列表中应包含从 {node2_id} 来的边"
 
 
722
  assert has_edge_to_node4, f"节点 {node3_id} 的边列表中应包含到 {node4_id} 的边"
723
  assert has_edge_to_node5, f"节点 {node3_id} 的边列表中应包含到 {node5_id} 的边"
724
-
725
  print("无向图特性验证成功:批量获取的节点边包含所有相关的边(无论方向)")
726
 
727
  # 7. 清理数据
@@ -789,7 +822,7 @@ async def test_graph_undirected_property(storage):
789
 
790
  # 2. 测试插入边后的无向图特性
791
  print("\n== 测试插入边后的无向图特性")
792
-
793
  # 插入边1: 计算机科学 -> 数据结构
794
  edge1_data = {
795
  "relationship": "包含",
@@ -798,24 +831,26 @@ async def test_graph_undirected_property(storage):
798
  }
799
  print(f"插入边1: {node1_id} -> {node2_id}")
800
  await storage.upsert_edge(node1_id, node2_id, edge1_data)
801
-
802
  # 验证正向查询
803
  forward_edge = await storage.get_edge(node1_id, node2_id)
804
  print(f"正向边属性: {forward_edge}")
805
  assert forward_edge is not None, f"未能读取正向边属性: {node1_id} -> {node2_id}"
806
-
807
  # 验证反向查询
808
  reverse_edge = await storage.get_edge(node2_id, node1_id)
809
  print(f"反向边属性: {reverse_edge}")
810
  assert reverse_edge is not None, f"未能读取反向边属性: {node2_id} -> {node1_id}"
811
-
812
  # 验证正向和反向边属性是否一致
813
- assert forward_edge == reverse_edge, "正向和反向边属性不一致,无向图特性验证失败"
 
 
814
  print("无向图特性验证成功:正向和反向边属性一致")
815
-
816
  # 3. 测试边的度数的无向图特性
817
  print("\n== 测试边的度数的无向图特性")
818
-
819
  # 插入边2: 计算机科学 -> 算法
820
  edge2_data = {
821
  "relationship": "包含",
@@ -824,77 +859,102 @@ async def test_graph_undirected_property(storage):
824
  }
825
  print(f"插入边2: {node1_id} -> {node3_id}")
826
  await storage.upsert_edge(node1_id, node3_id, edge2_data)
827
-
828
  # 验证正向和反向边的度数
829
  forward_degree = await storage.edge_degree(node1_id, node2_id)
830
  reverse_degree = await storage.edge_degree(node2_id, node1_id)
831
  print(f"正向边 {node1_id} -> {node2_id} 的度数: {forward_degree}")
832
  print(f"反向边 {node2_id} -> {node1_id} 的度数: {reverse_degree}")
833
- assert forward_degree == reverse_degree, "正向和反向边的度数不一致,无向图特性验证失败"
 
 
834
  print("无向图特性验证成功:正向和反向边的度数一致")
835
-
836
  # 4. 测试删除边的无向图特性
837
  print("\n== 测试删除边的无向图特性")
838
-
839
  # 删除正向边
840
  print(f"删除边: {node1_id} -> {node2_id}")
841
  await storage.remove_edges([(node1_id, node2_id)])
842
-
843
  # 验证正向边是否被删除
844
  forward_edge = await storage.get_edge(node1_id, node2_id)
845
  print(f"删除后查询正向边属性 {node1_id} -> {node2_id}: {forward_edge}")
846
  assert forward_edge is None, f"边 {node1_id} -> {node2_id} 应已被删除"
847
-
848
  # 验证反向边是否也被删除
849
  reverse_edge = await storage.get_edge(node2_id, node1_id)
850
  print(f"删除后查询反向边属性 {node2_id} -> {node1_id}: {reverse_edge}")
851
- assert reverse_edge is None, f"反向边 {node2_id} -> {node1_id} 也应被删除,无向图特性验证失败"
 
 
852
  print("无向图特性验证成功:删除一个方向的边后,反向边也被删除")
853
-
854
  # 5. 测试批量操作中的无向图特性
855
  print("\n== 测试批量操作中的无向图特性")
856
-
857
  # 重新插入边
858
  await storage.upsert_edge(node1_id, node2_id, edge1_data)
859
-
860
  # 批量获取边属性
861
- edge_dicts = [{"src": node1_id, "tgt": node2_id}, {"src": node1_id, "tgt": node3_id}]
862
- reverse_edge_dicts = [{"src": node2_id, "tgt": node1_id}, {"src": node3_id, "tgt": node1_id}]
863
-
 
 
 
 
 
 
864
  edges_dict = await storage.get_edges_batch(edge_dicts)
865
  reverse_edges_dict = await storage.get_edges_batch(reverse_edge_dicts)
866
-
867
  print(f"批量获取正向边属性结果: {edges_dict.keys()}")
868
  print(f"批量获取反向边属性结果: {reverse_edges_dict.keys()}")
869
-
870
  # 验证正向和反向边的属性是否一致
871
  for (src, tgt), props in edges_dict.items():
872
- assert (tgt, src) in reverse_edges_dict, f"反向边 {tgt} -> {src} 应在返回结果中"
873
- assert props == reverse_edges_dict[(tgt, src)], f"边 {src} -> {tgt} 和反向边 {tgt} -> {src} 的属性不一致"
874
-
 
 
 
 
 
875
  print("无向图特性验证成功:批量获取的正向和反向边属性一致")
876
-
877
  # 6. 测试批量获取节点边的无向图特性
878
  print("\n== 测试批量获取节点边的无向图特性")
879
-
880
  nodes_edges = await storage.get_nodes_edges_batch([node1_id, node2_id])
881
  print(f"批量获取节点边结果: {nodes_edges.keys()}")
882
-
883
  # 检查节点1的边是否包含所有相关的边(无论方向)
884
  node1_edges = nodes_edges[node1_id]
885
  node2_edges = nodes_edges[node2_id]
886
-
887
  # 检查节点1是否有到节点2和节点3的边
888
- has_edge_to_node2 = any((src == node1_id and tgt == node2_id) for src, tgt in node1_edges)
889
- has_edge_to_node3 = any((src == node1_id and tgt == node3_id) for src, tgt in node1_edges)
890
-
 
 
 
 
891
  assert has_edge_to_node2, f"节点 {node1_id} 的边列表中应包含到 {node2_id} 的边"
892
  assert has_edge_to_node3, f"节点 {node1_id} 的边列表中应包含到 {node3_id} 的边"
893
-
894
  # 检查节点2是否有到节点1的边
895
- has_edge_to_node1 = any((src == node2_id and tgt == node1_id) or (src == node1_id and tgt == node2_id) for src, tgt in node2_edges)
896
- assert has_edge_to_node1, f"节点 {node2_id} 的边列表中应包含与 {node1_id} 的连接"
897
-
 
 
 
 
 
 
898
  print("无向图特性验证成功:批量获取的节点边包含所有相关的边(无论方向)")
899
 
900
  # 7. 清理数据
@@ -972,7 +1032,7 @@ async def main():
972
  if advanced_result:
973
  ASCIIColors.cyan("\n=== 开始批量操作测试 ===")
974
  batch_result = await test_graph_batch_operations(storage)
975
-
976
  if batch_result:
977
  ASCIIColors.cyan("\n=== 开始无向图特性测试 ===")
978
  await test_graph_undirected_property(storage)
 
209
  else:
210
  print(f"读取边属性失败: {node1_id} -> {node2_id}")
211
  assert False, f"未能读取边属性: {node1_id} -> {node2_id}"
212
+
213
  # 5.1 验证无向图特性 - 读取反向边属性
214
  print(f"读取反向边属性: {node2_id} -> {node1_id}")
215
  reverse_edge_props = await storage.get_edge(node2_id, node1_id)
 
219
  print(f"反向边描述: {reverse_edge_props.get('description', '无描述')}")
220
  print(f"反向边权重: {reverse_edge_props.get('weight', '无权重')}")
221
  # 验证正向和反向边属性是否相同
222
+ assert (
223
+ edge_props == reverse_edge_props
224
+ ), "正向和反向边属性不一致,无向图特性验证失败"
225
  print("无向图特性验证成功:正向和反向边属性一致")
226
  else:
227
  print(f"读取反向边属性失败: {node2_id} -> {node1_id}")
228
+ assert (
229
+ False
230
+ ), f"未能读取反向边属性: {node2_id} -> {node1_id},无向图特性验证失败"
231
 
232
  print("基本测试完成,数据已保留在数据库中")
233
  return True
 
312
  node1_degree = await storage.node_degree(node1_id)
313
  print(f"节点 {node1_id} 的度数: {node1_degree}")
314
  assert node1_degree == 1, f"节点 {node1_id} 的度数应为1,实际为 {node1_degree}"
315
+
316
  # 2.1 测试所有节点的度数
317
  print("== 测试所有节点的度数")
318
  node2_degree = await storage.node_degree(node2_id)
 
329
  assert (
330
  edge_degree == 3
331
  ), f"边 {node1_id} -> {node2_id} 的度数应为3,实际为 {edge_degree}"
332
+
333
  # 3.1 测试反向边的度数 - 验证无向图特性
334
  print(f"== 测试反向边的度数: {node2_id} -> {node1_id}")
335
  reverse_edge_degree = await storage.edge_degree(node2_id, node1_id)
336
  print(f"反向边 {node2_id} -> {node1_id} 的度数: {reverse_edge_degree}")
337
+ assert (
338
+ edge_degree == reverse_edge_degree
339
+ ), "正向边和反向边的度数不一致,无向图特性验证失败"
340
  print("无向图特性验证成功:正向边和反向边的度数一致")
341
 
342
  # 4. 测试 get_node_edges - 获取节点的所有边
 
346
  assert (
347
  len(node2_edges) == 2
348
  ), f"节点 {node2_id} 应有2条边,实际有 {len(node2_edges)}"
349
+
350
  # 4.1 验证节点边的无向图特性
351
  print("== 验证节点边的无向图特性")
352
  # 检查是否包含与node1和node3的连接关系(无论方向)
 
354
  has_connection_with_node3 = False
355
  for edge in node2_edges:
356
  # 检查是否有与node1的连接(无论方向)
357
+ if (edge[0] == node1_id and edge[1] == node2_id) or (
358
+ edge[0] == node2_id and edge[1] == node1_id
359
+ ):
360
  has_connection_with_node1 = True
361
  # 检查是否有与node3的连接(无论方向)
362
+ if (edge[0] == node2_id and edge[1] == node3_id) or (
363
+ edge[0] == node3_id and edge[1] == node2_id
364
+ ):
365
  has_connection_with_node3 = True
366
+
367
+ assert (
368
+ has_connection_with_node1
369
+ ), f"节点 {node2_id} 的边列表中应包含与 {node1_id} 的连接"
370
+ assert (
371
+ has_connection_with_node3
372
+ ), f"节点 {node2_id} 的边列表中应包含与 {node3_id} 的连接"
373
  print(f"无向图特性验证成功:节点 {node2_id} 的边列表包含所有相关的边")
374
 
375
  # 5. 测试 get_all_labels - 获取所有标签
 
407
  edge_props = await storage.get_edge(node2_id, node3_id)
408
  print(f"删除后查询边属性 {node2_id} -> {node3_id}: {edge_props}")
409
  assert edge_props is None, f"�� {node2_id} -> {node3_id} 应已被删除"
410
+
411
  # 8.1 验证删除边的无向图特性
412
  print(f"== 验证删除边的无向图特性: {node3_id} -> {node2_id}")
413
  reverse_edge_props = await storage.get_edge(node3_id, node2_id)
414
  print(f"删除后查询反向边属性 {node3_id} -> {node2_id}: {reverse_edge_props}")
415
+ assert (
416
+ reverse_edge_props is None
417
+ ), f"反向边 {node3_id} -> {node2_id} 也应被删除,无向图特性验证失败"
418
  print("无向图特性验证成功:删除一个方向的边后,反向边也被删除")
419
 
420
  # 9. 测试 remove_nodes - 批量删除节点
 
673
  edges_dict[(node3_id, node4_id)]["relationship"]
674
  == edge5_data["relationship"]
675
  ), f"边 {node3_id} -> {node4_id} 关系不匹配"
676
+
677
  # 5.1 测试反向边的批量获取 - 验证无向图特性
678
  print("== 测试反向边的批量获取")
679
  # 创建反向边的字典列表
680
  reverse_edge_dicts = [{"src": tgt, "tgt": src} for src, tgt in edges]
681
  reverse_edges_dict = await storage.get_edges_batch(reverse_edge_dicts)
682
  print(f"批量获取反向边属性结果: {reverse_edges_dict.keys()}")
683
+ assert (
684
+ len(reverse_edges_dict) == 3
685
+ ), f"应返回3条反向边的属性,实际返回 {len(reverse_edges_dict)} 条"
686
+
687
  # 验证正向和反向边的属性是否一致
688
  for (src, tgt), props in edges_dict.items():
689
+ assert (
690
+ tgt,
691
+ src,
692
+ ) in reverse_edges_dict, f"反向边 {tgt} -> {src} 应在返回结果中"
693
+ assert (
694
+ props == reverse_edges_dict[(tgt, src)]
695
+ ), f"边 {src} -> {tgt} 和反向边 {tgt} -> {src} 的属性不一致"
696
+
697
  print("无向图特性验证成功:批量获取的正向和反向边属性一致")
698
 
699
  # 6. 测试 get_nodes_edges_batch - 批量获取多个节点的所有边
 
711
  assert (
712
  len(nodes_edges[node3_id]) == 3
713
  ), f"{node3_id} 应有3条边,实际有 {len(nodes_edges[node3_id])} 条"
714
+
715
  # 6.1 验证批量获取节点边的无向图特性
716
  print("== 验证批量获取节点边的无向图特性")
717
+
718
  # 检查节点1的边是否包含所有相关的边(无论方向)
719
+ node1_outgoing_edges = [
720
+ (src, tgt) for src, tgt in nodes_edges[node1_id] if src == node1_id
721
+ ]
722
+ node1_incoming_edges = [
723
+ (src, tgt) for src, tgt in nodes_edges[node1_id] if tgt == node1_id
724
+ ]
725
  print(f"节点 {node1_id} 的出边: {node1_outgoing_edges}")
726
  print(f"节点 {node1_id} 的入边: {node1_incoming_edges}")
727
+
728
  # 检查是否包含到机器学习、自然语言处理和计算机视觉的边
729
  has_edge_to_node2 = any(tgt == node2_id for _, tgt in node1_outgoing_edges)
730
  has_edge_to_node4 = any(tgt == node4_id for _, tgt in node1_outgoing_edges)
731
  has_edge_to_node5 = any(tgt == node5_id for _, tgt in node1_outgoing_edges)
732
+
733
  assert has_edge_to_node2, f"节点 {node1_id} 的边列表中应包含到 {node2_id} 的边"
734
  assert has_edge_to_node4, f"节点 {node1_id} 的边列表中应包含到 {node4_id} 的边"
735
  assert has_edge_to_node5, f"节点 {node1_id} 的边列表中应包含到 {node5_id} 的边"
736
+
737
  # 检查节点3的边是否包含所有相关的边(无论方向)
738
+ node3_outgoing_edges = [
739
+ (src, tgt) for src, tgt in nodes_edges[node3_id] if src == node3_id
740
+ ]
741
+ node3_incoming_edges = [
742
+ (src, tgt) for src, tgt in nodes_edges[node3_id] if tgt == node3_id
743
+ ]
744
  print(f"节点 {node3_id} 的出边: {node3_outgoing_edges}")
745
  print(f"节点 {node3_id} 的入边: {node3_incoming_edges}")
746
+
747
  # 检查是否包含从机器学习来的边,以及到自然语言处理和计算机视觉的��
748
  has_edge_from_node2 = any(src == node2_id for src, _ in node3_incoming_edges)
749
  has_edge_to_node4 = any(tgt == node4_id for _, tgt in node3_outgoing_edges)
750
  has_edge_to_node5 = any(tgt == node5_id for _, tgt in node3_outgoing_edges)
751
+
752
+ assert (
753
+ has_edge_from_node2
754
+ ), f"节点 {node3_id} 的边列表中应包含从 {node2_id} 来的边"
755
  assert has_edge_to_node4, f"节点 {node3_id} 的边列表中应包含到 {node4_id} 的边"
756
  assert has_edge_to_node5, f"节点 {node3_id} 的边列表中应包含到 {node5_id} 的边"
757
+
758
  print("无向图特性验证成功:批量获取的节点边包含所有相关的边(无论方向)")
759
 
760
  # 7. 清理数据
 
822
 
823
  # 2. 测试插入边后的无向图特性
824
  print("\n== 测试插入边后的无向图特性")
825
+
826
  # 插入边1: 计算机科学 -> 数据结构
827
  edge1_data = {
828
  "relationship": "包含",
 
831
  }
832
  print(f"插入边1: {node1_id} -> {node2_id}")
833
  await storage.upsert_edge(node1_id, node2_id, edge1_data)
834
+
835
  # 验证正向查询
836
  forward_edge = await storage.get_edge(node1_id, node2_id)
837
  print(f"正向边属性: {forward_edge}")
838
  assert forward_edge is not None, f"未能读取正向边属性: {node1_id} -> {node2_id}"
839
+
840
  # 验证反向查询
841
  reverse_edge = await storage.get_edge(node2_id, node1_id)
842
  print(f"反向边属性: {reverse_edge}")
843
  assert reverse_edge is not None, f"未能读取反向边属性: {node2_id} -> {node1_id}"
844
+
845
  # 验证正向和反向边属性是否一致
846
+ assert (
847
+ forward_edge == reverse_edge
848
+ ), "正向和反向边属性不一致,无向图特性验证失败"
849
  print("无向图特性验证成功:正向和反向边属性一致")
850
+
851
  # 3. 测试边的度数的无向图特性
852
  print("\n== 测试边的度数的无向图特性")
853
+
854
  # 插入边2: 计算机科学 -> 算法
855
  edge2_data = {
856
  "relationship": "包含",
 
859
  }
860
  print(f"插入边2: {node1_id} -> {node3_id}")
861
  await storage.upsert_edge(node1_id, node3_id, edge2_data)
862
+
863
  # 验证正向和反向边的度数
864
  forward_degree = await storage.edge_degree(node1_id, node2_id)
865
  reverse_degree = await storage.edge_degree(node2_id, node1_id)
866
  print(f"正向边 {node1_id} -> {node2_id} 的度数: {forward_degree}")
867
  print(f"反向边 {node2_id} -> {node1_id} 的度数: {reverse_degree}")
868
+ assert (
869
+ forward_degree == reverse_degree
870
+ ), "正向和反向边的度数不一致,无向图特性验证失败"
871
  print("无向图特性验证成功:正向和反向边的度数一致")
872
+
873
  # 4. 测试删除边的无向图特性
874
  print("\n== 测试删除边的无向图特性")
875
+
876
  # 删除正向边
877
  print(f"删除边: {node1_id} -> {node2_id}")
878
  await storage.remove_edges([(node1_id, node2_id)])
879
+
880
  # 验证正向边是否被删除
881
  forward_edge = await storage.get_edge(node1_id, node2_id)
882
  print(f"删除后查询正向边属性 {node1_id} -> {node2_id}: {forward_edge}")
883
  assert forward_edge is None, f"边 {node1_id} -> {node2_id} 应已被删除"
884
+
885
  # 验证反向边是否也被删除
886
  reverse_edge = await storage.get_edge(node2_id, node1_id)
887
  print(f"删除后查询反向边属性 {node2_id} -> {node1_id}: {reverse_edge}")
888
+ assert (
889
+ reverse_edge is None
890
+ ), f"反向边 {node2_id} -> {node1_id} 也应被删除,无向图特性验证失败"
891
  print("无向图特性验证成功:删除一个方向的边后,反向边也被删除")
892
+
893
  # 5. 测试批量操作中的无向图特性
894
  print("\n== 测试批量操作中的无向图特性")
895
+
896
  # 重新插入边
897
  await storage.upsert_edge(node1_id, node2_id, edge1_data)
898
+
899
  # 批量获取边属性
900
+ edge_dicts = [
901
+ {"src": node1_id, "tgt": node2_id},
902
+ {"src": node1_id, "tgt": node3_id},
903
+ ]
904
+ reverse_edge_dicts = [
905
+ {"src": node2_id, "tgt": node1_id},
906
+ {"src": node3_id, "tgt": node1_id},
907
+ ]
908
+
909
  edges_dict = await storage.get_edges_batch(edge_dicts)
910
  reverse_edges_dict = await storage.get_edges_batch(reverse_edge_dicts)
911
+
912
  print(f"批量获取正向边属性结果: {edges_dict.keys()}")
913
  print(f"批量获取反向边属性结果: {reverse_edges_dict.keys()}")
914
+
915
  # 验证正向和反向边的属性是否一致
916
  for (src, tgt), props in edges_dict.items():
917
+ assert (
918
+ tgt,
919
+ src,
920
+ ) in reverse_edges_dict, f"反向边 {tgt} -> {src} 应在返回结果中"
921
+ assert (
922
+ props == reverse_edges_dict[(tgt, src)]
923
+ ), f"边 {src} -> {tgt} 和反向边 {tgt} -> {src} 的属性不一致"
924
+
925
  print("无向图特性验证成功:批量获取的正向和反向边属性一致")
926
+
927
  # 6. 测试批量获取节点边的无向图特性
928
  print("\n== 测试批量获取节点边的无向图特性")
929
+
930
  nodes_edges = await storage.get_nodes_edges_batch([node1_id, node2_id])
931
  print(f"批量获取节点边结果: {nodes_edges.keys()}")
932
+
933
  # 检查节点1的边是否包含所有相关的边(无论方向)
934
  node1_edges = nodes_edges[node1_id]
935
  node2_edges = nodes_edges[node2_id]
936
+
937
  # 检查节点1是否有到节点2和节点3的边
938
+ has_edge_to_node2 = any(
939
+ (src == node1_id and tgt == node2_id) for src, tgt in node1_edges
940
+ )
941
+ has_edge_to_node3 = any(
942
+ (src == node1_id and tgt == node3_id) for src, tgt in node1_edges
943
+ )
944
+
945
  assert has_edge_to_node2, f"节点 {node1_id} 的边列表中应包含到 {node2_id} 的边"
946
  assert has_edge_to_node3, f"节点 {node1_id} 的边列表中应包含到 {node3_id} 的边"
947
+
948
  # 检查节点2是否有到节点1的边
949
+ has_edge_to_node1 = any(
950
+ (src == node2_id and tgt == node1_id)
951
+ or (src == node1_id and tgt == node2_id)
952
+ for src, tgt in node2_edges
953
+ )
954
+ assert (
955
+ has_edge_to_node1
956
+ ), f"节点 {node2_id} 的边列表中应包含与 {node1_id} 的连接"
957
+
958
  print("无向图特性验证成功:批量获取的节点边包含所有相关的边(无论方向)")
959
 
960
  # 7. 清理数据
 
1032
  if advanced_result:
1033
  ASCIIColors.cyan("\n=== 开始批量操作测试 ===")
1034
  batch_result = await test_graph_batch_operations(storage)
1035
+
1036
  if batch_result:
1037
  ASCIIColors.cyan("\n=== 开始无向图特性测试 ===")
1038
  await test_graph_undirected_property(storage)