Managing Tags and Collections
To manage metadata like tags and collections, you use the BookmarkService facade, which orchestrates updates across the BookmarkRepository, search index, and cache.
Creating and Managing Tags
Tags are created by passing a dictionary of attributes to the BookmarkService.create_tag method. The service validates the input and persists the Tag entity to the BookmarkRepository.
from app.services.bookmark_service import BookmarkService
from app.models.tag import TagColor
service = BookmarkService()
# Create a new tag
tag_data = {
"name": "Research",
"color": "blue",
"description": "Academic papers and articles"
}
tag, error = service.create_tag(tag_data)
if tag:
print(f"Created tag: {tag.name} (ID: {tag.id})")
Cascading Tag Deletion
When a tag is deleted via BookmarkService.delete_tag, the system automatically performs a cascading update. It removes the tag ID from all associated bookmarks, saves the updated bookmarks back to the repository, and invalidates their cache entries.
# Deleting a tag triggers a cascade to all associated bookmarks
success = service.delete_tag("tag_id_123")
The underlying logic in BookmarkService ensures data consistency:
def delete_tag(self, tag_id: str) -> bool:
"""Delete a tag and strip it from all bookmarks."""
tag = self._repo.get_tag(tag_id)
if not tag:
return False
for bookmark in self._repo.get_bookmarks_with_tag(tag_id):
bookmark.remove_tag(tag_id)
self._repo.save_bookmark(bookmark)
self._cache.invalidate(bookmark.id)
self._repo.delete_tag(tag_id)
return True
Organizing with Collections
Collections allow you to group bookmarks. You can create a collection and then add or remove bookmarks using their unique IDs.
# Create a manual collection
collection_data = {
"name": "Project Alpha",
"collection_type": "manual"
}
collection, error = service.create_collection(collection_data)
# Add a bookmark to the collection
if collection:
service.add_to_collection(collection.id, "bookmark_id_456")
Working with Smart Collections
The system supports two types of collections defined in CollectionType:
- MANUAL: Bookmarks are added and removed explicitly by the user.
- SMART: Bookmarks are included automatically based on a
filter_rule.
Smart Collection Constraints
You cannot manually add bookmarks to a smart collection. The Collection.add_bookmark method (and consequently BookmarkService.add_to_collection) will return False if attempted.
from app.models.collection import CollectionType
# Creating a smart collection
smart_data = {
"name": "Recent PDF's",
"collection_type": "smart",
"filter_rule": "extension:pdf"
}
smart_collection, _ = service.create_collection(smart_data)
# This will fail because it's a smart collection
success = service.add_to_collection(smart_collection.id, "some_id")
# success will be False
Persistence and Constraints
The BookmarkRepository serves as the persistence layer. In the current implementation, it uses in-memory dictionaries (_tags and _collections), meaning data is lost when the application restarts.
Key constraints to keep in mind:
- Tag Names: Must be non-empty and are limited to 50 characters (validated in
Tag.rename). - Collection Names: Required for creation in
BookmarkService.create_collection. - In-Memory: All mutation methods in
BookmarkRepository(likesave_tagandsave_collection) persist immediately to the internal dictionaries.
class BookmarkRepository:
def __init__(self) -> None:
self._bookmarks: Dict[str, Bookmark] = {}
self._tags: Dict[str, Tag] = {}
self._collections: Dict[str, Collection] = {}
def save_tag(self, tag: Tag) -> None:
"""Insert or update a tag."""
self._tags[tag.id] = tag
Troubleshooting
- Data Loss: If the application crashes or restarts, all tags and collections are wiped because
BookmarkRepositoryis in-memory. - Manual Add Failures: If
add_to_collectionreturnsFalse, verify that the collection is not aSMARTtype and that the bookmark is not already present in the collection. - Tag Usage Counts: The
Tagmodel maintains ausage_count, but this must be manually incremented or decremented when bookmarks are updated outside of theBookmarkServicecascading logic.