query - Cómo buscar una parte de una palabra con ElasticSearch
elasticsearch tutorial (8)
Recientemente comencé a usar ElasticSearch y parece que no puedo hacer que busque una parte de una palabra.
Ejemplo: Tengo tres documentos de mi couchdb indexados en ElasticSearch:
{
"_id" : "1",
"name" : "John Doeman",
"function" : "Janitor"
}
{
"_id" : "2",
"name" : "Jane Doewoman",
"function" : "Teacher"
}
{
"_id" : "3",
"name" : "Jimmy Jackal",
"function" : "Student"
}
Así que ahora, quiero buscar todos los documentos que contengan "Doe"
curl http://localhost:9200/my_idx/my_type/_search?q=Doe
Eso no devuelve ningún golpe. Pero si busco
curl http://localhost:9200/my_idx/my_type/_search?q=Doeman
Devuelve un documento (John Doeman).
Intenté configurar diferentes analizadores y diferentes filtros como propiedades de mi índice. También intenté usar una consulta completa (por ejemplo:
{
"query": {
"term": {
"name": "Doe"
}
}
}
) Pero nada parece funcionar.
¿Cómo puedo hacer que ElasticSearch encuentre tanto a John Doeman como a Jane Doewoman cuando busco "Doe"?
ACTUALIZAR
Traté de usar el tokenizador y filtro nGram, como Igor propuso, así:
{
"index": {
"index": "my_idx",
"type": "my_type",
"bulk_size": "100",
"bulk_timeout": "10ms",
"analysis": {
"analyzer": {
"my_analyzer": {
"type": "custom",
"tokenizer": "my_ngram_tokenizer",
"filter": [
"my_ngram_filter"
]
}
},
"filter": {
"my_ngram_filter": {
"type": "nGram",
"min_gram": 1,
"max_gram": 1
}
},
"tokenizer": {
"my_ngram_tokenizer": {
"type": "nGram",
"min_gram": 1,
"max_gram": 1
}
}
}
}
}
El problema que estoy teniendo ahora es que todas y cada una de las consultas devuelve TODOS los documentos. ¿Alguna sugerencia? La documentación de ElasticSearch sobre el uso de nGram no es genial ...
Creo que no es necesario cambiar ningún mapeo. Intenta usar query_string , es perfecto. Todos los escenarios funcionarán con el analizador estándar predeterminado:
Tenemos datos:
{"_id" : "1","name" : "John Doeman","function" : "Janitor"}
{"_id" : "2","name" : "Jane Doewoman","function" : "Teacher"}
Escenario 1:
{"query": {
"query_string" : {"default_field" : "name", "query" : "*Doe*"}
} }
Respuesta:
{"_id" : "1","name" : "John Doeman","function" : "Janitor"}
{"_id" : "2","name" : "Jane Doewoman","function" : "Teacher"}
Escenario 2:
{"query": {
"query_string" : {"default_field" : "name", "query" : "*Jan*"}
} }
Respuesta:
{"_id" : "1","name" : "John Doeman","function" : "Janitor"}
Escenario 3:
{"query": {
"query_string" : {"default_field" : "name", "query" : "*oh* *oe*"}
} }
Respuesta:
{"_id" : "1","name" : "John Doeman","function" : "Janitor"}
{"_id" : "2","name" : "Jane Doewoman","function" : "Teacher"}
EDITAR: la misma implementación con la búsqueda elástica de datos de primavera https://.com/a/43579948/2357869
Una explicación más sobre cómo query_string es mejor que otros https://.com/a/43321606/2357869
Elasticsearch tiene una consulta de comodín que se puede utilizar en este caso y es la más fácil. Devolverá ambos documentos coincidentes
Estoy usando nGram, también. Yo uso tokenizer estándar y nGram solo como un filtro. He aquí mi arreglo:
{
"index": {
"index": "my_idx",
"type": "my_type",
"analysis": {
"index_analyzer": {
"my_index_analyzer": {
"type": "custom",
"tokenizer": "standard",
"filter": [
"lowercase",
"mynGram"
]
}
},
"search_analyzer": {
"my_search_analyzer": {
"type": "custom",
"tokenizer": "standard",
"filter": [
"standard",
"lowercase",
"mynGram"
]
}
},
"filter": {
"mynGram": {
"type": "nGram",
"min_gram": 2,
"max_gram": 50
}
}
}
}
}
Vamos a encontrar partes de palabras de hasta 50 letras. Ajuste el max_gram como lo necesite. En alemán, las palabras pueden ser muy grandes, así que lo configuré con un alto valor.
La búsqueda con comodines iniciales y finales va a ser extremadamente lenta en un índice grande. Si desea poder buscar por prefijo de palabra, elimine el comodín principal. Si realmente necesita encontrar una subcadena en medio de una palabra, sería mejor usar ngram tokenizer.
No importa.
Tuve que mirar la documentación de Lucene. ¡Parece que puedo usar comodines! :-)
curl http://localhost:9200/my_idx/my_type/_search?q=*Doe*
¡Hace el truco!
Pruebe la solución con la que se describe aquí: Búsquedas de subcadenas exactas en ElasticSearch
{
"mappings": {
"my_type": {
"index_analyzer":"index_ngram",
"search_analyzer":"search_ngram"
}
},
"settings": {
"analysis": {
"filter": {
"ngram_filter": {
"type": "ngram",
"min_gram": 3,
"max_gram": 8
}
},
"analyzer": {
"index_ngram": {
"type": "custom",
"tokenizer": "keyword",
"filter": [ "ngram_filter", "lowercase" ]
},
"search_ngram": {
"type": "custom",
"tokenizer": "keyword",
"filter": "lowercase"
}
}
}
}
}
Para resolver el problema del uso del disco y el problema del término de búsqueda demasiado largo, se utilizan ngrams cortos de 8 caracteres (configurados con: "max_gram": 8 ). Para buscar términos con más de 8 caracteres, convierta su búsqueda en una consulta AND booleana que busque cada subcadena distinta de 8 caracteres en esa cadena. Por ejemplo, si un usuario buscara una yarda grande (una cadena de 10 caracteres), la búsqueda sería:
"arge ya Y arge yar Y rge yarda .
Si desea implementar funcionalidad de autocompletar, Completion Suggester es la solución más ordenada. La siguiente publicación del blog contiene una descripción muy clara de cómo funciona esto.
En dos palabras, se trata de una estructura de datos en memoria llamada FST que contiene sugerencias válidas y está optimizada para la recuperación rápida y el uso de la memoria. Básicamente, es solo un gráfico. Por ejemplo, y FST que contiene las palabras hotel
, marriot
, mercure
, munchen
y munich
se vería así:
sin cambiar las asignaciones de su índice, podría hacer una consulta de prefijo simple que hará búsquedas parciales como las que espera
es decir.
{
"query": {
"prefix" : { "name" : "Doe" }
}
}
https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl-prefix-query.html