Organizing with Tags and Collections
To organize bookmarks in this project, you use the BookmarkService to manage Tags for categorization and Collections for grouping. The service acts as a facade, ensuring that when you modify these entities, related bookmarks are updated and caches are invalidated.
Categorizing with Tags
You can create tags with specific names and colors, then associate them with bookmarks to make them easier to find.
Creating and Managing Tags
Use create_tag to define a new category. Tags require a name and can optionally include a color from the TagColor enum (e.g., red, blue, green).
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 and technical research papers"
}
tag, error = service.create_tag(tag_data)
if error:
print(f"Failed to create tag: {error}")
else:
print(f"Created tag: {tag.id}")
To update an existing tag's name or color, use update_tag:
# Rename a tag and change its color
update_data = {
"name": "Deep Learning",
"color": TagColor.PURPLE.value
}
updated_tag, error = service.update_tag(tag.id, update_data)
Assigning Tags to Bookmarks
Tags are associated with bookmarks by including their IDs in the tags list when creating a bookmark via the service.
bookmark_data = {
"url": "https://arxiv.org/abs/1706.03762",
"title": "Attention Is All You Need",
"tags": [tag.id] # Associate the 'Deep Learning' tag ID
}
bookmark, error = service.create_bookmark(bookmark_data)
Deleting Tags (Cascading Cleanup)
When you delete a tag using delete_tag, the BookmarkService automatically performs a cascading cleanup. It iterates through every bookmark that uses that tag, removes the tag reference, saves the bookmark, and invalidates the cache.
# This will remove the tag from all associated bookmarks before deleting the tag itself
success = service.delete_tag(tag.id)
Grouping with Collections
Collections allow you to group bookmarks into named sets. There are two types of collections: Manual (where you explicitly add bookmarks) and Smart (which auto-populate based on a filter rule).
Creating Collections
You create collections by specifying a name and a type (manual or smart).
# Create a manual collection
manual_data = {
"name": "Reading List",
"type": "manual"
}
reading_list, _ = service.create_collection(manual_data)
# Create a smart collection that automatically includes bookmarks with 'python' in the title
smart_data = {
"name": "Python Articles",
"type": "smart",
"filter_rule": "python"
}
python_collection, _ = service.create_collection(smart_data)
Managing Manual Collections
For manual collections, use add_to_collection and remove_from_collection to manage the membership.
# Add a bookmark to the 'Reading List'
success = service.add_to_collection(reading_list.id, bookmark.id)
if not success:
# Fails if collection is smart or bookmark is already present
print("Could not add bookmark to collection")
# Remove a bookmark
service.remove_from_collection(reading_list.id, bookmark.id)
Troubleshooting and Constraints
Reserved Tag Names
The system prevents you from creating tags with names that conflict with internal system views. The following names are reserved and will return a validation error:
alluntaggedarchivedtrash
Validation Limits
- Tag Names: Must be 50 characters or fewer.
- Collection Names: Required and cannot be empty (whitespace is stripped).
- Smart Collections: The
filter_ruleis currently limited to keyword matching against the bookmark's title and description.
Performance of Tag Deletion
Because delete_tag performs a linear scan and update of all bookmarks containing that tag, deleting a very popular tag (used by thousands of bookmarks) may be a slow operation as it triggers multiple repository saves and cache invalidations.
Smart Collection Population
While the Collection model supports an internal _apply_filter method, the BookmarkService currently focuses on manual membership. Smart collections are primarily used for UI-side filtering or internal repository queries based on the filter_rule.