ElasticSearch за допомогою Django - простий спосіб

Деякий час тому я працював над проектом Django і хотів здійснити швидкий пошук вільного тексту. Замість того, щоб використовувати звичайну базу даних для цієї функції пошуку - наприклад, MySQL або PostgreSQL - я вирішив використовувати базу даних NoSQL. Саме тоді я виявив ElasticSearch.

ElasticSearch індексує документи для ваших даних, а не використовує таблиці даних, як це робить звичайна реляційна база даних. Це пришвидшує пошук і пропонує безліч інших переваг, яких ви не отримуєте від звичайної бази даних. Я також регулярно зберігав реляційну базу даних для зберігання даних користувачів, логінів та інших даних, які ElasticSearch не потрібно було індексувати.

Після довгого пошуку того, як правильно впровадити ElasticSearch за допомогою Django, я насправді не знайшов жодної задовільної відповіді. Деякі посібники або навчальні посібники були заплутаними і, здавалося, робили непотрібні кроки для індексації даних у ElasticSearch. Було досить багато інформації про те, як виконувати пошук, але не стільки про те, як слід проводити індексацію. Я відчував, що там повинно бути простіше рішення, тому вирішив спробувати сам.

Я хотів зробити це максимально простим, оскільки, на мій погляд, прості рішення, як правило, є найкращими. ПОЦІЛУЙ (Нехай це буде просто дурно), Менше - це більше, і все це - щось, що дуже резонує у мене, особливо коли кожне інше рішення там складне. Я вирішив використати приклад Honza Král у цьому відео, щоб мати на чому базувати свій код. Я рекомендую переглянути його, хоча на даний момент він трохи застарів.

Оскільки я використовував Django - який написаний на Python - мені було легко взаємодіяти з ElasticSearch. Є дві клієнтські бібліотеки для взаємодії з ElasticSearch з Python. Є elasticsearch-py, який є офіційним клієнтом низького рівня. І є elasticsearch-dsl, який побудований на першому, але дає абстракцію вищого рівня з трохи меншою функціональністю.

Незабаром ми розглянемо якийсь приклад, але спочатку мені потрібно пояснити, що ми хочемо досягти:

  • Налаштування ElasticSearch на нашій локальній машині та забезпечення належної роботи
  • Налаштування нового проекту Django
  • Масова індексація даних, які вже є в базі даних
  • Індексація кожного нового екземпляра, який користувач зберігає в базі даних
  • Основний приклад пошуку

Гаразд, це здається досить простим. Давайте почнемо з установки ElasticSearch на нашу машину. Крім того, весь код буде доступний на моєму GitHub, щоб ви могли легко слідувати прикладам.

Встановлення ElasticSearch

Оскільки ElasticSearch працює на Java, ви повинні переконатися, що у вас є оновлена ​​версія JVM. Перевірте, яка у вас версія java -versionв терміналі. Потім ви запускаєте такі команди, щоб створити новий каталог, завантажити, розпакувати та запустити ElasticSearch:

mkdir elasticsearch-example
wget //artifacts.elastic.co/downloads/elasticsearch/elasticsearch-5.1.1.tar.gz
tar -xzf elasticsearch-5.1.1.tar.gz
./elasticsearch-5.1.1/bin/elasticsearch

Коли ElasticSearch запускається, у вікні терміналу має бути надруковано багато результатів. Щоб перевірити, чи правильно він працює і працює, відкрийте нове вікно терміналу та запустіть цю curlкоманду:

curl -XGET //localhost:9200

Відповідь повинна бути приблизно такою:

{ "name" : "6xIrzqq", "cluster_name" : "elasticsearch", "cluster_uuid" : "eUH9REKyQOy4RKPzkuRI1g", "version" : { "number" : "5.1.1", "build_hash" : "5395e21", "build_date" : "2016-12-06T12:36:15.409Z", "build_snapshot" : false, "lucene_version" : "6.3.0" }, "tagline" : "You Know, for Search"

Чудово, тепер у вас на вашому локальному комп'ютері працює ElasticSearch! Пора налаштувати свій проект Django.

Налаштування проекту Django

Спочатку ви створюєте віртуальне середовище virtualenv venvта вводите його source venv/bin/activate, щоб зберегти все, що міститься. Потім ви встановлюєте кілька пакетів:

pip install djangopip install elasticsearch-dsl

Щоб розпочати новий проект Django, який ви запускаєте:

django-admin startproject elasticsearchprojectcd elasticsearchprojectpython manage.py startapp elasticsearchapp

Після створення нових проектів Django вам потрібно створити модель, яку ви будете використовувати. У цьому посібнику я вибрав старий добрий приклад публікації в блозі. У models.pyвас розміщується такий код:

from django.db import modelsfrom django.utils import timezonefrom django.contrib.auth.models import User# Create your models here.# Blogpost to be indexed into ElasticSearchclass BlogPost(models.Model): author = models.ForeignKey(User, on_delete=models.CASCADE, related_name="blogpost") posted_date = models.DateField(default=timezone.now) title = models.CharField(max_length=200) text = models.TextField(max_length=1000)

Поки що прямо вперед. Не забудьте додати elasticsearchappдо INSTALLED_APPSв settings.pyі зареєструвати нову модель BlogPost в admin.pyтакий спосіб:

from django.contrib import adminfrom .models import BlogPost# Register your models here.# Need to register my BlogPost so it shows up in the adminadmin.site.register(BlogPost)

Ви повинні також python manage.py makemigrations, python manage.py migrate і python manage.py createsuperuserстворити базу даних і обліковий запис адміністратора. Тепер python manage.py runserverперейдіть на сторінку //localhost:8000/admin/та увійдіть. Тепер ви зможете побачити там свою модель публікацій у блозі. Вперед і створіть свій перший допис у блозі в адміністраторі.

Вітаємо, тепер у вас є діючий проект Django! Нарешті настав час зайнятися цікавою справою - підключити ElasticSearch.

Підключення ElasticSearch до Django

Ви починаєте зі створення нового файлу, який називається search.pyу нашому elasticsearchappкаталозі. Тут буде жити код ElasticSearch. Перше, що вам потрібно зробити тут, це створити підключення з вашої програми Django до ElasticSearch. Ви робите це у своєму search.pyфайлі:

from elasticsearch_dsl.connections import connectionsconnections.create_connection()

Тепер, коли у вас є глобальне підключення до вашої установки ElasticSearch, вам потрібно визначити, що ви хочете індексувати в неї. Напишіть цей код:

from elasticsearch_dsl.connections import connectionsfrom elasticsearch_dsl import DocType, Text, Dateconnections.create_connection()class BlogPostIndex(DocType): author = Text() posted_date = Date() title = Text() text = Text() class Meta: index = 'blogpost-index'

Це схоже на вашу модель, так? У DocTypeпрацює як обгортка , щоб ви могли писати індекс , як модель, а Textі Dateє поля так , що вони отримують правильний формат , коли вони отримують індексуються.

Усередині мета ви говорите ElasticSearch, як ви хочете, щоб індекс називався. Це буде точкою відліку для ElasticSearch, щоб він знав, з яким індексом має справу, коли ініціалізує його в базі даних і зберігає кожен новий створений екземпляр об'єкта.

Тепер вам потрібно фактично створити відображення вашого щойно створеного BlogPostIndexв ElasticSearch. Ви можете це зробити, а також створити спосіб одночасного масового індексування - наскільки зручно, правда?

Масова індексація даних

bulkКоманда знаходиться в elasticsearch.helpersякий включається при установці , elasticsearch_dslтак як він побудований на вершині цієї бібліотеки. Виконайте наступне search.py:

...from elasticsearch.helpers import bulkfrom elasticsearch import Elasticsearchfrom . import models...
...def bulk_indexing(): BlogPostIndex.init() es = Elasticsearch() bulk(client=es, actions=(b.indexing() for b in models.BlogPost.objects.all().iterator()))

"Що тут відбувається?" ви можете думати. Насправді це не так складно.

Оскільки ви хочете робити масове індексування лише кожного разу, коли ви щось змінюєте в нашій моделі, ви init()створюєте модель, яка відображає її в ElasticSearch. Потім ви використовуєте bulkта Elasticsearch()передаєте йому екземпляр, який створить підключення до ElasticSearch. Потім ви передаєте генератор actions=і переглядаєте всі BlogPostоб'єкти, які є у вашій звичайній базі даних, і викликаєте .indexing()метод для кожного об'єкта. Чому саме генератор? Тому що якби у вас було багато об’єктів для ітерації над генератором, не потрібно було б спочатку завантажувати їх у пам’ять.

Існує лише одна проблема з наведеним вище кодом. У вас ще немає .indexing()методу на вашій моделі. Давайте виправте, що:

...from .search import BlogPostIndex...
...# Add indexing method to BlogPostdef indexing(self): obj = BlogPostIndex( meta={'id': self.id}, author=self.author.username, posted_date=self.posted_date, title=self.title, text=self.text ) obj.save() return obj.to_dict(include_meta=True)

Ви додаєте метод індексації до BlogPostмоделі. Він повертає a BlogPostIndexі зберігається в ElasticSearch.

Давайте спробуємо це зараз і подивимось, чи можете ви масово індексувати попередньо створену публікацію в блозі. При запуску python manage.py shellви йдете в Django оболонки і імпортувати search.pyз , from elasticsearchapp.search import *а потім запустити bulk_indexing()індексувати всі повідомлення в блозі у вашій базі даних. Щоб побачити, чи це спрацювало, запустіть таку команду curl:

curl -XGET 'localhost:9200/blogpost-index/blog_post_index/1?pretty'

Вам слід повернути свою першу публікацію в блозі в терміналі.

Індексація щойно збереженого екземпляра

Далі вам потрібно додати сигнал, який спрацьовує .indexing()на кожному новому екземплярі, який зберігається кожного разу, коли користувач зберігає нову публікацію в блозі. У elasticsearchappстворенні нового файлу з ім'ям signals.pyі додайте цей код:

from .models import BlogPostfrom django.db.models.signals import post_savefrom django.dispatch import [email protected](post_save, sender=BlogPost)def index_post(sender, instance, **kwargs): instance.indexing()

post_saveСигнал буде гарантувати , що збережений екземпляр буде проіндексовано з .indexing()методом після того, як він буде збережений.

Щоб це працювало, нам також потрібно зареєструвати Django, що ми використовуємо сигнали. Ми робимо це відкриття apps.pyі додаємо наступний код:

from django.apps import AppConfigclass ElasticsearchappConfig(AppConfig): name = 'elasticsearchapp' def ready(self): import elasticsearchapp.signals

Для завершення цього нам також потрібно повідомити Django, що ми використовуємо цю нову конфігурацію. Ми робимо це __init__.pyвсередині нашого elasticsearchappкаталогу, додаючи:

default_app_config = 'elasticsearchapp.apps.ElasticsearchappConfig'

Тепер post_saveсигнал зареєстрований у Django і готовий до прослуховування кожного разу, коли зберігається новий блог.

Спробуйте це по-новому, зайшовши ще раз у адміністратора Django та збереживши новий допис у блозі. Потім перевірте за допомогою curlкоманди, чи вона була успішно проіндексована в ElasticSearch.

Простий пошук

Тепер давайте зробимо просту функцію пошуку, search.pyщоб знайти всі дописи, відфільтровані автором:

...from elasticsearch_dsl import DocType, Text, Date, Search...
...def search(author): s = Search().filter('term', author=author) response = s.execute() return response

Давайте спробуємо пошук. У оболонці: from elasticsearchapp.search import *і біжиprint(search(author="gt;")) :

>>> print(search(author="home"))

There you have it! You have now successfully indexed all your instances into ElasticSearch, created a post_save signal that indexes each newly saved instance, and created a function to search our ElasticSearch database for your data.

Conclusion

This was a quite lengthy article but I hope it is written simple enough for even the beginner to be able to understand.

I explained how to connect a Django model to ElasticSearch for indexing and searching, but there is so much more that ElasticSearch can do. I recommend reading on their website and exploring what other possibilities exist, such as spatial operations and full text search with intelligent highlighting. Its a great tool and I will be sure to use it in future projects!

If you liked this article or have a comment or suggestion, please feel free to leave a message below. And stay tuned for more interesting stuff!

Original text