linkding/bookmarks/queries.py
Sascha Ißbrücker dd5e65ecd7
Display selected tags in tag cloud (#307)
* Add links to remove tags from current query

* Display selected tags in tag cloud

* Add tag cloud tests

* Fix tag cloud in archive

* Add tests for bookmark views

* Expose parse query string

* Improve tag cloud tests

* Cleanup

* Fix rebase issues

* Ignore casing when removing tags from query

Co-authored-by: Jon Hauris <jonp@hauris.org>
2022-08-04 20:31:24 +02:00

142 lines
4.1 KiB
Python

from typing import Optional
from django.contrib.auth.models import User
from django.db.models import Q, Count, Aggregate, CharField, Value, BooleanField, QuerySet
from bookmarks.models import Bookmark, Tag
from bookmarks.utils import unique
class Concat(Aggregate):
function = 'GROUP_CONCAT'
template = '%(function)s(%(distinct)s%(expressions)s)'
def __init__(self, expression, distinct=False, **extra):
super(Concat, self).__init__(
expression,
distinct='DISTINCT ' if distinct else '',
output_field=CharField(),
**extra)
def query_bookmarks(user: User, query_string: str) -> QuerySet:
return _base_bookmarks_query(user, query_string) \
.filter(is_archived=False)
def query_archived_bookmarks(user: User, query_string: str) -> QuerySet:
return _base_bookmarks_query(user, query_string) \
.filter(is_archived=True)
def query_shared_bookmarks(user: Optional[User], query_string: str) -> QuerySet:
return _base_bookmarks_query(user, query_string) \
.filter(shared=True) \
.filter(owner__profile__enable_sharing=True)
def _base_bookmarks_query(user: Optional[User], query_string: str) -> QuerySet:
# Add aggregated tag info to bookmark instances
query_set = Bookmark.objects \
.annotate(tag_count=Count('tags'),
tag_string=Concat('tags__name'),
tag_projection=Value(True, BooleanField()))
# Filter for user
if user:
query_set = query_set.filter(owner=user)
# Split query into search terms and tags
query = parse_query_string(query_string)
# Filter for search terms and tags
for term in query['search_terms']:
query_set = query_set.filter(
Q(title__contains=term)
| Q(description__contains=term)
| Q(website_title__contains=term)
| Q(website_description__contains=term)
| Q(url__contains=term)
)
for tag_name in query['tag_names']:
query_set = query_set.filter(
tags__name__iexact=tag_name
)
# Untagged bookmarks
if query['untagged']:
query_set = query_set.filter(
tags=None
)
# Unread bookmarks
if query['unread']:
query_set = query_set.filter(
unread=True
)
# Sort by date added
query_set = query_set.order_by('-date_added')
return query_set
def query_bookmark_tags(user: User, query_string: str) -> QuerySet:
bookmarks_query = query_bookmarks(user, query_string)
query_set = Tag.objects.filter(bookmark__in=bookmarks_query)
return query_set.distinct()
def query_archived_bookmark_tags(user: User, query_string: str) -> QuerySet:
bookmarks_query = query_archived_bookmarks(user, query_string)
query_set = Tag.objects.filter(bookmark__in=bookmarks_query)
return query_set.distinct()
def query_shared_bookmark_tags(user: Optional[User], query_string: str) -> QuerySet:
bookmarks_query = query_shared_bookmarks(user, query_string)
query_set = Tag.objects.filter(bookmark__in=bookmarks_query)
return query_set.distinct()
def query_shared_bookmark_users(query_string: str) -> QuerySet:
bookmarks_query = query_shared_bookmarks(None, query_string)
query_set = User.objects.filter(bookmark__in=bookmarks_query)
return query_set.distinct()
def get_user_tags(user: User):
return Tag.objects.filter(owner=user).all()
def parse_query_string(query_string):
# Sanitize query params
if not query_string:
query_string = ''
# Split query into search terms and tags
keywords = query_string.strip().split(' ')
keywords = [word for word in keywords if word]
search_terms = [word for word in keywords if word[0] != '#' and word[0] != '!']
tag_names = [word[1:] for word in keywords if word[0] == '#']
tag_names = unique(tag_names, str.lower)
# Special search commands
untagged = '!untagged' in keywords
unread = '!unread' in keywords
return {
'search_terms': search_terms,
'tag_names': tag_names,
'untagged': untagged,
'unread': unread,
}