search.py 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293
  1. from django.contrib.postgres.search import SearchQuery, SearchRank, SearchVector
  2. from django.utils.translation import gettext_lazy as _
  3. from misago.conf import settings
  4. from misago.core.shortcuts import paginate, pagination_dict
  5. from misago.search import SearchProvider
  6. from .filtersearch import filter_search
  7. from .models import Post, Thread
  8. from .permissions import exclude_invisible_threads
  9. from .serializers import FeedSerializer
  10. from .utils import add_categories_to_items
  11. from .viewmodels import ThreadsRootCategory
  12. HITS_CEILING = settings.MISAGO_POSTS_PER_PAGE * 5
  13. class SearchThreads(SearchProvider):
  14. name = _("Threads")
  15. icon = "forum"
  16. url = "threads"
  17. def search(self, query, page=1):
  18. root_category = ThreadsRootCategory(self.request)
  19. threads_categories = [root_category.unwrap()] + root_category.subcategories
  20. if len(query) > 2:
  21. visible_threads = exclude_invisible_threads(
  22. self.request.user_acl, threads_categories, Thread.objects
  23. )
  24. results = search_threads(self.request, query, visible_threads)
  25. else:
  26. results = []
  27. list_page = paginate(
  28. results,
  29. page,
  30. settings.MISAGO_POSTS_PER_PAGE,
  31. settings.MISAGO_POSTS_TAIL,
  32. allow_explicit_first_page=True,
  33. )
  34. paginator = pagination_dict(list_page)
  35. posts = []
  36. threads = []
  37. if paginator["count"]:
  38. posts = list(
  39. list_page.object_list.select_related("thread", "poster", "poster__rank")
  40. )
  41. threads = []
  42. for post in posts:
  43. threads.append(post.thread)
  44. add_categories_to_items(
  45. root_category.unwrap(), threads_categories, posts + threads
  46. )
  47. results = {
  48. "results": FeedSerializer(
  49. posts, many=True, context={"user": self.request.user}
  50. ).data
  51. }
  52. results.update(paginator)
  53. return results
  54. def search_threads(request, query, visible_threads):
  55. search_query = SearchQuery(
  56. filter_search(query), config=settings.MISAGO_SEARCH_CONFIG
  57. )
  58. search_vector = SearchVector(
  59. "search_document", config=settings.MISAGO_SEARCH_CONFIG
  60. )
  61. queryset = Post.objects.filter(
  62. is_event=False,
  63. is_hidden=False,
  64. is_unapproved=False,
  65. thread_id__in=visible_threads.values("id"),
  66. search_vector=search_query,
  67. )
  68. if queryset[: HITS_CEILING + 1].count() > HITS_CEILING:
  69. queryset = queryset.order_by("-id")[:HITS_CEILING]
  70. return (
  71. Post.objects.filter(id__in=queryset.values("id"))
  72. .annotate(rank=SearchRank(search_vector, search_query))
  73. .order_by("-rank", "-id")
  74. )