suggester - autocomplete elasticsearch
Sugerencia de finalización orientada a palabras(ElasticSearch 5.x) (3)
ElasticSearch 5.x introdujo algunos cambios (de ruptura) en la API del Sugerente ( Documentation ). El cambio más notable es el siguiente:
El sugerente de finalización está orientado a documentos
Las sugerencias son conscientes del documento al que pertenecen. Ahora, los documentos asociados (
_source
) se devuelven como parte de las sugerencias de finalización.
En resumen, todas las consultas de finalización devuelven todos los documentos coincidentes en lugar de solo palabras coincidentes. Y aquí radica el problema: la duplicación de palabras autocompletadas si aparecen en más de un documento.
Digamos que tenemos este sencillo mapeo:
{
"my-index": {
"mappings": {
"users": {
"properties": {
"firstName": {
"type": "text"
},
"lastName": {
"type": "text"
},
"suggest": {
"type": "completion",
"analyzer": "simple"
}
}
}
}
}
}
Con unos pocos documentos de prueba:
{
"_index": "my-index",
"_type": "users",
"_id": "1",
"_source": {
"firstName": "John",
"lastName": "Doe",
"suggest": [
{
"input": [
"John",
"Doe"
]
}
]
}
},
{
"_index": "my-index",
"_type": "users",
"_id": "2",
"_source": {
"firstName": "John",
"lastName": "Smith",
"suggest": [
{
"input": [
"John",
"Smith"
]
}
]
}
}
Y una consulta por el libro:
POST /my-index/_suggest?pretty
{
"my-suggest" : {
"text" : "joh",
"completion" : {
"field" : "suggest"
}
}
}
Lo que arroja los siguientes resultados:
{
"_shards": {
"total": 5,
"successful": 5,
"failed": 0
},
"my-suggest": [
{
"text": "joh",
"offset": 0,
"length": 3,
"options": [
{
"text": "John",
"_index": "my-index",
"_type": "users",
"_id": "1",
"_score": 1,
"_source": {
"firstName": "John",
"lastName": "Doe",
"suggest": [
{
"input": [
"John",
"Doe"
]
}
]
}
},
{
"text": "John",
"_index": "my-index",
"_type": "users",
"_id": "2",
"_score": 1,
"_source": {
"firstName": "John",
"lastName": "Smith",
"suggest": [
{
"input": [
"John",
"Smith"
]
}
]
}
}
]
}
]
}
En resumen, para una sugerencia completa para el texto "joh", se devolvieron dos (2) documentos , ambos de John y ambos tenían el mismo valor de la propiedad de text
.
Sin embargo, me gustaría recibir una (1) palabra . Algo tan simple como esto:
{
"_shards": {
"total": 5,
"successful": 5,
"failed": 0
},
"my-suggest": [
{
"text": "joh",
"offset": 0,
"length": 3,
"options": [
"John"
]
}
]
}
Pregunta : cómo implementar un sugerente de finalización basado en palabras. No es necesario devolver ningún dato relacionado con el documento, ya que no lo necesito en este momento.
¿Es el "Sugerencia de finalización" incluso apropiado para mi escenario? ¿O debería usar un enfoque completamente diferente?
EDITAR : Como muchos de ustedes señalaron, un índice adicional de solo finalización sería una solución viable. Sin embargo, puedo ver varios problemas con este enfoque:
- Manteniendo sincronizado el nuevo índice.
- Completar automáticamente las palabras subsiguientes probablemente sería global, en lugar de limitarse. Por ejemplo, digamos que tiene las siguientes palabras en el índice adicional:
"John", "Doe", "David", "Smith"
. Al consultar"John D"
, el resultado de la palabra incompleta debe ser"Doe"
y no"Doe", "David"
.
Para superar el segundo punto, solo la indexación de palabras individuales no sería suficiente, ya que también necesitaría asignar todas las palabras a los documentos para restringir correctamente las palabras subsiguientes. Y con esto, realmente tiene el mismo problema que consultar el índice original. Por lo tanto, el índice adicional ya no tiene sentido.
Como se indicó en el comentario, otra forma de lograr esto sin obtener los documentos duplicados es crear un subcampo para el primer campo que contiene ngrams del campo. Primero definas tu mapeo así:
PUT my-index
{
"settings": {
"analysis": {
"analyzer": {
"completion_analyzer": {
"type": "custom",
"filter": [
"lowercase",
"completion_filter"
],
"tokenizer": "keyword"
}
},
"filter": {
"completion_filter": {
"type": "edge_ngram",
"min_gram": 1,
"max_gram": 24
}
}
}
},
"mappings": {
"users": {
"properties": {
"autocomplete": {
"type": "text",
"fields": {
"raw": {
"type": "keyword"
},
"completion": {
"type": "text",
"analyzer": "completion_analyzer",
"search_analyzer": "standard"
}
}
},
"firstName": {
"type": "text"
},
"lastName": {
"type": "text"
}
}
}
}
}
A continuación, indexa algunos documentos:
POST my-index/users/_bulk
{"index":{}}
{ "firstName": "John", "lastName": "Doe", "autocomplete": "John Doe"}
{"index":{}}
{ "firstName": "John", "lastName": "Deere", "autocomplete": "John Deere" }
{"index":{}}
{ "firstName": "Johnny", "lastName": "Cash", "autocomplete": "Johnny Cash" }
Luego puedes consultar por joh
y obtener un resultado para John
y otro para Johnny
{
"size": 0,
"query": {
"term": {
"autocomplete.completion": "john d"
}
},
"aggs": {
"suggestions": {
"terms": {
"field": "autocomplete.raw"
}
}
}
}
Resultados:
{
"aggregations": {
"suggestions": {
"doc_count_error_upper_bound": 0,
"sum_other_doc_count": 0,
"buckets": [
{
"key": "John Doe",
"doc_count": 1
},
{
"key": "John Deere",
"doc_count": 1
}
]
}
}
}
Nos enfrentamos exactamente al mismo problema. En Elasticsearch 2.4, el enfoque como el que describiste solía funcionar bien para nosotros, pero ahora, como usted dice, el sugestor se ha basado en el documento, mientras que al igual que usted, solo estamos interesados en palabras únicas, no en los documentos.
La única "solución" en la que podríamos pensar hasta ahora es crear un índice separado solo para las palabras en las que queremos realizar las consultas de sugerencias y en este índice separado, asegúrese de que de alguna manera las palabras idénticas solo se indexen una vez. Luego, podría realizar las consultas de sugerencias en este índice separado. Esto dista mucho de ser ideal, aunque solo sea porque entonces debemos asegurarnos de que este índice permanezca sincronizado con el otro índice que necesitamos para nuestras otras consultas.
Se agregará un campo adicional skip_duplicates en la próxima versión 6.x.
De los documentos en https://www.elastic.co/guide/en/elasticsearch/reference/master/search-suggesters-completion.html#skip_duplicates :
POST music/_search?pretty
{
"suggest": {
"song-suggest" : {
"prefix" : "nor",
"completion" : {
"field" : "suggest",
"skip_duplicates": true
}
}
}
}