Cómo buscar una parte de una palabra con ElasticSearch

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 …

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.

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 spring https://stackoverflow.com/a/43579948/2357869

Una explicación más sobre cómo query_string es mejor que otros https://stackoverflow.com/a/43321606/2357869

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

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í:

enter image description here

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

puedes usar regexp.

 { "_id" : "1", "name" : "John Doeman" , "function" : "Janitor"} { "_id" : "2", "name" : "Jane Doewoman","function" : "Teacher" } { "_id" : "3", "name" : "Jimmy Jackal" ,"function" : "Student" } 

si usas esta consulta:

 { "query": { "regexp": { "name": "J.*" } } } 

le dará todos los datos que su nombre comience con “J”. Considere que quiere recibir solo los primeros dos registros que su nombre termine con “hombre” para que pueda usar esta consulta:

 { "query": { "regexp": { "name": ".*man" } } } 

y si desea recibir todo el registro que en su nombre existe “m”, puede usar esta consulta:

 { "query": { "regexp": { "name": ".*m.*" } } } 

Esto funciona para mí. Y espero que mi respuesta sea adecuada para resolver su problema.

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!