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:

  1. Habilitar extensión pg_trgm en PostgreSQL
  2. Crear índices GIN en columnas de búsqueda
  3. Cambiar de Fuse.js a queries con similarity() o % operator
  4. El componente de UI se mantiene igual, solo cambia el servicio

Documento de referencia para la implementación del buscador