Documentacion Elasticsearch Alumnos
Documentacion Elasticsearch Alumnos
Documentacion Elasticsearch Alumnos
Administración y uso
Ramón Rubio Hortelano
1.2. Portfolio
Un cluster es una colección de uno o mas nodos ( servidores ) que contienen toda la información.
Un cluster se identifica por su nombre, por defecto "elasticsearch". Cada nodo solo puede tener un
cluster y se une a él mediante su nombre.
1.3.2. Nodo
Es un servidor que forma parte del cluster donde se guarda toda la información. Un nodo se
identifica por su nombre. Cada nodo tiene un “cluster_name”, por defecto “elasticsearch”, y todos
los nodos con el mismo nombre se unen automáticamente al mismo cluster.
1.3.3. Índice
1.3.4. Tipo
Dentro de un índice, se pueden definir un solo tipo. Un tipo es una categoría/partición de un índice.
1
Anteriormente, se podían definir múltiples tipos, pero estamos en época de
transición, y desaparecerán en la versión 7 de Elasticsearch. Ahora no es posible
crear más de un tipo por índice, no como en la versión 5.x donde se podía activar
la función de múltiples tipos desde el fichero de configuración
1.3.5. Documento
Es una unidad de información, representada en formato JSON, que puede ser indexada. Dentro de
un índice podemos guardar tantos documentos como queramos, siempre que tengan asignados un
tipo y este a su vez un índice.
Un índice puede almacenar gran cantidad de datos, que pueden exceder los límites de un nodo.
Para resolver este problema elasticsearch divide esta información en trozos, llamados shards. Cada
shard es completamente funcional e independiente y puede ser almacenado en cualquier nodo del
cluster.
Para proporcionar alta disponibilidad ( control a fallos ), elasticsearch crea réplicas de estos shards.
Por defecto se crea una réplica por cada shard aunque podemos cambiarlo dinámicamente ==
Instalación
Para poder instalar elasticsearch es necesario tener instalado Java8 (06/2017), se recomienda la
versión 1.8.0_131 o posterior. Para instalarlo solo se necesita descargar la última versión de
elasticsearch o bien descargarse el proyecto de su página de Github. Una vez instalado usaremos los
paquetes precompilados RPM, DEB o bien el zip/tar
2
$ sudo rpm --import https://artifacts.elastic.co/GPG-KEY-elasticsearch
/etc/yum.repos.d/elasticsearch.repo
[elasticsearch-5.x]
name=Elasticsearch repository for 6.x packages
baseurl=https://artifacts.elastic.co/packages/6.x/yum
gpgcheck=1
gpgkey=https://artifacts.elastic.co/GPG-KEY-elasticsearch
enabled=1
autorefresh=1
type=rpm-md
Para instalarlo:
$./elasticsearch
También podemos iniciarlo cambiando los nombres del cluster y el nodo, aunque se puede hacer
una vez iniciado igualmente.
3
[elasticsearch@localhost ~]$ curl 'http://localhost:9200/?pretty'
{
"name" : "oXhGnWQ",
"cluster_name" : "elasticsearch",
"cluster_uuid" : "zyBc8IT-T7iaIKK6kAl3iA",
"version" : {
"number" : "6.4.2",
"build_flavor" : "default",
"build_type" : "rpm",
"build_hash" : "04711c2",
"build_date" : "2018-09-26T13:34:09.098244Z",
"build_snapshot" : false,
"lucene_version" : "7.4.0",
"minimum_wire_compatibility_version" : "5.6.0",
"minimum_index_compatibility_version" : "5.0.0"
},
"tagline" : "You Know, for Search"
}
1.5. Configuración
Elasticsearch viene prácticamente configurado, preparado para entornos simples y su
configuración está muy bien documentada.
/etc/elasticsearch/elasticsearch.yml
4
Las directivas de Index no se pueden definir directamente en el fichero de
configuración. Se deben definir con el servidor iniciado indicando cual va a ser su
resultado final.
Debemos tener en cuenta que elasticsearch, durante las indexaciones, poseyendo gran cantidad de
shards y réplicas, es imprescindible mantener por encima de 32.000 el límite de ficheros abiertos.
En Linux podemos encontrar esta configuración en el directorio /etc/security/limits.conf, y podemos
ver rápidamente el límite con el comando ulimit
Para editar esta configuración debemos editar el fichero config/jvm.options si nos hemos
descargado el zip/rar o editar /etc/default/elasticsearch si hemos usado los paquetes para linux.
Debemos tener en cuenta la configuración de la variable -Xms/-Xmx que nos permite configurar la
memoria que podrá tener el proceso java de elasticsearch.
Podemos configurar la memoria mínima -Xms ( 2g por defecto ), y la memoria máxima -Xmx ( 2g
por defecto ). Normalmente le daremos el mismo valor a las dos variables ( la mitad de la memoria
RAM disponible ).
5
Capítulo 2. Modelo distribuido
Elasticsearch puede trabajar como un sistema standalone, pero para poder trabajar con gran
cantidad de datos es recomendable usar un Cluster de instancias.
Cada vez que se inicia una instancia de elasticsearch, se inicia un nodo. Una colección de estos
nodos conectados, se llama cluster. Si hemos iniciado un solo nodo, entonces tendremos un cluster
de elasticsearch con un solo nodo.
Por defecto elasticsearch es de naturaleza distribuida y realiza de manera automática todas las
operaciones de descubrimiento, balanceo de carga y comunicación entre sus nodos
Cada vez que añadimos un nuevo índice a nuestro nodo, elasticsearch crea por defecto 5 shards y 1
réplica por cada uno, y dependiendo de cuántos nodos dispongamos se encarga de balancearlos.
Aunque cada shard tenga su réplica, solo uno será primary. si el primary se cae, automáticamente
su réplica es promovida.
Elasticsearch intenta separar los shard en distintos nodos y sus réplicas para proporcionar alta
disponibilidad y que la caía de un nodo en nuestro sistema, no nos suponga una pérdida de datos.
• Aumento del cluster de forma dinámica y redistribución de shards en los nuevos miembros.
Todo ello sin necesidad de gestionarlo directamente. Sin embargo, algunos valores si son necesarios
asignar según la naturaleza de nuestros índices y de nuestros nodos.
6
[elasticsearch@localhost ~]$ curl 'http://localhost:9200/_cluster/health?pretty'
{
"cluster_name" : "elasticsearch",
"status" : "green",
"timed_out" : false,
"number_of_nodes" : 1,
"number_of_data_nodes" : 1,
"active_primary_shards" : 0,
"active_shards" : 0,
"relocating_shards" : 0,
"initializing_shards" : 0,
"unassigned_shards" : 0,
"delayed_unassigned_shards" : 0,
"number_of_pending_tasks" : 0,
"number_of_in_flight_fetch" : 0,
"task_max_waiting_in_queue_millis" : 0,
"active_shards_percent_as_number" : 100.0
}
En la respuesta de cluster/health podemos ver el campo _status este campo indica el estado del
servidor.
• Si está en amarillo, todos los datos están disponibles, pero algunas réplicas no han podido ser
asignadas, el cluster es completamente funcional, aunque no puede garantizar alta
disponibilidad.
• Si está en rojo significa que algunos datos no están disponibles por alguna razón. Aunque el
estado sea rojo, el cluster es parcialmente funcional y puede seguir devolviendo respuestas de
los shards disponibles.
7
Capítulo 3. Operaciones básicas
Todas las APIs proporcionadas por elasticsearch siguen el mismo patrón de acceso
• Indica en orden la salud, el estado, el nombre del índice, el identificador, numero de primary
shards, número de réplicas, documentos indexados, número de documentos eliminados,
Ramaño del almacenamiento utilizado, total de almacenamiento utilizado de los shards y
réplicas.
• Los documentos almacenados y eliminados se cuentan a nivel de Apache Lucene, de esa forma
también indicará todos los documentos ocultos, como los documentos anidados.
3.3. Insertar
Para insertar documentos
8
[elasticsearch@localhost ~]$ curl -XPUT 'localhost:9200/customer/external/1?pretty' -d '{"name": "John Doe"}' -H 'Content
-Type: application/json'
{
"_index" : "customer",
"_type" : "external",
"_id" : "1",
"_version" : 1,
"result" : "created",
"_shards" : {
"total" : 2,
"successful" : 1,
"failed" : 0
},
"_seq_no" : 0,
"_primary_term" : 1
}
3.4. Mostrar
Mostrar un documento
9
[elasticsearch@localhost ~]$ curl -XGET 'localhost:9200/customer/external/1?pretty'
{
"_index" : "customer",
"_type" : "external",
"_id" : "1",
"_version" : 1,
"found" : true,
"_source" : {
"name" : "John Doe"
}
}
3.5. Actualizar
Actualizar un documento
3.6. Borrar
Borrar un documento
10
[elasticsearch@localhost ~]$ curl -XDELETE 'localhost:9200/customer/external/1?pretty'
{
"_index" : "customer",
"_type" : "external",
"_id" : "1",
"_version" : 3,
"result" : "deleted",
"_shards" : {
"total" : 2,
"successful" : 1,
"failed" : 0
},
"_seq_no" : 2,
"_primary_term" : 1
}
Borrar un documento requiere mucha mas carga de CPU debido a que lleva asociadas otras
operaciones, es mucho menos costoso borrar el índice completo.
11
[elasticsearch@localhost ~]$ curl -H 'Content-Type: application/json' -XPOST
'localhost:9200/customer/external/_bulk?pretty' -d '
{"index":{"_id":"1"}}
{"name": "John Doe" }
{"index":{"_id":"2"}}
{"name": "Jane Doe" }
'
{
"took" : 6,
"errors" : false,
"items" : [
{
"index" : {
"_index" : "customer",
"_type" : "external",
"_id" : "1",
"_version" : 1,
"result" : "created",
"_shards" : {
"total" : 2,
"successful" : 1,
"failed" : 0
},
"_seq_no" : 3,
"_primary_term" : 1,
"status" : 201
}
},
{
"index" : {
"_index" : "customer",
"_type" : "external",
"_id" : "2",
"_version" : 1,
"result" : "created",
"_shards" : {
"total" : 2,
"successful" : 1,
"failed" : 0
},
"_seq_no" : 0,
"_primary_term" : 1,
"status" : 201
}
}
]
}
12
3.9. Ejercicio en clase
• Se debe importar una nueva base de datos que encontrareis en el fichero data.bulk
• Para ello usareis el entorno CMD e importareis el fichero por medio de una instrucción BULK
• Indica el número de documentos devueltos por defecto al consultar el índice y el tipo con la
terminación _search. Son todos? Porqué?
13
Capítulo 4. API de búsquedas
Para realizar búsquedas en elasticsearch lo hacemos a través de su API _search. Podemos
realizarlas de dos maneras.
• REST request body. mandamos los parámetros de búsqueda a través de un body JSON
GET accounts/_search?q=firstname:M*
{
"took": 175,
"timed_out": false,
"_shards": {
"total": 6,
"successful": 6,
"skipped": 0,
"failed": 0
},
"hits": {
"total": 111,
"max_score": 1,
"hits": [
{
"_index": "accounts",
"_type": "account",
"_id": "265",
"_score": 1,
"_source": {
"account_number": 265,
"balance": 46910,
"firstname": "Marion",
"lastname": "Schneider",
"age": 26,
"gender": "F",
"address": "574 Everett Avenue",
"employer": "Evidends",
"email": "[email protected]",
"city": "Maplewood",
"state": "WY"
}
14
},
{
"_index": "accounts",
"_type": "account",
"_id": "551",
"_score": 1,
"_source": {
"account_number": 551,
"balance": 21732,
"firstname": "Milagros",
"lastname": "Travis",
"age": 27,
"gender": "F",
"address": "380 Murdock Court",
"employer": "Sloganaut",
"email": "[email protected]",
"city": "Homeland",
"state": "AR"
}
},
...
]
}
}
GET accounts/_search?q=firstname:M*&filter_path=hits.hits._source.firstname,hits.hits._source.lastname
{
"hits": {
"hits": [
{
"_source": {
"firstname": "Melissa",
"lastname": "Gould"
}
},
{
"_source": {
"firstname": "Marion",
"lastname": "Schneider"
}
},
...
15
◦ lowercase_expanded_terms=<true/false> (por defecto a true, terminos por defecto a
lowercase )
◦ from=<numero inicial>
4.2. Matching
• Las búsquedas por coincidencias se pueden realizar también en el body y solo permiten la
consulta de un solo campo. Sin embargo, el campo es procesado con el motor de búsquedas
16
POST accounts/_search
{
"query": {
"match": {
"firstname": "Melissa"
}
}
}
"took": 15,
"timed_out": false,
"_shards": {
"total": 6,
"successful": 6,
"skipped": 0,
"failed": 0
},
"hits": {
"total": 1,
"max_score": 4.742029,
"hits": [
{
"_index": "accounts",
"_type": "account",
"_id": "253",
"_score": 4.742029,
"_source": {
"account_number": 253,
"balance": 20240,
"firstname": "Melissa",
"lastname": "Gould",
"age": 31,
"gender": "M",
"address": "440 Fuller Place",
"employer": "Buzzopia",
"email": "[email protected]",
"city": "Lumberton",
"state": "MD"
}
}
]
}
}
17
POST accounts/_search
{
"took": 8,
"timed_out": false,
"_shards": {
"total": 6,
"successful": 6,
"skipped": 0,
"failed": 0
},
"hits": {
"total": 1000,
"max_score": 1,
"hits": [
...
POST accounts/_search
{
"query": {
"match_phrase": {
"address": "574 Everett Avenue"
}
}
}
4.4. Range
• Es posible definir rangos en las consultas para aplicar condiciones según el tipo de dato
18
POST accounts/_search
{
"query": {
"range": {
"age": {
"from": 20,
"to": 30
}
}
}
}
POST accounts/_search
{
"query": {
"range": {
"age": {
"gte": 20,
"lte": 30
}
}
}
}
4.5. Bool
• Para hacer búsquedas booleanas, se definen las condiciones en el booleano con should, must o
must_not indicando si sebe cumplir todas o algunas de ellas
Condición must
POST accounts/_search
{
"query": {
"bool": {
"must": [
{ "match": { "address": "mill" } },
{ "match": { "address": "lane" } }
]
}
}
}
19
Condición should
POST accounts/_search
{
"query": {
"bool": {
"should": [
{ "match": { "address": "mill" } },
{ "match": { "address": "lane" } }
]
}
}
}
Condición must_not
POST accounts/_search
{
"query": {
"bool": {
"must_not": [
{ "match": { "address": "mill" } },
{ "match": { "address": "lane" } }
]
}
}
}
• Construye una consulta que muestre aquellos que posean como "lastname" "ortiz" o "jones"
20
Capítulo 5. Análisis de texto
5.1. Inverted Index
Elasticsearch usa una estructura llamada inverted index que permite búsquedas muy rápidas de
tipo full text. Consiste en una lista de palabras únicas que aparecen en cualquier documento. Una
forma muy simple de entenderlo es comprobar la operación de un indice invertido por medio de
dos documentos Documento1: El dia de la bestia Documento2: La bella y la bestia
Term D1 D2
El X
dia X
de X
la X X
bestia X X
La X
bella X
y X
Term D1 D2
bestia X X
bella X
Comprobamos que el documento 2 posee más hits que el primero. De hecho se producen una serie
de transformaciones al crear los índices * Paso a minúsculas todos los elementos * Paso a singular
Y de hecho, también se realiza lo que se llama un análisis en la consulta que realiza los mismos
cambios en la consulta generada para que sean "perfect match"
5.2. Analizadores
• Los analizadores permiten tokenizar un texto en términos prácticos para su uso en un inverted
index
• Normaliza los términos en formato estándar para mejorar las opciones de búsqueda.
◦ Filtro de caracteres: La cadena es pasada por un filtro de caracteres. Permite por ejemplo
eliminar etiquetas html o transformar textos (& por and o similares)
◦ Tokenizador: Las cadenas son separadas en términos individuales. El más sencillo separa
por espacios o signos de puntuación.
◦ Filtros de token: Permite cambiar tokens, como poner en minúsculas, eliminar términos
como de, a o y, y agregar nuevos términos como los sinónimos.
21
• Para poder probar el resultado de cada analizador, podemos indicarlo con el endpoint _analyze
• Elasticsearch, posee una serie de analizadores por defecto, y por medio del API de configuración
(settings) podemos crear nuevos analizadores a nivel de índice o cluster.
POST _analyze
{
"analyzer" : "standard",
"text" : "this is a test"
}
5.2.1. Estándar
• Uso por defecto. Para textos en cualquier lenguaje. Usa reglas de palabras definidas por el
Consorcio Unicode y elimina signos de puntuación.
22
POST _analyze
{
"analyzer" : "standard",
"text" : "function to delete(5)"
}
{
"tokens": [
{
"token": "pseudo",
"start_offset": 0,
"end_offset": 6,
"type": "<ALPHANUM>",
"position": 0
},
{
"token": "function",
"start_offset": 7,
"end_offset": 15,
"type": "<ALPHANUM>",
"position": 1
},
{
"token": "to",
"start_offset": 16,
"end_offset": 18,
"type": "<ALPHANUM>",
"position": 2
},
{
"token": "delete",
"start_offset": 19,
"end_offset": 25,
"type": "<ALPHANUM>",
"position": 3
},
{
"token": "3",
"start_offset": 26,
"end_offset": 27,
"type": "<NUM>",
"position": 4
}
]
}
5.2.2. Simple
23
POST _analyze
{
"analyzer" : "simple",
"text" : "Pseudo-function to delete(5)"
}
{
"tokens": [
{
"token": "pseudo",
"start_offset": 0,
"end_offset": 6,
"type": "word",
"position": 0
},
{
"token": "function",
"start_offset": 7,
"end_offset": 15,
"type": "word",
"position": 1
},
{
"token": "to",
"start_offset": 16,
"end_offset": 18,
"type": "word",
"position": 2
},
{
"token": "delete",
"start_offset": 19,
"end_offset": 25,
"type": "word",
"position": 3
}
]
}
5.2.3. Whitespace
24
POST _analyze
{
"analyzer" : "whitespace",
"text" : "Pseudo-function to delete(3)"
}
{
"tokens": [
{
"token": "Pseudo-function",
"start_offset": 0,
"end_offset": 15,
"type": "word",
"position": 0
},
{
"token": "to",
"start_offset": 16,
"end_offset": 18,
"type": "word",
"position": 1
},
{
"token": "delete(3)",
"start_offset": 19,
"end_offset": 28,
"type": "word",
"position": 2
}
]
}
5.2.4. Language
• Permite definir el lenguaje del campo para realizar un analizador mas conciso
25
POST _analyze
{
"analyzer" : "english",
"text" : "Pseudo-function to delete(3)"
}
{
"tokens": [
{
"token": "pseudo",
"start_offset": 0,
"end_offset": 6,
"type": "<ALPHANUM>",
"position": 0
},
{
"token": "function",
"start_offset": 7,
"end_offset": 15,
"type": "<ALPHANUM>",
"position": 1
},
{
"token": "delet",
"start_offset": 19,
"end_offset": 25,
"type": "<ALPHANUM>",
"position": 3
},
{
"token": "3",
"start_offset": 27,
"end_offset": 28,
"type": "<NUM>",
"position": 4
}
]
}
26
No es ejectutable, se crean en los índices
PUT char-filter
{
"settings": {
"analysis": {
"char_filter": {
"lol_to_genial": {
"type": "mapping",
"mappings":["lol=> genial "]
}
}
}
}
}
• Podemos usar tantos como queramos, de tal forma que podemos crear los char_filter y luego
aplicar los que queramos a la vez.
"char_filter": ["html_strip","lol_to_genial"]
• html_strip: Elimina elementos de html como <head/> y decodifica entidades html como
ú
POST char-filter/_analyze
{
"tokenizer": "keyword",
"char_filter": [ "html_strip","lol_to_genial" ],
"text": "<p>I'm so <b>happy</b> lol!</p>"
}
El tokenizer keyword permite devolver el texto tal cual sin dividir la información
27
GET _analyze
{
"tokenizer" : "keyword",
"filter" : ["lowercase"],
"char_filter" : ["html_strip"],
"text" : "this is a <b>test</b>"
}
• pattern: Reemplaza un patron por el reemplazo indicado, usa patrones basados en soporte
Java.
"char_filter": {
"my_char_filter": {
"type": "pattern_replace",
"pattern": "(\\d+)-(?=\\d)",
"replacement": "$1_"
}
}
5.3.2. Tokenizers
Standard
• Tokenizador basado en gramática por medio del algoritmo de segmentación de texto unicode.
Funciona correctamente para la mayoría de los lenguajes
POST _analyze
{
"tokenizer": "standard",
"text": "Tokenizadores estándar, Hello everybody. I've a lot of things to show"
}
Letter
28
POST _analyze
{
"tokenizer": "letter",
"text": "Separamos por caracteres, en inglés, I've a problem with tokenizer"
}
Lowercase
• Más eficiente que letter tokenizer con el token filter de lowercase. Realiza ambas operaciones
en un solo paso.
POST _analyze
{
"tokenizer": "lowercase",
"text": "LAS 5 PALABRAS PASAn a mInÚsCuLas"
}
Whitespace
POST _analyze
{
"tokenizer": "whitespace",
"text": "Separación por espacios"
}
POST _analyze
{
"tokenizer": "uax_url_email",
"text": "Correo electrónico [email protected]"
}
Classic
• Tokenizador basado en gramática correcto para lenguaje inglés. Posee heurística para el
tratamiento de acrónimos, correos electrónicos, nombres de compañías, y nombres de host.
separa y elimina signos de puntuación, pero si el punto no posee un espacio, se considera parte
del token
29
POST _analyze
{
"tokenizer": "classic",
"text": "The 2 QUICK Brown-Foxes jumped over the lazy dog's bone."
}
NGram
• Permite generar términos a partir de palabras tomando por defecto un caracter y un máximo
de dos
PUT tokenizer-ngram
{
"settings": {
"analysis": {
"analyzer": {
"analyzer-ngram": {
"tokenizer": "tokenizer-ngram"
}
},
"tokenizer": {
"tokenizer-ngram": {
"type": "ngram",
"min_gram": 3,
"max_gram": 3,
"token_chars": [
"letter",
"digit"
]
}
}
}
}
}
POST tokenizer-ngram/_analyze
{
"analyzer": "analyzer-ngram",
"text": "3 Tristes Tigres"
}
Edge NGram
Muy util para definir búsquedas de tipo search-as-you-type. Por defecto separa los primeros
caracteres de cada término en términos de búsqueda.
30
PUT tokenizer-edge-ngram
{
"settings": {
"analysis": {
"analyzer": {
"analyzer-edge-ngram": {
"tokenizer": "tokenizer-edge-ngram"
}
},
"tokenizer": {
"tokenizer-edge-ngram": {
"type": "edge_ngram",
"min_gram": 2,
"max_gram": 10,
"token_chars": [
"letter",
"digit"
]
}
}
}
}
}
• Probamos el resultado:
POST tokenizer-edge-ngram/_analyze
{
"analyzer": "analyzer-edge-ngram",
"text": "3 Tristes Tigres comen trigo en un trigal."
}
POST _analyze
{
"tokenizer": "keyword",
"text": "Texto sin procesar"
}
31
PUT tokenizer-pattern
{
"settings": {
"analysis": {
"analyzer": {
"analyzer-pattern": {
"tokenizer": "tokenizer-pattern"
}
},
"tokenizer": {
"tokenizer-pattern": {
"type": "pattern",
"pattern": "[,;:]"
}
}
}
}
}
POST tokenizer-pattern/_analyze
{
"analyzer": "analyzer-pattern",
"text": "Ruben,Gomez;Garcia:Profesor"
}
Path:
32
PUT analyzer-path
{
"settings": {
"analysis": {
"analyzer": {
"analyzer-path": {
"tokenizer": "tokenizer-path"
}
},
"tokenizer": {
"tokenizer-path": {
"type": "path_hierarchy",
"delimiter": "_",
"replacement": "/",
"skip": 2
}
}
}
}
}
POST analyzer-path/_analyze
{
"analyzer": "analyzer-path",
"text": "c_Archivos de Programa_software_artefactos"
}
5.4.1. Sinónimos
• Para crear un filtro de tipo sinónimo debemos crear el filtro e indicar los sinonimos en un array
separado por cadenas
33
PUT /filter-sinonimos
{
"settings": {
"index" : {
"analysis" : {
"filter" : {
"mi_filtro_de_sinonimos": {
"type": "synonym",
"synonyms": [
"cabello,pelo",
"bonito,hermoso"
]
}
},
"analyzer" : {
"analyzer-sinonimos" : {
"filter" : [
"standard",
"lowercase",
"stop",
"mi_filtro_de_sinonimos"
],
"type" : "custom",
"tokenizer" : "standard"
}
}
}
}
}
}
POST filter-sinonimos/_analyze
{
"analyzer" : "analyzer-sinonimos",
"text": "cabello hermoso"
}
• Como la lista de sinónimos puede ser muy grande, se puede utilizar un almacenamiento relativo
al directorio de configuración de elasticsearch que permite almacenar los sinónimos en un
fichero.
34
PUT /filter-sinonimos-path/
{
"settings": {
"index" : {
"analysis" : {
"analyzer" : {
"analyzer-sinonimos-path" : {
"tokenizer" : "whitespace",
"filter" : ["synonym"]
}
},
"filter" : {
"synonym" : {
"type" : "synonym",
"synonyms_path" : "lista_sinonimos.txt"
}
}
}
}
}
}
• Si se realizan búsquedas con los sinónimos, los hits se preservan, ya que las posiciones son las
mismas
/etc/elasticsearch/lista_sinonimos.txt
guapo,bonito,precioso
pelo,cabello
POST filter-sinonimos-path/_analyze
{
"analyzer" : "analyzer-sinonimos-path",
"text": "Que cabello más hermoso, que guapo es el caballero"
}
35
Elasticsearch. Si se usan plugins, podemos agregar más.
• Por defecto, poseemos 8 analizadores estándar, que no son más que 8 combinaciones de
tokenizers, token filters y character filters.
PUT /indice_con_sinonimos_tras_estudio
{
"settings": {
"analysis": {
"analyzer": {
"html-snowball-personalizado": {
"tokenizer": "standard",
"filter": [
"lowercase",
"stop",
"snowball"
],
"char_filter": ["html_strip"]
}
}
}
}
}
• Si probamos ahora nuestro índice, comprobaremos como nuestro analizador procesa nuestros
textos como deseamos
POST indice_con_sinonimos_tras_estudio/_analyze
{
"analyzer" : "html-snowball-personalizado",
"text": "estoy <em>muy<em> interesado con tu formacion"
}
{
"tokens": [
{
"token": "estoy",
"start_offset": 0,
"end_offset": 5,
"type": "<ALPHANUM>",
"position": 0
},
{
"token": "muy",
"start_offset": 10,
"end_offset": 17,
"type": "<ALPHANUM>",
"position": 1
36
},
{
"token": "interesado",
"start_offset": 18,
"end_offset": 28,
"type": "<ALPHANUM>",
"position": 2
},
{
"token": "con",
"start_offset": 29,
"end_offset": 32,
"type": "<ALPHANUM>",
"position": 3
},
{
"token": "tu",
"start_offset": 33,
"end_offset": 35,
"type": "<ALPHANUM>",
"position": 4
},
{
"token": "formacion",
"start_offset": 36,
"end_offset": 45,
"type": "<ALPHANUM>",
"position": 5
}
]
}
• También se debe tener en cuenta el tipo de búsqueda. Si buscamos en vez de palabras, frases,
también podemos optimizar las búsquedas de frases a costa de memoria y proceso.
• El secreto está en generar trozos de texto y crea grupos de frases. Se trata de un token filter
nGram que trabaja a nivel de palabra en vez de a nivel de caracter.
max_shingle_size: 3
min_shingle_size: 1
• En este caso hace grupos de una, dos o tres palabras para las búsquedas, a costa de
almacenamiento y memoria de los índices.
37
como por ejemplo palabras compuestas, existe el plugin de descomposición
5.6. Segmentos
• Los Index Segments son mini-índices. Al realizar una búsqueda, Lucene realiza la búsqueda en
cada segmento, y tras buscar la información, fusiona los resultados.
• Al generar un índice de tipo texto, éste se alimenta con el resultado del analizador definido. Esto
provoca los mini-indices. El problema es que se puede tener demasiados mini-índices creados,
para eso Lucene puede fusionar esos segmentos según sus políticas. En esa fase, aquellos
documentos que hayan sido eliminados, son descartados. Por eso, a veces al agregar
documentos, el tamaño del índice puede disminuir.
• Se permite la manipulación de los merges por medio de configuración, o por medio del API de
optimización.
38
Capítulo 6. Mappings
6.1. ¿Que es un mapping?
El mapeo es el proceso por el cual definimos cómo es nuestro documento, qué campos contiene y
cómo está guardado en el índice. Definimos:
Que campos de tipo string deben ser tratados como “full text”. Que campos son de tipo numéricos,
fechas o geolocalizados. Cuando los valores en un documento deben ser indexados dentro del
campo _all. El formato de los campos tipo fecha. Definir reglas para el control del mapeo dinámico
de datos.
Cada campo del documento dentro de un índice tiene un tipo de dato, que puede ser
• Core datatypes: text, date, long, double, boolean, binary ( Base64 encoded )
Todos los campos, dentro de un mismo índice, tienen el mismo mapeo, ya que internamente están
guardados como el mismo campo.
Aunque no podemos tener dos tipos de datos distintos, si podemos tener un mapeo un poco distinto
para el mismo campo.
Ejemplo con un campo de tipo string analizado con el analizador por defecto
PUT usuarios
POST usuarios/usuario/_mapping
{
"properties": {
"name": {
"type": "text"
}
}
}
39
Ejemplo con un campo de tipo string no analizado
POST usuarios/usuario/_mapping
{
"properties": {
"cif": {
"type": "keyword"
}
}
}
{
"_index": “my_index",
"_type": "my_type",
"_id": "1",
"_version": 1,
"found": true,
"_source": {
"name": "John Smith"
}
}
Podemos mapear algunos de estos campos, por ejemplo si no queremos que nuestro índice tenga
habilitado el meta campo _all añadimos El campo _all es un elemento que permite la búsqueda de
todo el contenido sin saber en que campo en concreto se encuentra. Se trata de un campo de texto
que contiene todos los demás elementos. El campo _all es analizado, indexado pero no almacenado
ni devuelto. Las búsquedas de tipo _search?q= o de tipo query_string apuntan por defecto al campo
_all
{
"_all": {
"enabled": false
}
}
Podemos cambiar el tipo de analyzer; algoritmo que se utiliza para convertir un string en tokens,
en los cuales se basará la búsqueda.
40
POST usuarios/usuario/_mapping
{
"properties": {
"description": {
"type": "text",
"analyzer": "english"
}
}
}
Con el meta campo copy_to, podemos crear campos tipo _all, el parámetro redirige la salida de un
campo a otro, formando campos compuestos.
POST usuarios/usuario/_mapping
{
"properties": {
"nombre_completo": {
"type": "text"
},
"nombre": { "type": "text", "copy_to": "nombre_completo"},
"apellido1": { "type": "text", "copy_to": "nombre_completo"},
"apellido2": { "type": "text", "copy_to": "nombre_completo"}
}
}
• Para poder buscar por el campo, podemos aprovechar la directiva and de match:
POST usuarios/usuario
{
"apellido1":"Gomez",
"apellido2":"Garcia",
"nombre":"Ruben"
}
GET usuarios/usuario/_search
{
"query": {
"match": {
"nombre_completo": {
"query": "Ruben Gomez Garcia",
"operator": "and"
}
}
}
}
• Cada mapping tiene un número de elementos asociados que pueden ser utilizados para
controlar los metadatos
41
6.1.1. _uid
6.1.2. _id
• Por defecto cada documento está asociado a un id y un tipo, que por defecto no es indexado ni
almacenado
6.1.3. _type
6.1.4. _source
{
"transaction" : {
"_source" : {
"enabled" : false
}
}
}
Las reglas de mapeo automático se pueden cambiar, podemos configurar el campo default mapping
para cambiar el mapeo por defecto de los campos, o configurar las reglas de mapeo dinámico.
• Para agregar nuevos campos, es tan sencillo como indicar el tipo de documento y el campo
necesario, teniendo en cuenta que no debe existir con anterioridad.
42
PUT /transactions
{
"mappings": {
"internal" : {
"properties" : {
"description" : {
"type" : "text",
"analyzer": "english"
},
"date" : {
"type" : "date"
},
"ccc" : {
"type" : "text"
}
}
}
}
}
PUT /transactions/internal/_mapping
{
"properties": {
"ccc_dest" : {
"type" : "keyword"
}
}
• Podemos evaluar el comportamiento del analizador a nivel de campo para comprobar como ha
sido procesado
43
GET /transactions/_analyze
{
"field": "ccc_dest",
"text" : "123-123-123-123"
}
{
"tokens": [
{
"token": "123-123-123-123",
"start_offset": 0,
"end_offset": 15,
"type": "word",
"position": 0
}
]
}
PUT /transactions
{
"mappings": {
"internal" : {
"properties" : {
"description" : {
"type" : "string",
"analyzer": "english"
},
"date" : {
"type" : "date"
},
"ccc" : {
"type" : "string"
}
}
}
}
}
44
◦ El nombre del campo
6.5.1. match_mapping_type
• Por ejemplo, podemos crear en un índice un mapping que cuando detecte longs los pase a
integers
PUT /match-mapping/users/1
{
"name" : "Ruben",
"value" : 1
}
GET /match-mapping/users/_mapping
{
"match-mapping": {
"mappings": {
"users": {
"properties": {
"name": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"value": {
"type": "long"
}
}
}
}
}
}
45
PUT /match-dynamic
{
"mappings": {
"users": {
"dynamic_templates": [
{
"de-long-a-integer": {
"match_mapping_type": "long",
"mapping": {
"type": "integer"
}
}
}
]
}
}
}
46
PUT /match-dynamic/users/1
{
"name" : "Ruben",
"value" : 1
}
GET /match-dynamic/users/_mapping
{
"match-dynamic": {
"mappings": {
"users": {
"dynamic_templates": [
{
"de-long-a-integer": {
"match_mapping_type": "long",
"mapping": {
"type": "integer"
}
}
}
],
"properties": {
"name": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"value": {
"type": "integer"
}
}
}
}
}
}
• Permite definir un patrón que si coincide con el nombre del campo aplica la directiva que se
definal
• De forma inversa, podemos generar un patrón que si no coincide, aplica la directiva definida
47
PUT /match-unmatch-dynamic
{
"mappings": {
"users": {
"dynamic_templates": [
{
"longs-como-strings": {
"match_mapping_type": "string",
"match": "l_*",
"unmatch": "l_t*",
"mapping": {
"type": "integer"
}
}
}
]
}
}
}
• Si insertamos un campo que haga match en el mapping por defecto y en el apartado match, y
otro en el apartado unmatch
POST dynamic-match-unmatch/users
{
"l_name_chars":"1",
"l_text":"22"
}
• Podemos comprobar cual es el resultado del mapping, que ha sido alterado por el template
POST match-unmatch-dynamic/users
{
"l_name_chars":"1",
"l_text":"22"
}
GET match-unmatch-dynamic
{
"match-unmatch-dynamic": {
"aliases": {},
"mappings": {
"users": {
"dynamic_templates": [
{
"longs-como-strings": {
"match": "l_*",
"unmatch": "l_t*",
"match_mapping_type": "string",
48
"mapping": {
"type": "integer"
}
}
}
],
"properties": {
"l_name_chars": {
"type": "integer"
},
"l_text": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
}
}
}
},
"settings": {
"index": {
"creation_date": "1539021510217",
"number_of_shards": "5",
"number_of_replicas": "1",
"uuid": "wS8cJ3R5QYSGigcfR5z5uA",
"version": {
"created": "6040299"
},
"provided_name": "match-unmatch-dynamic"
}
}
}
}
6.5.3. match_pattern
49
Matching de tipo numeros_12312
PUT dynamic-match-pattern
{
"mappings": {
"users": {
"dynamic_templates": [
{
"longs_como_strings": {
"match_mapping_type": "string",
"match_pattern": "regex",
"match": "^numeros_\\d+$",
"mapping": {
"type": "long"
}
}
}
]
}
}
}
POST dynamic-match-pattern/users
{
"name" : "Ruben",
"numeros_123":"22150",
"numeros_223_texto":"22150"
}
50
GET dynamic-match-pattern/users/_mapping
{
"dynamic-match-pattern": {
"mappings": {
"users": {
"dynamic_templates": [
{
"longs_como_strings": {
"match": "^numeros_\\d+$",
"match_mapping_type": "string",
"match_pattern": "regex",
"mapping": {
"type": "long"
}
}
}
],
"properties": {
"name": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"numeros_123": {
"type": "long"
},
"numeros_223_texto": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
}
}
}
}
}
}
Como match, pero opera por medio de un path completo por medio del sistema de puntos
51
PUT dynamic-path-match
{
"mappings": {
"users": {
"dynamic_templates": [
{
"full_name": {
"path_match": "direccion.*",
"path_unmatch": "*.numero",
"mapping": {
"type": "text",
"copy_to": "direccion_completa"
}
}
}
]
}
}
}
POST dynamic-path-match/users
{
"nombre": "Ruben",
"direccion": {
"tipo" : "Calle",
"calle" : "Real",
"numero" : 3
}
}
GET dynamic-path-match/users/_mapping
{
"dynamic-path-match": {
"mappings": {
"users": {
"dynamic_templates": [
{
"full_name": {
"path_match": "direccion.*",
"path_unmatch": "*.numero",
"mapping": {
"copy_to": "direccion_completa",
"type": "text"
}
52
}
}
],
"properties": {
"direccion": {
"properties": {
"calle": {
"type": "text",
"copy_to": [
"direccion_completa"
]
},
"numero": {
"type": "long"
},
"tipo": {
"type": "text",
"copy_to": [
"direccion_completa"
]
}
}
},
"direccion_completa": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"nombre": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
}
}
}
}
}
}
53
• Las plantillas incluyen settings, mappings y un patrón que controla cuando debe aplicarse el
índice nuevo
PUT _template/plantilla_nueva2
{
"index_patterns": "pl*",
"settings": {
"number_of_shards": 2
},
"mappings": {
"users": {
"_source": {
"enabled": false
},
"properties": {
"email": {
"type": "keyword"
},
"fecha_creacion": {
"type": "date",
"format": "EEE MMM dd HH:mm:ss Z YYYY"
}
}
}
}
}
PUT plex-template
POST plex-template/users
{
"name":"ruben"
}
• Comprobamos que el índice posee los shards configurados y que el tipo users posee los nuevos
campos del template
54
Muestra del nuevo índice
GET plex-template/_settings
{
"plex-template": {
"settings": {
"index": {
"creation_date": "1499096928446",
"number_of_shards": "2",
"number_of_replicas": "1",
"uuid": "O8zp3IT_SHOIB2GhtK9w_w",
"version": {
"created": "5040299"
},
"provided_name": "plex-template"
}
}
}
}
55
Muestra del nuevo tipo
{
"plex-template": {
"mappings": {
"users": {
"_source": {
"enabled": false
},
"properties": {
"email": {
"type": "keyword"
},
"fecha_creacion": {
"type": "date",
"format": "EEE MMM dd HH:mm:ss Z YYYY"
},
"name": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
}
}
}
}
}
}
56
GET _templates
{
"plex-template": {
"aliases": {},
"mappings": {
"users": {
"_source": {
"enabled": false
},
"properties": {
"email": {
"type": "keyword"
},
"fecha_creacion": {
"type": "date",
"format": "EEE MMM dd HH:mm:ss Z YYYY"
},
"name": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
}
}
}
},
"settings": {
"index": {
"creation_date": "1499097328963",
"number_of_shards": "2",
"number_of_replicas": "1",
"uuid": "-vaoeuetQ6y6-KwllfKFug",
"version": {
"created": "5040299"
},
"provided_name": "plex-template"
}
}
}
}
57
DELETE _template/plantilla_nueva
{
"acknowledged": true
}
58
Capítulo 7. Búsquedas avanzadas
• No solo podemos realizar búsquedas _search para buscar contenido en ciertos campos,
podemos aprovechar el API de elasticsearch para realizar una gran cantidad de peticiones más
avanzadas que las vistas hasta ahora. Algunas de ellas son las siguientes
• Para realizar las consultas de tipo term_based, indica que toma la petición como si fuera un
término único
POST accounts/_search
{
"query": {
"term": {
"address": {
"value": "171 Putnam Avenue"
}
}
}
}
{
"took": 1,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"failed": 0
},
"hits": {
"total": 0,
"max_score": null,
"hits": []
}
}
• Como el campo está analizado, no sabe que la dirección es exactamente esa, lo ha separado en
términos individuales
59
Agregación de un nuevo campo como no analizado
POST accounts/account/_mapping
{
"properties": {
"message": {
"type": "keyword"
}
}
}
GET accounts/_mapping
{
...
"message": {
"type": "keyword"
}
GET accounts/_mapping
POST accounts/account/25/_update
{
"doc" : {
"message" : "toma mensaje"
}
}
60
Consulta de tipo term
POST accounts/_search
{
"query": {
"term": {
"message": {
"value": "toma mensaje"
}
}
}
}
{
"took": 10,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"skipped": 0,
"failed": 0
},
"hits": {
"total": 1,
"max_score": 0.2876821,
"hits": [
{
"_index": "accounts",
"_type": "account",
"_id": "25",
"_score": 0.2876821,
"_source": {
"account_number": 25,
"balance": 40540,
"firstname": "Virginia",
"lastname": "Ayala",
"age": 39,
"gender": "F",
"address": "171 Putnam Avenue",
"employer": "Filodyne",
"email": "[email protected]",
"city": "Nicholson",
"state": "PA",
"message": "toma mensaje"
}
}
]
}
}
• En caso de realizar múltiples consultas, podemos dar mayor valor a uno de ellos en concreto
61
POST accounts/account/_search
{
"query": {
"bool": {
"should": [
{
"term": {
"state": {
"value": "dc",
"boost": 2.0
}
}
},
{
"term": {
"age": 27
}
}
]
}
}
}
7.2. Filtros
El entorno de consulta depende de como se usan el contexto de consulta y el entorno de filtro
• Contexto de consulta: Una consulta busca las coincidencias. Este contexto calcula el _score,
como se aproxima a las coincidencias, relativo al resto de coincidencias
• Contexto de filtro: Un contexto busca coincidencias, pero no elabora el score, o coincide con lo
esperado o no.
62
POST accounts/account/_search
{
"query": {
"bool": {
"should": [
{
"term": {
"state": {
"value": "dc"
}
}
},{
"term": {
"lastname": {
"value": "moran"
}
}
}
],
"filter": [
{
"term": {
"age": 27
}
}
]
}
}
}
7.3. match_phrase
• Analiza el texto y crea una consulta de tipo phrase fuera del texto analizado
63
POST accounts/_search
{
"query": {
"match_phrase": {
"address": "574 Everett Avenue"
}
}
}
{
"took": 21,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"failed": 0
},
"hits": {
"total": 1,
"max_score": 9.961143,
"hits": [
{
"_index": "accounts",
"_type": "account",
"_id": "265",
"_score": 9.961143,
"_source": {
"account_number": 265,
"balance": 46910,
"firstname": "Marion",
"lastname": "Schneider",
"age": 26,
"gender": "F",
"address": "574 Everett Avenue",
"employer": "Evidends",
"email": "[email protected]",
"city": "Maplewood",
"state": "WY"
}
}
]
}
}
64
POST accounts/_search
{
"query": {
"match_phrase": {
"address": {
"query" : "574 Everett Avenue",
"analyzer" : "standard"
}
}
}
}
7.4. match_phrase_prefix
• Se trata del mismo concepto que match_phrase, pero permite expandir el sufijo por medio de la
directiva max_expansions
POST accounts/_search
{
"query": {
"match_phrase_prefix": {
"address": {
"query" : "574 Everett Ave",
"analyzer" : "standard",
"max_expansions": 10
}
}
}
}
7.5. multi_match_query
• Permite generar consultas de tipo multi-match contra distintos campos
65
GET /_search
{
"query": {
"multi_match" : {
"query": "toma mensaje",
"fields": [ "otro", "message" ]
}
}
}
• Podemos usar wildcards para buscar campos y ^ para lanzar un boost a un campo concreto
GET /_search
{
"query": {
"multi_match" : {
"query": "ayala",
"fields": [ "*name", "email^3" ]
}
}
}
7.5.1. best_fields
• Uso por defecto. Busca documentos que posean coincidencias, usa _score de la mejor
coincidencia. El objetivo es bucar muchas palabras en un campo:
GET /_search
{
"query": {
"multi_match" : {
"query": "virginia ayala",
"type": "best_fields",
"fields": [ "firstname","lastname" ],
"tie_breaker" : 0.3
}
}
}
66
• Otra opción es usar el operador para el multimatch
GET /_search
{
"query": {
"multi_match" : {
"query": "virginia ayala",
"type": "best_fields",
"fields": [ "firstname","lastname" ],
"tie_breaker": 0.3,
"operator" : "or"
}
}
}
7.5.2. most_fields
• Usa un analizador como si fuera un campo inmenso. Busca cada palabra en cada campo. El
objetivo es buscar la misma palabra en distintos campos
GET /_search
{
"query": {
"multi_match" : {
"query": "ayala",
"type": "most_fields",
"fields": [ "lastname","email" ],
"tie_breaker": 0.3,
"operator" : "or"
}
}
}
7.5.3. phrase
GET /_search
{
"query": {
"multi_match" : {
"query": "171 Putnam Av",
"type": "phrase_prefix",
"fields": [ "address", "message" ]
}
}
}
67
• phase_prefix: Lanza un match_phrase_prefix en cada campo y combina los _score de cada
campo
7.6. fuzziness
• Permite buscar con un efecto de borrosidad. Se trata de un match de tipo fuzzy.
◦ Manual: 0, 1 o 2
◦ Automática:
POST accounts/_search
{
"query": { "fuzzy": { "firstname" : "Veras" }}
}
POST accounts/_search
{
"query": {
"fuzzy" : {
"firstname" : {
"value" : "Veras",
"fuzziness" : 2
}
}
}
}
68
GET /_search
{
"query": {
"fuzzy" : {
"lastname" : {
"value" : "vera",
"boost" : 1.0,
"fuzziness" : 2,
"prefix_length" : 0,
"max_expansions": 100
}
}
}
}
• prefix_length: Es el número de caracteres iniciales que no deben ser modificados. Por defecto 0
7.7. highlighting
• Permite búsquedas resaltadas de forma automática.
GET /_search
{
"highlight": {
"fields": {
"address":{}
}
},
"query": {
"term": {
"address": {
"value": "scott"
}
}
}
}
• Como resultado, aparecerá a parte de los hits, un apartado con el campo resaltadas
69
{
"took": 100,
"timed_out": false,
"_shards": {
"total": 92,
"successful": 92,
"failed": 0
},
"hits": {
"total": 1,
"max_score": 4.311029,
"hits": [
{
"_index": "accounts",
"_type": "account",
"_id": "573",
"_score": 4.311029,
"_source": {
"account_number": 573,
"balance": 32171,
"firstname": "Callie",
"lastname": "Castaneda",
"age": 36,
"gender": "M",
"address": "799 Scott Avenue",
"employer": "Earthwax",
"email": "[email protected]",
"city": "Marshall",
"state": "NH"
},
"highlight": {
"address": [
"799 <em>Scott</em> Avenue"
]
}
}
]
}
}
7.8. more_like_this
• Las consultas more_like_this buscan documentos que se parezcan
• Para conseguirlo, MLT selecciona un grupo representativo de términos, genera una consulta con
esos términos, ejecuta la consulta y devuelve los resultados.
70
GET /_search
{
"query": {
"more_like_this" : {
"fields" : ["city", "address"],
"like" : "marshall scott avenue",
"min_term_freq" : 0,
"max_query_terms" : 12
}
}
}
◦ unlike: En conjunto con like, para definir los documentos que no se quieren buscar
71
Capítulo 8. Resultado de consultas
8.1. Boost
• El boost permite dar importancia a ciertos campos, unos por encima de otros
PUT prueba-boosting
{
"mappings": {
"peliculas": {
"properties": {
"titulo": {
"type": "text",
"boost": 2
},
"sinopsis": {
"type": "text"
}
}
}
}
}
POST prueba-boosting/peliculas
{
"titulo":"El tio de los anillos",
"sinopsis":"Pues va de unos arillos y el master que los lleva"
}
POST prueba-boosting/peliculas
{
"titulo": "El master de los arillos",
"sinopsis": "Pues va de unos arillos de cebolla que vende un tio"
}
GET prueba-boosting/_search?q=master
GET prueba-boosting/_search?q=tio
72
◦ La petición se procesa en dos fases. Primero la query avanza a todos los shards
involucrados.
◦ Cada shard devuelve el resultado al nodo encargado de fusionar y ordenar las respuestas
8.2.2. Dfs_query_then_fetch
• Realiza las mismas fases que el anterior, pero con una fase previa de evaluación de los scores
para mayor precisión
GET prueba-boosting/_search?q=master&search_type=dfs_query_then_fetch
8.3. Sort
• Podemos ordenar los resultados por distintos campos.
GET accounts/_search
{
"sort": [
{
"age":{
"order": "desc"
}
}
],
"query": {
"range": {
"age": {
"from": 30,
"to": 35
}
}
}
}
• Las ordenaciones por campos de texto (que no por _score) producen un consumo excesivo de
memoria, por lo cual no se recomiendan excepto en casos concretos. Para ello se debe activar la
opción de permitir usar consumo de disco para ordenar.
73
8.4. From / Size
• Las paginaciones se realizan desde las opciones From y Size
• Se pueden agregar en el cuerpo del mensaje o desde la url para mayor comodidad
GET accounts/_search
{
"sort": [
{
"age":{
"order": "desc"
}
}
],
"from": 30,
"size": 10,
"query": {
"range": {
"age": {
"from": 30,
"to": 35
}
}
}
}
8.5. Scroll
• Permite el acceso al concepto de cursos a los documentos asociados a una consulta.
74
GET accounts/_search?scroll=1m
{
"sort": [
{
"age":{
"order": "desc"
}
}
],
"query": {
"range": {
"age": {
"from": 30,
"to": 35
}
}
}
}
{
"_scroll_id":
"DnF1ZXJ5VGhlbkZldGNoBQAAAAAAAABSFnIzZkFaamg1VHZXcW5ObkYtVm1sSmcAAAAAAAAAVRZyM2ZBWmpoNVR2V3FuTm5GLVZtbEpnAAAAAAAAAFEWcjNm
QVpqaDVUdldxbk5uRi1WbWxKZwAAAAAAAABUFnIzZkFaamg1VHZXcW5ObkYtVm1sSmcAAAAAAAAAUxZyM2ZBWmpoNVR2V3FuTm5GLVZtbEpn",
"took": 11,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"failed": 0
},
"hits": {
...
GET _search/scroll
{
"scroll_id":
"DnF1ZXJ5VGhlbkZldGNoBQAAAAAAAABSFnIzZkFaamg1VHZXcW5ObkYtVm1sSmcAAAAAAAAAVRZyM2ZBWmpoNVR2V3FuTm5GLVZtbEpnAAAAAAAAAFEWcjNm
QVpqaDVUdldxbk5uRi1WbWxKZwAAAAAAAABUFnIzZkFaamg1VHZXcW5ObkYtVm1sSmcAAAAAAAAAUxZyM2ZBWmpoNVR2V3FuTm5GLVZtbEpn",
"scroll":"1m"
}
• Devolverá resultados hasta que caduque el scroll (1 minuto por ejecución) o termine el flujo de
las respuestas
◦ _primary_first : Primero en los shards primarios que están disponibles, sino otros
(deprecado en vesión 6.1.0, desaparece en la 7.0.0).
75
desaparece en la 7.0.0).
GET accounts/_search?preference=_shards:2,3
{
"sort": [
{
"age":{
"order": "desc"
}
}
],
"query": {
"range": {
"age": {
"from": 30,
"to": 35
}
}
}
}
76
Capítulo 9. Modelo distribuido
• Por defecto, Elasticsearch se construye para ser un servicio siempre disponib le y preparado
para el escalado según nuestras necesidades.
• El escalado puede venir por aumentar las características de la máquina (escalado vertical) o
iniciando nuevos nodos (escalado horizontal)
• El escalado vertical posee sus límites, mientras que el escalado horizontal permite un
crecimiento mas estable.
• ELasticsearch es un software distribuido por naturaleza, lo que permite manejar sin problemas
múltiples nodos para el escalado y la alta disponibilidad.
-Xms512m
-Xmx512m
node.name: node-1
./elasticsearch
77
PUT nuevo-indice
{
"settings" : {
"number_of_shards" : 3,
"number_of_replicas" : 2
}
}
• Los shards es como elasticsearch redistribuye la información a traves del cluster. Se consideran
contenedores de datos.
• Otra opción es colocar 3 shards por nodo, calculando, 3 nodos x 3 shards = 9 shards en total.
9.5. Replicación
• Para realizar una replicación completa, debemos iniciar otras dos instancas de Elasticsearch.
78
9.6. Split Brain
• El split-brain se produce en el caso de que la mitad del cluster (por defecto se vea
comprometida. El caso más común es en el cluster de dos nodos. En este caso, los dos nodos se
comunican por la capa de transporte. Si ese canal de transporte falla, puede haber un corte de
suministro. Por defecto en dos nodos, con que uno solo esté levantado, toma el control del
cluster y continúa dando servicio. Sin embargo en el caso del corte de suministro en la
comunicación, produce un efecto de separación de cerebros o split-brain.
• En este caso, ninguno de los dos nodos se ven entre sí, y en ese caso, los dos piensan que el nodo
contrario se ha caido. En caso de que esto ocurra (simulación que puede pasar en grandes
clusters de servidores) se vuelven clusters independientes, y si se realiza alguna modificación
en alguno de ellos, se descoordinarán y no podrán volver a unirse. Para evitar el split-brain se
recominda usar la siguiente directiva:
discovery.zen.minimum_master_nodes: 5
# En caso de redes con latencia, se recomienda ampliar el timeout para evitar falsos positivos de nodos caidos.
discovery.zen.ping.timeout: 3s
De esta forma, el cluster con la mitad + 1 de nodos elegibles será el único que puede tomar el
control, evitando el efecto de split-brain.
• Cada nodo puede gestionar HTTP y el tráfico de transporte por defecto. Esta última capa,
permite la comunicación entre nodos. El protocolo HTTP es el indicado para los clientes REST.
• Existen cuatro tipos de nodos básicos y uno extra en base al plugin X-Pack
1. node.master: nodo elegible como maestro (true por defecto). Permite que el nodo pueda ser
coordinador del cluster.
a. Es útil crear maestros elegibles que no posean carga excesiva, en caso de poseer clusters
grandes.
configuración
node.master: true
node.data: false
node.ingest: false
1. node.data: (true por defecto) nodo que almacena y realiza operaciones con datos, como
79
búsquedas, agregaciones u operaciones CRUD
a. Es importante monitorizar cargas para agregar nuevos datanodes cuando sea necesario.
configuración
node.master: false
node.data: true
node.ingest: false
1. node.ingest: (true por defecto). Permite usar una cadena de ejecuciones para transformar y
enriquecer un documento antes de indexarlo. Muy útil en caso de que las transformaciones de
documentos sean extremas y de gran carga. Se trata de un conjunto de procesadores que se
ejecutan en orden y permiten la manipulación de documentos previamente a su indexación.
Desde agregar fechas, a reconstrucciones completas.
configuración
node.master: false
node.data: false
node.ingest: true
# Evita la búsqueda entre clusters (true por defecto)
search.remote.connect: false
1. tribe.*: coordinador que permite conectar a múltiples clusters y realizar búsquedas a través de
todos los clusters conectados.
◦ Es posible crear un nodo de coordinación que balancee las peticiones, gestione la enrutación
de recursos, gestion la fase de busqueda y reducción y distribuya el indexado en bloque.
(son como los balanceadores de carga inteligentes)
configuración
node.master: false
node.data: false
node.ingest: false
search.remote.connect: false
• Una vez iniciado, sea desarrollo o producción, se evaluan las características básicas de sistema
deseables para elasticsearch. Los mensajes vienen en forma de warnings para que apliquemos
los cambios.
80
es.enforce.bootstrap.checks: true
ES_JAVA_OPTS=-Des.enforce.bootstrap.checks=true
• Por defecto, las configuraciones de Elasticsearch están definidas para desarrollo. En entornos de
producción, se deben modificar ciertas directivas.
• La primera es el tamaño de la memoria, que por defecto son 2GB. Se recomienda una
configuración de 30GB por instancia y servidor de elasticsearch.
• Por defecto, Elasticsearch apunta a Localhost para http y el transporte. Perfecto para pruebas
básicas o de concepto con elasticsearch. En producción, se debe definir la interfaz para que
todos los nodos se vean en la capa de transporte.
81
Capítulo 10. Updates
10.1. API de actualizaciones
El API de actualizaciones permite actualizar un documento por medio de un script. La operación
obtiene el documento del índice, ejecuta el script e indexa el resultado. Usa el versionado para
evitar conflitos de actualización Es imprescindible que se habilite el campo _source para ello. Al
terminar se realiza una reindexación completa del documento
Agregación de un documento
PUT login
POST login/users/1
{
"username" : "Ruben",
"groups" : ["admin"],
"count_access" : 0
}
82
Agregación por scripting, usando "painless"
POST login/users/1/_update
{
"script" : {
"source": "ctx._source.count_access += params.hits",
"lang": "painless",
"params" : {
"hits" : 1
}
}
}
GET login/users/1
{
"_index": "login",
"_type": "users",
"_id": "1",
"_version": 2,
"found": true,
"_source": {
"username": "Ruben",
"groups": [
"admin"
],
"count_access": 1
}
}
83
POST login/users/1/_update
{
"script" : {
"source": "ctx._source.groups.add(params.tag)",
"lang": "painless",
"params" : {
"tag" : "operator"
}
}
}
{
"_index": "login",
"_type": "users",
"_id": "1",
"_version": 3,
"result": "updated",
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
}
}
GET login/users/1
{
"_index": "login",
"_type": "users",
"_id": "1",
"_version": 3,
"found": true,
"_source": {
"username": "Ruben",
"groups": [
"admin",
"operator"
],
"count_access": 1
}
}
POST login/users/1/_update
{
"script" : "ctx._source.password = \"madalena\""
}
Eliminar campos
84
POST login/users/1/_update
{
"script" : "ctx._source.remove(\"password\")"
}
POST login/users/1/_update
{
"doc" : {
"active" : true
}
}
{
"_index": "login",
"_type": "users",
"_id": "1",
"_version": 24,
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
}
}
10.4. Noop
• Si el documento se fusiona con un origen, y no produce cambios, el resultado es noop (no
operation)
85
POST login/users/1/_update
{
"doc" : {
"active" : true
}
}
{
"_index": "login",
"_type": "users",
"_id": "1",
"_version": 24,
"_shards": {
"total": 0,
"successful": 0,
"failed": 0
}
}
• En caso de que nos interese activamente que haga la sustitución aunque sea de tipo noop,
podemos deshabilitarlo
POST login/users/1/_update
{
"doc" : {
"active" : true
},
"detect_noop": false
}
{
"_index": "login",
"_type": "users",
"_id": "1",
"_version": 25,
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
}
}
10.5. Upserts
• Si realizamos una actualización de un documento y este no funciona, podemos aprovechar el
concepto de upsert.
• Si existe actualizo, si no existe lo creo. Es aplicable a todos los casos anteriores, aplicando la
opción upsert e indicando el documento que se va a generar por defecto
86
Ejemplo con script para generación de hits de usuarios no dados de alta previamente
POST login/users/luis/_update
{
"script" : {
"source": "ctx._source.count_access += params.hits",
"lang": "painless",
"params" : {
"hits" : 1
}
},
"upsert":{
"hits": 1
}
}
GET login/users/luis
{
"_index": "login",
"_type": "users",
"_id": "luis",
"_version": 1,
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
}
}
• Para realizar múltiples actualizaciones e intentar obviar los casos particulares, se puede
agregar la opción conflicts=proceed
• Las actualizaciones de este tipo, permiten lanzar las mismas consultas que con el API de
_update
87
POST accounts/_update_by_query
{
"script" : {
"source": "ctx._source.age += params.hits",
"lang": "painless",
"params" : {
"hits" : 1
}
},
"query": {
"range": {
"age": {
"from": 30,
"to": 35
}
}
}
}
88
Capítulo 11. Delete
• Permite borrar un documento JSON con un índice específico basado en su id
• Permite indicar la espera por shards activos, lo que obliga a que haya al menos un número
mínimo de shards
DELETE login/users/luis
{
"found": true,
"_index": "login",
"_type": "users",
"_id": "luis",
"_version": 5,
"_shards": {
"total": 2,
"successful": 1,
"failed": 0
}
}
POST login/_delete_by_query
{
"query": {
"match": {
"count_access": 1
}
}
}
• Cuando se realiza la operación, genera un snapshot del índice, usando un sistema de versiones
interno. Si el documento cambia desde que el snapshot se realizó y la consulta es procesada,
produce un conflicto.
89
• Similar al update by query
90
Capítulo 12. Agregaciones
• El framework de agregaciones permite la cosntrucción de bloques llamados agregaciones que se
componen en orden para construir conjuntos de datos complejos
• Cada agregación se le considera como una unidad de trabajo que genera información a partir
de un conjunto de documentos
12.1. Tipos
• Existen distintos tipode de agregaciones:
◦ Bucketing: Agregación que genera buckets, donde cada uno está asociado a una clave y a un
criterio de documento. Todos los criterios son evaluados.
• Como dato a tener muy en cuenta, al ser un bucket un conjunto de documentos, se puede
asociar una agregación dentro de otra, produciendo la anidación de documentos.
"aggregations" : {
"<nombre_de_agregación>" : {
"<tipo_de_agregacion>" : {
<cuerpo_de_la_agregación>
}
[,"meta" : { [<cuerpo de metadatos>] } ]?
[,"aggregations" : { [<sub_agregaciones>]+ } ]?
}
[,"<nombre_de_segunda_agregacion>" : { ... } ]*
}
12.2. Métricas
• Computación de métricas basadas en valores extraidos de documentos o de otras agregaciones,
e incluso de scripts.
91
POST /accounts/account/_search?size=0
{
"aggs" : {
"media" : { "avg" : { "field" : "balance" } }
}
}
POST /accounts/account/_search?size=0
{
"aggs" : {
"media-con-iva" : {
"avg" : {
"script" : {
"source" : "doc.balance.value * 1.21"
}
}
}
}
}
POST /accounts/account/_search?size=0
{
"aggs" : {
"cardinalidad" : {
"cardinality" : {
"field" : "firstname.keyword"
}
}
}
}
92
POST /accounts/account/_search?size=0
{
"aggs" : {
"estadisticas" : {
"extended_stats" : {
"field" : "balance"
}
}
}
}
{ "aggregations": {
"estadisticas": {
"count": 1000,
"min": 1011,
"max": 49989,
"avg": 25714.837,
"sum": 25714837,
"sum_of_squares": 858626807735,
"variance": 197373965.79843104,
"std_deviation": 14048.984511288745,
"std_deviation_bounds": {
"upper": 53812.80602257749,
"lower": -2383.1320225774907
}
}
}
}
POST /accounts/account/_search?size=0
{
"aggs" : {
"minimo" : {
"min" : {
"field" : "balance"
}
},
"maximo" : {
"max" : {
"field" : "balance"
}
}
}
}
93
12.3. Rangos
• Basado en generat buckets categorizados.
POST accounts/account/_search?size=0
{
"aggs":{
"rango-de-balances": {
"range":{
"field":"balance",
"ranges": [
{"to":3000},
{"from":3000,"to":10000},
{"from":10000,"to":15000},
{"from":15000,"to":20000},
{"from":20000}
]
}
}
}
}
94
POST agregaciones/fechas
{ "date": "2017-01-01" }
POST agregaciones/fechas
{ "date": "2017-02-01" }
POST agregaciones/fechas
{ "date": "2017-03-01" }
POST agregaciones/fechas
{ "date": "2017-04-01" }
POST agregaciones/fechas
{ "date": "2017-05-01" }
POST /agregaciones/fechas/_search?size=0
{
"aggs": {
"range": {
"date_range": {
"field": "date",
"format": "MM-yyyy",
"ranges": [
{ "from": "01-2017", "to": "03-2017", "key": "primero" },
{ "from": "03-2017", "to": "06-2017", "key": "segundo" }
],
"keyed": true
}
}
}
}
12.5. Términos
• Se basan en el calculo de buckets de documentos
POST accounts/account/_search?size=0
{
"aggs":{
"primera-agregacion": {
"terms":{
"field":"gender.keyword"
}
}
}
}
12.6. Anidaciones
• Las anidaciones pertenecen a la categoría bucket
95
• Realizar agregaciones en anidaciones requieren indicar cual es el path del anidamiento
POST cuentas/cuenta/_search?size=0
{
"query" : {
"match" : { "ccc" : "214324322543535" }
},
"aggs" : {
"movimientos" : {
"nested" : {
"path" : "movimientos"
},
"aggs" : {
"maximo-movimiento" : { "max" : { "field" : "movimientos.valor" } }
}
}
}
}
96
Capítulo 13. Modelado de datos
• La base de datos de Elasticsearch permiten realizar operaciones no disponibles en bases de
datos tradicionales, pero eso no es gratuito.
• Las relaciones entre entidades no es tan obvia como estamos acostumbrados con SQL
• Elasticsearch está preparado para un escalado horizontal que debemos tener en cuenta
◦ Desnormalización de datos
◦ Anidación
◦ Relaciones padre-hijo
PUT uniones-app-c/cliente/1
{
"nombre":"Roberto",
"descripcion": "Cliente majo"
}
PUT uniones-app-f/facturas/1
{
"codigo":"fmg-22",
"precio":22554.21,
"cliente":1
}
• En este caso, se realizarán dos consultas para obtener los datos relacionados.
• Si se quiere obtener las facturas del cliente por nombre, se consultará el nombre para obtener el
id, y con el id se buscará el cliente
• Ventajas:
◦ Normalización de datos
• Inconvenientes:
• Uso:
97
◦ Si la primera entidad pose un número de documentos pequeños
13.2. Desnormalización
• Para aumentar el máximo de rendimiento en nuestras consultas, se desnormaliza para realizar
menos consultas y se elimina la necesidad de joins
PUT desnormalizacion-app-c/cliente/1
{
"nombre":"Roberto",
"pago":"paypal",
"descripcion": "Cliente majo"
}
PUT desnormalizacion-app-f/facturas/1
{
"codigo":"fmg-22",
"precio":22554.21,
"cliente":{
"id":1,
"nombre":"Roberto",
"pago":"paypal"
}
}
GET /desnormalizacion-app-f/facturas/_search
{
"query": {
"bool": {
"must": [
{ "match": { "codigo":"fmg-*" }},
{ "match": { "cliente.nombre": "Roberto"}}
]
}
}
}
◦ Un campo que va a ser el centro de una agregación, debe almacenarse en un campo de tipo
keyword para agrupar por el nombre completo.
98
PUT desnormalizacion-fields
PUT desnormalizacion-fields/facturas/_mapping
{
"properties": {
"cliente": {
"properties": {
"id": {
"type": "long"
},
"nombre": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"pago": {
"type": "text",
"fields": {
"raw": {
"type": "keyword",
"ignore_above": 256
}
}
}
}
},
"codigo": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"precio": {
"type": "float"
}
}
}
• Por defecto, los campos ya se crean con ese formato de field de tipo keyword
99
Introducimos datos para probar las consultas
POST desnormalizacion-fields/facturas
{
"codigo":"fmg-21",
"precio":225234.21,
"cliente":{
"id":1,
"nombre":"Roberto",
"pago":"paypal"
}
}
POST desnormalizacion-fields/facturas
{
"codigo":"fmg-22",
"precio":1554.21,
"cliente":{
"id":2,
"nombre":"Luis",
"pago":"paypal"
}
}
POST desnormalizacion-fields/facturas
{
"codigo":"fmg-23",
"precio":23554.21,
"cliente":{
"id":3,
"nombre":"Alberto",
"pago":"Tarjeta de crédito"
}
}
100
GET /desnormalizacion-fields/facturas/_search
{
"size" : 0,
"query": {
"bool": {
"should": [
{ "match": { "codigo": "fmg-*" }},
{ "match": { "cliente.nombre": "luis" }}
]
}
}
,
"aggs": {
"formas-pago": {
"terms": {
"field": "cliente.pago.raw"
}
}
}
}
101
{
"took": 2,
"timed_out": false,
"_shards": {
"total": 5,
"successful": 5,
"failed": 0
},
"hits": {
"total": 3,
"max_score": 0,
"hits": []
},
"aggregations": {
"formas-pago": {
"doc_count_error_upper_bound": 0,
"sum_other_doc_count": 0,
"buckets": [
{
"key": "paypal",
"doc_count": 2
},
{
"key": "Tarjeta de crédito",
"doc_count": 1
}
]
}
}
}
• Ventajas
◦ Velocidad, ya que cada documento posee toda la información necesaria para poder
encontral lo necesario en las consultas
◦ No necesita de joins
• Invonvenientes
◦ El índice es mayor ya que el documento _source para cada entrada es mayor y existen
mayor cantidad de campos indexados. No es un gran problema ya que los datos en disco
están comprimidos.
◦ En caso de alta concurrencia, esto es, mientras realizamos cientos, miles o millones de
actualizaciones, un usuario actualiza un documento. Puesto que elasticsearch no soporta
transacciones ACID, de múltiples documentos, debemos solucionar los problemas de
concurrencia. Para ello tenemos tres soluciones
102
▪ Concurrencia: Bloqueo global ( PUT /index/type/global/_create {} ) creación de un
documento. En este caso se crea bloqueo global, si y a hay uno da error. Al terminar
(DELETE /index/type/global). Debemos comprobar siempre si este documento existe o no
como operación previa
PUT /embebidos/facturas/1
{
"codigo":"33fb1-23",
"etiquetas": [ "informatica", "ventas" ],
"productos": [
{
"titulo": "Ordenador",
"comentarios": "Modelo 3.214",
"valor":2425.21
},
{
"titulo": "Raton",
"comentarios": "Logitech v12",
"valor":225.11
},{
"titulo": "Monitor",
"comentarios": "LG 2131t2",
"valor":722.15
}
]
}
GET embebidos/facturas/_mapping
{
"embebidos": {
"mappings": {
"facturas": {
"properties": {
"codigo": {
"type": "text",
103
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"etiquetas": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"productos": {
"properties": {
"comentarios": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"titulo": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"valor": {
"type": "float"
}
}
}
}
}
}
}
}
• Pero debemos tener en cuenta las consecuencias del almacenamiento de la información de esta
forma
104
GET embebidos/facturas/_search
{
"query": {
"bool": {
"must": [
{ "match": { "productos.titulo": "Raton" }},
{ "match": { "productos.valor": 722.15 }}
]
}
}
}
• Para ello existen los documentos anidados. Los documentos anidados se indexan de forma
independiente, y mantienen sus relaciones.
• Unir el documento padre a los hijos es muy rápido, casi como si estuviera en el mismo
documento
105
PUT /embebido-final
{
"mappings": {
"facturas": {
"properties": {
"codigo": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"etiquetas": {
"type": "text",
"fields": {
"keyword": {
"type": "keyword",
"ignore_above": 256
}
}
},
"productos": {
"type": "nested",
"properties": {
"titulo": { "type": "text" },
"comentarios": { "type": "text" },
"valor": { "type": "integer" }
}
}
}
}
}
}
106
PUT /embebido-final/facturas/1
{
"codigo":"33fb1-23",
"etiquetas": [ "informatica", "ventas" ],
"productos": [
{
"titulo": "Ordenador",
"comentarios": "Modelo 3.214",
"valor":2425.21
},
{
"titulo": "Raton",
"comentarios": "Logitech v12",
"valor":225.11
},{
"titulo": "Monitor",
"comentarios": "LG 2131t2",
"valor":722.15
}
]
}
107
GET /embebido-final/facturas/_search
{
"query": {
"bool": {
"must": [
{
"match": {
"codigo": "33fb1-23"
}
},
{
"nested": {
"path": "productos",
"query": {
"bool": {
"must": [
{
"match": {
"productos.titulo": "Raton"
}
},
{
"match": {
"productos.comentarios":"Modelo 3.214"
}
}
]
}
}
}
}
]
}}}
• Los documentos anidados se usan en caso de que no haya un documento principal claro, y con
un número controlado de elementos.
• Ventajas
• Inconvenientes
108
documento debe ser indexado, con un gran coste
• Como los tipos ya no existen, no se puede generar más de un tipo por índice (y pronto
desaparecerán) tenemos la opción de join:
PUT empresas
{
"mappings": {
"_doc": {
"properties": {
"campo_join": {
"type": "join",
"relations": {
"cliente": "factura"
}
}
}
}
}
}
PUT empresas/_doc/1
{
"nombre": "Ruben",
"apellido1": "Gomez",
"campo_join": "cliente"
}
PUT empresas/_doc/2
{
"nombre": "Luis",
"apellido1": "Perez",
"campo_join": "cliente"
}
109
PUT empresas/_doc/3?refresh&routing=1
{
"factura": "El caso es deber algo",
"campo_join": {
"name": "factura",
"parent": "1"
}
}
PUT empresas/_doc/4?refresh&routing=1
{
"factura": "El caso es deber otra cosa",
"campo_join": {
"name": "factura",
"parent": "1"
}
}
PUT empresas/_doc/5?refresh&routing=1
{
"factura": "El caso es deber algo a otro",
"campo_join": {
"name": "factura",
"parent": "2"
}
}
GET empresas/_search
{
"query": {
"parent_id":{
"type":"factura",
"id":1
}
}
}
• El resultado es todos los parents que posean estas coincidencias de los hijos, y no devuelve los
hijos de los padres.
110
GET /empresa/facturas/_search
{
"query": {
"has_parent": {
"type": "cliente",
"query": {
"match": {
"nombre": "Alberto"
}
}
}
}
}
• Consideraciones finales
◦ Cada generación de padres, necesitan su campo _id almacenado en memoria (mucha ram)
◦ No se deben usar multiples joins con padres e hijos en una consulta única
◦ Se deben mantener los ids de los padres pequeños para que se compriman en valores doc y
usen menos memoria.
13.5. i18n
• El uso de analizadores implica la generación de distintas soluciones a problemas de modelado
de datos.
• Los problemas de uso de multi idioma deben ser evaluados a la hora de construir nuestras
estructuras.
• El hecho de unir distintos lenguajes en el mismo inverted index puede ser un problema real
◦ Las reglas de stemming para un idioma pueden ser totalmente distintos a las reglas de otros
idiomas.
◦ El uso de distintos stemmers a la vez contra el mismo campo, puede realizar el doble
stemming en un solo término, lo que produce errores de comprensión y de generación de
términos.
◦ Si un término aparece muchas veces, el score aumenta y si ordenamos por _score aparecerá
en un puesto superior. Esto produce que algunos idiomas, al tener menos términos o más
repetitivos o cortos, se potencien frente a otros. *EL uso de lenguajes también lleva a
distintos comportamientos según los deseos del cliente
111
• Para solventar todas estas casuísticas, debemos resolverlos por medio de alguna de las
siguientes opciones:
PUT /services-es
{
"mappings": {
"extra": {
"properties": {
"name": {
"type": "string",
"fields": {
"i18n": {
"type": "string",
"analyzer": "spanish"
}
}
}
}
}
}
}
PUT /services-en
{
"mappings": {
"extra": {
"properties": {
"name": {
"type": "string",
"fields": {
"i18n": {
"type": "string",
"analyzer": "english"
}
}
}
}
}
}
}
112
Definición de índice en francés
PUT /services-fr
{
"mappings": {
"extra": {
"properties": {
"name": {
"type": "string",
"fields": {
"i18n": {
"type": "string",
"analyzer": "french"
}
}
}
}
}
}
}
Introducción de datos
POST services-es/extra
{
"name":"Mensaje en español"
}
POST services-en/extra
{
"name":"Message in spanish"
}
POST services-fr/extra
{
"name":"Messaje en espagnol"
}
• Para hacer las consultas por idioma, usamos un índice distinto o buscamos en todos los índices:
113
GET /services-*/extra/_search
{
"query": {
"multi_match": {
"query": "mensaje en español",
"fields": [ "name","name.i18n" ] ,
"type": "most_fields"
}
},
"indices_boost": {
"services-en": 3,
"services-fr": 2,
"services-es": 1
}
}
• Es el mas sencillo de todos, y a pesar de estar en el mismo índice, no están en el mismo inverted
index
PUT /services-i18n
{
"mappings": {
"extra": {
"properties": {
"name": {
"type": "string"
},
"name_es": {
"type": "string",
"analyzer": "spanish"
},
"name_en": {
"type": "string",
"analyzer": "english"
},
"name_fr": {
"type": "string",
"analyzer": "french"
}
}
}
}
}
114
Agregamos un documento
POST services-i18n/extra
{
"name":"Mensaje por defecto",
"name_es":"Mensaje por defecto",
"name_en":"Default message",
"name_fr":"message par défaut"
}
GET /services-i18n/extra/_search
{
"query": {
"multi_match": {
"query": "defecto",
"fields": [ "name*", "name_es^3" ],
"type": "most_fields"
}
}
}
• Para un uso limitado de lenguajes, se usa la capacidad de multi-field. De esa forma se realiza un
análisis por campo.
115
PUT /services-multimapping
{
"mappings": {
"extra": {
"properties": {
"name": {
"type": "string",
"fields": {
"i18n-es": {
"type": "string",
"analyzer": "spanish"
},
"i18n-en": {
"type": "string",
"analyzer": "english"
},
"i18n-fr": {
"type": "string",
"analyzer": "french"
}
}
}
}
}
}
}
Introducción de información
POST services-multimapping/extra
{
"name":"texto en español"
}
GET /services-multimapping/extra/_search
{
"query": {
"multi_match": {
"query": "español",
"fields": [ "name.i18n*^2.5", "name" ],
"type": "most_fields",
"minimum_should_match": "75%"
}
}
}
• minimum_should_match permite filtrar los scores más bajos para evitar problemas
116