Add retrun status to entity and relation delete operations
Browse files- lightrag/lightrag.py +34 -7
- lightrag/utils_graph.py +55 -14
lightrag/lightrag.py
CHANGED
|
@@ -1908,11 +1908,14 @@ Rebuilt: {len(entities_to_rebuild)} entities, {len(relationships_to_rebuild)} re
|
|
| 1908 |
status_code=500,
|
| 1909 |
)
|
| 1910 |
|
| 1911 |
-
async def adelete_by_entity(self, entity_name: str) ->
|
| 1912 |
"""Asynchronously delete an entity and all its relationships.
|
| 1913 |
|
| 1914 |
Args:
|
| 1915 |
-
entity_name: Name of the entity to delete
|
|
|
|
|
|
|
|
|
|
| 1916 |
"""
|
| 1917 |
from .utils_graph import adelete_by_entity
|
| 1918 |
|
|
@@ -1923,16 +1926,29 @@ Rebuilt: {len(entities_to_rebuild)} entities, {len(relationships_to_rebuild)} re
|
|
| 1923 |
entity_name,
|
| 1924 |
)
|
| 1925 |
|
| 1926 |
-
def delete_by_entity(self, entity_name: str) ->
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1927 |
loop = always_get_an_event_loop()
|
| 1928 |
return loop.run_until_complete(self.adelete_by_entity(entity_name))
|
| 1929 |
|
| 1930 |
-
async def adelete_by_relation(
|
|
|
|
|
|
|
| 1931 |
"""Asynchronously delete a relation between two entities.
|
| 1932 |
|
| 1933 |
Args:
|
| 1934 |
-
source_entity: Name of the source entity
|
| 1935 |
-
target_entity: Name of the target entity
|
|
|
|
|
|
|
|
|
|
| 1936 |
"""
|
| 1937 |
from .utils_graph import adelete_by_relation
|
| 1938 |
|
|
@@ -1943,7 +1959,18 @@ Rebuilt: {len(entities_to_rebuild)} entities, {len(relationships_to_rebuild)} re
|
|
| 1943 |
target_entity,
|
| 1944 |
)
|
| 1945 |
|
| 1946 |
-
def delete_by_relation(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1947 |
loop = always_get_an_event_loop()
|
| 1948 |
return loop.run_until_complete(
|
| 1949 |
self.adelete_by_relation(source_entity, target_entity)
|
|
|
|
| 1908 |
status_code=500,
|
| 1909 |
)
|
| 1910 |
|
| 1911 |
+
async def adelete_by_entity(self, entity_name: str) -> DeletionResult:
|
| 1912 |
"""Asynchronously delete an entity and all its relationships.
|
| 1913 |
|
| 1914 |
Args:
|
| 1915 |
+
entity_name: Name of the entity to delete.
|
| 1916 |
+
|
| 1917 |
+
Returns:
|
| 1918 |
+
DeletionResult: An object containing the outcome of the deletion process.
|
| 1919 |
"""
|
| 1920 |
from .utils_graph import adelete_by_entity
|
| 1921 |
|
|
|
|
| 1926 |
entity_name,
|
| 1927 |
)
|
| 1928 |
|
| 1929 |
+
def delete_by_entity(self, entity_name: str) -> DeletionResult:
|
| 1930 |
+
"""Synchronously delete an entity and all its relationships.
|
| 1931 |
+
|
| 1932 |
+
Args:
|
| 1933 |
+
entity_name: Name of the entity to delete.
|
| 1934 |
+
|
| 1935 |
+
Returns:
|
| 1936 |
+
DeletionResult: An object containing the outcome of the deletion process.
|
| 1937 |
+
"""
|
| 1938 |
loop = always_get_an_event_loop()
|
| 1939 |
return loop.run_until_complete(self.adelete_by_entity(entity_name))
|
| 1940 |
|
| 1941 |
+
async def adelete_by_relation(
|
| 1942 |
+
self, source_entity: str, target_entity: str
|
| 1943 |
+
) -> DeletionResult:
|
| 1944 |
"""Asynchronously delete a relation between two entities.
|
| 1945 |
|
| 1946 |
Args:
|
| 1947 |
+
source_entity: Name of the source entity.
|
| 1948 |
+
target_entity: Name of the target entity.
|
| 1949 |
+
|
| 1950 |
+
Returns:
|
| 1951 |
+
DeletionResult: An object containing the outcome of the deletion process.
|
| 1952 |
"""
|
| 1953 |
from .utils_graph import adelete_by_relation
|
| 1954 |
|
|
|
|
| 1959 |
target_entity,
|
| 1960 |
)
|
| 1961 |
|
| 1962 |
+
def delete_by_relation(
|
| 1963 |
+
self, source_entity: str, target_entity: str
|
| 1964 |
+
) -> DeletionResult:
|
| 1965 |
+
"""Synchronously delete a relation between two entities.
|
| 1966 |
+
|
| 1967 |
+
Args:
|
| 1968 |
+
source_entity: Name of the source entity.
|
| 1969 |
+
target_entity: Name of the target entity.
|
| 1970 |
+
|
| 1971 |
+
Returns:
|
| 1972 |
+
DeletionResult: An object containing the outcome of the deletion process.
|
| 1973 |
+
"""
|
| 1974 |
loop = always_get_an_event_loop()
|
| 1975 |
return loop.run_until_complete(
|
| 1976 |
self.adelete_by_relation(source_entity, target_entity)
|
lightrag/utils_graph.py
CHANGED
|
@@ -4,6 +4,7 @@ import time
|
|
| 4 |
import asyncio
|
| 5 |
from typing import Any, cast
|
| 6 |
|
|
|
|
| 7 |
from .kg.shared_storage import get_graph_db_lock
|
| 8 |
from .prompt import GRAPH_FIELD_SEP
|
| 9 |
from .utils import compute_mdhash_id, logger
|
|
@@ -12,7 +13,7 @@ from .base import StorageNameSpace
|
|
| 12 |
|
| 13 |
async def adelete_by_entity(
|
| 14 |
chunk_entity_relation_graph, entities_vdb, relationships_vdb, entity_name: str
|
| 15 |
-
) ->
|
| 16 |
"""Asynchronously delete an entity and all its relationships.
|
| 17 |
|
| 18 |
Args:
|
|
@@ -25,18 +26,43 @@ async def adelete_by_entity(
|
|
| 25 |
# Use graph database lock to ensure atomic graph and vector db operations
|
| 26 |
async with graph_db_lock:
|
| 27 |
try:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 28 |
await entities_vdb.delete_entity(entity_name)
|
| 29 |
await relationships_vdb.delete_entity_relation(entity_name)
|
| 30 |
await chunk_entity_relation_graph.delete_node(entity_name)
|
| 31 |
|
| 32 |
-
|
| 33 |
-
|
| 34 |
-
)
|
| 35 |
await _delete_by_entity_done(
|
| 36 |
entities_vdb, relationships_vdb, chunk_entity_relation_graph
|
| 37 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 38 |
except Exception as e:
|
| 39 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 40 |
|
| 41 |
|
| 42 |
async def _delete_by_entity_done(
|
|
@@ -60,7 +86,7 @@ async def adelete_by_relation(
|
|
| 60 |
relationships_vdb,
|
| 61 |
source_entity: str,
|
| 62 |
target_entity: str,
|
| 63 |
-
) ->
|
| 64 |
"""Asynchronously delete a relation between two entities.
|
| 65 |
|
| 66 |
Args:
|
|
@@ -69,6 +95,7 @@ async def adelete_by_relation(
|
|
| 69 |
source_entity: Name of the source entity
|
| 70 |
target_entity: Name of the target entity
|
| 71 |
"""
|
|
|
|
| 72 |
graph_db_lock = get_graph_db_lock(enable_logging=False)
|
| 73 |
# Use graph database lock to ensure atomic graph and vector db operations
|
| 74 |
async with graph_db_lock:
|
|
@@ -78,10 +105,14 @@ async def adelete_by_relation(
|
|
| 78 |
source_entity, target_entity
|
| 79 |
)
|
| 80 |
if not edge_exists:
|
| 81 |
-
|
| 82 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 83 |
)
|
| 84 |
-
return
|
| 85 |
|
| 86 |
# Delete relation from vector database
|
| 87 |
rel_ids_to_delete = [
|
|
@@ -96,13 +127,23 @@ async def adelete_by_relation(
|
|
| 96 |
[(source_entity, target_entity)]
|
| 97 |
)
|
| 98 |
|
| 99 |
-
|
| 100 |
-
|
| 101 |
-
)
|
| 102 |
await _delete_relation_done(relationships_vdb, chunk_entity_relation_graph)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 103 |
except Exception as e:
|
| 104 |
-
|
| 105 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 106 |
)
|
| 107 |
|
| 108 |
|
|
|
|
| 4 |
import asyncio
|
| 5 |
from typing import Any, cast
|
| 6 |
|
| 7 |
+
from .base import DeletionResult
|
| 8 |
from .kg.shared_storage import get_graph_db_lock
|
| 9 |
from .prompt import GRAPH_FIELD_SEP
|
| 10 |
from .utils import compute_mdhash_id, logger
|
|
|
|
| 13 |
|
| 14 |
async def adelete_by_entity(
|
| 15 |
chunk_entity_relation_graph, entities_vdb, relationships_vdb, entity_name: str
|
| 16 |
+
) -> DeletionResult:
|
| 17 |
"""Asynchronously delete an entity and all its relationships.
|
| 18 |
|
| 19 |
Args:
|
|
|
|
| 26 |
# Use graph database lock to ensure atomic graph and vector db operations
|
| 27 |
async with graph_db_lock:
|
| 28 |
try:
|
| 29 |
+
# Check if the entity exists
|
| 30 |
+
if not await chunk_entity_relation_graph.has_node(entity_name):
|
| 31 |
+
logger.warning(f"Entity '{entity_name}' not found.")
|
| 32 |
+
return DeletionResult(
|
| 33 |
+
status="not_found",
|
| 34 |
+
doc_id=entity_name,
|
| 35 |
+
message=f"Entity '{entity_name}' not found.",
|
| 36 |
+
status_code=404,
|
| 37 |
+
)
|
| 38 |
+
# Retrieve related relationships before deleting the node
|
| 39 |
+
edges = await chunk_entity_relation_graph.get_node_edges(entity_name)
|
| 40 |
+
related_relations_count = len(edges) if edges else 0
|
| 41 |
+
|
| 42 |
await entities_vdb.delete_entity(entity_name)
|
| 43 |
await relationships_vdb.delete_entity_relation(entity_name)
|
| 44 |
await chunk_entity_relation_graph.delete_node(entity_name)
|
| 45 |
|
| 46 |
+
message = f"Entity '{entity_name}' and its {related_relations_count} relationships have been deleted."
|
| 47 |
+
logger.info(message)
|
|
|
|
| 48 |
await _delete_by_entity_done(
|
| 49 |
entities_vdb, relationships_vdb, chunk_entity_relation_graph
|
| 50 |
)
|
| 51 |
+
return DeletionResult(
|
| 52 |
+
status="success",
|
| 53 |
+
doc_id=entity_name,
|
| 54 |
+
message=message,
|
| 55 |
+
status_code=200,
|
| 56 |
+
)
|
| 57 |
except Exception as e:
|
| 58 |
+
error_message = f"Error while deleting entity '{entity_name}': {e}"
|
| 59 |
+
logger.error(error_message)
|
| 60 |
+
return DeletionResult(
|
| 61 |
+
status="fail",
|
| 62 |
+
doc_id=entity_name,
|
| 63 |
+
message=error_message,
|
| 64 |
+
status_code=500,
|
| 65 |
+
)
|
| 66 |
|
| 67 |
|
| 68 |
async def _delete_by_entity_done(
|
|
|
|
| 86 |
relationships_vdb,
|
| 87 |
source_entity: str,
|
| 88 |
target_entity: str,
|
| 89 |
+
) -> DeletionResult:
|
| 90 |
"""Asynchronously delete a relation between two entities.
|
| 91 |
|
| 92 |
Args:
|
|
|
|
| 95 |
source_entity: Name of the source entity
|
| 96 |
target_entity: Name of the target entity
|
| 97 |
"""
|
| 98 |
+
relation_str = f"{source_entity} -> {target_entity}"
|
| 99 |
graph_db_lock = get_graph_db_lock(enable_logging=False)
|
| 100 |
# Use graph database lock to ensure atomic graph and vector db operations
|
| 101 |
async with graph_db_lock:
|
|
|
|
| 105 |
source_entity, target_entity
|
| 106 |
)
|
| 107 |
if not edge_exists:
|
| 108 |
+
message = f"Relation from '{source_entity}' to '{target_entity}' does not exist"
|
| 109 |
+
logger.warning(message)
|
| 110 |
+
return DeletionResult(
|
| 111 |
+
status="not_found",
|
| 112 |
+
doc_id=relation_str,
|
| 113 |
+
message=message,
|
| 114 |
+
status_code=404,
|
| 115 |
)
|
|
|
|
| 116 |
|
| 117 |
# Delete relation from vector database
|
| 118 |
rel_ids_to_delete = [
|
|
|
|
| 127 |
[(source_entity, target_entity)]
|
| 128 |
)
|
| 129 |
|
| 130 |
+
message = f"Successfully deleted relation from '{source_entity}' to '{target_entity}'"
|
| 131 |
+
logger.info(message)
|
|
|
|
| 132 |
await _delete_relation_done(relationships_vdb, chunk_entity_relation_graph)
|
| 133 |
+
return DeletionResult(
|
| 134 |
+
status="success",
|
| 135 |
+
doc_id=relation_str,
|
| 136 |
+
message=message,
|
| 137 |
+
status_code=200,
|
| 138 |
+
)
|
| 139 |
except Exception as e:
|
| 140 |
+
error_message = f"Error while deleting relation from '{source_entity}' to '{target_entity}': {e}"
|
| 141 |
+
logger.error(error_message)
|
| 142 |
+
return DeletionResult(
|
| 143 |
+
status="fail",
|
| 144 |
+
doc_id=relation_str,
|
| 145 |
+
message=error_message,
|
| 146 |
+
status_code=500,
|
| 147 |
)
|
| 148 |
|
| 149 |
|