# Estrategia Técnica: Búsqueda por Siglas y Acrónimos TES ## Arquitectura Frontend - PWA Sanitaria Crítica **Objetivo:** Añadir soporte para búsqueda por siglas comunes del ámbito TES sin romper la búsqueda existente. **Principio rector:** Compatibilidad total hacia atrás. La búsqueda actual debe seguir funcionando exactamente igual. --- ## 1. Análisis del Estado Actual ### Funciones de Búsqueda Existentes **`src/data/procedures.ts`** ```typescript export const searchProcedures = (query: string): Procedure[] => { const lowerQuery = query.toLowerCase(); return procedures.filter( (p) => p.title.toLowerCase().includes(lowerQuery) || p.shortTitle.toLowerCase().includes(lowerQuery) || p.steps.some((s) => s.toLowerCase().includes(lowerQuery)) ); }; ``` **`src/data/drugs.ts`** ```typescript export const searchDrugs = (query: string): Drug[] => { const lowerQuery = query.toLowerCase(); return drugs.filter( (d) => d.genericName.toLowerCase().includes(lowerQuery) || d.tradeName.toLowerCase().includes(lowerQuery) || d.indications.some((i) => i.toLowerCase().includes(lowerQuery)) ); }; ``` ### Punto de Integración Actual **`src/components/layout/SearchModal.tsx`** (líneas 52-104) - Llama a `searchProcedures(query)` y `searchDrugs(query)` directamente - No hay preprocesamiento de la query - Búsqueda case-insensitive mediante `.toLowerCase()` ### Observaciones Clave 1. ✅ **Búsqueda simple y determinista** - Usa `.includes()`, no algoritmos complejos 2. ✅ **Offline-compatible** - Todo es código TypeScript, sin llamadas externas 3. ✅ **Fácil de mantener** - Lógica clara y directa 4. ⚠️ **No soporta sinónimos** - "OVACE" no encuentra "Obstrucción de Vía Aérea" 5. ⚠️ **No soporta acrónimos** - "EAP" no encuentra "Enfermedad Arterial Periférica" --- ## 2. Propuesta de Arquitectura ### 2.1. Dónde Integrar el Mapeo de Siglas **OPCIÓN RECOMENDADA: Capa de Preprocesamiento en SearchModal** ``` Usuario escribe "OVACE" ↓ SearchModal.tsx (expandAcronyms) ↓ Query expandida: "OVACE" → "OVACE OR obstrucción vía aérea OR cuerpo extraño" ↓ searchProcedures(query expandida) + searchDrugs(query expandida) ↓ Resultados combinados ``` **Ventajas:** - ✅ No toca funciones de búsqueda existentes (`searchProcedures`, `searchDrugs`) - ✅ Cambio localizado en un solo componente - ✅ Fácil de desactivar si hay problemas (feature flag) - ✅ Permite expandir a otros tipos de búsqueda en el futuro **Alternativa (NO recomendada):** - Modificar `searchProcedures` y `searchDrugs` directamente - ❌ Riesgo: Cambia comportamiento de funciones usadas en otros lugares - ❌ Riesgo: Más difícil de testear y revertir ### 2.2. Estructura de Archivos ``` src/data/ ├── procedures.ts [NO TOCAR] ├── drugs.ts [NO TOCAR] └── acronyms.ts [NUEVO] ← Diccionario de siglas ``` **`src/data/acronyms.ts`** - Nuevo archivo - Diccionario estático de siglas → términos expandidos - Type-safe con TypeScript - Sin dependencias externas **`src/components/layout/SearchModal.tsx`** - Modificar mínimamente - Añadir función `expandAcronyms()` antes de llamar a búsqueda - Mantener toda la lógica existente intacta ### 2.3. Formato del Diccionario de Siglas ```typescript // src/data/acronyms.ts export interface AcronymMapping { acronym: string; // "OVACE" expandedTerms: string[]; // ["obstrucción vía aérea", "cuerpo extraño"] context?: 'procedure' | 'drug' | 'both'; // Opcional: filtrar contexto priority?: number; // Opcional: prioridad en resultados } export const acronymMappings: AcronymMapping[] = [ { acronym: 'OVACE', expandedTerms: ['obstrucción vía aérea', 'cuerpo extraño', 'vía aérea'], context: 'procedure', }, { acronym: 'RCP', expandedTerms: ['reanimación cardiopulmonar', 'parada cardiorrespiratoria', 'PCR'], context: 'both', }, { acronym: 'PCR', expandedTerms: ['parada cardiorrespiratoria', 'reanimación cardiopulmonar', 'RCP'], context: 'both', }, { acronym: 'SVB', expandedTerms: ['soporte vital básico'], context: 'procedure', }, { acronym: 'SVA', expandedTerms: ['soporte vital avanzado'], context: 'procedure', }, { acronym: 'EAP', expandedTerms: ['enfermedad arterial periférica'], context: 'both', }, { acronym: 'IAM', expandedTerms: ['infarto agudo miocardio', 'síndrome coronario agudo'], context: 'both', }, { acronym: 'SCACEST', expandedTerms: ['síndrome coronario agudo con elevación ST', 'infarto con elevación ST'], context: 'both', }, { acronym: 'IOT', expandedTerms: ['intubación orotraqueal', 'vía aérea avanzada'], context: 'procedure', }, { acronym: 'DEA', expandedTerms: ['desfibrilador externo automático'], context: 'procedure', }, { acronym: 'ROSC', expandedTerms: ['retorno circulación espontánea'], context: 'procedure', }, { acronym: 'GCS', expandedTerms: ['glasgow', 'escala glasgow'], context: 'both', }, { acronym: 'AVDN', expandedTerms: ['alerta verbal dolor no responde'], context: 'procedure', }, { acronym: 'FR', expandedTerms: ['frecuencia respiratoria'], context: 'both', }, { acronym: 'FC', expandedTerms: ['frecuencia cardiaca'], context: 'both', }, { acronym: 'TA', expandedTerms: ['tensión arterial'], context: 'both', }, { acronym: 'SpO2', expandedTerms: ['saturación oxígeno', 'saturación'], context: 'both', }, { acronym: 'ETCO2', expandedTerms: ['capnografía', 'dióxido carbono espirado'], context: 'procedure', }, { acronym: 'FV', expandedTerms: ['fibrilación ventricular'], context: 'procedure', }, { acronym: 'TVSP', expandedTerms: ['taquicardia ventricular sin pulso'], context: 'procedure', }, { acronym: 'AESP', expandedTerms: ['actividad eléctrica sin pulso'], context: 'procedure', }, { acronym: 'ACR', expandedTerms: ['asistolia', 'parada cardiorrespiratoria'], context: 'procedure', }, { acronym: 'TCE', expandedTerms: ['traumatismo craneoencefálico'], context: 'both', }, { acronym: 'TEP', expandedTerms: ['tromboembolismo pulmonar'], context: 'both', }, { acronym: 'EPOC', expandedTerms: ['enfermedad pulmonar obstructiva crónica'], context: 'both', }, { acronym: 'INR', expandedTerms: ['razón normalizada internacional', 'coagulación'], context: 'both', }, ]; /** * Expande una sigla/acrónimo a sus términos relacionados * @param acronym - La sigla a expandir (ej: "OVACE") * @returns Array de términos expandidos (ej: ["obstrucción vía aérea", "cuerpo extraño"]) */ export function expandAcronym(acronym: string): string[] { const upperAcronym = acronym.toUpperCase(); const mapping = acronymMappings.find(m => m.acronym.toUpperCase() === upperAcronym); return mapping ? mapping.expandedTerms : []; } /** * Obtiene todas las siglas que contienen un término * Útil para búsqueda inversa: "vía aérea" → ["OVACE", "IOT"] */ export function getAcronymsByTerm(term: string): string[] { const lowerTerm = term.toLowerCase(); return acronymMappings .filter(m => m.expandedTerms.some(expanded => expanded.toLowerCase().includes(lowerTerm)) ) .map(m => m.acronym); } ``` ### 2.4. Algoritmo de Expansión de Query **Estrategia: Búsqueda OR con términos expandidos** ```typescript // Pseudocódigo de expandAcronyms() function expandAcronyms(query: string): string { const terms = query.split(/\s+/); // Dividir por espacios const expandedTerms: string[] = []; for (const term of terms) { // Si el término es una sigla conocida, expandir const expanded = expandAcronym(term); if (expanded.length > 0) { // Añadir término original + términos expandidos expandedTerms.push(term); expandedTerms.push(...expanded); } else { // Si no es sigla, mantener término original expandedTerms.push(term); } } // Retornar query original + términos expandidos // La búsqueda actual con .includes() encontrará cualquiera de estos términos return expandedTerms.join(' '); } ``` **Ejemplo de funcionamiento:** ``` Input: "OVACE" ↓ expandAcronyms() Output: "OVACE obstrucción vía aérea cuerpo extraño" ↓ searchProcedures() Encuentra: Procedimiento con shortTitle "OVACE" ✅ Encuentra: Procedimiento con title "Obstrucción de Vía Aérea" ✅ ``` **Input: "RCP adulto"** ↓ expandAcronyms() Output: "RCP reanimación cardiopulmonar parada cardiorrespiratoria PCR adulto" ↓ searchProcedures() Encuentra: Procedimientos con "RCP" en shortTitle ✅ Encuentra: Procedimientos con "reanimación" en title ✅ --- ## 3. Compatibilidad con Búsquedas Actuales ### 3.1. Garantías de Compatibilidad **✅ Búsquedas existentes siguen funcionando:** - "parada" → encuentra "parada cardiorrespiratoria" (como antes) - "adrenalina" → encuentra fármaco (como antes) - "glasgow" → encuentra calculadora (como antes) **✅ Búsquedas nuevas funcionan:** - "OVACE" → encuentra "Obstrucción de Vía Aérea" (nuevo) - "EAP" → encuentra protocolos relacionados (nuevo) - "SVB" → encuentra "Soporte Vital Básico" (nuevo) **✅ Búsquedas mixtas funcionan:** - "RCP adulto" → encuentra RCP adulto (término original + expandido) - "OVACE pediátrico" → encuentra OVACE pediátrico ### 3.2. Estrategia de Búsqueda OR (No AND) **Problema potencial:** Si expandimos "OVACE" a "obstrucción vía aérea cuerpo extraño", y el usuario busca "OVACE pediátrico", podríamos encontrar resultados que solo contengan "pediátrico" sin "OVACE". **Solución:** Mantener término original en la query expandida ``` Input: "OVACE pediátrico" Expansión: "OVACE obstrucción vía aérea cuerpo extraño pediátrico" Búsqueda: .includes("OVACE") OR .includes("obstrucción") OR .includes("pediátrico") ``` **Resultado:** Encuentra protocolos que contengan: - "OVACE" Y "pediátrico" ✅ (mejor match) - "obstrucción" Y "pediátrico" ✅ (match parcial) - Solo "OVACE" ⚠️ (match parcial, pero mejor que nada) **Mejora futura (opcional):** Sistema de scoring para priorizar matches completos. ### 3.3. Case-Insensitive La expansión debe ser case-insensitive: - "ovace" → expande igual que "OVACE" - "Rcp" → expande igual que "RCP" Ya está cubierto porque `expandAcronym()` compara con `.toUpperCase()`. --- ## 4. Qué NO Tocar (Zonas de Riesgo) ### 4.1. Archivos que NO deben modificarse **❌ `src/data/procedures.ts`** - Función `searchProcedures()` - NO TOCAR - Array `procedures` - NO TOCAR - Interfaces `Procedure` - NO TOCAR - **Razón:** Cambiar esto afectaría toda la aplicación **❌ `src/data/drugs.ts`** - Función `searchDrugs()` - NO TOCAR - Array `drugs` - NO TOCAR - Interfaces `Drug` - NO TOCAR - **Razón:** Cambiar esto afectaría toda la aplicación **❌ Estructura de datos existente** - No añadir campos nuevos a `Procedure` o `Drug` para siglas - No modificar `title`, `shortTitle`, etc. para incluir siglas - **Razón:** Cambios estructurales requieren migración y testing exhaustivo ### 4.2. Componentes que NO deben modificarse (excepto SearchModal) **❌ `src/pages/SoporteVital.tsx`** - No añadir lógica de búsqueda aquí - **Razón:** La búsqueda está centralizada en SearchModal **❌ `src/pages/Farmacos.tsx`** - No añadir lógica de búsqueda aquí - **Razón:** La búsqueda está centralizada en SearchModal **❌ Otros componentes de búsqueda** - Si existen otros puntos de búsqueda, evaluar caso por caso - **Razón:** Mantener consistencia ### 4.3. Service Worker y Offline **❌ NO modificar `public/sw.js` o lógica de Service Worker** - El diccionario de siglas es código TypeScript compilado - Se incluirá automáticamente en el bundle - **Razón:** No requiere cambios en SW, ya funciona offline --- ## 5. Orden de Implementación Recomendado ### Fase 1: Preparación (Sin riesgo) 1. **Crear `src/data/acronyms.ts`** - Definir interfaz `AcronymMapping` - Crear array `acronymMappings` con siglas más comunes (10-15) - Implementar `expandAcronym()` y `getAcronymsByTerm()` - **Testing:** Unit tests para funciones de expansión 2. **Testing aislado** - Test: `expandAcronym("OVACE")` → `["obstrucción vía aérea", ...]` - Test: `expandAcronym("xyz")` → `[]` (sigla desconocida) - Test: Case-insensitive funciona ### Fase 2: Integración Mínima (Riesgo bajo) 3. **Modificar `SearchModal.tsx`** - Importar `expandAcronym` desde `acronyms.ts` - Crear función `expandAcronyms(query: string): string` - Aplicar expansión ANTES de llamar a `searchProcedures()` y `searchDrugs()` - **Cambio mínimo:** Solo añadir una línea de preprocesamiento 4. **Testing de integración** - Test manual: Buscar "OVACE" → debe encontrar protocolo OVACE - Test manual: Buscar "parada" → debe seguir funcionando (compatibilidad) - Test manual: Buscar "OVACE pediátrico" → debe encontrar resultados relevantes ### Fase 3: Validación y Expansión (Riesgo bajo) 5. **Validar con usuarios TES** - Probar búsquedas reales: "EAP", "SVB", "SVA", "IOT", etc. - Verificar que no se rompe búsqueda existente - Recopilar feedback sobre siglas faltantes 6. **Expandir diccionario** - Añadir más siglas basadas en feedback - Validar que no hay conflictos (misma sigla → diferentes significados) - Documentar decisiones (ej: "PCR" puede ser "Parada" o "Proteína C Reactiva") ### Fase 4: Optimización (Opcional, bajo riesgo) 7. **Mejoras opcionales** - Sistema de scoring para priorizar matches completos - Búsqueda fuzzy para typos (ej: "OVAC" → "OVACE") - Cache de expansiones para performance - **Nota:** Solo si hay necesidad real, no pre-optimizar --- ## 6. Riesgos Identificados y Mitigación ### 🔴 RIESGO ALTO: Falsos positivos **Problema:** Expandir "EAP" a "enfermedad arterial periférica" podría encontrar resultados no relevantes si el término "arterial" aparece en otros contextos. **Mitigación:** - Mantener término original en query expandida (prioridad al match exacto) - Considerar contexto opcional en `AcronymMapping` (`context: 'procedure' | 'drug'`) - Testing exhaustivo con casos reales ### 🟡 RIESGO MEDIO: Performance con muchas siglas **Problema:** Si el diccionario crece mucho (100+ siglas), la expansión podría ser lenta. **Mitigación:** - Diccionario pequeño inicial (20-30 siglas más comunes) - Expansión solo si el término coincide exactamente (no fuzzy matching inicial) - Si es necesario, cache de expansiones ### 🟡 RIESGO MEDIO: Ambiguidad de siglas **Problema:** "PCR" puede ser "Parada Cardiorrespiratoria" o "Proteína C Reactiva". **Mitigación:** - Incluir ambos significados en `expandedTerms` - La búsqueda encontrará ambos, usuario decide cuál es relevante - Documentar ambigüedades en comentarios del código ### 🟢 RIESGO BAJO: Cambios en comportamiento de búsqueda **Problema:** Usuarios acostumbrados a que "OVACE" no encuentra nada podrían confundirse si ahora sí encuentra. **Mitigación:** - Esto es una mejora, no un problema - Documentar en changelog - Si hay quejas, se puede desactivar fácilmente (feature flag) --- ## 7. Testing Strategy ### Tests Unitarios (Recomendado) ```typescript // tests/acronyms.test.ts describe('expandAcronym', () => { it('expande OVACE correctamente', () => { expect(expandAcronym('OVACE')).toContain('obstrucción vía aérea'); }); it('es case-insensitive', () => { expect(expandAcronym('ovace')).toEqual(expandAcronym('OVACE')); }); it('retorna array vacío para sigla desconocida', () => { expect(expandAcronym('XYZ')).toEqual([]); }); }); ``` ### Tests de Integración (Manual inicialmente) 1. **Búsqueda "OVACE"** → Debe encontrar protocolo OVACE 2. **Búsqueda "parada"** → Debe seguir funcionando (compatibilidad) 3. **Búsqueda "RCP adulto"** → Debe encontrar RCP adulto 4. **Búsqueda "EAP"** → Debe encontrar protocolos relacionados ### Tests de Regresión - Todas las búsquedas que funcionaban antes deben seguir funcionando - No debe haber cambios en resultados para queries sin siglas --- ## 8. Consideraciones de Mantenimiento ### 8.1. Añadir Nuevas Siglas **Proceso simple:** 1. Añadir entrada a `acronymMappings` en `acronyms.ts` 2. Testing manual 3. Deploy **No requiere:** - Cambios en funciones de búsqueda - Migración de datos - Cambios en UI ### 8.2. Documentación **Mantener documentado:** - Lista de siglas soportadas (en comentarios o README) - Decisiones sobre ambigüedades (ej: "PCR" tiene 2 significados) - Fuentes de las siglas (manual TES, protocolos oficiales) ### 8.3. Versionado **Si hay cambios breaking (poco probable):** - Versionar el diccionario - Mantener versiones anteriores para compatibilidad - Migración gradual si es necesario --- ## 9. Resumen Ejecutivo ### Cambios Propuestos 1. **Nuevo archivo:** `src/data/acronyms.ts` - Diccionario de siglas 2. **Modificación mínima:** `src/components/layout/SearchModal.tsx` - Preprocesamiento de query 3. **Sin cambios:** `src/data/procedures.ts`, `src/data/drugs.ts` - Funciones de búsqueda intactas ### Beneficios - ✅ Búsqueda por siglas funciona (OVACE, EAP, SVB, etc.) - ✅ Compatibilidad total con búsqueda existente - ✅ Offline-compatible (todo en código TypeScript) - ✅ Fácil de mantener (diccionario simple) - ✅ Fácil de revertir (cambio localizado) ### Riesgos - 🟡 Falsos positivos (mitigado con término original) - 🟡 Performance (mitigado con diccionario pequeño) - 🟢 Cambio de comportamiento (mejora, no problema) ### Esfuerzo Estimado - **Fase 1 (Preparación):** 2-3 horas - **Fase 2 (Integración):** 1-2 horas - **Fase 3 (Validación):** 2-4 horas (testing con usuarios) - **Total:** 5-9 horas ### Recomendación Final **✅ PROCEDER con la implementación** La estrategia es segura, localizada y reversible. El riesgo es bajo y el beneficio es alto para usuarios TES que usan siglas constantemente. --- *Documento de arquitectura - No incluye código de implementación, solo diseño y estrategia.*