¿Rápido o más rápido? Django con ElasticSearch

Niriana Blasco
7 min readOct 16, 2018

--

Sabemos lo rápido que es desarrollar un proyecto usando nuestro potente framework Django, pero si queremos hacerlo más rápido en cuanto a búsqueda y análisis de data se refiere, entonces te invito a probar elasticsearch. Ésta herramienta es realmente grandiosa, nos permite indexar toda nuestra data, aplicar filtros que van más allá de simples consultas por fechas, o por coincidencia de texto en un campo; además de poder aplicar métodos de ordenamiento, y todo en tiempos de respuestas muy rápidos.

En este post hablaremos de cómo integrar elasticsearch en nuestro proyecto Django, pero comencemos citando desde la wikipedia la definición de elasticsearch:

Es un servidor de búsqueda basado en Lucene. Provee un motor de búsqueda de texto completo, distribuido y con capacidad de multi-tenencia con una interfaz web RESTful y con documentos JSON. Elasticsearch está desarrollado en Java y está publicado como código abierto bajo las condiciones de la licencia Apache.

Veamos algunos conceptos de forma resumida:

NRT ( Near Real Time): Es un plataforma de búsqueda en “tiempo casi real”, es decir, tiene una latencia de aproximadamente un segundo entre el momento en que se sincronizan los datos y el momento en que están disponibles para su consulta.

Cluster: es una colección de uno o más nodos que, en conjunto, contiene todos los datos, proporciona capacidades de indexación y búsqueda federadas en todos los nodos. Es identificado por un nombre, por defecto el nombre es “elasticsearch”. Asegúrese de no usar el mismo nombre para diferentes entornos, podría usarlos de la siguiente manera: logging-dev, logging-stage y logging-prod.

Nodo: es un servidor único que forma parte de un clúster, almacena sus datos y participa en las capacidades de indexación y búsqueda del clúster. También se identifica por un nombre único, por defecto se le asigna un UUID.

Index: Es una colección de documentos que tienen características similares, en nuestro caso se podría considerar un índice por cada modelo de Django que queramos agregar al motor de búsqueda.

Document: es un documento en formato JSON almacenado en un índice, tiene un type y un id. Es como una fila en una tabla en una base de datos relacional.

Shards & Replicas: Un índice puede almacenar una gran cantidad de datos que pueden exceder los límites de hardware de un solo nodo. Para solucionar éste problema, elasticsearch tiene la habilidad de subdividir su index en múltiples piezas llamadas shards (fragmentos), cada shard es en sí mismo un “índice” totalmente funcional e independiente que puede alojarse en cualquier nodo del clúster.

Shard le permite: dividir o escalar horizontalmente su volumen de contenido, distribuir y paralelizar operaciones a través de fragmentos, aumentando el rendimiento.

Por defecto, a cada índice en Elasticsearch se le asignan 5 fragmentos primarios y 1 réplica, lo que significa que si tiene al menos dos nodos en su clúster, su índice tendrá 5 fragmentos primarios y otros 5 fragmentos de réplica (1 réplica completa) para un total de 10 fragmentos por índice.

Ya es suficiente de definiciones, les recomiendo profundizar un poco e ir a la documentación oficial.

Como lo indica el título del post, vamos a integrar elasticsearch como motor de búsqueda en nuestro proyecto Django, para ello necesitamos algunas configuraciones previas:

  • Elasticsearch ( en local)
  • Proyecto Django

En mi caso tomaré mi pequeño proyecto de shopping_cart alojado en github para realizar la integración.

¡Comencemos!

Instalar Elasticsearch:

Para simplificar el trabajo, usaré una imagen docker con elasticsearch:

  • Descargar la imagen docker de elasticsearch, indicando la versión de elasticsearch ( Esta versión dependerá de la librería que utilizará para hacer la conexión con Django):
docker pull docker.elastic.co/elasticsearch/elasticsearch:6.2.4
  • Ejecutar elasticsearch
docker run -p 9200:9200 -p 9300:9300 -e "discovery.type=single-node" docker.elastic.co/elasticsearch/elasticsearch:6.2.4

¡Listo! tenemos elasticsearch corriendo en nuestra máquina local, para comprobar que funciona ir a un navegador, agregar la url http://localhost:9200/ y nos mostrará la siguiente información:

¿Qué librería usar para Django?

Existen un par de librerías que se pueden integrar en Django, les mencionaré algunas de ellas:

  • hayStack
  • django-elasticsearch-dsl

Yo elegí usar django-elasticsearch-dsl, éste paquete permite la indexación de modelos de Django en elasticsearch, construido como un envoltorio delgado alrededor de elasticsearch-dsl-py (librería de alto nivel de elasticsearch-py) permitiendo el uso de todas sus características y beneficios.

Características:

  • Hace uso de django signals para actualizar los datos en elasticsearch.
  • Tiene comandos de django que permiten crear índices, poblarlos o eliminarlos.
  • Hace el mapping de tus datos a elasticsearch.

Requerimientos:

  • Python 2.7, 3.4, 3.5, 3.6
  • Django >= 1.8
  • Elasticsearch >=2.0 <7.0

Integrar Elasticsearch

Clonamos el proyecto en github, y seguimos las instrucciones.

source venv/bin/activate

Instalar la librería:

pip install django-elasticsearch-dsl

Editar el archivo de configuración del proyecto llamado settings.py y agregar la app django_elasticsearch_dsl a las aplicaciones instaladas:

En el mismo archivo de configuración debemos agregar una variable ELASTICSEARCH_DSL para indicarle a Django a cuál servicio de elasticsearch debe conectarse para la sincronización de datos, en mi caso, como he instalado elasticsearch en local, le proporciono la url donde se encuentra:

Ya tenemos configurado nuestro elasticsearch, ahora debemos decirle qué modelos queremos indexar, para ello, tomaré mi app cart y crearé un archivo llamado documents.py que contendrá la siguiente información:

La libreria django-elasticsearch-dsl reconoce todos los objetos que contiene el decorador @nombre_variable_index.doc_type (@item.doc_type), ahora necesitamos enviar todos nuestros datos a elasticsearch, para ello ésta librería nos proporciona varios comandos:

# Create index
python manage.py search_index --create
# Populate index, es necesario que exista el indice
python manage.py search_index --populate
# Create and populate index
python manage.py search_index --rebuild
# Delete index
python manage.py search_index --delete

Voy a usar el siguiente comando:

python manage.py search_index --rebuild

Ya con los datos sincronizados, podemos hacer consultas a elasticsearch, hay varias opciones para realizar las consultas al api, pero esto dependerá de los objetivos de cada quien. En ésta primera consulta, la haré directamente en el navegador, para ello veamos la estructura de la url que proporciona elasticsearch:

http://localhost:9200/name_index/_search?pretty# Por ejemplo:
http://localhost:9200/item/_search?pretty
  • name_index: corresponde al nombre del índice que definimos en el archivo documents.py , en mi caso: item
  • _search: le indicamos a elasticsearch que deseamos hacer una consulta.
  • pretty: Es para ver los resultados de la búsqueda en formato json de forma legible.

Al colocar ésta url, los datos que se muestran son:

En la imagen, se puede observar toda el estructura del documento correspondiente al índice item (agregué más campos a mi modelo para poder mostrar en el siguiente post consultas usando todo el potencial que nos brinda elasticsearch), cada elemento de la consulta contiene la siguiente información:

  • _index: Nombre del índice al que corresponde, es útil cuando hacemos consultas a varios índices en una sola consulta.
  • _id: Es el id del ítem en nuestra base de datos.
  • _score: Es uno de los datos más importante en las consultas, ya que nos indica numéricamente la frecuencia y coincidencia de la búsqueda en los campos seleccionados. En este caso, estamos mostrando todos los resultados, por lo que éste valor es 1 para todos los elementos.
  • _source: Contiene los campos que escogimos procesar de nuestra base de datos.
  • hits →total, nos indica el total de elementos que contiene nuestro índice. Este valor es importante para poder hacer paginación.

Por defecto elasticsearch muestra sólo los primeros 10 elementos en la búsqueda, si queremos aumentar o disminuir éste valor, debemos pasar size como parámetro a la consulta: http://localhost:9200/item/_search?pretty&size=4

Pedimos sólo 4 elementos en la consulta.

Cuando queramos personalizar las consultas a elasticsearch usando todas sus características como: filtros, ordenamientos, entre otros, hacerla directamente en el navegador no es viable, por lo que debemos usar herramientas que nos permitan pasar parámetros a la consulta, así que usaré Postman lo que me permitió hacer algunas pruebas simples; cuando necesitaba analizar y comparar los datos arrojados por la consulta programé un simple script en python.

Les mostraré un pequeño ejemplo en Postman y en un próximo artículo profundizaremos en las queries y la manipulación de la librería para enviar más datos.

Hemos cumplido el primer objetivo integrar elasticsearch con django, como mencioné al inicio fue realmente rápido y simple. Ahora debemos preocuparnos sólo por elaborar consultas que sean significativas para el usuario. Mostrarle data de su interés al instante. En el próximo post hablaremos sobre las consultas, filtros y métodos de ordenamiento. Espero les haya gustado tanto como a mi, nos vemos el próximo post!

--

--