tutoriales.com

Aprovechando Redis como Buscador de Texto Completo con Módulos RediSearch y ReJSON

Este tutorial te guiará a través de la implementación de un motor de búsqueda de texto completo utilizando Redis. Aprenderás a aprovechar los módulos RediSearch para una indexación y consulta eficientes, y ReJSON para almacenar documentos estructurados, permitiéndote crear soluciones de búsqueda rápidas y escalables.

Intermedio18 min de lectura10 views
Reportar error

🚀 Introducción al Poder de la Búsqueda con Redis

En la era digital, la capacidad de buscar y recuperar información de forma rápida y precisa es fundamental. Aunque Redis es ampliamente conocido por su velocidad como base de datos en memoria y caché, su ecosistema de módulos lo transforma en una herramienta sorprendentemente potente para mucho más, incluyendo la construcción de motores de búsqueda de texto completo.

Tradicionalmente, la búsqueda de texto completo ha requerido bases de datos especializadas como Elasticsearch o Apache Solr. Sin embargo, para muchos casos de uso, especialmente aquellos que ya utilizan Redis y buscan simplificar su pila tecnológica, la combinación de RediSearch y ReJSON ofrece una alternativa atractiva y de alto rendimiento. En este tutorial, exploraremos cómo podemos integrar estas dos joyas de los módulos de Redis para construir un sistema de búsqueda de texto completo robusto y eficiente que maneje documentos JSON.

¿Por qué Redis para Búsqueda de Texto Completo? 🤔

Redis, por sí solo, no es un motor de búsqueda de texto completo. Sin embargo, con la adición de RediSearch, obtiene capacidades avanzadas como:

  • Indexación invertida: Para búsquedas rápidas.
  • Ranking: Relevancia de los resultados.
  • Tokenización y stemming: Mejora la precisión de la búsqueda.
  • Soporte multilingüe: Para diversas aplicaciones.
  • Búsqueda facetada y agregaciones: Análisis de datos.

Cuando combinamos esto con ReJSON, que permite almacenar, actualizar y recuperar documentos JSON de forma nativa en Redis, obtenemos una sinergia poderosa para manejar datos estructurados y hacerlos fácilmente buscables.

💡 Consejo: Esta configuración es ideal para aplicaciones que necesitan baja latencia en la búsqueda, un control granular sobre sus datos y la simplicidad de una única plataforma de datos en memoria.

🛠️ Requisitos Previos e Instalación

Antes de sumergirnos en el código y los comandos, necesitamos asegurarnos de tener Redis con los módulos RediSearch y ReJSON instalados y en funcionamiento.

📦 Instalando Redis Stack

La forma más sencilla de obtener Redis con estos módulos es utilizando Redis Stack, que incluye RediSearch, ReJSON y otros módulos populares preempaquetados. Puedes instalarlo a través de Docker o directamente en tu sistema operativo.

Opción 1: Docker (Recomendado) 🐳

Esta es la forma más rápida y limpia de empezar. Ejecuta el siguiente comando en tu terminal:

docker run -d --name redis-stack-server -p 6379:6379 redis/redis-stack-server:latest

Este comando descargará y ejecutará la imagen de Redis Stack, mapeando el puerto 6379 de tu máquina al contenedor.

Opción 2: Instalación Directa (Linux/macOS) 🐧🍎

Para Debian/Ubuntu:

curl -fsSL https://packages.redis.io/gpg | sudo gpg --dearmor -o /usr/share/keyrings/redis-archive-keyring.gpg
echo "deb [signed-by=/usr/share/keyrings/redis-archive-keyring.gpg] https://packages.redis.io/deb stable main" | sudo tee /etc/apt/sources.list.d/redis.list
sudo apt-get update
sudo apt-get install redis-stack-server

Para macOS (Homebrew):

brew tap redis-stack/redis-stack
brew install redis-stack

Una vez instalado, puedes iniciar el servidor con redis-stack-server (o como servicio, dependiendo de la instalación).

⚠️ Advertencia: Asegúrate de que el puerto 6379 no esté siendo utilizado por otra aplicación antes de iniciar Redis Stack.

🎯 Conceptos Clave: RediSearch y ReJSON

Antes de empezar a indexar, es crucial entender los roles de RediSearch y ReJSON.

ReJSON: Documentos JSON en Redis 📄

ReJSON es un módulo que añade el tipo de dato JSON a Redis. Esto significa que puedes almacenar y manipular documentos JSON de forma nativa, con comandos específicos para:

  • JSON.SET: Establecer un documento JSON.
  • JSON.GET: Recuperar un documento o parte de él.
  • JSON.ARRAPPEND, JSON.ARRINSERT, etc.: Manipular arrays dentro de un JSON.

Ejemplo de un documento JSON que almacenaremos:

{
  "id": "doc1",
  "title": "Introducción a Redis y sus Módulos",
  "author": "Ana García",
  "content": "Redis es un almacén de estructura de datos en memoria de código abierto...",
  "tags": ["Redis", "bases de datos", "NoSQL"]
}

RediSearch: El Motor de Búsqueda 🔍

RediSearch es el módulo que nos permite crear índices y realizar búsquedas de texto completo sobre los datos almacenados en Redis. Sus comandos clave incluyen:

  • FT.CREATE: Crear un índice. Aquí es donde definimos qué campos de nuestros documentos serán indexados y cómo.
  • FT.ADD: Añadir documentos al índice (aunque cuando usamos ReJSON, RediSearch puede indexar automáticamente).
  • FT.SEARCH: Realizar consultas de búsqueda.
  • FT.AGGREGATE: Para análisis y agregaciones sobre los resultados.

Cuando usamos ReJSON, RediSearch puede indexar directamente los objetos JSON. Esto simplifica enormemente el proceso, ya que no necesitamos gestionar claves separadas para cada campo.

Aplicación (Cliente Redis) RedisJSON 2. Almacena JSON (Estructura de Árbol) RediSearch 4. Indexa campos (Índices Invertidos) 1. JSON.SET 3. Observa cambios 5. FT.SEARCH 6. Resultados Ecosistema Redis: JSON + Search

✍️ Creando un Índice RediSearch sobre Documentos ReJSON

El primer paso es crear un índice que le diga a RediSearch qué campos de nuestros documentos JSON debe indexar y con qué tipos. Usaremos FT.CREATE con la cláusula ON JSON.

Vamos a crear un índice para artículos, donde cada artículo tendrá un título, autor, contenido y tags.

Definiendo el Esquema de Indexación 🏗️

Nuestro índice se llamará idx:articulos. Definiremos los siguientes campos:

  • Título: Campo de texto para búsquedas completas.
  • Autor: Campo de texto para filtrar por autor.
  • Contenido: Campo de texto para la búsqueda principal del cuerpo del artículo.
  • Tags: Campo de tag (etiqueta) para filtros precisos.
FT.CREATE idx:articulos ON JSON PREFIX 1 "articulo:" SCHEMA \
    $.title AS title TEXT WEIGHT 10 \
    $.author AS author TEXT \
    $.content AS content TEXT WEIGHT 5 \
    $.tags[*] AS tags TAG

Desglosemos este comando:

  • FT.CREATE idx:articulos: Crea un índice llamado idx:articulos.
  • ON JSON: Indica que estamos indexando documentos JSON.
  • PREFIX 1 "articulo:": RediSearch solo indexará las claves JSON que comiencen con articulo:. Esto es crucial para organizar tus datos y evitar indexar elementos no deseados.
  • SCHEMA: Define los campos a indexar y sus tipos:
    • $.title AS title TEXT WEIGHT 10: El campo title dentro de nuestro JSON se indexará como TEXT (texto completo). WEIGHT 10 le da una mayor relevancia en los resultados de búsqueda.
    • $.author AS author TEXT: El campo author como texto.
    • $.content AS content TEXT WEIGHT 5: El campo content como texto, con un peso menor que el título.
    • $.tags[*] AS tags TAG: El campo tags (que es un array) se indexará como un campo de tipo TAG. Esto es ideal para filtrar por etiquetas exactas.
📌 Nota: Los selectores `$.campo` utilizan la sintaxis de JSONPath para indicar qué parte del documento JSON debe ser indexada.

📝 Almacenando y Consultando Documentos

Ahora que tenemos nuestro índice, podemos empezar a añadir documentos y realizar búsquedas.

Añadiendo Documentos JSON con ReJSON 💾

Vamos a añadir algunos artículos de ejemplo usando el comando JSON.SET. Recuerda usar el prefijo articulo: para que RediSearch los indexe.

JSON.SET articulo:1 $ '{"id": "1", "title": "El Futuro de las Bases de Datos NoSQL", "author": "Laura Pérez", "content": "Las bases de datos NoSQL están revolucionando la forma en que manejamos grandes volúmenes de datos no estructurados...", "tags": ["NoSQL", "bases de datos", "big data"]}'
JSON.SET articulo:2 $ '{"id": "2", "title": "Optimización de Rendimiento en Aplicaciones Web", "author": "Juan López", "content": "Mejorar el rendimiento de una aplicación web es crucial para la experiencia del usuario. Técnicas como el caching y la compresión son esenciales.", "tags": ["web", "rendimiento", "caching"]}'
JSON.SET articulo:3 $ '{"id": "3", "title": "Introducción a Machine Learning con Python", "author": "Ana García", "content": "El Machine Learning es un campo en expansión que permite a las computadoras aprender de los datos sin ser programadas explícitamente.", "tags": ["Machine Learning", "Python", "IA"]}'
JSON.SET articulo:4 $ '{"id": "4", "title": "Redis: Más Allá del Caching", "author": "Ana García", "content": "Redis ofrece una variedad de estructuras de datos que lo hacen versátil para colas, mensajería, y más allá del caching simple.", "tags": ["Redis", "caching", "bases de datos"]}'

Después de ejecutar estos comandos, RediSearch indexará automáticamente estos documentos. Esto puede tomar un momento dependiendo del volumen.

Realizando Búsquedas con FT.SEARCH 🔎

Ahora que tenemos datos indexados, podemos empezar a buscar. El comando FT.SEARCH es el corazón de las consultas de RediSearch.

Búsqueda Básica de Texto Completo

Busquemos artículos que contengan la palabra "Redis":

FT.SEARCH idx:articulos "Redis"

Esto devolverá los documentos que contengan "Redis" en los campos de texto indexados (title, author, content). Los resultados se ordenarán por relevancia.

Búsqueda por Campo Específico

Podemos especificar en qué campos queremos buscar. Por ejemplo, buscar "bases de datos" solo en el título:

FT.SEARCH idx:articulos "@title:"bases de datos""

Filtrado por Autor

Busquemos artículos escritos por "Ana García":

FT.SEARCH idx:articulos "@author:"Ana García""

Filtrado por Tags (etiquetas) ✅

Para buscar artículos con una etiqueta específica, usamos el tipo TAG:

FT.SEARCH idx:articulos "@tags:{Redis}"

Esto buscará artículos etiquetados exactamente con "Redis". Puedes combinar tags:

FT.SEARCH idx:articulos "@tags:{Redis|NoSQL}"
🔥 Importante: Para búsquedas exactas de tags, usa llaves `{}`. Para búsquedas de texto en campos `TEXT`, usa comillas `""` o ninguna para términos individuales.

Combinando Criterios de Búsqueda

Podemos combinar diferentes criterios para búsquedas más complejas. Por ejemplo, buscar "rendimiento" en el contenido, escrito por "Juan López" y que tenga la etiqueta "web":

FT.SEARCH idx:articulos "@content:rendimiento @author:"Juan López" @tags:{web}"

Opciones Avanzadas de Búsqueda ✨

RediSearch ofrece muchas opciones para refinar tus búsquedas:

  • LIMIT offset count: Paginación de resultados.
  • SORTBY field ASC|DESC: Ordenar resultados por un campo.
  • RETURN fields: Seleccionar qué campos del documento original devolver.
  • NOCONTENT: Solo devolver los IDs de los documentos, no su contenido.
  • WITHPAYLOADS, WITHSCORES: Para obtener información adicional sobre la coincidencia.

Ejemplo con paginación y selección de campos:

FT.SEARCH idx:articulos "Redis" RETURN 1 title author LIMIT 0 2

Este comando buscará "Redis", devolverá solo el título y el autor de los primeros 2 resultados (empezando por el offset 0).

¿Cómo funciona la relevancia (scoring)? RediSearch utiliza un algoritmo de *scoring* basado en TF-IDF (Term Frequency-Inverse Document Frequency) y la proximidad de los términos, junto con los pesos asignados a los campos en el esquema. Los documentos con mayor *score* aparecen primero en los resultados por defecto.

📊 Agregaciones y Facetas con RediSearch

Más allá de la búsqueda simple, RediSearch nos permite realizar potentes operaciones de agregación y crear facetas para refinar la navegación de los resultados. El comando FT.AGGREGATE es el encargado de estas operaciones.

Contando Documentos por Autor 🧑‍💻

Podemos contar cuántos artículos ha escrito cada autor:

FT.AGGREGATE idx:articulos "*" GROUPBY 1 @author REDUCE COUNT 0 AS num_articles SORTBY 2 @num_articles DESC

Desglose:

  • FT.AGGREGATE idx:articulos "*": Realiza una agregación en todos los documentos del índice idx:articulos.
  • GROUPBY 1 @author: Agrupa los resultados por el campo author.
  • REDUCE COUNT 0 AS num_articles: Para cada grupo, cuenta el número de elementos y lo nombra num_articles.
  • SORTBY 2 @num_articles DESC: Ordena los resultados por el número de artículos de forma descendente.

Resultado esperado:

authornum_articles
------
Ana García2
Laura Pérez1
------
Juan López1
80% de implementación de búsqueda avanzada

Creando Facetas por Tags 🏷️

Las facetas son útiles para mostrar a los usuarios opciones para filtrar sus búsquedas. Por ejemplo, cuántos artículos hay para cada etiqueta:

FT.AGGREGATE idx:articulos "*" GROUPBY 1 @tags REDUCE COUNT 0 AS num_articles SORTBY 2 @num_articles DESC

Esto devolverá un conteo de artículos por cada etiqueta única.

Paso 1: Definir el esquema del índice con FT.CREATE.
Paso 2: Almacenar documentos JSON con JSON.SET (usando el prefijo).
Paso 3: Realizar búsquedas básicas con FT.SEARCH.
Paso 4: Explorar búsquedas avanzadas (por campo, tags, combinaciones).
Paso 5: Usar FT.AGGREGATE para análisis y facetas.

🧑‍💻 Integración con Lenguajes de Programación

Aunque hemos usado el CLI de redis-cli, en una aplicación real interactuarás con Redis a través de un cliente en tu lenguaje de programación preferido. La mayoría de los clientes de Redis modernos tienen soporte para RediSearch y ReJSON.

Aquí tienes un ejemplo básico en Python usando la librería redis-py (asegúrate de tener redis-py y redis-json instalados: pip install redis redis-json):

import redis
from redis.commands.json.path import Path

# Conectar a Redis Stack
r = redis.Redis(host='localhost', port=6379, decode_responses=True)
json_client = r.json()

# Asegurarse de que el índice existe (o crearlo si no)
try:
    r.execute_command('FT.INFO', 'idx:articulos')
except redis.exceptions.ResponseError as e:
    if "Unknown Index Name" in str(e):
        print("Creando índice 'idx:articulos'...")
        r.execute_command("FT.CREATE", "idx:articulos", "ON", "JSON", "PREFIX", "1", "articulo:", "SCHEMA", \
                          "$.title", "AS", "title", "TEXT", "WEIGHT", "10", \
                          "$.author", "AS", "author", "TEXT", \
                          "$.content", "AS", "content", "TEXT", "WEIGHT", "5", \
                          "$.tags[*]", "AS", "tags", "TAG")
        print("Índice creado.")
    else:
        raise e

# Añadir algunos documentos si no existen
articles_data = [
    {
        "id": "1",
        "title": "El Futuro de las Bases de Datos NoSQL",
        "author": "Laura Pérez",
        "content": "Las bases de datos NoSQL están revolucionando la forma en que manejamos grandes volúmenes de datos no estructurados...",
        "tags": ["NoSQL", "bases de datos", "big data"]
    },
    {
        "id": "2",
        "title": "Optimización de Rendimiento en Aplicaciones Web",
        "author": "Juan López",
        "content": "Mejorar el rendimiento de una aplicación web es crucial para la experiencia del usuario. Técnicas como el caching y la compresión son esenciales.",
        "tags": ["web", "rendimiento", "caching"]
    },
    {
        "id": "3",
        "title": "Introducción a Machine Learning con Python",
        "author": "Ana García",
        "content": "El Machine Learning es un campo en expansión que permite a las computadoras aprender de los datos sin ser programadas explícitamente.",
        "tags": ["Machine Learning", "Python", "IA"]
    },
    {
        "id": "4",
        "title": "Redis: Más Allá del Caching",
        "author": "Ana García",
        "content": "Redis ofrece una variedad de estructuras de datos que lo hacen versátil para colas, mensajería, y más allá del caching simple.",
        "tags": ["Redis", "caching", "bases de datos"]
    }
]

for article in articles_data:
    key = f"articulo:{article['id']}"
    # Solo añadir si no existe para evitar sobrescribir en cada ejecución
    if not r.exists(key):
        json_client.set(key, Path.root(), article)
        print(f"Documento {key} añadido.")

# Realizar una búsqueda
print("\n--- Resultados de búsqueda para 'Redis' ---")
# La librería redis-py tiene un módulo de búsqueda específico
from redis.commands.search.query import Query

query = Query("Redis")
result = r.ft('idx:articulos').search(query)

for doc in result.docs:
    print(f"ID: {doc.id}, Título: {doc.title}, Autor: {doc.author}")


print("\n--- Resultados de búsqueda para '@author:\"Ana García\"' ---")
query_ana = Query("@author:\"Ana García\"")
result_ana = r.ft('idx:articulos').search(query_ana)

for doc in result_ana.docs:
    print(f"ID: {doc.id}, Título: {doc.title}, Autor: {doc.author}")


print("\n--- Agregación: Conteo de artículos por autor ---")
from redis.commands.search.query import AggregateQuery, GroupBy, Reducer

agg_query = AggregateQuery("*") \
    .groupby(GroupBy().add_property("@author").reduce(Reducer("COUNT").as_name("num_articles"))) \
    .sort_by("@num_articles", asc=False)

agg_result = r.ft('idx:articulos').aggregate(agg_query)

for row in agg_result.rows:
    print(f"Autor: {row.author}, Artículos: {row.num_articles}")

# Limpiar el índice y los documentos (opcional)
# print("\nLimpiando índice y documentos...")
# r.execute_command('FT.DROPINDEX', 'idx:articulos')
# for article in articles_data:
#     r.delete(f"articulo:{article['id']}")
# print("Limpieza completada.")

Este ejemplo demuestra cómo realizar las mismas operaciones de creación de índice, adición de documentos y búsquedas/agregaciones utilizando el cliente Python, lo que facilita la integración en aplicaciones web o de backend.

Intermedio Pro

🚀 Optimización y Buenas Prácticas

Para sacar el máximo provecho de tu buscador de texto completo con Redis, considera las siguientes optimizaciones y buenas prácticas:

  • Prefijos de Claves: Siempre usa prefijos claros para tus claves JSON (articulo:, usuario:, etc.). Esto ayuda a organizar tus datos y a que RediSearch indexe solo lo que necesitas, mejorando el rendimiento y la gestión de memoria.
  • Diseño del Esquema: Un buen diseño de esquema es crucial. Define los tipos de campo (TEXT, TAG, NUMERIC, GEO) correctamente y ajusta los WEIGHT para mejorar la relevancia de los resultados.
    • TEXT: Para campos donde se espera texto libre y búsqueda de texto completo.
    • TAG: Para categorías, etiquetas o valores discretos que necesitan búsquedas exactas.
    • NUMERIC: Para rangos numéricos.
  • Memoria: Redis es una base de datos en memoria. Monitorea el uso de RAM, especialmente con grandes volúmenes de documentos y índices complejos. RediSearch consume memoria adicional para sus índices.
  • Paginación: Siempre implementa paginación (LIMIT offset count) en tus consultas para evitar transferir grandes cantidades de datos y sobrecargar tanto a Redis como a tu aplicación.
  • Consultas Eficientes: Aprende a construir consultas eficientes utilizando operadores lógicos (AND, OR, NOT), filtros por campo, prefijos y sufijos, etc. Evita las búsquedas de "comodín" * al principio de las palabras si no son estrictamente necesarias, ya que pueden ser costosas.
  • Durabilidad: Para entornos de producción, configura la persistencia de Redis (RDB y/o AOF) para asegurar que tus índices y documentos no se pierdan en caso de un reinicio del servidor.
  • Clustering: Para escalabilidad horizontal y alta disponibilidad, considera usar Redis Cluster. RediSearch es compatible con Redis Cluster, distribuyendo los índices entre los nodos.
💡 Consejo: Usa `FT.INFO ` para obtener estadísticas y detalles sobre tu índice, incluyendo el número de documentos, el uso de memoria y las métricas de rendimiento.

Conclusión 🎉

Hemos visto cómo la combinación de los módulos RediSearch y ReJSON transforma Redis en una solución sorprendentemente capaz para la búsqueda de texto completo. Desde la instalación y configuración hasta la indexación de documentos JSON, la ejecución de consultas complejas y la realización de agregaciones, Redis Stack ofrece una plataforma unificada y de alto rendimiento para gestionar y buscar tus datos.

Esta aproximación no solo simplifica tu infraestructura al consolidar las funcionalidades de base de datos y búsqueda, sino que también aprovecha la velocidad inherente de Redis para ofrecer una experiencia de usuario excepcional. Ya sea para un catálogo de productos, un sistema de documentación o un motor de búsqueda interno, Redis con RediSearch y ReJSON es una herramienta que merece ser considerada en tu arsenal.

¡Anímate a experimentar y construir tu propio motor de búsqueda con Redis!

Tutoriales relacionados

Comentarios (0)

Aún no hay comentarios. ¡Sé el primero!