Busque en el sistema de información corporativo , ya que esta frase se queda atrapada en la boca. Es bueno si tienes uno, ni siquiera tienes que pensar en una experiencia de usuario positiva. ¿Cómo revertir la actitud de los usuarios mimados por los motores de búsqueda y crear un producto rápido, preciso y perfectamente comprensible? Necesitamos tomar una buena parte de Elasticsearch, un puñado de servicios inteligentes y amasarlos en esta guía.
Hay muchos artículos sobre cómo ajustar la búsqueda de texto completo basada en Elasticsearch a la base de datos existente. Pero claramente no hay suficientes artículos sobre cómo hacer una búsqueda realmente inteligente.
Al mismo tiempo, la frase "Búsqueda inteligente" ya se ha convertido en una palabra de moda y se utiliza para el lugar y no. Entonces, ¿qué debe hacer un motor de búsqueda para ser considerado inteligente? En última instancia, esto puede describirse como el resultado que el usuario realmente necesita, incluso si este resultado no coincide con el texto de la solicitud. Los motores de búsqueda populares como Google y Yandex van más allá y no solo encuentran la información que necesitan, sino que responden directamente a las preguntas de los usuarios.
De acuerdo, no vamos a barrer una decisión de ultimátum de inmediato, pero ¿qué se puede hacer para acercar una búsqueda de texto completo a una inteligente ?
Elementos de inteligencia
Búsqueda inteligente: este es el caso cuando la cantidad puede entrar en calidad y muchas características pequeñas y bastante simples pueden formar un sentido de magia.
- Corrección de errores del usuario, ya sea un error tipográfico, un diseño incorrecto o, tal vez, una solicitud con un número sospechosamente pequeño de resultados, pero similar a una solicitud para la que hay mucha más información.
- Para
th Chats de PNL (procesamiento de lenguaje natural, no lo que pensaba): si el usuario ingresó ofertas comerciales durante el último año , ¿realmente quería buscar estas palabras en el texto de todos los documentos o realmente solo necesitaba ofertas comerciales y solo el año pasado? ? - Predecir entradas basadas en consultas anteriores o documentos populares.
- La presentación del resultado es el punto culminante habitual del fragmento encontrado, información adicional dependiendo de lo que estaba buscando. Dado que se necesitaban propuestas comerciales en el párrafo anterior, ¿quizás tenga sentido mostrar de inmediato el tema de la propuesta y de qué organización proviene?
- Desglose fácil: la capacidad de refinar la consulta de búsqueda utilizando filtros adicionales, facetas.
Introductorio
Hay un DIRECTO ECM con muchos documentos. El documento consta de una tarjeta con metainformación y un cuerpo, que puede tener varias versiones.
El objetivo es buscar rápida y convenientemente información en estos documentos de la manera habitual para un usuario de motores de búsqueda.
Indexación
Para buscar algo bien, primero debe indexarlo bien.
Los documentos en ECM no son estáticos, los usuarios modifican texto, crean nuevas versiones, cambian datos en tarjetas; constantemente se crean nuevos documentos y los antiguos a veces se eliminan.
Para mantener la información actualizada en Elasticsearch, los documentos deben reindexarse constantemente. Afortunadamente, ECM ya tiene su propia cola de eventos asincrónicos, así que cuando cambie un documento, simplemente agréguelo a la cola para indexarlo.
Asignación de documentos ECM a documentos de Elasticsearch
Un cuerpo de documento en ECM puede tener varias versiones. En Elasticsearch, esto podría considerarse como una matriz de objetos anidados, pero luego resulta inconveniente trabajar con ellos: se vuelve más difícil escribir consultas, al cambiar una de las versiones, debe reindexar todo, no se pueden almacenar diferentes versiones del mismo documento en diferentes índices (¿por qué podría ser necesario?) en la siguiente sección). Por lo tanto, desnormalizamos un documento de ECM en varios documentos de Elasticsearch con la misma tarjeta pero con cuerpos diferentes.
Además de la tarjeta y el cuerpo, se agrega diversa información de servicio al documento de Elasticsearch, que vale la pena mencionar por separado:
- una lista de ID de grupos y usuarios que tienen derechos sobre el documento, para búsquedas con derechos;
- la cantidad de llamadas al documento, para ajustar la relevancia;
- hora de la última indexación.
Composición de índice
Sí, índices plurales. Por lo general, se utilizan varios índices para almacenar información que tiene un significado similar en Elasticsearch solo si esta información es inmutable y está vinculada a algún tipo de período de tiempo, por ejemplo, registros. Luego, los índices se crean cada mes / día o más a menudo dependiendo de la intensidad de la carga. En nuestro caso, cualquier documento puede cambiarse y sería posible almacenar todo en un índice.
Pero, los documentos en el sistema pueden estar en diferentes idiomas, y almacenar datos multilingües en Elasticsearch trae 2 problemas:
- Mal derivado. Para algunas palabras, la base se encontrará correctamente, para algunos - incorrectamente (habrá otra palabra en el índice), para algunos - no se encontrará en absoluto (el índice se obstruirá con formas de palabras). Para algunas palabras de diferentes idiomas y con diferentes significados, la base será la misma y luego se perderá el significado de la palabra. El uso de varios stemmers en una fila puede conducir a un cálculo adicional de la base para uno ya calculado.
Tartamudeo: encontrar la base de la palabra. La raíz no tiene que ser la raíz de la palabra o su forma normal. Por lo general, es suficiente que las palabras relacionadas se proyecten en un marco.
La lematización es un tipo de derivación en la cual la forma normal (vocabulario) de una palabra se considera la base.
- Frecuencia de palabras incorrecta. Algunos mecanismos de determinación de relevancia en ES tienen en cuenta la frecuencia de las palabras buscadas en el documento (cuanto más frecuente, mayor es la relevancia) y la frecuencia de las palabras buscadas en el índice (cuanto más frecuente, menor es la relevancia). Entonces, una pequeña difusión del discurso ruso en un documento en inglés, cuando los documentos en inglés están predominantemente en el índice, tendrá un gran peso, pero vale la pena mezclar los documentos en inglés y ruso en el índice, y el peso disminuirá.
El primer problema puede resolverse para el caso en que diferentes idiomas usan diferentes conjuntos de caracteres (los documentos ruso-ingleses usan letras cirílicas y latinas): los usuarios de idiomas solo procesarán "sus" caracteres.
Solo para resolver el segundo problema, utilizamos el enfoque con un índice separado para cada idioma.
Combinando ambos enfoques, obtenemos índices de idiomas, que sin embargo contienen analizadores para varios idiomas que no se cruzan en conjuntos de caracteres: ruso-inglés (y por separado inglés-ruso), polaco-ruso, alemán-ruso, ucraniano-inglés, etc. .
Para no crear todos los índices posibles por adelantado, utilizamos plantillas de índice: Elasticsearch le permite especificar una plantilla que contiene configuraciones y asignaciones, y especificar un patrón de nombre de índice. Cuando intentas indexar un documento en un índice inexistente, cuyo nombre coincide con uno de los patrones de la plantilla, no solo se creará un nuevo índice, sino que se aplicarán configuraciones y asignaciones de la plantilla correspondiente.
Estructura de índice
Para la indexación, utilizamos dos analizadores a la vez (a través de múltiples campos): predeterminado para buscar por frase exacta y personalizado para todo lo demás:
"ru_en_analyzer": { "filter": [ "lowercase", "russian_morphology", "english_morphology", "word_delimiter", "ru_en_stopwords" ], "char_filter": [ "yo_filter" ], "type": "custom", "tokenizer": "standard"}
Con el filtro en minúsculas, todo está claro, te contaré sobre el resto.
Los filtros ruso_morfología e inglés_morfología están destinados al análisis morfológico de textos en ruso e inglés, respectivamente. No forman parte de Elasticsearch y se incluyen como parte de un complemento de análisis-morfología separado. Estos son lematizadores que utilizan el enfoque de vocabulario en combinación con algunas heurísticas y funcionan significativamente, MUCHO, mejor que los filtros integrados para los idiomas correspondientes.
POST _analyze { "analyzer": "russian", "text": " " } >>
Y:
POST _analyze { "analyzer": "ru_en_analyzer", "text": " " } >>
Muy curioso filtro word_delimiter. Por ejemplo, ayuda a eliminar errores tipográficos cuando no hay espacio después del punto. Usamos la siguiente configuración:
"word_delimiter": { "catenate_all": "true", "type": "word_delimiter", "preserve_original": "true" }
yo_filter le permite ignorar la diferencia entre E y E:
"yo_filter": { "type": "mapping", "mappings": [ " => ", " => " ] }
tipo de filtro ru_en_stopwords stop: nuestro diccionario de palabras de detención.
Proceso de indexación
Los cuerpos de los documentos en ECM son, por regla general, archivos de formatos de oficina: .docx, .pdf, etc. Para extraer texto, el complemento de ingesta de archivos adjuntos se utiliza con la siguiente tubería:
{ "document_version": { "processors": [ { "attachment": { "field": "content", "target_field": "attachment", "properties": [ "content", "content_length", "content_type", "language" ], "indexed_chars": -1, "ignore_failure": true } }, { "remove": { "field": "content", "ignore_failure": true } }, { "script": { "lang": "painless", "params": { "languages": ["ru", "en" ], "language_delimeter": "_" }, "source": "..." } }, { "remove": { "field": "attachment", "ignore_failure": true } } ] } }
De lo inusual en la tubería, ignorar los errores de la ausencia del cuerpo (esto sucede para los documentos encriptados) y determinar el índice objetivo en función del idioma del texto. Este último se realiza en un guión indoloro, cuyo cuerpo daré por separado, porque debido a restricciones JSON, tiene que escribirse en una línea. Junto con las dificultades de depuración (la forma recomendada es lanzar excepciones aquí y allá), se convierte completamente en una dolorosa.
if (ctx.attachment != null) { if (params.languages.contains(ctx.attachment.language)) ctx._index = ctx._index + params.language_delimeter + ctx.attachment.language; if (ctx.attachment.content != null) ctx.content = ctx.attachment.content; if (ctx.attachment.content_length != null) ctx.content_length = ctx.attachment.content_length; if (ctx.attachment.content_type != null) ctx.content_type = ctx.attachment.content_type; if (ctx.attachment.language != null) ctx.language = ctx.attachment.language; }
Por lo tanto, siempre enviamos el documento a index_name . Si el idioma no está definido o no es compatible, entonces el documento se establece en este índice, de lo contrario cae en index_name_language .
No almacenamos el cuerpo original del archivo, pero el campo _source está habilitado, porque se requiere actualizar parcialmente el documento y resaltar lo encontrado.
Si solo la tarjeta ha cambiado desde la última indexación, entonces usamos la API Actualizar por consulta sin canalización para actualizarla. Esto permite, en primer lugar, no arrastrar cuerpos de documentos potencialmente pesados desde ECM, y en segundo lugar, acelera significativamente la actualización en el lado de Elasticsearch: no es necesario extraer el texto de los documentos de formatos de oficina, lo que requiere muchos recursos.
Como tal, no hay ninguna actualización del documento en Elasticsearch, técnicamente, cuando se actualiza desde el índice, el documento antiguo se saca, se cambia y se indexa por completo nuevamente.
Pero si el cuerpo cambió, entonces el documento antiguo generalmente se elimina e indexa desde cero. Esto permite que los documentos se muevan de un índice de idioma a otro.
Buscar
Para facilitar la descripción, daré una captura de pantalla del resultado final

Texto completo
El tipo principal de consulta que tenemos es Simple Query String Query :
"simple_query_string": { "fields": [ "card.d*.*_text", "card.d*.*_text.exact", "card.name^2", "card.name.exact^2", "content", "content.exact" ], "query": " ", "default_operator": "or", "analyze_wildcard": true, "minimum_should_match": "-35%", "quote_field_suffix": ".exact" }
donde .exact son los campos indexados por el analizador predeterminado . La importancia del nombre del documento es dos veces mayor que el resto de los campos. La combinación de "default_operator": "or"
y "minimum_should_match": "-35%"
permite encontrar documentos que no contienen hasta el 35% de las palabras buscadas.
Sinónimos
En general, se utilizan diferentes analizadores para indexar y buscar, pero la única diferencia en ellos es la adición de un filtro para agregar sinónimos a la consulta de búsqueda:
"search_analyzer": { "filter": [ "lowercase", "russian_morphology", "english_morphology", "synonym_filter", "word_delimiter", "ru_en_stopwords" ], "char_filter": [ "yo_filter" ], "tokenizer": "standard" }
"synonym_filter": { "type": "synonym_graph", "synonyms_path": "synonyms.txt" }
Derechos contables
Para las búsquedas basadas en derechos, la consulta principal se incrusta en Bool Query , con la adición de un filtro:
"bool": { "must": [ { "simple_query_string": {...} } ], "filter": [ { "terms": { "rights": [ ] } } ] }
Como recordamos de la sección sobre indexación, el índice tiene un campo con la ID de usuarios y grupos que tienen derechos sobre el documento. Si hay una intersección de este campo con la matriz pasada, entonces hay derechos.
Ajuste de relevancia
Por defecto, Elasticsearch evalúa la relevancia de los resultados usando el algoritmo BM25 usando la consulta y el texto del documento. Decidimos que tres factores más deberían influir en la evaluación del cumplimiento del resultado deseado y real:
- el momento de la última edición del documento: cuanto más lejos estuvo en el pasado, menos probable es que se necesite este documento;
- la cantidad de llamadas al documento: cuanto más, más probable es que se necesite este documento;
Las versiones del cuerpo de ECM tienen varios estados posibles: en desarrollo, operacionales y en desuso. Es lógico que la actuación sea más importante que las demás.
Puede lograr este efecto con la ayuda de Function Score Query :
"function_score": { "functions": [ { "gauss": { "modified_date": { "origin": "now", "scale": "1095d", "offset": "31d", "decay": 0.5 } } }, { "field_value_factor": { "field": "access_count", "missing": 1, "modifier": "log2p" } }, { "filter": { "term": { "life_stage_value_id": { "value": "" } } }, "weight": 1.1 } ], "query": { "bool": {...} } }
Como resultado, ceteris paribus, obtenemos aproximadamente la siguiente dependencia del modificador de calificación de resultados en la fecha de su último cambio X y el número de visitas Y:

Inteligencia externa
Para parte de la funcionalidad de búsqueda inteligente, necesitamos extraer varios hechos de la consulta de búsqueda: fechas con su aplicación (creación, modificación, aprobación, etc.), nombres de organizaciones, tipos de documentos buscados, etc.
También es deseable clasificar la solicitud en una determinada categoría, por ejemplo, documentos por organización, por empleado, regulatorio, etc.
Estas dos operaciones son realizadas por el módulo inteligente ECM - DIRECTUM Ario .
Proceso de búsqueda inteligente
Es hora de considerar con más detalle qué mecanismos son elementos de inteligencia implementados.
Corrección de errores del usuario
La exactitud del diseño se determina en función del modelo de lenguaje de trigrama: para una línea, se calcula la probabilidad de cumplir con sus secuencias de tres caracteres en textos en inglés y ruso. Si el diseño actual se considera menos probable, en primer lugar, se muestra una pista con un diseño correcto:

y en segundo lugar, los pasos adicionales de la búsqueda se realizan con el diseño correcto:

Y si no se puede encontrar nada con el diseño corregido, la búsqueda comienza con la línea original.
La corrección de errores tipográficos se implementa utilizando Phrase Suggester . Hay un problema con esto: si ejecuta una consulta en varios índices al mismo tiempo, puede sugerir que no devuelva nada, mientras que si ejecuta solo en un índice, hay resultados. Esto se trata estableciendo la confianza = 0, pero luego sugiere sugerir reemplazar las palabras con su forma normal. De acuerdo, será extraño cuando busque "letra a " para obtener una respuesta en el espíritu: ¿ Quizás estaba buscando una carta?
Esto se puede eludir usando dos indicaciones en la solicitud:
"suggest": { "content_suggest": { "text": " ", "phrase": { "collate": { "query": { {{suggestion}} } }, } }, "check_suggest": { "text": "", "phrase": { "collate": { "query": { {{suggestion}} - ({{source_query}}) }, "params": { "source_query": " " } }, } } }
De los parámetros comunes utilizados
"confidence": 0.0, "max_errors": 3.0, "size": 1
Si el primer opinador devolvió el resultado, pero el segundo no lo hizo, entonces este resultado es la cadena original en sí, posiblemente con palabras en otras formas, y no hay necesidad de mostrar una pista. Si aún se necesita la pista, la frase de búsqueda original se fusiona con la pista. Esto sucede al reemplazar solo las palabras corregidas y solo aquellas que el corrector ortográfico (usando Hunspell) considera incorrectas.
Si la búsqueda en la cadena de origen arrojó 0 resultados, entonces se reemplaza por la cadena obtenida por la fusión y la búsqueda se realiza nuevamente:

De lo contrario, la cadena de solicitud resultante se devuelve solo como una solicitud para la búsqueda:

Clasificación de consultas y extracción de hechos
Como mencioné, usamos DIRECTUM Ario, es decir, el servicio de clasificación de texto y el servicio de extracción de hechos. Para hacer esto, proporcionamos a los analistas consultas de búsqueda anónimas y una lista de hechos que nos interesan. Sobre la base de consultas y conocimientos sobre qué documentos hay en el sistema, los analistas identificaron varias categorías y capacitaron al servicio de clasificación para determinar la categoría de acuerdo con el texto de la consulta. Con base en las categorías resultantes y la lista de hechos, formulamos las reglas para usar estos hechos. Por ejemplo, la frase del último año en la categoría Todos se considera la fecha de creación del documento, y en la categoría Por organización : la fecha de registro. Al mismo tiempo, los creados el año pasado deberían, en cualquier categoría, caer en la fecha de creación.
Desde el lado de la búsqueda, hicieron una configuración en la que registraron las categorías, qué hechos se aplican a qué filtros de facetas.
Entrada completada
Además de las correcciones de diseño ya mencionadas, las búsquedas anteriores del usuario y los documentos públicos se completan automáticamente.

Se implementan utilizando otro tipo de Suggester: Completion Suggester , pero cada uno tiene sus propios matices.
Autocompletado: Historial de búsqueda
Hay muchos menos usuarios en ECM que motores de búsqueda, y asignan suficientes consultas comunes para ellos por qué hongo Lenin No es posible Mostrar todo en una fila tampoco vale la pena debido a consideraciones de privacidad. El Sugerencia de finalización habitual solo puede buscar el conjunto completo de documentos en el índice, pero Context Suggester viene al rescate, una forma de establecer un contexto para cada pista y filtrar por estos contextos. Si los nombres de usuario se usan como contextos, solo su historial se puede mostrar a todos.
También debe darle al usuario la oportunidad de eliminar la solicitud por la que se avergüenza. Como clave para la eliminación, utilizamos el nombre de usuario y el texto de información sobre herramientas. Como resultado, para el índice con sugerencias, obtuvimos una asignación tan ligeramente duplicada:
"mappings": { "document": { "properties": { "input": { "type": "keyword" }, "suggest": { "type": "completion", "analyzer": "simple", "preserve_separators": true, "preserve_position_increments": true, "max_input_length": 50, "contexts": [ { "name": "user", "type": "CATEGORY" } ] }, "user": { "type": "keyword" } } } }
El peso para cada nueva sugerencia se establece en uno y aumenta cada vez que vuelve a ingresarlo utilizando la API Actualizar por consulta con un script ctx._source.suggest.weight++
muy simple.
Autocompletado: documentos
Pero puede haber muchos documentos y posibles combinaciones de derechos. Por lo tanto, aquí, por el contrario, decidimos no filtrar por derechos cuando se completa automáticamente, sino solo indexar documentos públicos. Sí, y no necesita eliminar consejos individuales de este índice. Parecería que la implementación en todo es más fácil que la anterior, si no fuera por dos puntos:
El primero: Completion Suggester solo admite la búsqueda de prefijos, y a los clientes les encanta asignar números de artículos a todo, y algunas .01.01
medida que escribe una consulta No .01.01
. Aquí, junto con el nombre completo, también puede indexar n-gramos derivados de él:
{ "extension": "pdf", "name": ".01.01 ", "suggest": [ { "input": "", "weight": 70 }, { "input": " ", "weight": 80 }, { "input": " ", "weight": 90 }, { "input": ".01.01 ", "weight": 100 } ] }
Esto no fue tan crítico con la historia, sin embargo, el mismo usuario ingresa aproximadamente la misma línea si busca algo nuevamente. Probablemente
El segundo: de manera predeterminada, todos los consejos son iguales, pero nos gustaría hacer que algunos de ellos sean más iguales y preferiblemente para que sea coherente con la clasificación de los resultados de búsqueda. Para hacer esto, repita aproximadamente las funciones gauss y field_value_factor utilizadas en la consulta de puntuación de función .
Resulta que aquí hay una tubería así:
{ "dir_public_documents_pipeline": { "processors": [ ... { "set": { "field": "terms_array", "value": "{{name}}" } }, { "split": { "field": "terms_array", "separator": "\\s+|$" } }, { "script": { "source": "..." } } ] } }
con el siguiente script:
Date modified = new Date(0); if (ctx.modified_date != null) modified = new SimpleDateFormat('dd.MM.yyyy').parse(ctx.modified_date); long dayCount = (System.currentTimeMillis() - modified.getTime())/(1000*60*60*24); double score = Math.exp((-0.7*Math.max(0, dayCount - 31))/1095) * Math.log10(ctx.access_count + 2); int count = ctx.terms_array.length; ctx.suggest = new ArrayList(); ctx.suggest.add([ 'input': ctx.terms_array[count - 1], 'weight': Math.round(score * (255 - count + 1)) ]); for (int i = count - 2; i >= 0 ; --i) { if (ctx.terms_array[i].trim() != "") { ctx.suggest.add([ "input": ctx.terms_array[i] + " " + ctx.suggest[ctx.suggest.length - 1].input, "weight": Math.round(score * (255 - i))]); } } ctx.remove('terms_array'); ctx.remove('access_count'); ctx.remove('modified_date');
¿Por qué molestarse con una tubería indolora en lugar de escribirla en un idioma más conveniente? Porque ahora, utilizando la API Reindex , puede adelantar el contenido de los índices de búsqueda en índices para obtener pistas (especificando solo los campos necesarios, por supuesto) en un solo comando.
La composición de los documentos públicos realmente necesarios no se actualiza a menudo, por lo que este comando se puede dejar en un inicio manual.
Mostrar resultados
Categorias
La categoría determina qué facetas estarán disponibles y cómo se verá el fragmento. Puede ser detectado automáticamente por inteligencia externa o seleccionado manualmente sobre la barra de búsqueda.
Facetas
Las facetas son algo muy intuitivo para todos cuyo comportamiento, sin embargo, se describe mediante reglas muy poco triviales. Aquí hay algunos de ellos:
Los valores de las facetas dependen de los resultados de búsqueda, PERO y los resultados de la búsqueda dependen de las facetas seleccionadas. ¿Cómo evitar la recursividad?
Seleccionar valores dentro de una faceta no afecta a otros valores de esta faceta, pero sí afecta a los valores en otras facetas:

- Los valores de faceta seleccionados por el usuario no deberían desaparecer, incluso si una elección en otra faceta los aniquila a 0 o ya no están en la parte superior:

En elasticidad, las facetas se realizan a través del mecanismo de agregación, pero para cumplir con las reglas descritas, estas agregaciones deben invertirse entre sí y filtrarse entre sí.
Considere los fragmentos de solicitud responsables de esto:
Código demasiado grande { ... "post_filter": { "bool": { "must": [ { "terms": { "card.author_value_id": [ "1951063" ] } }, { "terms": { "editor_value_id": [ "2337706", "300643" ] } } ] } }, "query": {...} "aggs": { "card.author_value_id": { "filter": { "terms": { "editor_value_id": [ "2337706", "300643" ] } }, "aggs": { "card.author_value_id": { "terms": { "field": "card.author_value_id", "size": 11, "exclude": [ "1951063" ], "missing": "" } }, "card.author_value_id_selected": { "terms": { "field": "card.author_value_id", "size": 1, "include": [ "1951063" ], "missing": "" } } } }, ... "editor_value_id": { "filter": { "terms": { "card.author_value_id": [ "1951063" ] } }, "aggs": { "editor_value_id": { "terms": { "field": "editor_value_id", "size": 11, "exclude": [ "2337706", "300643" ], "missing": "" } }, "editor_value_id_selected": { "terms": { "field": "editor_value_id", "size": 2, "include": [ "2337706", "300643" ], "missing": "" } } } }, ... } }
¿Qué hay aquí que:
- post_filter le permite imponer una condición adicional en los resultados de una consulta ya completada y no afecta los resultados de las agregaciones. Esa misma brecha de recursión. Incluye todos los valores seleccionados de todas las facetas.
- agregaciones de nivel superior, en el ejemplo card.author_value_id y editor_value_id . Cada uno tiene:
- filtre por los valores de todas las demás facetas, excepto la suya propia;
- agregación anidada para valores de faceta seleccionados: protección contra la aniquilación ;
- agregación anidada para otros valores de faceta. Mostramos los 10 principales y solicitamos los 11 principales para determinar si se muestra el botón Mostrar todo .
Fragmentos
Dependiendo de la categoría seleccionada, el fragmento puede verse diferente, por ejemplo, el mismo documento al buscar en una categoría
Todos :

y empleados :

O recuerde, ¿queríamos ver el tema de una oferta comercial y de quién vino?

Para no arrastrar toda la tarjeta desde el elástico (esto ralentiza la búsqueda), se utiliza el filtrado de origen :
{ ... "_source": { "includes": [ "id", "card.name", "card.card_type_value_id", "card.life_stage_value_id", "extension", ... ] }, "query": {...} ... }
Para resaltar las palabras que se encuentran en el texto del documento, se utiliza el resaltador Fast Vector , como generador de los fragmentos más apropiados para textos grandes, y para el nombre, resaltador unificado , como el menos exigente en recursos y estructura de índice:
"highlight": { "pre_tags": [ "<strong>" ], "post_tags": [ "</strong>" ], "encoder": "html", "fields": { "card.name": { "number_of_fragments": 0 }, "content": { "fragment_size": 300, "number_of_fragments": 3, "type": "fvh" } } },
En este caso, el nombre se resalta en su totalidad, y del texto obtenemos hasta 3 fragmentos de 300 caracteres de longitud. El texto devuelto por el resaltador Fast Vector se comprime aún más mediante un algoritmo improvisado para obtener un estado de fragmento minimizado.
Colapso
Históricamente, los usuarios de este ECM están acostumbrados al hecho de que la búsqueda les devuelve documentos , pero de hecho Elasticsearch busca entre versiones de documentos . Puede resultar que se encuentren varias versiones casi idénticas en la misma consulta. Esto saturará los resultados y confundirá al usuario. Afortunadamente, este comportamiento puede evitarse utilizando el mecanismo de colapso de campo , una versión ligera de agregaciones que ya funciona en los resultados finales (en esto se parece a post_filter, dos muletas son un par ). El colapso dará como resultado el objeto de colapso más relevante.
{ ... "query": {...} ... "collapse": { "field": "id" } }
Desafortunadamente, el colapso tiene una serie de efectos desagradables, por ejemplo, varias características numéricas del resultado de la búsqueda continúan regresando como si no hubiera colapso. Es decir, el número de resultados, el número de valores de faceta: todos serán ligeramente incorrectos, pero el usuario generalmente no se da cuenta de esto, al igual que el lector cansado, que es poco probable que haya leído esta propuesta antes.
El final