ElasticSearch Django-val a könnyű út

Nemrégiben egy Django projekten dolgoztam, és gyors szabad szöveges keresést akartam megvalósítani. Ahelyett, hogy rendszeres adatbázist használnék ehhez a keresési funkcióhoz - például MySQL vagy PostgreSQL -, úgy döntöttem, hogy NoSQL adatbázist használok. Ekkor fedeztem fel az ElasticSearch alkalmazást.

Az ElasticSearch indexeli az Ön dokumentumait, ahelyett, hogy adattáblákat használna, mint egy szokásos relációs adatbázis. Ez felgyorsítja a keresést, és sok más olyan előnyt kínál, amelyeket nem kap meg egy szokásos adatbázissal. Rendszeres relációs adatbázist vezettem a felhasználói adatok, bejelentkezési adatok és egyéb adatok tárolására, amelyeket az ElasticSearch-nak nem kellett indexelnie.

Miután hosszú ideig kerestem az ElasticSearch megfelelő megvalósítását Djangóval, nem igazán találtam kielégítő választ. Egyes útmutatók vagy oktatóanyagok összezavarodtak, és úgy tűnt, hogy felesleges lépéseket tesznek annak érdekében, hogy az adatokat indexeljék az ElasticSearch alkalmazásba. Elég sok információ volt arról, hogyan kell végrehajtani a keresést, de nem annyira, hogy miként kell elvégezni az indexelést. Úgy éreztem, hogy biztosan létezik egyszerűbb megoldás, ezért úgy döntöttem, hogy magam is kipróbálom.

A lehető legegyszerűbbet akartam tartani, mert véleményem szerint az egyszerű megoldások általában a legjobbak. KISS (Keep It Simple Stupid), a kevesebb több, és ezek a dolgok nagyon visszhangoznak velem, főleg, ha minden más megoldás odakint komplex. Úgy döntöttem, hogy Krza Honza példáját felhasználom, hogy legyen mire alapozni a kódomat. Ajánlom megnézni, bár ezen a ponton kissé elavult.

Mivel a Django-t használtam - amelyet Python írt - könnyű volt interakcióba lépni az ElasticSearch-lel. Az ElasticSearch és a Python között két kliens könyvtár működik. Van az elasticsearch-py, amely a hivatalos alacsony szintű kliens. És ott van az elasticsearch-dsl, amely az előbbire épül, de magasabb szintű absztrakciót ad, valamivel kevesebb funkcionalitással.

Hamarosan bemutatunk néhány példát, de először tisztáznom kell, mit akarunk elérni:

  • Az ElasticSearch beállítása a helyi gépünkön és annak megfelelő működésének biztosítása
  • Új Django projekt felállítása
  • Az adatbázisban már szereplő adatok tömeges indexelése
  • A felhasználó minden új példányának indexelése az adatbázisba
  • Alapvető keresési példa

Rendben, ez elég egyszerűnek tűnik. Kezdjük azzal, hogy telepítjük az ElasticSearch gépünket. Ezenkívül az összes kód elérhető lesz a GitHub-on, így könnyen követheti a példákat.

Az ElasticSearch telepítése

Mivel az ElasticSearch Java-on fut, meg kell győződnie arról, hogy rendelkezik-e frissített JVM-verzióval. Ellenőrizze, hogy milyen verzióval rendelkezik java -versiona terminálon. Ezután futtatja a következő parancsokat egy új könyvtár létrehozásához, letöltéséhez, kibontásához és az ElasticSearch indításához:

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

Az ElasticSearch indításakor sok kimenetet kell kinyomtatni a terminál ablakába. Annak ellenőrzéséhez, hogy megfelelően működik-e, nyisson meg egy új terminálablakot és futtassa ezt a curlparancsot:

curl -XGET //localhost:9200

A válasznak ilyennek kell lennie:

{ "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"

Remek, most már az ElasticSearch fut a helyi gépen! Itt az ideje létrehozni a Django projektet.

Django projekt felállítása

Először hozzon létre egy virtuális környezetet a rendszerrel, virtualenv venvés lépjen be azzal source venv/bin/activate, hogy minden benne maradjon. Ezután telepít néhány csomagot:

pip install djangopip install elasticsearch-dsl

A futtatott új Django-projekt elindításához:

django-admin startproject elasticsearchprojectcd elasticsearchprojectpython manage.py startapp elasticsearchapp

Miután létrehozta új Django projektjeit, létre kell hoznia egy modellt, amelyet használni fog. Ehhez az útmutatóhoz egy jó, régimódi blogbejegyzés példát választottam. A models.pykövetkező kódot helyezi el:

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)

Eddig elég egyenesen. Ne felejtsd el hozzáadni elasticsearchappa INSTALLED_APPSbe settings.pyés regisztrálja az új blogbejegyzés modell admin.py, mint ez:

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)

Meg kell azt is python manage.py makemigrations, python manage.py migrate és python manage.py createsuperuseraz adatbázis létrehozása és egy rendszergazdai fiókot. Most, python manage.py runserverlépjen be //localhost:8000/admin/és jelentkezzen be. Most már láthatná a blogbejegyzések modelljét. Folytassa, és hozza létre első blogbejegyzését az adminban.

Gratulálunk, most van egy működő Django projektje! Végre itt az ideje, hogy belekezdjön a szórakoztató dolgokba - összekapcsolva az ElasticSearch-ot.

Az ElasticSearch összekapcsolása Djangóval

Először hozzon létre egy új fájlt search.pya elasticsearchappkönyvtárunkban. Itt fog élni az ElasticSearch kód. Az első dolog, amit meg kell tennie, az a kapcsolat létrehozása a Django alkalmazásból az ElasticSearch-hez. Ezt a search.pyfájljában teszi:

from elasticsearch_dsl.connections import connectionsconnections.create_connection()

Most, hogy globális kapcsolata van az ElasticSearch beállításához, meg kell határoznia, hogy mit szeretne indexelni. Írja be ezt a kódot:

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'

Nagyon hasonlít a modelljéhez, igaz? A DocTypeművek egy wrapper, hogy lehetővé teszi, hogy írjon egy index, mint egy modell, és a Textés Datea mezőket, hogy azok megkapják a megfelelő méret, amikor kap indexelt.

A Meta belsejében elmondja az ElasticSearch-nek, hogy mit szeretne indexelni. Ez lesz az ElasticSearch hivatkozási pontja, hogy tudja, milyen indexszel foglalkozik, amikor inicializálja azt az adatbázisban, és minden új létrehozott objektum-példányt elment.

Most valóban létre kell hoznia az újonnan létrehozott BlogPostIndextérképet az ElasticSearch alkalmazásban. Megteheti ezt, és létrehozhat egy módot a tömeges indexelés egyidejű elvégzésére is - mennyire kényelmes?

Az adatok tömeges indexelése

A bulkparancs található, elasticsearch.helpersamely a telepítéskor benne van, elasticsearch_dslmivel az a könyvtár tetejére épül. Tegye a következőket 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()))

"Mi történik itt?" gondolkodhat. Valójában nem is olyan bonyolult.

Since you only want to do bulk indexing whenever you change something in our model you init() the model which maps it into ElasticSearch. Then, you use the bulk and pass it an instance of Elasticsearch() which will create a connection to ElasticSearch. You then pass a generator to actions= and iterate over all the BlogPost objects you have in your regular database and call the .indexing() method on each object. Why a generator? Because if you had a lot of objects to iterate over a generator would not have to first load them into memory.

There is just one problem with the above code. You don’t have an .indexing() method on your model yet. Lets fix that:

...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)

You add the indexing method to the BlogPost model. It returns a BlogPostIndex and gets saved to ElasticSearch.

Lets try this out now and see if you can bulk index the blog post you previously created. By running python manage.py shell you go into the Django shell and import your search.py with from elasticsearchapp.search import * and then run bulk_indexing() to index all the blog posts in your database. To see if it worked you run the following curl command:

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

You should get back your first blog post in the terminal.

Indexing of newly saved instance

Next you need to add a signal that fires the .indexing() on each new instance that is saved every time a user saves a new blog post. In elasticsearchapp create a new file called signals.py and add this code:

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()

The post_save signal will ensure that the saved instance will get indexed with the .indexing() method after it is saved.

In order for this to work we also need to register Django that we’re using signals. We do this opening apps.py and adding the following code:

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

To to complete this we also need to tell Django that we’re using this new configuration. We do this inside the __init__.py inside our elasticsearchapp directory by adding:

default_app_config = 'elasticsearchapp.apps.ElasticsearchappConfig'

Now the post_save signal is registered with Django and is ready to listen for whenever a new blogpost is being saved.

Try it our by going into the Django admin again and saving a new blogpost. Then check with a curl command if it was successfully indexed into ElasticSearch.

Simple search

Now lets make a simple search function in search.py to find all posts filtered by author:

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

Lets try the search out. In the shell: from elasticsearchapp.search import * and run 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!