Muchas aplicaciones necesitan dar a los usuarios la capacidad de personalizar consultas de manera que complementen lo que las búsquedas por sí solas pueden hacer. En este capítulo vas a aprender sobre filtrado, una técnica que permite especificar que una consulta de búsqueda se ejecuta solo sobre el subconjunto de documentos contenidos en un índice que cumple una condición dada.
Introducción a las consultas booleanas
Antes de poder implementar filtros, tienes que entender cómo se implementan las consultas compuestas en Elasticsearch.
Una consulta compuesta permite a una aplicación combinar dos o más consultas individuales, de modo que se ejecuten juntas y, si corresponde, devuelvan un conjunto combinado de resultados. La forma estándar de crear consultas compuestas en Elasticsearch es usar una consulta booleana.
Una consulta booleana actúa como un envoltorio para dos o más consultas o cláusulas individuales. Hay cuatro formas diferentes de combinar consultas:
bool.must: la cláusula debe coincidir. Si se dan varias cláusulas, todas deben coincidir (similar a una operación lógica AND).bool.should: cuando se usa sinmust, al menos una cláusula debería coincidir (similar a una operación lógica OR). Cuando se combina conmustcada cláusula correspondiente aumenta el puntaje de relevancia del documento.bool.filter: solo los documentos que coinciden con la(s) cláusula(s) se consideran candidatos a resultados de búsqueda.bool.must_not: solo los documentos que no coinciden con la(s) cláusula(s) se consideran candidatos a resultados de búsqueda.
Como probablemente puedas deducir por lo anterior, las consultas booleanas implican bastante complejidad y pueden usar de diversas maneras. En este capítulo vas a aprender a combinar la cláusula de búsqueda de texto completo multi-coincidencia implementada en los capítulos anteriores con un filtro que restringe los resultados a una sola categoría de documentos. Recuerda que el conjunto de datos empleado con este tutorial incluye un campo category que puede configurar como sharepoint, teams o github.
Agregar un filtro a una consulta
La consulta multi-coincidencia que actualmente se implementa en la aplicación tutorial emplea la siguiente estructura:
Para agregar un filtro que restrinja esta búsqueda a una categoría específica, la consulta debe expandir de la siguiente manera:
Veamos en detalle los nuevos componentes de esta consulta.
En primer lugar, la consulta multi_match se movió dentro de una cláusula de bool.must . La cláusula bool.must suele ser el lugar donde se define la consulta base. Ten en cuenta que must acepta una lista de consultas para buscar, por lo que esto permite combinar varias consultas a nivel base cuando se desea.
El filtrado se implementa en una sección bool.filter , usando un nuevo tipo de consulta, la consulta term . Usar una consulta match o multi_match como filtro no es buena idea, porque son consultas de búsqueda en texto completo. Para efectos de filtrado, la consulta debe devolver una respuesta absoluta de verdadero o falso para cada documento y no un puntaje de relevancia como hacen las consultas de coincidencia.
El término consulta realiza una búsqueda exacta del valor a en un campo dado. Este tipo de consulta es útil para buscar identificadores, etiquetas, etiquetas o, como en este caso, categorías.
Esta consulta no funciona bien con campos indexados para búsqueda en texto completo. A los campos de cadena se les asigna un tipo de texto predeterminado y se les analiza y separa su contenido en palabras individuales antes de ser indexados. Elasticsearch asigna a los campos de cadena un tipo secundario de palabra clave, que indexa el contenido del campo en su conjunto, haciéndolos más apropiados para filtrar con la consulta term . Al usar el nombre de campo category.keyword en la parte de filtro de la consulta, se emplea la variante keyword tipada del campo en lugar de la predeterminada text una.
Especificación de un filtro
Antes de poder implementar la consulta filtrada, es necesario agregar una forma para que los usuarios finales introduzcan el filtro deseado. La solución implementada en este tutorial buscará un patrón category:<category-name> en el texto de la consulta de búsqueda. Vamos a agregar una función llamada extract_filters() a app.py para buscar expresiones de filtro:
La función acepta la consulta introducida por el usuario y devuelve una tupla con los filtros encontrados en la consulta, y la consulta modificada tras eliminar los filtros. Para buscar el patrón del filtro emplea una expresión regular. La función está diseñada para ampliar con filtros adicionales.
Cuando se encuentra un filtro, la lista de filters se extiende con una expresión de filtro correspondiente, que en este caso se basa en la consulta term , como se discutió antes.
Para entender mejor cómo funciona esta función, inicia una sesión en Python (cerciórate primero de activar el entorno virtual) y ejecuta el siguiente código:
La tupla devuelto desde la función debe ser:
Implementación de la búsqueda filtrada
Lo que queda por hacer es cambiar la función handle_search() para enviar una consulta actualizada que combine la expresión de búsqueda en texto completo con un filtro, si es que el usuario lo da. A continuación se muestra la nueva versión de esta función:
La consulta ahora fue modificada para enviar una expresión bool , y la expresión de búsqueda se movió dentro de una sección must debajo de ella. La función extract_filters() devuelve la parte de filtro de la consulta en el formulario que debe enviar a Elasticsearch, por lo que también se inserta en el diccionario de consultas bajo la clave de bool de nivel superior.
Prueba con una consulta de búsqueda como work from home category:sharepoint para ver cómo solo se devuelven los documentos de la categoría dada.
Filtros de rango
Elasticsearch admite una variedad de filtros además del filtro term . Otro que se usa comúnmente es el filtro range , que funciona con números y fechas. Vamos a agregar un filtro year que pueda usar para restringir los resultados según el año en que se actualizaron por última vez, que aparece en el campo updated_at .
A continuación se muestra una versión actualizada de la función extract_filters() que busca tanto category:<category> como year:<yyyy> como filtros:
Esta versión agrega una segunda expresión regular para encontrar year:yyyy en la cadena de consulta. Crea un filtro range para el campo updated_at y establece los límites bajo y alto del rango al año que se da luego del dos puntos, que se captura en la coincidencia de expresión regular como m.group(1).
Hay una pequeña complicación, porque el campo updated_at contiene fechas completas, y en este filtro solo necesita mirar el año. Por suerte, cuando el filtro de rango se usa con campo de fecha, los límites del rango pueden mejorar con matemáticas de fecha. El sufijo ||/y que se agrega a los parámetros gte (cota inferior) y lte (límite superior) del rango indica que el valor dado es un año que debe completar para formar una fecha completa que pueda comparar con el campo.
Con este cambio, puedes incluir una consulta como year:2020 work from home para ver solo los resultados del año aplicar. La consulta puede incluir también los dos filtros, por ejemplo year:2020 category:teams work from home.
La consulta de emparejamiento total
Antes de pasar a un nuevo tema, intenta introducir solo un filtro en el campo de búsqueda de la consulta, por ejemplo category:github. Desafortunadamente, esto no devuelve ningún resultado, pero el comportamiento esperado en este caso sería recibir todos los resultados que coincidan con la categoría aplicar.
Lo que ocurre es que la función extract_filters() devuelve una tupla con el(los) filtro(s) en el primer elemento y una cadena de consulta vacía en el segundo elemento. La consulta multi_match recibe la cadena vacía y devuelve una lista vacía de resultados, porque nada coincide con una cadena vacía.
Para abordar este caso especial, la consulta multi_match puede reemplazar por match_all cuando el texto de búsqueda esté vacío. La versión de la función handle_search() que aparece a continuación agrega lógica para ello. Actualiza la función en app.py.
Con esta versión, puedes pedir todos los documentos que coincidan con una categoría. Fíjate en cómo todos los resultados que se devuelven obtienen el mismo puntaje de 1,0, porque no hay términos de búsqueda para calcular los puntajes.
Previamente
PaginaciónPróximo
Búsqueda facetada