Skip to content

Instantly share code, notes, and snippets.

@jgram925
Last active June 15, 2019 17:52
Show Gist options
  • Save jgram925/a45f9e74124ece05b940fc15d2604870 to your computer and use it in GitHub Desktop.
Save jgram925/a45f9e74124ece05b940fc15d2604870 to your computer and use it in GitHub Desktop.
Elasticsearch-DSL Setup.md

Useful Commands

Create Index

This should only be used for testing. The index that will be used is created from the bulk_indexing method below.

curl -X PUT 'localhost:9200/<index_name>'

Check Indices

Easy way to check if index was created or deleted.

curl -X GET 'localhost:9200/_cat/indices?v'

Delete Index

If you make any changes to the index you'll need to delete it and rebuild it using the build_indexing method below.

curl -X DELETE 'localhost:9200/<index_name>'

Installing ElasticSearch

Source: Elasticsearch Setup

Import the ElasticSearch PGP Key

Download and install the public signing key:

wget -qO - https://artifacts.elastic.co/GPG-KEY-elasticsearch | sudo apt-key add -

Installing from the APT repository

You can install the ElasticSearch Debian package with:

sudo apt-get update && sudo apt-get install elasticsearch

Daemonize ElasticSearch with Systemd

To configure ElasticSearch to start automatically when the system boots up, run the following commands:

sudo /bin/systemctl daemon-reload
sudo /bin/systemctl enable elasticsearch.service

ElasticSearch can be started and stopped as follows:

sudo systemctl start elasticsearch.service
sudo systemctl stop elasticsearch.service

Checking for ElasticSearch

You can test that your Elasticsearch node is running by sending an HTTP request to port 9200 on localhost:

curl -X GET "localhost:9200/"

Which should give you a response something like this:

{
  "name" : "Cp8oag6",
  "cluster_name" : "elasticsearch",
  "cluster_uuid" : "AT69_T_DTp-1qgIJlatQqA",
  "version" : {
    "number" : "6.6.0",
    "build_flavor" : "default",
    "build_type" : "zip",
    "build_hash" : "f27399d",
    "build_date" : "2016-03-30T09:51:41.449Z",
    "build_snapshot" : false,
    "lucene_version" : "7.6.0",
    "minimum_wire_compatibility_version" : "1.2.3",
    "minimum_index_compatibility_version" : "1.2.3"
  },
  "tagline" : "You Know, for Search"
}

Connecting ElasticSearch with Django & Create Indexing

Source: elasticsearch-dsl setup

Install the connector package.

pip install elasticsearch-dsl

Create search.py in your app's folder to connect the app to ElasticSearch, and create the indexing model.

An analyzer is also created here to tokenize any text fields you'll want partial search on. This examples also stripes HTML tags and allows you to search in all upper or lower case letters.

search.py

from elasticsearch_dsl.connections import connections
from elasticsearch_dsl import Document, Text, Date, Integer, analyzer, tokenizer

connections.create_connection()

my_analyzer = analyzer("my_analyzer",
    tokenizer = tokenizer("trigram", nGram, min_gram=3, max_gram=10),
    filter = ['standard', 'lowercase', 'uppercase'],
    char_filter =  [ "html_strip" ]
)

class <ModelNameIndex>(Document):
    title = Text(analyzer=my_analyzer)
    text = Text()
    date = Date()
    number = Integer()

    class Index:
        name = '<ModelName>-index'

The Document wrapper enables writing the index like a model, and Text and Date are for formatting the fields when indexed. The Index class tells ElasticSearch what to name the index.

Bulk Indexing

The bulk command is in elasticsearch.helpers which is included with elasticsearch_dsl. Do the following in search.py:

search.py

from elasticsearch.helpers import bulk
from elasticsearch import Elasticsearch
from . import models

def bulk_indexing():
    <ModelNameIndex>.init()
    es = Elasticsearch()
    bulk(client=es, actions=(b.indexing() for b in models.<ModelName>.objects.all().iterator()))

init() maps the update to ElasticSearch. Pass the instance of Elasticsearch() to create the connection. bulk() uses a generator actions= and iterates over all the model objects by calling indexing().

Now create indexing() on your model.

models.py

from .search import <ModelNameIndex>

def indexing(self):
   obj = <ModelNameIndex>(
       meta={'id': self.id},
       title = self.title,
       text = self.text,
       date = self.date,
       number = self.number,
    )
   obj.save()
   return obj.to_dict(include_meta=True)

Very Important Step!

Complete the bulk indexing using the method below.

Run python manage.py shell.

import from <AppName>.search import * .

Then run bulk_indexing() to index all instances.

Indexing Saved Instances

A signal is needed that fires indexing(). In the app create a file named signals.py and add:

signals.py

from .models import <ModelName>
from django.db.models.signals import post_save
from django.dispatch import receiver

@receiver(post_save, sender=<ModelName>)
def index_post(sender, instance, **kwargs):
    instance.indexing()

The signal needs to be registered in Django. Do this by opening apps.py and adding:

apps.py

from django.apps import <AppNameConfig>

class <AppName>Config(<AppNameConfig>):
    name = 'article'

    def ready(self):
        import <ModelName>.signals

The signals configuration needs to be setup in Django. Do this by opening init.py and adding:

__init__.py

default_app_config = '<AppName>.apps.<AppNameConfig>'

Testing Connection

Create a simple search in search.py to find all articles by title:

search.py

from elasticsearch_dsl import Search

def search(title):
    s = Search().filter('term', title=title)
    response = s.execute()
    return response

Run python manage.py shell. The import from <AppName>.search import * and search using:

print(search(title="whatever title name"))

Setting up in Django

A get request can be used to pass the search.

The request is routed to the search.py search function through views.py.

Where you can pass the 'response' object into the context dictionary and render to a template.

{{template_name}}.html

<form action="{% url 'search_results' %}" method="GET" value="{{request.GET.esearch}}">
    <input type="text" name="esearch" placeholder="Search..."/>
</form>

search.py

def search(request):
    if request.method == 'GET':
        query = request.GET.get('esearch')
         s = Search().query(
            Q('match', title={'query': query, 'boost': 100}) |
            Q('match', content={'query': query, 'boost': 25})
        )
        response = s.execute() 
        ctxt = {"response": response}
        return render(request, 'article/search_results.html', ctxt)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment