BUSQUEDA.md
6.33 KB
Lógica del Buscador
Resumen
Buscador instantáneo con Fuse.js en cliente. Busca en clasificadores presupuestarios y navega a vistas de detalle.
Decisiones de Diseño
Motor de búsqueda
- Tecnología: Fuse.js (cliente)
- Razón: ~1,000 registros totales (~600 entidades + ~450 objetos), cabe en memoria, búsqueda instantánea sin latencia
Configuración Fuse.js
const fuseOptions = {
keys: [
{ name: 'nombre_normalizado', weight: 0.6 },
{ name: 'sigla_normalizada', weight: 0.2 },
{ name: 'codigo_normalizado', weight: 0.2 }
],
threshold: 0.4,
distance: 100,
includeScore: true,
includeMatches: true,
minMatchCharLength: 2,
ignoreLocation: true
};
Tolerancia a errores
- Normalización de acentos ("andres" → "andrés")
- Case-insensitive ("UMSA" = "umsa")
- Fuzzy matching ("univercidad" → "universidad")
Comportamiento
| Parámetro | Valor |
|---|---|
| Debounce | 200-300ms |
| Mínimo caracteres | 2 |
| Máximo resultados | 10-30 |
| Navegación | Flechas ↑↓ + Enter + scroll de mouse y loq ue corresponda para mobiles |
Estructura del Índice
Índice unificado (implementado)
Todos los clasificadores en un solo array con tipo:
// src/lib/services/search.js
const searchData = [
// Entidades
{
tipo: 'entidad',
codigo: 139,
nombre: 'Universidad Mayor De San Andrés',
sigla: 'UMSA',
contexto: 'Universidades Públicas',
años: 20,
nombre_normalizado: 'universidad mayor de san andres',
sigla_normalizada: 'umsa',
codigo_normalizado: '139'
},
// Objetos de gasto
{
tipo: 'objeto_gasto',
codigo: '25100',
nombre: 'Pasajes',
sigla: null,
contexto: 'Objeto del Gasto · Partida',
nivel: 'partida',
nombre_normalizado: 'pasajes',
sigla_normalizada: '',
codigo_normalizado: '25100'
}
]
Fuentes de datos
-
Entidades: tabla
ppto.clas_institucional -
Objetos de gasto: tabla
ppto.clas_objetos
Columnas de Búsqueda
Entidades (clas_institucional)
| Columna | Buscar | Mostrar |
|---|---|---|
desc_entidad |
✓ | ✓ nombre principal |
sigla_entidad |
✓ | ✓ entre paréntesis |
desc_area |
✗ | ✓ contexto |
n_gestiones |
✗ | ✓ "X años de datos" |
entidad |
✗ | para URL |
Objetos de gasto (clas_objetos)
| Columna | Buscar | Mostrar |
|---|---|---|
desc_objeto |
✓ | ✓ nombre principal |
objeto |
✓ | ✓ código (monospace) |
nivel |
✗ | ✓ contexto ("Objeto del Gasto · Partida") |
Flujo de Búsqueda
1. CARGA INICIAL
App monta → descarga clasificadores → construye índice Fuse.js
2. USUARIO ESCRIBE
Input → debounce 250ms → si ≥2 chars → Fuse.search()
3. RESULTADOS
Fuse devuelve matches → renderizar lista con highlighting
4. SELECCIÓN
Click o Enter → navegar a /entidad/[codigo] o /gasto/[codigo]
Estructura de URLs
/entidad/[codigo] → /entidad/139
/objeto/[codigo] → /objeto/25100
/area/[codigo] → /area/1.1.4 (futuro, opcional)
Formato de Resultados
┌─────────────────────────────────────────────────────┐
│ 🔍 [Buscar entidad u objeto de gasto...] │
├─────────────────────────────────────────────────────┤
│ Universidad Mayor De San Andrés (UMSA) │ ← Entidad
│ Universidades Públicas │
├─────────────────────────────────────────────────────┤
│ 25100 Pasajes │ ← Objeto de gasto
│ Objeto del Gasto · Partida │
├─────────────────────────────────────────────────────┤
│ ... │
└─────────────────────────────────────────────────────┘
Diferencias visuales
- Entidades: Nombre + (sigla) en primera línea
- Objetos de gasto: Código (monospace) + Nombre en primera línea
Estados del Buscador
| Estado | Qué mostrar |
|---|---|
| Vacío (< 2 chars) | Placeholder o sugerencias |
| Cargando índice | Spinner / "Cargando..." |
| Buscando | Nada (es instantáneo) |
| Con resultados | Lista de resultados |
| Sin resultados | "No se encontraron resultados para 'xyz'" |
| Error | "Error al cargar datos" |
Estructura de Archivos
src/lib/
├── components/
│ └── search/
│ ├── SearchBox.svelte ← Input + lógica de búsqueda
│ ├── SearchResults.svelte ← Lista de resultados
│ └── SearchResultItem.svelte ← Item individual
├── services/
│ ├── supabase.js ← Cliente Supabase
│ └── search.js ← Inicialización Fuse.js
├── stores/
│ └── searchStore.js ← Estado: query, results, loading
├── utils/
│ └── normalize.js ← Normalización de texto
└── data/
└── index.js ← Carga y construcción del índice
Estado de Implementación
| Clasificador | Estado | Tabla |
|---|---|---|
| Entidades | ✅ Implementado | ppto.clas_institucional |
| Objetos de gasto | ✅ Implementado | ppto.clas_objetos |
| Fuente de financiamiento | ⏳ Pendiente | - |
| Geográfico | ⏳ Pendiente | - |
Trabajo Futuro
Índice pre-computado
Posibilidad de pre-computar el índice en servidor y servir como JSON estático para mejorar tiempo de carga inicial.
Migración a servidor
Si el índice supera ~20,000 registros:
- Habilitar extensión
pg_trgmen PostgreSQL - Crear índices GIN en columnas de búsqueda
- Cambiar de Fuse.js a queries con
similarity()o%operator - El componente de UI se mantiene igual, solo cambia el servicio
Documento de referencia para la implementación del buscador