Organizing Data with Tags and Collections
Organizing data in etchblok-test-api involves using Tags for flexible labeling and Collections for structured grouping. The BookmarkService manages the lifecycle of these entities, ensuring that relationships between bookmarks and their labels remain consistent.
Managing Tags
Tags in etchblok-test-api are labels with a name and a color. You create and manage them through the BookmarkService, which validates inputs and persists them to the BookmarkRepository.
Creating and Updating Tags
To create a tag, provide a dictionary containing at least a name. The service validates that the name is non-empty and under 50 characters.
from app.services.bookmark_service import BookmarkService
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} with ID: {tag.id}")
# Update an existing tag
update_data = {"color": "green"}
updated_tag, error = service.update_tag(tag.id, update_data)
Deleting Tags and Referential Integrity
When you delete a tag using BookmarkService.delete_tag, the service automatically strips that tag from all associated bookmarks before removing the tag itself. This prevents orphaned tag IDs from remaining in bookmark metadata.
# This method iterates through all bookmarks containing the tag,
# removes the tag ID from their metadata, and then deletes the tag.
success = service.delete_tag("tag_id_123")
Querying Bookmarks by Tag
To retrieve all bookmarks associated with a specific tag, use the get_bookmarks_with_tag method in the BookmarkRepository.
from app.db.repository import BookmarkRepository
repo = BookmarkRepository()
# Retrieve all bookmarks that have the 'Research' tag attached
bookmarks = repo.get_bookmarks_with_tag("research_tag_id")
for b in bookmarks:
print(f"Found bookmark: {b.title}")
Managing Collections
Collections in etchblok-test-api allow you to group bookmarks. They come in two types: Manual (where you explicitly add bookmarks) and Smart (which are intended to filter bookmarks based on rules).
Creating a Collection
Collections are created via the BookmarkService. By default, they are created as MANUAL collections.
collection_data = {
"name": "Project Alpha",
"collection_type": "manual",
"is_pinned": True
}
collection, error = service.create_collection(collection_data)
Adding and Removing Bookmarks
For manual collections, you can add or remove bookmarks by their IDs. The Collection model in app/models/collection.py prevents manual additions to smart collections.
# Add a bookmark to a collection
success = service.add_to_collection(collection.id, "bookmark_id_456")
# Remove a bookmark from a collection
success = service.remove_from_collection(collection.id, "bookmark_id_456")
Smart Collections
Smart collections use a filter_rule to determine membership. While the Collection model includes an internal _apply_filter method, the current BookmarkService implementation for listing and retrieving collections returns the stored bookmark_ids.
smart_collection_data = {
"name": "Recent PDFS",
"collection_type": "smart",
"filter_rule": "extension:pdf"
}
smart_collection, error = service.create_collection(smart_collection_data)
Troubleshooting and Performance
In-Memory Persistence
The BookmarkRepository in etchblok-test-api is strictly in-memory. All tags, collections, and bookmark associations are lost when the application process restarts.
Tag Deletion Performance
The delete_tag operation in BookmarkService performs a linear scan of all bookmarks to maintain referential integrity:
# From app/services/bookmark_service.py
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)
In etchblok-test-api, get_bookmarks_with_tag iterates over the entire _bookmarks dictionary. As the number of bookmarks grows, deleting a tag that is widely used may become a blocking operation.
Tag Sorting
When listing tags via the API (e.g., in app/routes/tags.py), tags are sorted alphabetically by name using the Tag model's __lt__ implementation.
# Example of how tags are sorted in the routes
tags = service.list_tags()
sorted_tags = sorted(tags)