# sync_vector.dkl ## ¿Qué es y para qué sirve? Cuando una aplicación necesita ofrecer búsqueda inteligente —del tipo "encuentra productos similares a martillo" o "¿qué atenciones tuvieron síntomas parecidos a estos?"— el motor de búsqueda no trabaja con los datos tal como están en la base de datos. Trabaja con **vectores**: representaciones matemáticas del significado del texto que permiten encontrar coincidencias semánticas, no solo por palabra exacta. `sync_vector.dkl` es una herramienta de línea de comando que mantiene ese índice vectorial sincronizado con los datos de una tabla de base de datos. En lugar de reconstruir el índice completo cada vez, detecta qué registros son nuevos, cuáles cambiaron y cuáles ya no existen, y solo procesa los que realmente necesitan actualizarse. La herramienta es compatible con múltiples motores de base de datos. Actualmente soporta **MySQL** y **SQL Server**; el soporte para motores adicionales se irá incorporando en versiones futuras. --- ## ¿Cuándo se usa? Es la herramienta adecuada cuando se tiene una tabla de base de datos cuyos registros deben poder encontrarse mediante búsqueda semántica, y esa tabla cambia con el tiempo. Algunos casos típicos: **Catálogo de productos.** Un usuario escribe "algo para colgar cuadros" y el motor debe encontrar "taquete expansor" y "gancho para pared" aunque ninguna de esas palabras aparezca en la búsqueda. `sync_vector` mantiene el índice actualizado cada vez que se agregan o modifican productos. **Historial de atenciones o tickets.** Un agente de soporte busca "problema con factura duplicada" y el motor recupera casos anteriores con situaciones similares, aunque estén redactados de forma distinta. **Base de conocimiento o preguntas frecuentes.** Documentos, artículos o respuestas que deben encontrarse por su contenido aunque el usuario no use las palabras exactas del título. **Directorio de personas o empresas.** Búsquedas por perfil, descripción o actividad en lugar de por nombre exacto. En general: cualquier catálogo o tabla donde la búsqueda por texto exacto no es suficiente. --- ## Prerrequisitos Para ejecutar la herramienta correctamente, es necesario contar con lo siguiente: - **Acceso a la base de datos** que contiene la información a sincronizar, ya sea en **MySQL** o **SQL Server**. - **Motor de resultados IAE** previamente creado en la plataforma Induxsoft, junto con: - Su identificador (`seid`) - Su token de acceso correspondiente - **Archivo ejecutable `sync_vector.dkl`** disponible en el equipo. - Puede descargarse desde el siguiente enlace: - https://github.com/Induxsoft/sync_vector/tree/main/src - Es necesario copiar **todo el contenido del repositorio** en la misma ruta donde se encuentran los binarios de Devkron. - **Conocimientos básicos de SQL**, necesarios para construir la consulta que permitirá extraer los datos a sincronizar. #### Binarios requeridos - `dkl.async.dll` (incluido dentro del repositorio) ## Estructura de archivos La herramienta trabaja con tres archivos de configuración que el técnico prepara antes de ejecutar: ``` config.json → define qué datos sincronizar y cómo mapearlos params.json → valores de filtro para la consulta SQL (opcional) macros.json → fragmentos de SQL dinámico (opcional) ``` --- ## El archivo de configuración Es el archivo central. Contiene la consulta SQL y el mapeo de campos. Se crea una vez por cada trabajo de sincronización. ```json { "id": "productos_ferreteria", "query": "SELECT sys_pk, codigo, descripcion, notas FROM producto WHERE linea = @linea", "batch_size": 250, "batch_sleep": 100, "vector": { "keyfield": "sys_pk", "title": "codigo", "description": "descripcion", "content": "notas" } } ``` ### Explicación campo por campo **`id`** — nombre que identifica este trabajo de sincronización. Debe ser único por cada trabajo. La herramienta creará una tabla con este nombre en la base de datos para llevar el control de estado. Usar nombres descriptivos sin espacios, por ejemplo `productos_ferreteria`, `tickets_soporte_2024`, `faq_publico`. **`query`** — la consulta SQL que extrae los registros a sincronizar. Puede ser tan simple o compleja como se necesite: joins, filtros, campos calculados. Lo único que se requiere es que devuelva los campos que se usan en la sección `vector`. La sintaxis SQL debe ser compatible con el motor de base de datos que se esté usando. **`batch_size`** — cuántos registros se envían al motor en cada bloque. El valor predeterminado de 250 funciona bien en la mayoría de los casos. Reducirlo si se tienen problemas de timeout con registros de texto muy largo. **`batch_sleep`** — milisegundos de espera entre bloques. Útil si el servidor del motor tiene restricciones de tasa de llamadas. En la mayoría de los casos puede omitirse, el valor predeterminado es 100. **`vector.keyfield`** — el campo que identifica unívocamente cada registro en la tabla de origen. Equivale a la clave primaria. Debe ser un solo campo; si la tabla no tiene una clave simple, crear un campo calculado en el SQL (ver sección de ejemplos). **`vector.title`** — campo cuyo valor se usará como título del vector. Es uno de los dos campos que definen el significado del registro para el motor de búsqueda. **`vector.description`** — campo cuyo valor complementa el título para describir el registro. Junto con `title` forma el texto que el motor vectoriza. **`vector.content`** — campo con información adicional asociada al registro. No influye en cómo el motor "entiende" el registro, pero se recupera junto con los resultados de búsqueda. Útil para guardar datos que la aplicación necesita mostrar al encontrar el registro. > **Nota sobre title y description:** estos dos campos determinan cómo el motor interpreta cada registro para la búsqueda semántica. Si cambian, el motor recalcula el vector. Si solo cambia `content`, el motor actualiza el dato asociado pero no recalcula el vector, lo que hace la sincronización más eficiente. --- ## El archivo de parámetros Permite pasar valores de filtro a la consulta SQL de forma segura. Se referencia en la query con la notación `@nombre`. ```json { "linea": "FERRETERIA" } ``` Query correspondiente: ```sql SELECT ... FROM producto WHERE linea = @linea ``` Esto es útil cuando se quiere ejecutar el mismo trabajo de sincronización para distintos subconjuntos de datos simplemente cambiando el archivo de parámetros, sin modificar la configuración principal. Esta notación `@nombre` es compatible tanto con MySQL como con SQL Server. --- ## El archivo de macros Permite insertar fragmentos de SQL dinámico en la query. Se referencia con la notación `{{nombre}}`. ```json { "condicion": "activo = 1 AND categoria = 'HERRAMIENTA'" } ``` Query correspondiente: ```sql SELECT ... FROM producto WHERE {{condicion}} AND linea = @linea ``` La diferencia con los parámetros es que las macros se insertan textualmente en el SQL antes de ejecutarlo, lo que permite construir condiciones, ordenamientos o incluso nombres de tablas de forma dinámica. Si una macro no está definida en el archivo, se sustituye por texto vacío. --- ## Ejecución La sintaxis básica es: ``` sync_vector.dkl q=CONEXION s=SEID t=TOKEN c=config.json ``` Con parámetros y macros: ``` sync_vector.dkl q=CONEXION s=SEID t=TOKEN c=config.json p=params.json m=macros.json ``` | Argumento | Descripción | |-----------|-------------| | `q` | Cadena de conexión a la base de datos | | `s` | Identificador del motor de resultados (`seid`) | | `t` | Token de acceso al motor | | `c` | Archivo de configuración | | `p` | Archivo de parámetros SQL (opcional) | | `m` | Archivo de macros de texto (opcional) | --- ## ¿Qué hace la herramienta en cada ejecución? 1. Lee la configuración y ejecuta la consulta SQL. 2. Por cada registro del resultado, verifica si ya existe en el índice y si su contenido cambió. 3. Envía al motor únicamente los registros nuevos o modificados. 4. Al final, elimina del índice los registros que ya no aparecen en la consulta. Esto significa que la herramienta puede ejecutarse repetidamente —por ejemplo cada hora mediante una tarea programada— y solo hará trabajo real cuando haya cambios. --- ## Control de estado La herramienta crea automáticamente una tabla en la misma base de datos con el nombre definido en `config.id`. Esa tabla guarda el estado de cada registro sincronizado y es lo que permite detectar cambios sin procesar todo el catálogo en cada ejecución. No es necesario crearla manualmente ni darle mantenimiento. Si se necesita forzar una resincronización completa, basta con vaciar o eliminar esa tabla; la herramienta la recreará en la siguiente ejecución y tratará todos los registros como nuevos. --- ## Subconjuntos y múltiples trabajos Un trabajo de sincronización es responsable únicamente de los registros que su query devuelve. Si la query filtra por línea de producto, solo esos productos participan en el índice de ese trabajo. Esto es intencional: permite mantener índices vectoriales separados para distintos propósitos o audiencias. Es perfectamente válido tener varios trabajos apuntando a la misma tabla con distintos filtros, cada uno con su propio `config.id` y su propia tabla de control: ``` productos_ferreteria → WHERE linea = 'FERRETERIA' productos_plomeria → WHERE linea = 'PLOMERIA' productos_destacados → WHERE destacado = 1 ``` Cada trabajo mantiene su índice de forma independiente. --- ## Ejemplos prácticos ### Ejemplo 1: Catálogo de productos simple ```json { "id": "catalogo_productos", "query": "SELECT id, nombre, descripcion, especificaciones FROM producto WHERE activo = 1", "vector": { "keyfield": "id", "title": "nombre", "description": "descripcion", "content": "especificaciones" } } ``` ### Ejemplo 2: Catálogo filtrado por parámetro `config.json`: ```json { "id": "productos_por_linea", "query": "SELECT id, nombre, descripcion, sku FROM producto WHERE linea = @linea AND activo = 1", "vector": { "keyfield": "id", "title": "nombre", "description": "descripcion", "content": "sku" } } ``` `params.json`: ```json { "linea": "FERRETERIA" } ``` ### Ejemplo 3: Tabla sin clave simple — usar CONCAT en el SQL Cuando la tabla no tiene un campo de clave única simple, se puede construir uno directamente en la query: ```json { "id": "detalle_pedidos", "query": "SELECT CONCAT(id_pedido, '-', id_producto) AS clave, producto, descripcion, cantidad FROM detalle_pedido", "vector": { "keyfield": "clave", "title": "producto", "description": "descripcion", "content": "cantidad" } } ``` ### Ejemplo 4: Combinar varios campos en title o description Si se quiere que el motor considere tanto el código como el nombre al vectorizar, se concatenan en el SQL: ```json { "id": "productos_completo", "query": "SELECT id, CONCAT(codigo, ' ', nombre) AS titulo, descripcion, notas FROM producto", "vector": { "keyfield": "id", "title": "titulo", "description": "descripcion", "content": "notas" } } ``` ### Ejemplo 5: Usar macros para condiciones dinámicas `config.json`: ```json { "id": "tickets_soporte", "query": "SELECT id, asunto, detalle, resolucion FROM ticket WHERE {{filtro_fecha}} AND estado = 'cerrado'", "vector": { "keyfield": "id", "title": "asunto", "description": "detalle", "content": "resolucion" } } ``` `macros.json`: ```json { "filtro_fecha": "fecha_cierre >= '2024-01-01'" } ``` --- ## Programar ejecuciones automáticas La forma habitual de usar esta herramienta es programarla para que se ejecute periódicamente mediante el planificador de tareas del sistema operativo. **En Windows (Programador de tareas):** Crear una tarea que ejecute el comando completo con la frecuencia deseada (cada hora, cada noche, etc.). **En Linux/macOS (cron):** ``` 0 * * * * /ruta/sync_vector.dkl q=CONEXION s=SEID t=TOKEN c=/ruta/config.json ``` La frecuencia adecuada depende de qué tan crítica sea la actualización del índice para la aplicación. Para catálogos que cambian poco, una vez al día puede ser suficiente. Para sistemas de tickets o atenciones activas, cada 15 o 30 minutos es razonable. --- ## Mensajes de salida y errores La herramienta escribe en la salida estándar el progreso de la ejecución y cualquier error encontrado. Los mensajes más importantes son: | Situación | Qué indica | |-----------|------------| | Falta el argumento `q`, `s`, `t` o `c` | No se proporcionó un argumento obligatorio | | Archivo no encontrado o JSON inválido | El archivo de configuración, parámetros o macros tiene un error de formato o no existe | | Error de base de datos al ejecutar la query | La consulta SQL tiene un error o la conexión falló | | Error HTTP al enviar lote | El servicio del motor respondió con error; los registros del lote quedan en estado `error` y se reintentarán cuando su contenido cambie | | Error HTTP al eliminar vector | El motor no pudo eliminar un registro dado de baja; permanece en la tabla de control | Si un lote falla al enviarse, la herramienta continúa con el siguiente lote en lugar de detenerse. Al final de la ejecución, los registros con error pueden identificarse consultando directamente la tabla de control en la base de datos (`SELECT * FROM {config.id} WHERE status = 'error'`). --- ## Preguntas frecuentes **¿Qué pasa si ejecuto la herramienta y no hay cambios?** No se envía nada al motor. La herramienta recorre los registros, verifica que no cambiaron y solo actualiza la fecha de última verificación en la tabla de control. **¿Puedo cambiar la query después de la primera ejecución?** Sí. Si la query devuelve más o menos registros que antes, la herramienta agregará los nuevos y eliminará del índice los que ya no aparezcan. **¿Qué pasa si elimino la tabla de control?** En la siguiente ejecución la herramienta la recrea y trata todos los registros como nuevos, enviándolos todos al motor. Útil para forzar una resincronización completa. **¿La herramienta modifica mis datos?** No. La única tabla que escribe o modifica en la base de datos es la tabla de control (cuyo nombre es el `config.id`). Los datos de origen nunca se tocan. **¿Funciona con SQL Server?** Sí. La herramienta soporta MySQL y SQL Server. La cadena de conexión en el argumento `q` determina el motor a usar. La sintaxis SQL de la query debe ser compatible con el motor que se esté usando. **¿Puedo tener varios trabajos apuntando al mismo motor vectorial?** Sí, siempre que cada trabajo use un `config.id` diferente. Los vectores de distintos trabajos coexisten en el mismo índice identificados por su `vector_id`. **¿Qué significa que un registro quede en estado `error`?** Que el servicio del motor respondió con un error al intentar sincronizarlo. El registro permanece pendiente y se reintentará automáticamente en la siguiente ejecución en que su contenido cambie. Si el error persiste, revisar la conectividad con el servicio y que el token sea válido.