Создайте невероятно быстрый REST API, используя Django + Elasticsearch + Haystack

Поиск является наиболее важной частью вашего веб-сайта, а быстрый поиск еще более важен. Хотя пользователь не может найти что-то вручную или у него заканчивается время, он использует поиск, и медленный поиск может еще больше расстроить вашего пользователя.

В этом блоге мы создадим REST API, используя

  1. Джанго REST Framework
  2. Джанго Хейстек
  3. ДРФ — стог сена
  4. Эластичный поиск 7.

Здесь мы создаем API для приложения для фильмов, такого как netflix или любой другой потоковый веб-сайт. если вы хотите напрямую проверить код тогда прыгай сюда.

На данный момент ElasticSearch — лучшая поисковая система для поиска текста, и она очень быстрая. Его также очень легко интегрировать с Django. При поиске в приложении вы также можете фильтровать результаты по году, жанру и рейтингу. Если вы сделаете эту же функциональность, используя поиск Django по умолчанию, например, используя icontains или contains тогда поиск и фильтрация результатов займет много времени, поскольку мы попадаем в базу данных, а попадание в БД стоит вам времени. Но с ElasticSearch это происходит очень быстро, поскольку вместо того, чтобы обращаться к базе данных, мы обращаемся к эластичным данным поиска, которые являются индексированными данными, и, таким образом, гораздо быстрее выполнять запросы, чем обычная БД.

Я не буду углубляться, так как для каждой технологии, используемой в этом руководстве, можно написать несколько блогов.

Прежде чем двигаться дальше, я предполагаю, что у вас есть некоторые базовые знания о

если вы соответствуете вышеперечисленным требованиям, то все готово, а если нет, давайте двигаться вперед, и вы поймете, как все работает при создании такого типа функциональности.🙂

Итак, начнем — LetsGetStartedСубботаНочьLiveGIF.gif

Чтобы сэкономить время, я уже сделал git репозиторий с полной инструкцией. Я буду делать бросок того, как я сделал это с достаточным количеством объяснений.

git clone 
sudo apt update -y sudo apt install libpq-dev postgresql postgresql-contrib -y
sudo service postgresql start sudo add-apt-repository ppa:deadsnakes/ppa sudo apt update -y
sudo apt-get install apt-transport-https
sudo apt install python3.8 python3.8-dev python3.8-venv build-essential -y sudo apt install openjdk-11-jdk openjdk-11-jre -y curl -fsSL  | sudo apt-key add -
echo "deb  stable main" | sudo tee -a /etc/apt/sources.list.d/elastic-7.x.list
sudo apt update
sudo apt install elasticsearch -y
sudo service elasticsearch start
sudo service elasticsearch status

Выше мы устанавливаем Elastic Search вместе с Java, которая требуется для запуска. В то время как вы, ребята, возможно, в основном используете реляционные базы данных, такие как Postgres или mysql, которые хранят данные в форме таблицы. Elastic Search — это база данных NoSQL с открытым исходным кодом, которая хранит данные в формате JSON. Кроме того, данные индексируются и очень быстро запрашиваются, если вы сравнивали время поиска с другими базами данных.

Эластичный поиск имеет открытый исходный код, но есть услуги, за которые вам, возможно, придется платить так же, как вы платите за них. монгодб при использовании их платных услуг. Теперь здесь. Мы используем Postgres для хранения данных фильма и эластичного поиска для хранения копии данных фильма, но эти данные будут проиндексированы, как я сказал выше.

sudo -u postgres psql
CREATE DATABASE django_flix;
CREATE USER django_flix_user WITH PASSWORD 'html_programmer';
ALTER ROLE django_flix_user SET client_encoding TO 'utf8';
ALTER ROLE django_flix_user SET default_transaction_isolation TO 'read committed';
ALTER ROLE django_flix_user SET timezone TO 'UTC';
GRANT ALL PRIVILEGES ON DATABASE django_flix TO django_flix_user;
\q
python3.8 -m venv venv
source venv/bin/activate
pip install --upgrade pip
pip install wheel
pip install -r requirements.txt

./manage.py migrate
./manage.py createsuperuser

Подождите .. вы изменили каталог на папку проекта. если нет, пожалуйста.😁

Теперь нам нужно сгенерировать много данных для тестирования нашего API. В терминале введите

./manage.py generate_test_data 1000000

ЯнМашGIF.gif

  • 💡Если вы не можете дождаться создания больших данных, вот совет. Чтобы сэкономить время, я попробовал этот API только с 600 тыс. записей, что заняло много времени. Поэтому я бы предложил попробовать всего 5000 записей и запустить эту команду в разных окнах терминала для параллельной генерации данных.
./manage.py runserver


,genre:rise

📌Вы можете игнорировать это предупреждение ElasticSearch, пока не используете его в производственной среде.

предупреждение.png

📌Сгенерированные данные нереалистичны… они просто для демонстрации. Но, как вы можете видеть на панели отладки справа, счетчик SQL равен 0, что означает, что он не попадает в базу данных. Это прямое попадание в ElasticSearch

Скриншот 09.10.2022 084412.png

Теперь, когда вы протестировали API, давайте посмотрим, как он работает. открытым settings.pyДжанго Хейстек это пакет django, который предоставляет SearchQuerySet и многие другие API, которые вы можете использовать для эффективного взаимодействия с ElasticSearch и поиска ваших данных.

INSTALLED_APPS = [ "django.contrib.admin", "django.contrib.auth", "django.contrib.contenttypes", "django.contrib.sessions", "django.contrib.messages", "django.contrib.staticfiles", "django_extensions", "rest_framework", "haystack", "apps.core", "debug_toolbar",
]
HAYSTACK_CONNECTIONS = { "default": { "ENGINE": "haystack.backends.elasticsearch7_backend.Elasticsearch7SearchEngine", "URL": " "INDEX_NAME": "django_flix", },
} HAYSTACK_SIGNAL_PROCESSOR = "haystack.signals.RealtimeSignalProcessor"

После этого направляйтесь к core/search_indexes.py. этот файл содержит как сопоставление. например, как вы хотите индексировать данные SQL DB в ElasticSearch.

Вот как это устроено —

from haystack import indexes
from .models import Movie class MovieIndex(indexes.SearchIndex, indexes.Indexable): id = indexes.IntegerField(model_attr="id") text = indexes.CharField(document=True, use_template=True) title = indexes.CharField(model_attr="title") description = indexes.CharField(model_attr="description") year = indexes.IntegerField(model_attr="year") rating = indexes.FloatField(model_attr="rating") global_ranking = indexes.IntegerField(model_attr="global_ranking") length = indexes.CharField(model_attr="length", faceted=True) revenue = indexes.FloatField(model_attr="revenue", faceted=True) genre = indexes.CharField(model_attr="genre", faceted=True) country = indexes.CharField(model_attr="country", faceted=True) director = indexes.CharField(model_attr="director", faceted=True) def get_model(self): return Movie def prepare_director(self, obj): return obj.director.name def prepare_genre(self, obj): return obj.genre.name def prepare_country(self, obj): return obj.country.name def index_queryset(self, using=None): return self.get_model().objects.all()

Некоторые вещи, чтобы отметить здесь —

Facet -> Перейдите на Amazon, и при поиске любого продукта вы увидите несколько фильтров с левой стороны. эти фильтры на самом деле являются фасетными полями.

faceted=True -> какое бы поле вы ни ввели в этом kwarg, это поле затем можно будет использовать для целей фильтрации. Поэтому при поиске вы можете искать так 👇

model_attr -> Поле модели, которое вы имеете в виду на карте.

base_api_url.com/?q=some_query&facets=year:1983,genre:rise

Теперь перейдите к core/views.py и первое представление, которое вы там увидели, отвечает за рендеринг этих данных.

class SearchViewElk(APIView, LimitOffsetPagination): default_limit = 10 serializer_class = MovieHayStackSerializer def get(self, request): query = request.GET.get("q", None) highlight = request.GET.get("highlight", None) facets = request.GET.get("facets", None) sqs = SearchQuerySet().models(Movie) if query: query_list = query.split(" ") qs_item = reduce( operator.and_, (Q(text__contains=item) for item in query_list) ) sqs = sqs.filter(qs_item) if highlight: sqs = sqs.highlight() if facets: sqs = self.filter_sqs_by_facets(sqs, facets) page = self.paginate_queryset(sqs, request, view=self) movie_serializer = self.serializer_class(page, many=True) facets = self.get_facet_fields(sqs) summary = self.prepare_summary(sqs) data = {"movies": movie_serializer.data, "facets": facets, "summary": summary} return Response(data, status=HTTP_200_OK) def filter_sqs_by_facets(self, sqs, facets): facet_list = facets.split(",") for facet in facet_list: facet_key, facet_value = facet.split(":") sqs = sqs.narrow(f"{facet_key}:{facet_value}") return sqs def get_facet_fields(self, sqs): facet_fields = ( sqs.facet("year") .facet("rating") .facet("global_ranking") .facet("length") .facet("revenue") .facet("country") .facet("genre") ) return facet_fields.facet_counts() def prepare_summary(self, sqs): summary = { "total": sqs.count(), "next_page": self.get_next_link(), "previous_page": self.get_previous_link(), } return summary

Хоть я и упомянул правильные комментарии, но есть некоторые термины, новые для вас.

SearchQuerySet() -> Класс SearchQuery действует как посредник между абстракцией SearchQuerySet и фактическим поиском SearchBackend. Учитывая метаданные, предоставленные SearchQuerySet, SearchQuery создает фактический запрос и взаимодействует с SearchBackend от имени SearchQuerySet. Любой SearchQuerySet obj в основном такой же, как Django Queryset Объект. Это означает, что вы можете использовать фильтр и другие операции, как вы используете Django Model QuerySet.

sqs.highlight() -> Это приведет к тому, что результаты будут содержать highlighted ключ в ответе Json, и выделенный текст будет заключен в <em> теги. вы также можете настроить его на пользовательский класс, а затем выполнить стилизацию на лицевой стороне для данного класса.

sqs.facet('some_facetable_field') -> Получите все возможные грани, например, если вы перевернете year поле, которое также имеет faceted=true в search_indexes.py. вы получите объект, который содержит информацию о том, сколько фильмов было создано в каждый конкретный год.

.facet_counts() -> Так как sqs.facet возвращает объект, нам нужно получить значения из этого объекта, и этот атрибут сделал эту работу.

Существует также сериализатор для преобразования SearchQuerySet из Haystack в json.

from drf_haystack.serializers import HaystackSerializer from .search_indexes import MovieIndex class MovieHayStackSerializer(HaystackSerializer): class Meta: index_classes = [MovieIndex] fields = [ "title", "description", "year", "rating", "global_ranking", "length", "revenue", "genre", "country", "director",
        ]

UnamusedCatGIF.gif

И теперь у вас есть готовый API. вы можете сделать другие настройки, такие как AutoQuery который позволяет искать так же, как Google. это означает, что если вы добавите - перед любым запросом, то результаты, соответствующие запросу, будут исключены. и есть слишком много других вариантов настройки, которые можно проверить на документация.

Это мой первый блог, и я знаю, что он не очень понятен, но я постараюсь стать лучше. 😁

Полезные ссылки

Отказ от ответственности — я не эксперт и все еще учусь. Таким образом, могут быть некоторые вещи, которые я мог пропустить или которые могут быть неправильно объяснены. Как только я получу какой-либо комментарий об ошибках или где-то выясню, я исправлю это в блоге.

Похожие записи

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *