Organizing with Tags and Collections
To manage metadata and group bookmarks in this project, you use the BookmarkService from app.services.bookmark_service. This service acts as a central facade that coordinates operations across the repository, search index, and cache to ensure data consistency.
Managing Tags
Tags are labels used to categorize bookmarks. The BookmarkService handles tag lifecycle and ensures that deleting a tag also removes it from all associated bookmarks.
Creating and Updating Tags
You can create a tag by providing a name and an optional color from the TagColor enum defined in app.models.tag.
from app.services.bookmark_service import BookmarkService
from app.models.tag import TagColor
service = BookmarkService()
# Create a new tag
tag_data = {
"name": "Research",
"color": TagColor.BLUE.value,
"description": "Academic papers and articles"
}
tag, error = service.create_tag(tag_data)
if error:
print(f"Failed to create tag: {error}")
# Update an existing tag
update_data = {"color": TagColor.GREEN.value}
updated_tag, error = service.update_tag(tag.id, update_data)
Deleting Tags and Automatic Stripping
When you delete a tag using delete_tag, the service automatically iterates through every bookmark associated with that tag to remove the reference. This ensures that no bookmark points to a non-existent tag.
# This operation strips the tag from all bookmarks before deleting the tag itself
success = service.delete_tag("tag_id_123")
if not success:
print("Tag not found")
The internal logic in app/services/bookmark_service.py performs the following steps during deletion:
- Retrieves the tag from the repository.
- Fetches all bookmarks containing that
tag_id. - Calls
bookmark.remove_tag(tag_id)on each. - Saves the updated bookmark and invalidates its cache entry.
- Finally, deletes the tag from the repository.
Grouping with Collections
Collections allow you to group bookmarks either manually or automatically via "smart" filters.
Creating Collections
Collections are created via create_collection. You can specify the type as manual (default) or smart.
# Create a manual collection
manual_data = {"name": "Project Alpha", "type": "manual"}
collection, error = service.create_collection(manual_data)
# Create a smart collection with a filter rule
smart_data = {
"name": "Python Docs",
"type": "smart",
"filter_rule": "python"
}
smart_collection, error = service.create_collection(smart_data)
Managing Manual Membership
For manual collections, you use add_to_collection and remove_from_collection to manage the bookmark_ids list.
# Add a bookmark to a collection
success = service.add_to_collection("collection_id_abc", "bookmark_id_789")
# Remove a bookmark from a collection
success = service.remove_from_collection("collection_id_abc", "bookmark_id_789")
Note that add_to_collection will return False if the collection is a "smart" collection, as membership in smart collections is determined by the filter_rule rather than manual assignment.
Maintaining Data Consistency
The BookmarkService ensures that any change to metadata is reflected across the entire system. When a bookmark is updated (e.g., when a tag is removed or the bookmark is added to a collection), the service:
- Persists the change via
BookmarkRepository. - Updates the
SearchIndexso the change is searchable. - Invalidates the
LRUCacheentry for that bookmark to ensure subsequent reads fetch the fresh data.
Troubleshooting and Limitations
- Tag Deletion Performance: Deleting a tag is an O(N) operation, where N is the number of bookmarks using that tag. For tags with thousands of bookmarks, this may cause a delay as the service must update and re-index each bookmark individually.
- Smart Collection Updates: While the
Collectionmodel inapp/models/collection.pyincludes an internal_apply_filtermethod, theBookmarkServicecurrently focuses on managing thebookmark_idslist for manual collections. Smart collections are primarily used for UI filtering based on thefilter_rule. - Soft Deletion: The
delete_bookmarkmethod inBookmarkServiceperforms a "soft delete" by moving the bookmark to the trash (viabookmark.trash()) rather than removing it from the database entirely. Userestore_bookmarkto bring it back.