moviewyrm/bookwyrm/views/search.py
2021-04-30 18:35:09 -07:00

110 lines
3.3 KiB
Python

""" search views"""
import re
from django.contrib.postgres.search import TrigramSimilarity
from django.db.models.functions import Greatest
from django.http import JsonResponse
from django.template.response import TemplateResponse
from django.views import View
from bookwyrm import models
from bookwyrm.connectors import connector_manager
from bookwyrm.utils import regex
from .helpers import is_api_request, privacy_filter
from .helpers import handle_remote_webfinger
# pylint: disable= no-self-use
class Search(View):
"""search users or books"""
def get(self, request, search_type=None):
"""that search bar up top"""
query = request.GET.get("q")
min_confidence = request.GET.get("min_confidence", 0.1)
if is_api_request(request):
# only return local book results via json so we don't cascade
book_results = connector_manager.local_search(
query, min_confidence=min_confidence
)
return JsonResponse([r.json() for r in book_results], safe=False)
data = {"query": query or "", "type": search_type}
results = {}
if query:
# make a guess about what type of query this is for
if search_type == "user" or (not search_type and "@" in query):
results = user_search(query, request.user)
elif search_type == "list":
results = list_search(query, request.user)
else:
results = book_search(query, min_confidence)
return TemplateResponse(
request,
"search/{:s}.html".format(search_type or "book"),
{**data, **results}
)
def book_search(query, min_confidence):
"""that search bar up top"""
return {
"query": query or "",
"results": connector_manager.search(query, min_confidence=min_confidence),
}
def user_search(query, viewer):
"""that search bar up top"""
# logged out viewers can't search users
if not viewer.is_authenticated:
return None
# use webfinger for mastodon style account@domain.com username to load the user if
# they don't exist locally (handle_remote_webfinger will check the db)
if re.match(regex.full_username, query):
handle_remote_webfinger(query)
return {
"query": query,
"results": (
models.User.viewer_aware_objects(viewer)
.annotate(
similarity=Greatest(
TrigramSimilarity("username", query),
TrigramSimilarity("localname", query),
)
)
.filter(
similarity__gt=0.5,
)
.order_by("-similarity")[:10]
),
}
def list_search(query, viewer):
"""any relevent lists?"""
return {
"query": query,
"results": (
privacy_filter(
viewer,
models.List.objects,
privacy_levels=["public", "followers"],
)
.annotate(
similarity=Greatest(
TrigramSimilarity("name", query),
TrigramSimilarity("description", query),
)
)
.filter(
similarity__gt=0.1,
)
.order_by("-similarity")[:10]
),
}