From 06c3ba6f3ba222eaf0557f95e10999a79c9c038c Mon Sep 17 00:00:00 2001 From: Their Name Date: Thu, 30 Dec 2021 15:53:50 -0500 Subject: [PATCH] show search result from meilisearch --- common/index.py | 39 +++++++++++++++++++- common/templates/common/search_result.html | 10 +++--- common/views.py | 42 ++++++++++++++++++++++ 3 files changed, 85 insertions(+), 6 deletions(-) diff --git a/common/index.py b/common/index.py index 44d73edf..1502b1a1 100644 --- a/common/index.py +++ b/common/index.py @@ -7,12 +7,13 @@ from django.db.models.signals import post_save, post_delete # use post_save, post_delete # search result translate back to model INDEX_NAME = 'items' -INDEX_SEARCHABLE_ATTRIBUTES = ['title', 'orig_title', 'other_title', 'subtitle', 'artist', 'author', 'translator', 'developer', 'brief', 'contents', 'track_list', 'pub_house', 'company', 'publisher', 'isbn', 'imdb_code', 'UPC', 'TMDB_ID', 'BANDCAMP_ALBUM_ID'] +INDEX_SEARCHABLE_ATTRIBUTES = ['title', 'orig_title', 'other_title', 'subtitle', 'artist', 'author', 'translator', 'developer', 'director', 'actor', 'playwright', 'brief', 'contents', 'track_list', 'pub_house', 'company', 'publisher', 'isbn', 'imdb_code', 'UPC', 'TMDB_ID', 'BANDCAMP_ALBUM_ID'] INDEXABLE_DIRECT_TYPES = ['BigAutoField', 'BooleanField', 'CharField', 'PositiveIntegerField', 'PositiveSmallIntegerField', 'TextField', 'ArrayField'] INDEXABLE_TIME_TYPES = ['DateTimeField'] INDEXABLE_DICT_TYPES = ['JSONField'] INDEXABLE_FLOAT_TYPES = ['DecimalField'] # NONINDEXABLE_TYPES = ['ForeignKey', 'FileField',] +SEARCH_PAGE_SIZE = 20 def item_post_save_handler(sender, instance, **kwargs): @@ -32,6 +33,8 @@ def tag_post_delete_handler(sender, instance, **kwargs): class Indexer: + class_map = {} + @classmethod def instance(self): return meilisearch.Client(settings.MEILISEARCH_SERVER, settings.MEILISEARCH_KEY).index(INDEX_NAME) @@ -54,6 +57,7 @@ class Indexer: @classmethod def update_model_indexable(self, model): + self.class_map[model.__name__] = model model.indexable_fields = ['tags'] model.indexable_fields_time = [] model.indexable_fields_dict = [] @@ -108,3 +112,36 @@ class Indexer: for f in fields: data[f] = getattr(obj, f) self.instance().update_documents(documents=[data], primary_key=[pk]) + + @classmethod + def search(self, q, page=1, category=None, tag=None, sort=None): + if category or tag: + f = [] + if category == 'music': + f.append("(_class = 'Album' OR _class = 'Song')") + elif category: + f.append(f"_class = '{category}'") + if tag: + f.append(f"tags = '{tag}'") + filter = ' AND '.join(f) + else: + filter = None + options = { + 'offset': (page - 1) * SEARCH_PAGE_SIZE, + 'limit': SEARCH_PAGE_SIZE, + 'filter': filter, + 'facetsDistribution': ['_class'], + 'sort': None + } + r = self.instance().search(q, options) + print(r) + import types + results = types.SimpleNamespace() + results.items = list(map(lambda i: self.item_to_obj(i), r['hits'])) + results.num_pages = (r['nbHits'] + SEARCH_PAGE_SIZE - 1) // SEARCH_PAGE_SIZE + print(results) + return results + + @classmethod + def item_to_obj(self, item): + return self.class_map[item['_class']].objects.get(id=item['id']) diff --git a/common/templates/common/search_result.html b/common/templates/common/search_result.html index b898550f..2d9e74f2 100644 --- a/common/templates/common/search_result.html +++ b/common/templates/common/search_result.html @@ -93,14 +93,14 @@ diff --git a/common/views.py b/common/views.py index 61fdd42c..440db3a6 100644 --- a/common/views.py +++ b/common/views.py @@ -24,6 +24,8 @@ from common.config import * from common.searcher import ExternalSources from management.models import Announcement from django.conf import settings +from common.index import Indexer + logger = logging.getLogger(__name__) @@ -33,8 +35,48 @@ def home(request): return user_home(request, request.user.id) + @login_required def search(request): + category = request.GET.get("c", default='all').strip().lower() + if category == 'all': + category = None + keywords = request.GET.get("q", default='').strip() + tag = request.GET.get("tag", default='').strip() + page_number = request.GET.get('page', default=1) + if not (keywords or tag): + return render( + request, + "common/search_result.html", + { + "items": None, + } + ) + url_validator = URLValidator() + try: + url_validator(keywords) + # validation success + return jump_or_scrape(request, keywords) + except ValidationError as e: + pass + # + result = Indexer.search(keywords, page=page_number, category=category, tag=tag) + for item in result.items: + item.tag_list = item.all_tag_list[:TAG_NUMBER_ON_LIST] + return render( + request, + "common/search_result.html", + { + "items": result.items, + "pagination": PageLinksGenerator(PAGE_LINK_NUMBER, page_number, result.num_pages), + "external_items": ExternalSources.search(category, keywords, page_number), + "categories": ['book', 'movie', 'music', 'game'], + } + ) + + +@login_required +def search2(request): if request.method == 'GET': # test if input serach string is empty or not excluding param ?c=