fix tests

This commit is contained in:
phernandez
2024-12-24 20:29:21 -06:00
parent d713097333
commit 90ca41fead
16 changed files with 137 additions and 138 deletions
+1 -9
View File
@@ -127,15 +127,7 @@ async def delete_relations(
data: DeleteRelationsRequest, knowledge_service: KnowledgeServiceDep
) -> CreateEntityResponse:
"""Delete relations between entities."""
to_delete = [
{
"from_id": relation.from_id,
"to_id": relation.to_id,
"relation_type": relation.relation_type,
}
for relation in data.relations
]
updated_entities = await knowledge_service.delete_relations(to_delete)
updated_entities = await knowledge_service.delete_relations(data.relations)
return CreateEntityResponse(
entities=[EntityResponse.model_validate(entity) for entity in updated_entities]
)
@@ -79,8 +79,12 @@ class EntityRepository(Repository[Entity]):
def get_load_options(self) -> List[LoaderOption]:
return [
selectinload(Entity.observations),
# Load from_relations and both entities for each relation
selectinload(Entity.from_relations).selectinload(Relation.from_entity),
selectinload(Entity.from_relations).selectinload(Relation.to_entity),
# Load to_relations and both entities for each relation
selectinload(Entity.to_relations).selectinload(Relation.from_entity),
selectinload(Entity.to_relations).selectinload(Relation.to_entity),
]
async def find_by_path_ids(self, path_ids: List[str]) -> Sequence[Entity]:
@@ -1,10 +1,10 @@
"""Repository for managing Relation objects."""
from typing import Sequence, List
from sqlalchemy import and_
from typing import Sequence, List, Optional
from sqlalchemy import select
from sqlalchemy.ext.asyncio import async_sessionmaker
from sqlalchemy.orm import selectinload
from sqlalchemy.orm import selectinload, aliased
from sqlalchemy.orm.interfaces import LoaderOption
from basic_memory.models import Relation, Entity
@@ -17,6 +17,25 @@ class RelationRepository(Repository[Relation]):
def __init__(self, session_maker: async_sessionmaker):
super().__init__(session_maker, Relation)
async def find_relation(self, from_path_id: str, to_path_id: str, relation_type: str) -> Optional[Relation]:
"""Find a relation by its from and to path IDs."""
from_entity = aliased(Entity)
to_entity = aliased(Entity)
query = (
select(Relation)
.join(from_entity, Relation.from_id == from_entity.id)
.join(to_entity, Relation.to_id == to_entity.id)
.where(
and_(
from_entity.path_id == from_path_id,
to_entity.path_id == to_path_id,
Relation.relation_type == relation_type
)
)
)
return await self.find_one(query)
async def find_by_entity(self, from_entity_id: int) -> Sequence[Relation]:
"""Find all relations from a specific entity."""
query = select(Relation).filter(Relation.from_id == from_entity_id)
+5
View File
@@ -8,3 +8,8 @@ class EntityNotFoundError(Exception):
"""Raised when an entity cannot be found"""
pass
class EntityCreationError(Exception):
"""Raised when an entity cannot be created"""
pass
@@ -7,6 +7,7 @@ from loguru import logger
from basic_memory.models import Entity as EntityModel
from basic_memory.schemas import Entity as EntitySchema
from .files import FileOperations
from ..exceptions import EntityCreationError, EntityNotFoundError
class EntityOperations(FileOperations):
@@ -49,7 +50,7 @@ class EntityOperations(FileOperations):
for entity in entities:
created_entity = await self.create_entity(entity)
created.append(created_entity)
return created
async def delete_entity(self, path_id: str) -> bool:
@@ -59,8 +60,6 @@ class EntityOperations(FileOperations):
try:
# Get entity first for file deletion
entity = await self.entity_service.get_by_path_id(path_id)
if not entity:
return True # Already deleted
# Delete file first (it's source of truth)
path = self.get_entity_path(entity)
@@ -68,7 +67,11 @@ class EntityOperations(FileOperations):
# Delete from DB (this will cascade to observations/relations)
return await self.entity_service.delete_entity(path_id)
except EntityNotFoundError:
logger.info(f"Entity not found: {path_id}")
return True # Already deleted
except Exception as e:
logger.error(f"Failed to delete entity: {e}")
raise
@@ -36,10 +36,11 @@ class ObservationOperations(RelationOperations):
updated_entity = await self.entity_service.get_by_path_id(path_id)
# Write updated file and checksum
checksum = await self.write_entity_file(entity)
_, checksum = await self.write_entity_file(entity)
await self.entity_service.update_entity(path_id, {"checksum": checksum})
return updated_entity
# query to fetch all relations
return await self.entity_service.get_by_path_id(path_id)
except Exception as e:
logger.error(f"Failed to add observations: {e}")
@@ -59,7 +60,7 @@ class ObservationOperations(RelationOperations):
await self.observation_service.delete_observations(entity.id, observations)
# Write updated file
checksum = await self.write_entity_file(entity)
_, checksum = await self.write_entity_file(entity)
await self.entity_service.update_entity(path_id, {"checksum": checksum})
# Get final entity with all updates
@@ -66,20 +66,26 @@ class RelationOperations(EntityOperations):
# select again to eagerly load all relations
return await self.entity_service.open_nodes([e.path_id for e in updated_entities])
async def delete_relations(self, to_delete: List[Dict[str, Any]]) -> Sequence[EntityModel]:
async def delete_relations(self, to_delete: List[RelationSchema]) -> Sequence[EntityModel]:
"""Delete relations and return all updated entities."""
logger.debug(f"Deleting {len(to_delete)} relations")
updated_entities = []
entities_to_update = set()
relations = []
try:
# Delete relations from DB
for relation in to_delete:
entities_to_update.add(relation["from_id"])
entities_to_update.add(relation["to_id"])
entities_to_update.add(relation.from_id)
entities_to_update.add(relation.to_id)
relation = await self.relation_service.find_relation(relation.from_id, relation.to_id, relation.relation_type)
if relation:
relations.append(relation)
deleted = await self.relation_service.delete_relations(to_delete)
if not deleted:
# pass Relation models to delete
num_deleted = await self.relation_service.delete_relations(relations)
if num_deleted == 0:
logger.warning("No relations were deleted")
# Get fresh copies of all updated entities
@@ -91,7 +97,7 @@ class RelationOperations(EntityOperations):
raise EntityNotFoundError(f"Entity not found: {path_id}")
# Write updated file
checksum = await self.write_entity_file(entity)
_, checksum = await self.write_entity_file(entity)
updated = await self.entity_service.update_entity(
path_id, {"checksum": checksum}
)
+7 -11
View File
@@ -23,6 +23,9 @@ class RelationService(BaseService[RelationRepository]):
logger.debug(f"Creating relation: {relation}")
return await self.repository.add(relation)
async def find_relation(self, from_path_id: str, to_path_id: str, relation_type: str) -> Relation:
return await self.repository.find_relation(from_path_id, to_path_id, relation_type)
async def delete_relation(
self, from_entity: Entity, to_entity: Entity, relation_type: str
) -> bool:
@@ -37,20 +40,13 @@ class RelationService(BaseService[RelationRepository]):
)
return result
async def delete_relations(self, relations: List[Dict[str, Any]]) -> bool:
async def delete_relations(self, relations: List[Relation]) -> int:
"""Delete relations matching specified criteria."""
logger.debug(f"Deleting {len(relations)} relations")
deleted = False
for relation in relations:
filters = {"from_id": relation["from_id"], "to_id": relation["to_id"]}
if "relation_type" in relation:
filters["relation_type"] = relation["relation_type"]
result = await self.repository.delete_by_fields(**filters)
if result:
deleted = True
return deleted
ids = [relation.id for relation in relations]
return await self.repository.delete_by_ids(ids)
async def create_relations(self, relations: List[Relation]) -> Sequence[Relation]:
"""Create multiple relations between entities."""