mirror of
https://github.com/basicmachines-co/basic-memory
synced 2026-06-21 13:47:35 +00:00
change entity.path_id to entity.permalink
This commit is contained in:
@@ -1,7 +1,7 @@
|
||||
"""Service for tracking and querying activity across the knowledge base."""
|
||||
|
||||
from datetime import datetime, timezone
|
||||
from typing import List, Optional, Sequence
|
||||
from typing import List, Optional
|
||||
|
||||
from . import EntityService, RelationService
|
||||
from ..schemas.activity import (
|
||||
@@ -32,11 +32,11 @@ class ActivityService:
|
||||
activity_types: Optional[List[str]] = None,
|
||||
) -> RecentActivity:
|
||||
"""Get all recent activity in the knowledge base.
|
||||
|
||||
|
||||
Args:
|
||||
timeframe: Time window to look back (1h, 1d, 1w, 1m)
|
||||
activity_types: Optional list of types to include
|
||||
|
||||
|
||||
Returns:
|
||||
RecentActivity object containing changes and summary
|
||||
"""
|
||||
@@ -47,9 +47,7 @@ class ActivityService:
|
||||
# Get changes based on requested types
|
||||
changes = []
|
||||
types_to_fetch = (
|
||||
[ActivityType(t) for t in activity_types]
|
||||
if activity_types
|
||||
else list(ActivityType)
|
||||
[ActivityType(t) for t in activity_types] if activity_types else list(ActivityType)
|
||||
)
|
||||
|
||||
for activity_type in types_to_fetch:
|
||||
@@ -68,81 +66,92 @@ class ActivityService:
|
||||
summary = ActivitySummary(
|
||||
entity_changes=len([c for c in changes if c.activity_type == ActivityType.ENTITY]),
|
||||
relation_changes=len([c for c in changes if c.activity_type == ActivityType.RELATION]),
|
||||
most_active_paths=self._get_most_active_paths(changes)
|
||||
most_active_paths=self._get_most_active_paths(changes),
|
||||
)
|
||||
|
||||
return RecentActivity(
|
||||
timeframe=timeframe,
|
||||
changes=changes,
|
||||
summary=summary
|
||||
)
|
||||
return RecentActivity(timeframe=timeframe, changes=changes, summary=summary)
|
||||
|
||||
async def _get_entity_changes(self, since: datetime) -> List[ActivityChange]:
|
||||
"""Get recent entity changes."""
|
||||
# Query entities updated since the cutoff
|
||||
entities = await self.entity_service.get_modified_since(since)
|
||||
|
||||
|
||||
changes = []
|
||||
for entity in entities:
|
||||
# Ensure timestamps are timezone-aware
|
||||
created_at = entity.created_at.replace(tzinfo=timezone.utc) if entity.created_at.tzinfo is None else entity.created_at
|
||||
updated_at = entity.updated_at.replace(tzinfo=timezone.utc) if entity.updated_at.tzinfo is None else entity.updated_at
|
||||
|
||||
created_at = (
|
||||
entity.created_at.replace(tzinfo=timezone.utc)
|
||||
if entity.created_at.tzinfo is None
|
||||
else entity.created_at
|
||||
)
|
||||
updated_at = (
|
||||
entity.updated_at.replace(tzinfo=timezone.utc)
|
||||
if entity.updated_at.tzinfo is None
|
||||
else entity.updated_at
|
||||
)
|
||||
|
||||
change_type = ChangeType.CREATED if created_at >= since else ChangeType.UPDATED
|
||||
|
||||
|
||||
changes.append(
|
||||
ActivityChange(
|
||||
activity_type=ActivityType.ENTITY,
|
||||
change_type=change_type,
|
||||
timestamp=updated_at,
|
||||
path_id=entity.path_id,
|
||||
permalink=entity.permalink,
|
||||
summary=f"{change_type.value.title()} entity: {entity.title}",
|
||||
content=entity.summary
|
||||
content=entity.summary,
|
||||
)
|
||||
)
|
||||
|
||||
return changes
|
||||
|
||||
return changes
|
||||
|
||||
async def _get_relation_changes(self, since: datetime) -> List[ActivityChange]:
|
||||
"""Get recent relation changes."""
|
||||
# Query relations updated since the cutoff
|
||||
relations = await self.relation_service.get_modified_since(since)
|
||||
|
||||
|
||||
changes = []
|
||||
for relation in relations:
|
||||
# Ensure timestamps are timezone-aware
|
||||
created_at = relation.created_at.replace(tzinfo=timezone.utc) if relation.created_at.tzinfo is None else relation.created_at
|
||||
updated_at = relation.updated_at.replace(tzinfo=timezone.utc) if relation.updated_at.tzinfo is None else relation.updated_at
|
||||
|
||||
created_at = (
|
||||
relation.created_at.replace(tzinfo=timezone.utc)
|
||||
if relation.created_at.tzinfo is None
|
||||
else relation.created_at
|
||||
)
|
||||
updated_at = (
|
||||
relation.updated_at.replace(tzinfo=timezone.utc)
|
||||
if relation.updated_at.tzinfo is None
|
||||
else relation.updated_at
|
||||
)
|
||||
|
||||
change_type = ChangeType.CREATED if created_at >= since else ChangeType.UPDATED
|
||||
|
||||
|
||||
changes.append(
|
||||
ActivityChange(
|
||||
activity_type=ActivityType.RELATION,
|
||||
change_type=change_type,
|
||||
timestamp=updated_at,
|
||||
path_id=f"{relation.from_id}->{relation.to_id}",
|
||||
permalink=f"{relation.from_id}->{relation.to_id}",
|
||||
summary=(
|
||||
f"{change_type.value.title()} relation: "
|
||||
f"{relation.from_id} {relation.relation_type} {relation.to_id}"
|
||||
),
|
||||
content=relation.context
|
||||
content=relation.context,
|
||||
)
|
||||
)
|
||||
|
||||
|
||||
return changes
|
||||
|
||||
def _get_most_active_paths(self, changes: List[ActivityChange], limit: int = 5) -> List[str]:
|
||||
"""Get the most frequently changed paths."""
|
||||
path_counts = {}
|
||||
for change in changes:
|
||||
path_counts[change.path_id] = path_counts.get(change.path_id, 0) + 1
|
||||
|
||||
path_counts[change.permalink] = path_counts.get(change.permalink, 0) + 1
|
||||
|
||||
# Sort by count descending and take top paths
|
||||
sorted_paths = sorted(
|
||||
path_counts.items(),
|
||||
key=lambda x: (-x[1], x[0]) # Sort by count desc, then path asc
|
||||
key=lambda x: (-x[1], x[0]), # Sort by count desc, then path asc
|
||||
)
|
||||
|
||||
return [path for path, _ in sorted_paths[:limit]]
|
||||
|
||||
return [path for path, _ in sorted_paths[:limit]]
|
||||
|
||||
@@ -17,7 +17,7 @@ def entity_model(entity: EntitySchema):
|
||||
title=entity.title,
|
||||
entity_type=entity.entity_type,
|
||||
entity_metadata=entity.entity_metadata,
|
||||
path_id=entity.path_id,
|
||||
permalink=entity.permalink,
|
||||
file_path=entity.file_path,
|
||||
summary=entity.summary,
|
||||
content_type=entity.content_type,
|
||||
@@ -57,7 +57,7 @@ class EntityService(BaseService[EntityModel]):
|
||||
except Exception as e:
|
||||
# Clean up on any failure
|
||||
if db_entity:
|
||||
await self.delete_entity(db_entity.path_id)
|
||||
await self.delete_entity(db_entity.permalink)
|
||||
await self.file_service.delete_entity_file(db_entity)
|
||||
logger.error(f"Failed to create entity: {e}")
|
||||
raise
|
||||
@@ -69,7 +69,7 @@ class EntityService(BaseService[EntityModel]):
|
||||
|
||||
async def update_entity(
|
||||
self,
|
||||
path_id: str,
|
||||
permalink: str,
|
||||
content: Optional[str] = None,
|
||||
metadata: Optional[Dict[str, Any]] = None,
|
||||
**update_fields: Any,
|
||||
@@ -77,7 +77,7 @@ class EntityService(BaseService[EntityModel]):
|
||||
"""Update an entity's content and metadata.
|
||||
|
||||
Args:
|
||||
path_id: Entity's path ID
|
||||
permalink: Entity's path ID
|
||||
content: Optional new content
|
||||
metadata: Optional metadata updates
|
||||
**update_fields: Additional entity fields to update
|
||||
@@ -88,12 +88,12 @@ class EntityService(BaseService[EntityModel]):
|
||||
Raises:
|
||||
EntityNotFoundError: If entity doesn't exist
|
||||
"""
|
||||
logger.debug(f"Updating entity with path_id: {path_id}")
|
||||
logger.debug(f"Updating entity with permalink: {permalink}")
|
||||
|
||||
# Get existing entity
|
||||
entity = await self.get_by_path_id(path_id)
|
||||
entity = await self.get_by_permalink(permalink)
|
||||
if not entity:
|
||||
raise EntityNotFoundError(f"Entity not found: {path_id}")
|
||||
raise EntityNotFoundError(f"Entity not found: {permalink}")
|
||||
|
||||
try:
|
||||
# Build update data
|
||||
@@ -128,13 +128,13 @@ class EntityService(BaseService[EntityModel]):
|
||||
logger.error(f"Failed to update entity: {e}")
|
||||
raise
|
||||
|
||||
async def delete_entity(self, path_id: str) -> bool:
|
||||
async def delete_entity(self, permalink: str) -> bool:
|
||||
"""Delete entity and its file."""
|
||||
logger.debug(f"Deleting entity: {path_id}")
|
||||
logger.debug(f"Deleting entity: {permalink}")
|
||||
|
||||
try:
|
||||
# Get entity first for file deletion
|
||||
entity = await self.get_by_path_id(path_id)
|
||||
entity = await self.get_by_permalink(permalink)
|
||||
|
||||
# Delete file first (it's source of truth)
|
||||
await self.file_service.delete_entity_file(entity)
|
||||
@@ -143,30 +143,30 @@ class EntityService(BaseService[EntityModel]):
|
||||
return await self.repository.delete(entity.id)
|
||||
|
||||
except EntityNotFoundError:
|
||||
logger.info(f"Entity not found: {path_id}")
|
||||
logger.info(f"Entity not found: {permalink}")
|
||||
return True # Already deleted
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to delete entity: {e}")
|
||||
raise
|
||||
|
||||
async def delete_entities(self, path_ids: List[str]) -> bool:
|
||||
async def delete_entities(self, permalinks: List[str]) -> bool:
|
||||
"""Delete multiple entities and their files."""
|
||||
logger.debug(f"Deleting entities: {path_ids}")
|
||||
logger.debug(f"Deleting entities: {permalinks}")
|
||||
success = True
|
||||
|
||||
for path_id in path_ids:
|
||||
await self.delete_entity(path_id)
|
||||
for permalink in permalinks:
|
||||
await self.delete_entity(permalink)
|
||||
success = True
|
||||
|
||||
return success
|
||||
|
||||
async def get_by_path_id(self, path_id: str) -> EntityModel:
|
||||
async def get_by_permalink(self, permalink: str) -> EntityModel:
|
||||
"""Get entity by type and name combination."""
|
||||
logger.debug(f"Getting entity by path_id: {path_id}")
|
||||
db_entity = await self.repository.get_by_path_id(path_id)
|
||||
logger.debug(f"Getting entity by permalink: {permalink}")
|
||||
db_entity = await self.repository.get_by_permalink(permalink)
|
||||
if not db_entity:
|
||||
raise EntityNotFoundError(f"Entity not found: {path_id}")
|
||||
raise EntityNotFoundError(f"Entity not found: {permalink}")
|
||||
return db_entity
|
||||
|
||||
async def get_all(self) -> Sequence[EntityModel]:
|
||||
@@ -188,10 +188,10 @@ class EntityService(BaseService[EntityModel]):
|
||||
logger.debug(f"Listing entities: type={entity_type} sort={sort_by}")
|
||||
return await self.repository.list_entities(entity_type=entity_type, sort_by=sort_by)
|
||||
|
||||
async def open_nodes(self, path_ids: List[str]) -> Sequence[EntityModel]:
|
||||
async def open_nodes(self, permalinks: List[str]) -> Sequence[EntityModel]:
|
||||
"""Get specific nodes and their relationships."""
|
||||
logger.debug(f"Opening nodes path_ids: {path_ids}")
|
||||
return await self.repository.find_by_path_ids(path_ids)
|
||||
logger.debug(f"Opening nodes permalinks: {permalinks}")
|
||||
return await self.repository.find_by_permalinks(permalinks)
|
||||
|
||||
async def delete_entity_by_file_path(self, file_path):
|
||||
await self.repository.delete_by_file_path(file_path)
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
"""Service for file operations with checksum tracking."""
|
||||
|
||||
from datetime import datetime, UTC
|
||||
from pathlib import Path
|
||||
from typing import Optional, Dict, Any, Tuple
|
||||
|
||||
@@ -31,12 +30,11 @@ class FileService:
|
||||
self.base_path = base_path
|
||||
self.knowledge_writer = knowledge_writer
|
||||
|
||||
|
||||
def get_entity_path(self, entity: EntityModel) -> Path:
|
||||
"""Generate filesystem path for entity."""
|
||||
if entity.file_path:
|
||||
return self.base_path / entity.file_path
|
||||
return self.base_path / f"{entity.path_id}.md"
|
||||
return self.base_path / f"{entity.permalink}.md"
|
||||
|
||||
async def write_entity_file(
|
||||
self,
|
||||
@@ -62,12 +60,11 @@ class FileService:
|
||||
logger.error(f"Failed to write entity file: {e}")
|
||||
raise FileOperationError(f"Failed to write entity file: {e}")
|
||||
|
||||
|
||||
async def read_entity_content(self, entity: EntityModel) -> str:
|
||||
"""Get entity's content if it's a note.
|
||||
|
||||
Args:
|
||||
path_id: Entity's path ID
|
||||
permalink: Entity's path ID
|
||||
|
||||
Returns:
|
||||
content without frontmatter
|
||||
@@ -75,7 +72,7 @@ class FileService:
|
||||
Raises:
|
||||
FileOperationError: If entity file doesn't exist
|
||||
"""
|
||||
logger.debug(f"Reading entity with path_id: {entity.path_id}")
|
||||
logger.debug(f"Reading entity with permalink: {entity.permalink}")
|
||||
|
||||
# For notes, read the actual file content
|
||||
file_path = self.get_entity_path(entity)
|
||||
@@ -86,7 +83,6 @@ class FileService:
|
||||
content = content.strip()
|
||||
return content
|
||||
|
||||
|
||||
async def delete_entity_file(self, entity: EntityModel) -> None:
|
||||
"""Delete entity file from filesystem."""
|
||||
try:
|
||||
@@ -96,7 +92,6 @@ class FileService:
|
||||
logger.error(f"Failed to delete entity file: {e}")
|
||||
raise FileOperationError(f"Failed to delete entity file: {e}")
|
||||
|
||||
|
||||
async def exists(self, path: Path) -> bool:
|
||||
"""
|
||||
Check if file exists.
|
||||
@@ -265,7 +260,7 @@ class FileService:
|
||||
) -> str:
|
||||
"""
|
||||
Add YAML frontmatter to content.
|
||||
|
||||
|
||||
Args:
|
||||
content: Content to add frontmatter to
|
||||
frontmatter: Frontmatter to add
|
||||
|
||||
@@ -32,7 +32,7 @@ class ObservationService(BaseService[ObservationRepository]):
|
||||
self.file_operations = file_service
|
||||
|
||||
async def add_observations(
|
||||
self, path_id: str, observations: List[ObservationCreate], context: str | None = None
|
||||
self, permalink: str, observations: List[ObservationCreate], context: str | None = None
|
||||
) -> EntityModel:
|
||||
"""Add observations to entity and update its file.
|
||||
|
||||
@@ -41,17 +41,17 @@ class ObservationService(BaseService[ObservationRepository]):
|
||||
- [category] Content text #tag1 #tag2 (optional context)
|
||||
|
||||
Args:
|
||||
path_id: Entity path ID
|
||||
permalink: Entity path ID
|
||||
observations: List of observations with categories
|
||||
context: Optional shared context for all observations
|
||||
"""
|
||||
logger.debug(f"Adding observations to entity: {path_id}")
|
||||
logger.debug(f"Adding observations to entity: {permalink}")
|
||||
|
||||
try:
|
||||
# Get entity to update
|
||||
entity = await self.entity_repository.get_by_path_id(path_id)
|
||||
entity = await self.entity_repository.get_by_permalink(permalink)
|
||||
if not entity:
|
||||
raise EntityNotFoundError(f"Entity not found: {path_id}")
|
||||
raise EntityNotFoundError(f"Entity not found: {permalink}")
|
||||
|
||||
# Add observations to DB
|
||||
await self.repository.create_all(
|
||||
@@ -68,33 +68,33 @@ class ObservationService(BaseService[ObservationRepository]):
|
||||
)
|
||||
|
||||
# Get updated entity
|
||||
entity = await self.entity_repository.get_by_path_id(path_id)
|
||||
entity = await self.entity_repository.get_by_permalink(permalink)
|
||||
|
||||
# Write updated file and checksum
|
||||
_, checksum = await self.file_operations.write_entity_file(entity)
|
||||
await self.entity_repository.update(entity.id, {"checksum": checksum})
|
||||
|
||||
# Return final entity with all updates and relations
|
||||
return await self.entity_repository.get_by_path_id(path_id)
|
||||
return await self.entity_repository.get_by_permalink(permalink)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to add observations: {e}")
|
||||
raise
|
||||
|
||||
async def delete_observations(self, path_id: str, observations: List[str]) -> EntityModel:
|
||||
async def delete_observations(self, permalink: str, observations: List[str]) -> EntityModel:
|
||||
"""Delete observations from entity and update its file.
|
||||
|
||||
Args:
|
||||
path_id: Entity path ID
|
||||
permalink: Entity path ID
|
||||
observations: List of observation contents to delete
|
||||
"""
|
||||
logger.debug(f"Deleting observations from entity {path_id}")
|
||||
logger.debug(f"Deleting observations from entity {permalink}")
|
||||
|
||||
try:
|
||||
# Get entity
|
||||
entity = await self.entity_repository.get_by_path_id(path_id)
|
||||
entity = await self.entity_repository.get_by_permalink(permalink)
|
||||
if not entity:
|
||||
raise EntityNotFoundError(f"Entity not found: {path_id}")
|
||||
raise EntityNotFoundError(f"Entity not found: {permalink}")
|
||||
|
||||
# Delete observations from DB by comparing the string value to the Observation content
|
||||
for observation in observations:
|
||||
@@ -107,7 +107,7 @@ class ObservationService(BaseService[ObservationRepository]):
|
||||
await self.entity_repository.update(entity.id, {"checksum": checksum})
|
||||
|
||||
# Return final entity with all updates
|
||||
return await self.entity_repository.get_by_path_id(path_id)
|
||||
return await self.entity_repository.get_by_permalink(permalink)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to delete observations: {e}")
|
||||
|
||||
@@ -37,8 +37,8 @@ class RelationService(BaseService[RelationRepository]):
|
||||
|
||||
for rs in relations:
|
||||
try:
|
||||
from_entity = await self.entity_repository.get_by_path_id(rs.from_id)
|
||||
to_entity = await self.entity_repository.get_by_path_id(rs.to_id)
|
||||
from_entity = await self.entity_repository.get_by_permalink(rs.from_id)
|
||||
to_entity = await self.entity_repository.get_by_permalink(rs.to_id)
|
||||
|
||||
relation = RelationModel(
|
||||
from_id=from_entity.id,
|
||||
@@ -58,10 +58,10 @@ class RelationService(BaseService[RelationRepository]):
|
||||
continue
|
||||
|
||||
# Get fresh copies of all updated entities
|
||||
for path_id in entities_to_update:
|
||||
for permalink in entities_to_update:
|
||||
try:
|
||||
# Get fresh entity
|
||||
entity = await self.entity_repository.get_by_path_id(path_id)
|
||||
entity = await self.entity_repository.get_by_permalink(permalink)
|
||||
|
||||
# Write updated file
|
||||
_, checksum = await self.file_service.write_entity_file(entity)
|
||||
@@ -70,11 +70,13 @@ class RelationService(BaseService[RelationRepository]):
|
||||
updated_entities.append(updated)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to update entity {path_id}: {e}")
|
||||
logger.error(f"Failed to update entity {permalink}: {e}")
|
||||
continue
|
||||
|
||||
# select again to eagerly load all relations
|
||||
return await self.entity_repository.find_by_path_ids([e.path_id for e in updated_entities])
|
||||
return await self.entity_repository.find_by_permalinks(
|
||||
[e.permalink for e in updated_entities]
|
||||
)
|
||||
|
||||
async def delete_relations(self, to_delete: List[RelationSchema]) -> Sequence[EntityModel]:
|
||||
"""Delete relations and return all updated entities."""
|
||||
@@ -104,12 +106,12 @@ class RelationService(BaseService[RelationRepository]):
|
||||
logger.warning("No relations were deleted")
|
||||
|
||||
# Get fresh copies of all updated entities
|
||||
for path_id in entities_to_update:
|
||||
for permalink in entities_to_update:
|
||||
try:
|
||||
# Get fresh entity
|
||||
entity = await self.entity_repository.get_by_path_id(path_id)
|
||||
entity = await self.entity_repository.get_by_permalink(permalink)
|
||||
if not entity:
|
||||
raise EntityNotFoundError(f"Entity not found: {path_id}")
|
||||
raise EntityNotFoundError(f"Entity not found: {permalink}")
|
||||
|
||||
# Write updated file
|
||||
_, checksum = await self.file_service.write_entity_file(entity)
|
||||
@@ -118,7 +120,7 @@ class RelationService(BaseService[RelationRepository]):
|
||||
updated_entities.append(updated)
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"Failed to update entity {path_id}: {e}")
|
||||
logger.error(f"Failed to update entity {permalink}: {e}")
|
||||
continue
|
||||
|
||||
return updated_entities
|
||||
@@ -128,9 +130,9 @@ class RelationService(BaseService[RelationRepository]):
|
||||
raise
|
||||
|
||||
async def find_relation(
|
||||
self, from_path_id: str, to_path_id: str, relation_type: str
|
||||
self, from_permalink: str, to_permalink: str, relation_type: str
|
||||
) -> RelationModel:
|
||||
return await self.repository.find_relation(from_path_id, to_path_id, relation_type)
|
||||
return await self.repository.find_relation(from_permalink, to_permalink, relation_type)
|
||||
|
||||
async def delete_relation(
|
||||
self, from_entity: EntityModel, to_entity: EntityModel, relation_type: str
|
||||
|
||||
@@ -60,7 +60,7 @@ class SearchService:
|
||||
*[f"{obs.category}: {obs.content}" for obs in entity.observations],
|
||||
# Add relations
|
||||
*[
|
||||
f"{rel.relation_type} {rel.to_entity.path_id}: {rel.context or ''}"
|
||||
f"{rel.relation_type} {rel.to_entity.permalink}: {rel.context or ''}"
|
||||
for rel in entity.relations
|
||||
],
|
||||
]
|
||||
@@ -77,7 +77,7 @@ class SearchService:
|
||||
background_tasks.add_task(
|
||||
self._do_index,
|
||||
content=content,
|
||||
path_id=entity.path_id,
|
||||
permalink=entity.permalink,
|
||||
file_path=entity.file_path,
|
||||
type=SearchItemType.ENTITY,
|
||||
metadata=metadata,
|
||||
@@ -85,20 +85,20 @@ class SearchService:
|
||||
else:
|
||||
await self._do_index(
|
||||
content=content,
|
||||
path_id=entity.path_id,
|
||||
permalink=entity.permalink,
|
||||
file_path=entity.file_path,
|
||||
type=SearchItemType.ENTITY,
|
||||
metadata=metadata,
|
||||
)
|
||||
|
||||
async def _do_index(
|
||||
self, content: str, path_id: str, file_path: str, type: SearchItemType, metadata: dict
|
||||
self, content: str, permalink: str, file_path: str, type: SearchItemType, metadata: dict
|
||||
) -> None:
|
||||
"""Actually perform the indexing."""
|
||||
await self.repository.index_item(
|
||||
content=content, path_id=path_id, file_path=file_path, type=type, metadata=metadata
|
||||
content=content, permalink=permalink, file_path=file_path, type=type, metadata=metadata
|
||||
)
|
||||
|
||||
async def delete_by_path_id(self, path_id: str):
|
||||
async def delete_by_permalink(self, permalink: str):
|
||||
"""Delete an item from the search index."""
|
||||
await self.repository.delete_by_path_id(path_id)
|
||||
await self.repository.delete_by_permalink(permalink)
|
||||
|
||||
Reference in New Issue
Block a user