Django を使った ElasticSearch の簡単な方法
アダム・ワティス著
少し前、私は Django プロジェクトに取り組んでいて、高速なフリーテキスト検索を実装したいと考えていました。この検索機能に通常のデータベース (MySQL や PostgreSQL など) を使用する代わりに、NoSQL データベースを使用することにしました。そんなときにElasticSearchを発見しました。
ElasticSearch は、通常のリレーショナル データベースのようにデータ テーブルを使用するのではなく、データのドキュメントにインデックスを付けます。これにより検索が高速化され、通常のデータベースでは得られない他の多くの利点が得られます。ユーザーの詳細、ログイン、および ElasticSearch がインデックスを作成する必要のないその他のデータを保存するために、通常のリレーショナル データベースも保持しました。
Django で ElasticSearch を適切に実装する方法について長い間検索しましたが、満足のいく答えは見つかりませんでした。一部のガイドやチュートリアルは複雑で、データを ElasticSearch にインデックスするために不必要な手順を踏んでいるように見えました。検索の実行方法についてはかなりの情報がありましたが、インデックス作成の方法についてはあまり情報がありませんでした。もっと簡単な解決策があるはずだと感じたので、自分で試してみることにしました。
私の意見では、シンプルな解決策が最善であることが多いため、できるだけシンプルにしたいと考えました。 KISS (Keep It Simple Stupid)、Less is More などはすべて、特に世の中のあらゆるソリューションが複雑な場合に、私にとってとても心に響くものです。コードのベースとなるものを得るために、このビデオで Honza Král の例を使用することにしました。現時点では少し古いですが、視聴することをお勧めします。
私は Python で書かれた Django を使用していたので、ElasticSearch とのやり取りは簡単でした。 Python を使用して ElasticSearch と対話するための 2 つのクライアント ライブラリがあります。公式の低レベルクライアントである elasticsearch-py があります。そして、elasticsearch-dsl があります。これは前者に基づいて構築されていますが、機能は少し少ないものの、より高いレベルの抽象化を提供します。
すぐにいくつかの例を取り上げますが、まず、何を達成したいのかを明確にする必要があります。
- ローカルマシンに ElasticSearch をセットアップし、正しく動作することを確認する
- 新しい Django プロジェクトのセットアップ
- すでにデータベース内にあるデータの一括インデックス作成
- ユーザーがデータベースに保存するすべての新しいインスタンスのインデックス作成
- 基本的な検索例
わかりました、それは十分に簡単なことのようです。 ElasticSearch をマシンにインストールすることから始めましょう。また、すべてのコードは私の GitHub で入手できるので、簡単に例に従うことができます。
ElasticSearch のインストール
ElasticSearch は Java 上で実行されるため、JVM バージョンが更新されていることを確認する必要があります。ターミナルの java -version
でバージョンを確認してください。次に、次のコマンドを実行して新しいディレクトリを作成し、ダウンロードして抽出し、ElasticSearch を開始します。
mkdir elasticsearch-example
wget https://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 http://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)
ここまではかなり簡単です。 settings.py
の INSTALLED_APPS
に elasticsearchapp
を追加し、admin.py
に新しい BlogPost モデルを登録することを忘れないでください。 > このように:
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 merge
、および python manage.py createsuperuser
も必要です。次に、python manage.py runserver
で、http://localhost:8000/admin/
に移動してログインします。そこにブログ投稿モデルが表示されるはずです。管理画面で最初のブログ投稿を作成してください。
おめでとうございます。Django プロジェクトが機能するようになりました。いよいよ楽しい作業、つまり ElasticSearch の接続に取り掛かります。
ElasticSearch と Django の接続
まず、elasticsearchapp
ディレクトリに search.py
という新しいファイルを作成します。ここに 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
は、それらを取得するためのフィールドです。インデックス作成時に正しい形式で保存されます。
Meta 内で、インデックスに付ける名前を ElasticSearch に指定します。これは ElasticSearch の参照点となり、データベース内でインデックスを初期化し、作成された各新しいオブジェクト インスタンスを保存するときに、どのインデックスを扱っているかを知ることができます。
次に、ElasticSearch で新しく作成した BlogPostIndex
のマッピングを実際に作成する必要があります。これを行うと同時に、一括インデックス作成を行う方法も作成できます。とても便利ですよね?
データの一括インデックス作成
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()))
「ここで何が起こっているのですか? 』と思っているかもしれません。実際にはそれほど複雑ではありません。
モデル内で何かを変更するたびに一括インデックスを作成したいだけなので、モデルを ElasticSearch にマッピングするモデルを init()
します。次に、bulk
を使用して、ElasticSearch への接続を作成する Elasticsearch()
のインスタンスを渡します。次に、ジェネレーターを actions=
に渡し、通常のデータベースにあるすべての BlogPost
オブジェクトを反復処理して、.indexing()
メソッドを呼び出します。それぞれのオブジェクトに。なぜ発電機なのか?反復処理するオブジェクトが多数ある場合、ジェネレーターは最初にオブジェクトをメモリにロードする必要がないからです。
上記のコードには問題が 1 つだけあります。モデルにはまだ .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
モデルに追加します。 BlogPostIndex
を返し、ElasticSearch に保存されます。
今すぐこれを試して、以前に作成したブログ投稿に一括インデックスを作成できるかどうかを確認してみましょう。 python manage.py shell
を実行すると、Django シェルに移動し、from elasticsearchapp.search import *
を使用して search.py
をインポートし、実行します。 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 receiver@receiver(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 に伝える必要もあります。これを elasticsearchapp
ディレクトリ内の __init__.py
内で次のように追加します。
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"))<Response: [<Result(blogpost-index/blog_post_index/1): {'text': 'Hello world, this is my first blog post', 'title':...}>]>
ほら、ありますよ!これで、すべてのインスタンスの ElasticSearch へのインデックス付けが完了し、新しく保存された各インスタンスにインデックス付けする post_save
シグナルが作成され、ElasticSearch データベースでデータを検索する関数が作成されました。
結論
かなり長い記事になってしまいましたが、初心者でも理解できるようにわかりやすく書いていただければ幸いです。
インデックス作成と検索のために Django モデルを ElasticSearch に接続する方法を説明しましたが、ElasticSearch でできることは他にもたくさんあります。彼らの Web サイトを読んで、空間操作やインテリジェントな強調表示による全文検索など、他の可能性が存在するかどうかを検討することをお勧めします。素晴らしいツールなので、将来のプロジェクトでも必ず使用します。
この記事が気に入った場合、またはコメントや提案がある場合は、お気軽に以下にメッセージを残してください。さらに興味深い内容にご期待ください!