This post is also available in English.
Búsqueda Vectorial con ES|QL
Hoy descubrimos una de las incorporaciones más interesantes en ES|QL: Soporte nativo para campos dense_vector, y las funciones para buscar en ellos: KNN y las funciones de similitud vectorial. Si tenías curiosidad por la búsqueda vectorial pero la sintaxis del Query DSL te parecía un poco intimidantes, ES|QL está a punto de convertirse en tu mejor aliado.
¿Por qué ES|QL para Búsqueda Vectorial?
ES|QL es el futuro de Elasticsearch - te permite realizar tus consultas como si fueran una serie de pasos en un proceso. Añadir búsqueda vectorial a ES|QL te proporciona un control experto sobre cómo realizas consultas semánticas usando vectores, ajustando el método de búsqueda (ya sea vecinos más cercanos aproximados mediante KNN, o búsqueda exacta mediante funciones de similitud vectorial).
Preparando Nuestro Entorno de Pruebas
Vamos a crear un índice simple con un campo dense_vector para almacenar algunos vectores sobre productos. Lo mantendremos muy pequeño, sólo 3 dimensiones, para poder razonar sobre los vectores con más facilidad.
PUT products-vectors
{
"mappings": {
"properties": {
"name": {
"type": "text"
},
"category": {
"type": "keyword"
},
"embedding": {
"type": "dense_vector",
"dims": 3,
"similarity": "cosine"
}
}
}
}
El parámetro clave es similarity: cosine (cómo medimos la cercanía de los vectores - otras opciones incluyen l2_norm y dot_product).
Ahora añadiremos algunos productos de ejemplo con sus correspondientes vectores (embeddings):
POST products-vectors/_bulk
{"index": {"_id": "1"}}
{"name": "Warm Winter Jacket", "category": "clothing", "embedding": [0.9, 0.1, 0.2]}
{"index": {"_id": "2"}}
{"name": "Summer Beach Shorts", "category": "clothing", "embedding": [0.1, 0.9, 0.3]}
{"index": {"_id": "3"}}
{"name": "Cozy Wool Sweater", "category": "clothing", "embedding": [0.85, 0.15, 0.25]}
{"index": {"_id": "4"}}
{"name": "Running Sneakers", "category": "footwear", "embedding": [0.4, 0.5, 0.8]}
{"index": {"_id": "5"}}
{"name": "Hiking Boots", "category": "footwear", "embedding": [0.6, 0.3, 0.7]}
Puedes usar ES|QL para recuperar tus datos, incluyendo los embeddings vectoriales:
FROM products-vectors
En este ejemplo sencillo, digamos que nuestra primera dimensión representa "abrigado", la segunda "veraniego", y la tercera "actividades al aire libre". Los embeddings reales de modelos como E5 u OpenAI tendrían cientos de dimensiones, pero el principio sigue siendo el mismo.
Búsqueda usando la función KNN
La función KNN de ES|QL realiza búsqueda aproximada de los k vecinos más cercanos en tus vectores:
FROM products-vectors METADATA _score
| WHERE KNN(embedding, [0.88, 0.12, 0.22])
| KEEP name, category, _score
| SORT _score DESC
| LIMIT 3
Entendamos bien esta query:
-
METADATA _score: Necesitamos este metadato para obtener la puntuación de la función KNN -
KNN(embedding, [0.88, 0.12, 0.22]): Encuentra los vecinos más cercanos a nuestro vector de consulta (query) -
El vector de consulta (query)
[0.88, 0.12, 0.22]representa algo "abrigado" (primera dimensión alta)
¿El resultado? Nuestros artículos de ropa de abrigo aparecen en la parte superior:
| name | category | _score |
|---|---|---|
| Warm Winter Jacket | clothing | 0.9994338750839233 |
| Cozy Wool Sweater | clothing | 0.9992702007293701 |
| Hiking Boots | footwear | 0.9067620635032654 |
Combinando KNN con Filtros
Uno de los superpoderes de ES|QL es que puedes combinar fácilmente búsqueda vectorial con filtros tradicionales:
FROM products-vectors
METADATA _score
| WHERE category == "clothing" AND KNN(embedding, [0.88, 0.12, 0.22])
| KEEP name, _score
| SORT _score DESC
| LIMIT 3
Esto aplica el filtrado como un pre-filtro antes de ejecutar la búsqueda KNN. ¡Eficiente y legible!
| name | category | _score |
|---|---|---|
| Warm Winter Jacket | clothing | 0.9994338750839233 |
| Cozy Wool Sweater | clothing | 0.9992702007293701 |
| Summer Beach Shorts | clothing | 0.658316433429718 |
Ajustando con Parámetros Opcionales
La función KNN acepta parámetros con nombre para tener un mayor control:
FROM products-vectors METADATA _score
| WHERE KNN(embedding, [0.1, 0.85, 0.3], {"k": 2, "boost": 1.5, "min_candidates": 50, "rescore_oversample": 3, "similarity": 0.0001})
| KEEP name, _score
| SORT _score DESC
Los parámetros permitidos son:
-
k: Número de vecinos a devolver (implícitamente desde LIMIT)
-
boost: Multiplicador de puntuación (por defecto: 1.0)
-
min_candidates: Mínimo de candidatos a considerar por shard (mayor = más preciso pero más lento)
-
similarity: La similitud mínima para considerar un resultado
-
visit_percentage: El porcentaje de vectores a explorar por shard al hacer búsqueda knn con
bbq_disk -
rescore_oversample: Aplica el factor de sobremuestreo especificado a
ken la búsqueda
Búsqueda usando Funciones de Similitud Vectorial
KNN es genial para buscar vectores a gran escala porque es aproximada - lo que significa que intentará encontrar resultados suficientemente buenos, pero sin considerar todos los posible resultados. Esto es lo que permite que KNN sea eficiente, ya que no tiene que considerar todos los documentos posibles y compararlos con la consulta uno a uno.
En caso de que realmente queramos examinar todos los resultados (porque ya hemos filtrado nuestros resultados, o porque no tenemos muchos documentos de partida), podemos usar funciones de similitud vectorial para calcular la similitud entre nuestra consulta y cada elemento:
FROM products-vectors
| EVAL my_score = V_COSINE(embedding, [0.1, 0.85, 0.3]) + 1.0
| KEEP name, my_score
| SORT my_score DESC
Las funciones de similitud vectorial te permiten realizar un score personalizado para vectores, y tener un cálculo exacto de los vecinos más cercanos.
Bonus: Función TEXT_EMBEDDING
Si tienes un endpoint de inferencia configurado, puedes generar embeddings directamente utilizando:
FROM products-vectors
METADATA _score
| WHERE KNN(embedding, TEXT_EMBEDDING("cozy winter wear", "my-embedding-model"))
| KEEP name, _score
| SORT _score DESC
| LIMIT 3
¡No necesitas precalcular los vectores de tu consulta—ES|QL lo hace por tí!
Conclusión
Las capacidades de búsqueda vectorial de ES|QL proporcionan un control total para la búsqueda semántica. Ajustar cómo obtienes los vecinos más cercanos para una consulta, o calcular un score personalizado, es posible gracias al soporte de ES|QL para el tipo de campo dense_vector, la función de búsqueda KNN, y las funciones de similitud vectorial.
Ya estés construyendo un sistema de recomendaciones, un motor de búsqueda semántica, o simplemente explorando tus datos vectoriales, la combinación de KNN, filtros y agregaciones convierte a ES|QL en un compañero ideal.
