Acerca de la demo
Tres formas de buscar una película
Elastiflix-Movies Java busca en un catálogo de películas guardado en un índice de Elasticsearch Serverless. Con la misma consulta puedes probar tres modos de búsqueda y ver cómo cambia lo que encuentra cada uno: por palabras, por significado o combinando ambos. Además puedes acotar los resultados con filtros y cargar nuevas películas al catálogo.
Full-text (léxica)
Busca coincidencias de términos en los campos title y original_title, ambos de tipo text. Elasticsearch analiza el texto de la consulta en tokens y los compara con los tokens indexados de cada título.
Cómo se calcula la relevancia: BM25
BM25 es el algoritmo de puntuación por defecto de Elasticsearch para campos text. Favorece los términos de la consulta que aparecen en el campo y que son poco comunes en el catálogo. Dos parámetros controlan su comportamiento: k1 (por defecto 1.2) limita cuánto suma que un término se repita, y b (por defecto 0.75) ajusta la puntuación según la longitud del campo.
Ajustes disponibles
- Tipo de consulta: match
- Busca en un solo campo, que eliges entre
titleyoriginal_title. - Tipo de consulta: multi-match
- Busca en los dos campos a la vez con el tipo
best_fields: cada campo se evalúa por separado y la película recibe la puntuación del campo que mejor coincide. - Operador: OR
- Basta con que aparezca una de las palabras de la consulta. Es el comportamiento por defecto de Elasticsearch.
- Operador: AND
- Deben aparecer todas las palabras. Con multi-match y
best_fields, todas deben estar en un mismo campo: no se pueden repartir entretitleyoriginal_title.
Documentación Match query Multi-match query Similarity (BM25)
Vectorial
Busca por significado en la sinopsis de cada película (campo plot). El texto de tu consulta se convierte en un vector con el modelo jina-embeddings-v5-omni-small y Elasticsearch devuelve las películas cuyos vectores de sinopsis están más cerca, usando el retriever knn.
Cómo funciona k-NN
k-NN (k vecinos más cercanos) busca los vectores más próximos al vector de la consulta. Elasticsearch lo hace de forma aproximada sobre un grafo HNSW: recorre el grafo en lugar de comparar contra todo el catálogo, lo que la hace rápida aunque no siempre exacta.
Las tres representaciones de la sinopsis
El objetivo de este modo es comprobar cómo el número de dimensiones del vector influye en la precisión de los resultados. Los tres campos vienen del mismo modelo: su dimensión nativa es 1024 y admite recorte Matryoshka a 256 y 512.
| Campo | Tipo | Dimensiones | Origen |
|---|---|---|---|
| plot_256 | dense_vector | 256 | Primeros valores del vector de 1024, renormalizados (Matryoshka) |
| plot_512 | dense_vector | 512 | Primeros valores del vector de 1024, renormalizados (Matryoshka) |
| plot_1024 | dense_vector | 1024 | Vector completo del modelo |
Una comparación justa. Los tres campos usan la misma configuración de índice: index_options de tipo hnsw con vectores float sin cuantización, los mismos m (16) y ef_construction (100), los valores por defecto de Elasticsearch, y similitud cosine. Así, la única diferencia entre ellos es el número de dimensiones.
Precisión medida por dimensión
Medido en el catálogo original de 4 322 películas. Se midió con 32 descripciones de trama (26 en español y 6 en inglés), cada una con una sola película correcta del catálogo y sin palabras de su título. Las posiciones se obtuvieron en Elasticsearch y se confirmaron con una búsqueda exacta por fuerza bruta: coinciden en todos los casos.
| Campo | Correcta en 1.er lugar | Entre las 5 primeras | Entre las 10 primeras | MRR@10 |
|---|---|---|---|---|
| plot_256 | 22 de 32 (69 %) | 29 | 30 | 0.774 |
| plot_512 | 25 de 32 (78 %) | 31 | 32 | 0.853 |
| plot_1024 | 26 de 32 (81 %) | 31 | 31 | 0.880 |
El MRR premia que la película correcta quede arriba: vale 1 si siempre sale 1.ª y 0.5 si siempre sale 2.ª. En 24 de las 32 consultas la posición es la misma en los tres campos; las diferencias se concentran en las descripciones más difíciles:
| Película | plot_256 | plot_512 | plot_1024 |
|---|---|---|---|
| Inception | 21.º | 4.º | 3.º |
| Life of Pi | 10.º | 3.º | 2.º |
| Spirited Away | 4.º | 1.º | 1.º |
| Titanic | 4.º | 3.º | 1.º |
| E.T. the Extra-Terrestrial | 3.º | 1.º | 1.º |
| Coco | 2.º | 1.º | 1.º |
- 256 dimensiones pierde precisión de forma clara; 512 queda muy cerca de 1024 con la mitad del tamaño.
- Más dimensiones no siempre es mejor en cada caso: Forrest Gump queda 21.º, 8.º y 18.º.
- Con 32 consultas es una tendencia clara, no una conclusión estadística firme.
Ajustes disponibles
- Vector k-NN
- El campo sobre el que se busca:
plot_256,plot_512oplot_1024. Más dimensiones suelen capturar más matices del significado, a cambio de más memoria y de comparaciones más costosas. - k
- Número máximo de resultados que devuelve la búsqueda vectorial, después de combinar los candidatos de todos los shards; se reparten en páginas de 25, 50 o 100. Debe ser menor o igual que
num_candidates. Valor inicial: 100. - num_candidates
- Candidatos que se evalúan en cada shard antes de quedarse con los
kmejores. Subirlo tiende a mejorar la precisión, a cambio de más tiempo de respuesta. Máximo 10 000. Valor inicial: 200.
Nota de implementación: para recuperación, el modelo espera el prefijo Query: en las consultas. Las sinopsis se vectorizan con input_type ingest y sin prefijo.
Documentación kNN retriever Approximate kNN Dense vector jina-embeddings-v5-omni-small
Híbrida
Combina los dos modos anteriores con los mismos ajustes. El retriever rrf agrupa un retriever standard, con la consulta match o multi_match del componente full-text, y un retriever knn del componente vectorial.
- 1 · Componente full-text Lista ordenada por BM25 sobre
titley/ooriginal_title. - 2 · Componente vectorial Lista ordenada por cercanía k-NN sobre el vector de sinopsis elegido.
- 3 · Fusión RRF Una sola lista final, ordenada por la posición de cada película en las dos listas.
Cómo funciona Reciprocal Rank Fusion
RRF combina las dos listas según la posición de cada película en ellas, no según su puntuación, porque las escalas de BM25 y de la similitud vectorial no son comparables. Una película que aparece arriba en ambas listas sube en el resultado final.
Parámetros de la fusión
La demo no los expone en la interfaz: rank_constant usa el valor por defecto de Elasticsearch y rank_window_size lo fija la aplicación.
- rank_constant = 60
- Cuanto más alto, más influencia tienen las películas que están más abajo en cada lista.
- rank_window_size = k
- Cuántos resultados de cada componente entran a la fusión y cuántos resultados combinados se pueden recorrer. La aplicación lo iguala a
k(100 por defecto), así que ambos componentes aportan la misma cantidad y las páginas de 25, 50 o 100 nunca salen vacías.
Documentación RRF retriever
Resultados y filtros
Los resultados se muestran por páginas, con la puntuación que corresponde a cada modo, y se pueden acotar con filtros que se calculan a partir de los propios resultados.
Paginación
- 25 · 50 · 100 por página
- El usuario elige el tamaño de página y se mueve con Anterior, Siguiente o el número de página. Al cambiar el tamaño o aplicar filtros se vuelve a la página 1.
- Full-text
- Se pueden recorrer todas las coincidencias con
fromysize, hasta el límite de 10 000 que fijaindex.max_result_window. - Vectorial
- Hay como máximo
kresultados (100 por defecto), repartidos en páginas. - Híbrida
- Hay como máximo
rank_window_sizeresultados combinados. La aplicación lo iguala ak, así quefrom + sizenunca supera la ventana y ninguna página sale vacía.
Puntuación de cada resultado
- Full-text
- Puntuación BM25. No tiene una escala fija: solo sirve para comparar resultados de una misma búsqueda.
- Vectorial
- Similitud k-NN. Con
cosine, Elasticsearch devuelve (1 + coseno) / 2, un valor entre 0 y 1. - Híbrida
- La puntuación BM25 y la similitud k-NN, cada una con la posición de la película en su lista, más la puntuación RRF que define el orden final.
Filtros
- Campos
adult,genres,release_date(rango desde–hasta),spoken_languagesyrating.- Combinación
- Entre filtros distintos se cumplen todos; dentro de un mismo filtro basta con uno de los valores marcados. Los cambios se aplican al pulsar APLICAR; la fecha «Desde» no puede ser posterior a «Hasta».
- Valores dinámicos
- Cada filtro muestra solo los valores presentes en los resultados actuales, con cuántos resultados tienen cada uno, y se recalculan cada vez que cambian los resultados. Los valores ya marcados siguen visibles para poder desmarcarlos.
- En Elasticsearch
- Full-text: los filtros van en el contexto
filterde la consulta, que no altera la puntuación BM25, y los valores salen de agregacionesterms. Vectorial: los filtros van en elfilterdel retrieverknn, que se aplica durante la búsqueda, así que se devuelvenkpelículas que los cumplen. Híbrida: van en elfilterdel retrieverrrf, que se aplica a las dos búsquedas. - Valores en Vectorial e Híbrida
- La aplicación los calcula sobre los resultados devueltos (hasta
k). En RRF las agregaciones incluirían todas las coincidencias de cada búsqueda, incluso películas que no aparecen en la lista combinada.
Cuando no hay resultados
Si la búsqueda no encuentra nada, la página lo dice y sugiere ajustes según el modo (por ejemplo, OR en lugar de AND). Si la búsqueda sí encontró películas pero ninguna cumple los filtros, lo explica y ofrece quitarlos.
Documentación Paginación Terms aggregation Filtrar kNN RRF retriever
Carga de datos
Se pueden agregar películas una por una o importar un archivo. Un archivo se puede cargar en dos modos: append, que agrega las películas al catálogo, o bulk, que lo reemplaza. En los dos se valida todo el archivo antes de escribir.
Formas de carga
- Registro individual
- Formulario con los campos del mapping.
id,titleyplotson obligatorios. - Archivo .json
- La exportación del índice (objeto con
documentsy, opcionalmente,mappingydocument_count), un arreglo de películas o NDJSON. - Archivo .csv
- Texto UTF-8 con o sin BOM, separado por comas y con encabezado. Un CSV sin encabezado se rechaza. Las listas y los objetos anidados van como JSON dentro de la celda.
Validaciones
- Antes de subir
- Solo .json o .csv, sin dobles extensiones, de máximo 25 MB y con contenido de texto UTF-8. El servidor repite estas comprobaciones.
- Contra el mapping
- Se revisa el archivo completo: cada campo debe existir en el mapping y tener su tipo (
release_dateen yyyy-MM-dd,adultbooleano,keywordsybelongs_to_collectioncomo objetos anidados).idno se puede repetir. - Si algo no coincide
- La carga se detiene y no se importa ningún registro. La página muestra cada error con su registro,
id, campo, valor recibido y lo que espera el mapping, y permite descargar el informe completo. - Advertencias
- Lo que no rompe el mapping no detiene la carga: por ejemplo, los valores vacíos dentro de una lista se omiten y los textos vacíos se guardan sin valor.
Importación
- Vectores
- Los tres vectores se generan siempre desde
plotcon el servicio de inferencia de Elasticsearch (jina-embeddings-v5-omni-small,input_typeingesty sin prefijo). Si el archivo traeplot_256,plot_512oplot_1024, se ignoran. - Modo append (por defecto)
- Las películas del archivo se agregan al índice vigente a través del alias, en lotes de 100 y sin índice temporal. Si un
idya existe, la película se reemplaza. Si un lote falla o se cancela la carga, lo ya escrito se queda y la página dice cuántas películas se agregaron. - Modo bulk
- Las películas del archivo se escriben en lotes de 100 en un índice temporal nuevo, que contiene solo las películas del archivo: el catálogo actual no se copia.
- Publicación (bulk)
- Al terminar se comprueba el conteo y el alias
elastiflix-reloadedpasa al índice nuevo en una sola operación atómica. El índice anterior se borra. - Si falla (bulk)
- Si Elasticsearch rechaza un lote o la comprobación no cuadra, se borra el índice temporal y el catálogo queda exactamente como estaba.
Documentación Alias de índices Dense vector
El catálogo
El catálogo está en elastiflix-reloaded, un alias que apunta al índice vigente: las búsquedas siempre leen el catálogo publicado. Una carga en bulk se publica de una sola vez o se descarta sin afectar a las búsquedas; una carga en append se suma al índice vigente lote por lote. Tiene 6 899 películas. Estas son todas sus propiedades, las de elastiflix-reloaded-mapping.json, agrupadas como en el formulario de «Cargar datos». La columna «Uso» dice dónde las usa la demo: la tarjeta es cada resultado de la búsqueda y el detalle, la página de cada película.
Las dos últimas columnas dicen qué tiene que traer un archivo para cargarse, en modo append o bulk (las reglas son las mismas): en un .json, cada película trae id, title y plot con valor; en un .csv, el encabezado tiene esas tres columnas y cada fila, su valor. Los demás campos son opcionales, pero si vienen deben respetar su tipo, y un campo o una columna que no está en estas tablas rechaza el archivo.
Identificación y títulos
| Campo | Tipo | Contenido | Uso | Obligatorio en .json | Obligatorio en .csv |
|---|---|---|---|---|---|
| id | keyword | Identificador de la película en TMDB; también es el _id del documento | Detalle | Sí | Sí |
| imdb_id | keyword | Identificador en IMDb, por ejemplo tt0120338; el detalle enlaza a su ficha | Detalle | No | No |
| title | text | Título de la película | Full-text, Híbrida, tarjeta y detalle | Sí | Sí |
| original_title | text | Título original | Full-text, Híbrida, tarjeta y detalle | No | No |
| original_language | keyword | Idioma original, como código ISO 639-1: en, es, fr… | Detalle | No | No |
Sinopsis y textos
| Campo | Tipo | Contenido | Uso | Obligatorio en .json | Obligatorio en .csv |
|---|---|---|---|---|---|
| plot | text | Sinopsis; origen de los tres vectores | Tarjeta y detalle; origen de los vectores | Sí | Sí |
| overview | text | Resumen de TMDB | Detalle | No | No |
| tagline | keyword | Frase promocional de la película | Detalle | No | No |
| plot_256 | dense_vector | Vector de 256 dimensiones: los primeros 256 valores del de 1024, renormalizados | Vectorial, Híbrida | No; si viene, se ignora y se genera | No; si viene, se ignora y se genera |
| plot_512 | dense_vector | Vector de 512 dimensiones: los primeros 512 valores del de 1024, renormalizados | Vectorial, Híbrida | No; si viene, se ignora y se genera | No; si viene, se ignora y se genera |
| plot_1024 | dense_vector | Vector de 1024 dimensiones de la sinopsis, generado con jina-embeddings-v5-omni-small | Vectorial, Híbrida | No; si viene, se ignora y se genera | No; si viene, se ignora y se genera |
Clasificación, fecha y estado
| Campo | Tipo | Contenido | Uso | Obligatorio en .json | Obligatorio en .csv |
|---|---|---|---|---|---|
| genres | keyword | Géneros, por ejemplo Drama, Comedy o Action | Filtro, tarjeta y detalle | No | No |
| keywords | nested | Palabras clave de TMDB: una lista de objetos con id y name | Detalle | No | No |
| keywords. | long | Identificador de la palabra clave en TMDB | Detalle | No | No; va en el JSON de la celda de su objeto |
| keywords. | text | La palabra clave, por ejemplo iceberg | Detalle | No | No; va en el JSON de la celda de su objeto |
| rating | keyword | Clasificación: G, PG, PG-13, R, NC-17 o NR | Filtro y detalle | No | No |
| release_date | date | Fecha de estreno en yyyy-MM-dd (medido en el catálogo original de 4 322 películas: de 1902 a 2023) | Filtro, tarjeta y detalle | No | No |
| runtime | integer | Duración en minutos | Tarjeta y detalle | No | No |
| status | keyword | Estado de producción, por ejemplo Released | Detalle | No | No |
| adult | boolean | Contenido para adultos (medido en el catálogo original de 4 322 películas: ninguna película del catálogo lo es) | Filtro y detalle | No | No |
| video | boolean | Si el registro es un video y no una película de cine | Detalle | No | No |
Métricas
| Campo | Tipo | Contenido | Uso | Obligatorio en .json | Obligatorio en .csv |
|---|---|---|---|---|---|
| popularity | float | Popularidad según TMDB | Detalle | No | No |
| vote_average | float | Promedio de los votos, de 0 a 10 | Tarjeta y detalle | No | No |
| vote_count | integer | Número de votos | Tarjeta y detalle | No | No |
| user_score | float | Puntuación de los usuarios | Detalle | No | No |
| budget | float | Presupuesto en USD; vacío o en 0 se muestra como N/A | Detalle | No | No |
| revenue | float | Recaudación en USD; vacía o en 0 se muestra como N/A | Detalle | No | No |
Reparto, producción y medios
| Campo | Tipo | Contenido | Uso | Obligatorio en .json | Obligatorio en .csv |
|---|---|---|---|---|---|
| cast | keyword | Reparto: una lista de nombres | Detalle | No | No |
| production_companies | keyword | Productoras | Detalle | No | No |
| production_countries | keyword | Países de producción | Detalle | No | No |
| spoken_languages | keyword | Idiomas hablados, con su nombre propio: English, Español, Français… | Filtro y detalle | No | No |
| poster_path | text | Ruta del póster en el CDN de imágenes de TMDB | Tarjeta y detalle | No | No |
| backdrop_path | keyword | Ruta de la imagen de fondo en el CDN de imágenes de TMDB | Detalle | No | No |
| homepage | keyword | Sitio web oficial; solo se enlaza si es una dirección http o https | Detalle | No | No |
| belongs_to_collection | nested | Colección o saga a la que pertenece: un objeto con id, name, poster_path y backdrop_path | Detalle | No | No |
| belongs_to_collection. | long | Identificador de la colección en TMDB | Detalle | No | No; va en el JSON de la celda de su objeto |
| belongs_to_collection. | text | Nombre de la colección, por ejemplo Harry Potter Collection | Detalle | No | No; va en el JSON de la celda de su objeto |
| belongs_to_collection. | text | Ruta del póster de la colección | Detalle | No | No; va en el JSON de la celda de su objeto |
| belongs_to_collection. | text | Ruta de la imagen de fondo de la colección | Detalle | No | No; va en el JSON de la celda de su objeto |
Créditos y aviso de uso
Fines académicos y demostrativos
Elastiflix-Movies Java es un proyecto académico que demuestra técnicas de búsqueda full-text, vectorial e híbrida. No es un producto comercial: no se vende, no muestra publicidad y de ninguna manera pretende obtener un lucro comercial con él ni con la información que usa.
Los créditos de la información de las películas corresponden a TMDB y a Wikipedia.
Los datos de las películas y las imágenes de pósters vienen de TMDB (The Movie Database).
This product uses the TMDB API but is not endorsed or certified by TMDB.
Este producto usa la API de TMDB, pero no está avalado ni certificado por TMDB.
Los textos de las películas vienen de Wikipedia, cuyo contenido se publica bajo la licencia Creative Commons Atribución-CompartirIgual 4.0.
Los nombres, marcas, pósters y textos pertenecen a sus respectivos titulares. Esta demo no está afiliada a TMDB ni a Wikipedia, ni avalada por ellos.