|
|
@@ -0,0 +1,297 @@
|
|
|
|
|
|
|
|
# ADR 0006 — Motor de búsqueda y filtros facetados (Fase 5, Etapa 3)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
**Estado:** aceptada — Etapa 2 en implementación (aprobada 2026-08-26 tras diagnóstico de memoria del host)
|
|
|
|
|
|
|
|
**Fecha:** 2026-08-25
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## Contexto
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
[ADR 0004](0004-evaluacion-stack-multi-categoria.md) ya identificó un
|
|
|
|
|
|
|
|
motor de búsqueda dedicado (Meilisearch, autohospedado) como "el
|
|
|
|
|
|
|
|
siguiente paso natural" para ARI Shopping, pero lo dejó fuera del
|
|
|
|
|
|
|
|
alcance de Etapa 2 explícitamente hasta que el catálogo se acercara al
|
|
|
|
|
|
|
|
techo real de ~100 productos activos que impone
|
|
|
|
|
|
|
|
`fetchProducts(filters, limit = 100)` en `medusa.ts`.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
**Ese techo todavía no llegó** — el catálogo real hoy son 26 productos
|
|
|
|
|
|
|
|
(17 legacy + 9 demo, ver [ADR 0005](0005-rediseno-storefront.md)
|
|
|
|
|
|
|
|
sección 5), lejos de 100. El disparador de esta propuesta no es el
|
|
|
|
|
|
|
|
techo de volumen sino la **complejidad de facetas**: la Etapa 2 ya
|
|
|
|
|
|
|
|
construyó una taxonomía real de 6 categorías raíz / 21 nodos
|
|
|
|
|
|
|
|
(navegación, mega-menú, breadcrumb — PRs #36-42) y un filtrado con
|
|
|
|
|
|
|
|
conteo por valor (`categoryCounts`/`brandCounts`/`ageCounts` en
|
|
|
|
|
|
|
|
`catalog-client.tsx`, PR #40) que hoy se calcula **100% client-side**
|
|
|
|
|
|
|
|
sobre los productos ya traídos — el mismo patrón de "deuda de
|
|
|
|
|
|
|
|
alcance" que el hallazgo #4 de ADR 0004 ya señaló, ahora con más
|
|
|
|
|
|
|
|
dimensiones de filtro que multiplicar. Sigue siendo tratable a 26
|
|
|
|
|
|
|
|
productos; deja de serlo mucho antes de llegar a 100 si el catálogo
|
|
|
|
|
|
|
|
crece a la vez en número de productos y en dimensiones filtrables.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Restricción no negociable, igual que en toda la Fase 5: el flujo
|
|
|
|
|
|
|
|
**Admin → evento `product.updated` → `revalidate-storefront.ts` →
|
|
|
|
|
|
|
|
`revalidateTag("medusa-products")`**, que hoy mantiene el storefront
|
|
|
|
|
|
|
|
sincronizado sin intervención manual, no se toca. Cualquier índice de
|
|
|
|
|
|
|
|
búsqueda debe sincronizarse por el mismo mecanismo de eventos, nunca
|
|
|
|
|
|
|
|
por un cron ni un paso manual.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## 1. Estado real de la integración Medusa v2 + Meilisearch
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Verificado contra la documentación oficial vigente el 2026-08-25, no
|
|
|
|
|
|
|
|
contra memoria de entrenamiento:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
- **Medusa no tiene un módulo de búsqueda propio integrado con
|
|
|
|
|
|
|
|
Meilisearch hoy.** La guía oficial
|
|
|
|
|
|
|
|
(`docs.medusajs.com/resources/integrations/guides/meilisearch`) es
|
|
|
|
|
|
|
|
un tutorial de módulo custom desde cero (`npm install meilisearch` +
|
|
|
|
|
|
|
|
workflows/subscribers propios) — no un paquete instalable.
|
|
|
|
|
|
|
|
- **Medusa sí introdujo un "Search Module" propio en la versión
|
|
|
|
|
|
|
|
2.19.0.** El paquete comunitario `@rokmohar/medusa-plugin-meilisearch`
|
|
|
|
|
|
|
|
(el más usado y mantenido para Meilisearch+Medusa) se conecta a ese
|
|
|
|
|
|
|
|
Search Module desde su versión `^2.0.0`, pero esa versión del plugin
|
|
|
|
|
|
|
|
**requiere Medusa `^2.19.0`**.
|
|
|
|
|
|
|
|
- **Nuestro backend corre Medusa `2.18.0`** (`commerce-backend/package.json`)
|
|
|
|
|
|
|
|
— justo por debajo de ese corte. La tabla de compatibilidad del
|
|
|
|
|
|
|
|
plugin (`github.com/rokmohar/medusa-plugin-meilisearch`) resuelve
|
|
|
|
|
|
|
|
esto: la línea `^1.4.1` soporta `Medusa >= 2.15 < 2.19`, servidor
|
|
|
|
|
|
|
|
Meilisearch `>= 1.5`, Node `>= 22`. El propio repo lo explica: *"The
|
|
|
|
|
|
|
|
2.19 release removed the search interface the v1 line was written
|
|
|
|
|
|
|
|
against, so the two lines do not overlap: v1 stops at Medusa 2.18,
|
|
|
|
|
|
|
|
v2 starts at 2.19."*
|
|
|
|
|
|
|
|
- Node de `commerce-backend` ya es `22.18.0` (Dockerfile) — cumple el
|
|
|
|
|
|
|
|
mínimo del plugin sin cambios.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
**Implicación concreta:** para adoptar Meilisearch sin subir de
|
|
|
|
|
|
|
|
versión Medusa (fuera de alcance de este ADR — sería su propia
|
|
|
|
|
|
|
|
migración), la pieza a instalar es `@rokmohar/medusa-plugin-meilisearch@^1.4.1`,
|
|
|
|
|
|
|
|
no la última versión del plugin. Esto es deuda técnica documentada,
|
|
|
|
|
|
|
|
no un obstáculo: si en el futuro se sube Medusa a `2.19+`, migrar a la
|
|
|
|
|
|
|
|
línea `^2.0.0` del plugin (y al Search Module nativo) es el camino
|
|
|
|
|
|
|
|
recto.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## 2. Impacto de recursos
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Estado real del host ZimaOS al momento de esta auditoría (`free -h`,
|
|
|
|
|
|
|
|
`docker stats`): 15 GiB RAM totales, **swap al 100% (5.2/5.2 GiB
|
|
|
|
|
|
|
|
usados)**, solo 351 MiB realmente libres (6.5 GiB "available" vía
|
|
|
|
|
|
|
|
cache reclamable), 4 vCPU — la misma clase de contención que ya forzó
|
|
|
|
|
|
|
|
el fix de `cpu_shares` de Gitea. Cualquier servicio nuevo en este host
|
|
|
|
|
|
|
|
es una decisión de capacidad real, no teórica.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
- **Meilisearch no publica un mínimo de RAM oficial.** Su propio FAQ
|
|
|
|
|
|
|
|
(`meilisearch.com/docs/resources/help/faq`) solo dice que "más RAM
|
|
|
|
|
|
|
|
implica búsquedas más rápidas" y recomienda ~10x el tamaño del
|
|
|
|
|
|
|
|
dataset crudo en disco — sin piso duro. La cifra de "~1 GiB
|
|
|
|
|
|
|
|
cómodo" que circula en blogs de la comunidad es una estimación, no
|
|
|
|
|
|
|
|
una garantía del proveedor — se cita aquí como referencia, no como
|
|
|
|
|
|
|
|
hecho verificado.
|
|
|
|
|
|
|
|
- Meilisearch usa almacenamiento memory-mapped: el índice puede vivir
|
|
|
|
|
|
|
|
parcialmente en disco cuando no entra en RAM, degradando latencia en
|
|
|
|
|
|
|
|
vez de fallar. A la escala actual (26 productos, dataset de KB) el
|
|
|
|
|
|
|
|
uso real de RAM del índice en sí es insignificante — el costo es el
|
|
|
|
|
|
|
|
proceso base de Meilisearch, no el dataset.
|
|
|
|
|
|
|
|
- **Comparación con Typesense** (pedida explícitamente, mantenida
|
|
|
|
|
|
|
|
breve porque la diferencia relevante para este caso es clara y no
|
|
|
|
|
|
|
|
requiere más profundidad): Typesense mantiene el índice completo en
|
|
|
|
|
|
|
|
RAM (~30% más RAM que Meilisearch para el mismo dataset, según
|
|
|
|
|
|
|
|
benchmarks públicos) y, a diferencia de Meilisearch, **no tiene
|
|
|
|
|
|
|
|
ningún plugin ni integración con Medusa** — habría que escribir la
|
|
|
|
|
|
|
|
sincronización completa desde cero igual que con la guía "custom"
|
|
|
|
|
|
|
|
de Meilisearch, pero sin ninguna base comunitaria mantenida
|
|
|
|
|
|
|
|
compatible con nuestra versión de Medusa. Con Meilisearch ya
|
|
|
|
|
|
|
|
teniendo un plugin activo para `Medusa 2.18` y menor huella de RAM,
|
|
|
|
|
|
|
|
no hay motivo para evaluar Typesense más a fondo en este caso.
|
|
|
|
|
|
|
|
- **Recomendación:** desplegar Meilisearch en k3d con `requests`/`limits`
|
|
|
|
|
|
|
|
explícitos y conservadores desde el día uno (ej. `request: 256Mi/0.1
|
|
|
|
|
|
|
|
CPU`, `limit: 768Mi/0.5 CPU` — a ajustar con datos reales de uso, no
|
|
|
|
|
|
|
|
supuestos), y tratarlo como el mismo tipo de riesgo operativo que ya
|
|
|
|
|
|
|
|
se vivió con Gitea: monitorear consumo real post-despliegue antes de
|
|
|
|
|
|
|
|
darlo por estable. Dado el swap ya saturado, este es el punto de
|
|
|
|
|
|
|
|
mayor incertidumbre de toda la propuesta — más que la elección de
|
|
|
|
|
|
|
|
motor de búsqueda en sí.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## 3. Modelo de indexación
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Basado en la taxonomía real ya viva en Medusa (Etapa 2) y en
|
|
|
|
|
|
|
|
`Product`/`Category` de `lib/headless/types.ts`:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| Campo | Buscable (texto, tolerante a typos) | Filtrable / faceta | Ordenable |
|
|
|
|
|
|
|
|
|---|---|---|---|
|
|
|
|
|
|
|
|
| `name` | Sí | No | No |
|
|
|
|
|
|
|
|
| `shortDescription` / `description` | Sí | No | No |
|
|
|
|
|
|
|
|
| `category` (nombre de la hoja real) | Sí | Sí (con conteo) | No |
|
|
|
|
|
|
|
|
| `collection` | Sí | Sí (con conteo) | No |
|
|
|
|
|
|
|
|
| `brand` | Sí | Sí (con conteo) | No |
|
|
|
|
|
|
|
|
| `ageRange` | No | Sí (con conteo) | No |
|
|
|
|
|
|
|
|
| `tags` | Sí | No | No |
|
|
|
|
|
|
|
|
| `price` | No | Sí (rango) | Sí |
|
|
|
|
|
|
|
|
| `compareAtPrice` | No | No | No (solo se usa para el badge, no como filtro) |
|
|
|
|
|
|
|
|
| `isNew` / `featured` | No | No | Sí (orden "Novedades"/"Destacados", igual que hoy) |
|
|
|
|
|
|
|
|
| `available` | No | Sí (booleano) | No |
|
|
|
|
|
|
|
|
| `slug`, `thumbnail`, `id` | No | No | No (solo se devuelven, no se indexan como facetas) |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
La jerarquía de categorías (raíz/hija) no se indexa como un campo
|
|
|
|
|
|
|
|
aparte: el índice guarda el nombre de la hoja real tal como hoy lo
|
|
|
|
|
|
|
|
hace `product.category`, y el breadcrumb/mega-menú siguen resolviendo
|
|
|
|
|
|
|
|
la jerarquía contra `/store/product-categories` (sin cambios, ya
|
|
|
|
|
|
|
|
funciona desde la sub-fase 2). El motor de búsqueda resuelve
|
|
|
|
|
|
|
|
**búsqueda de texto + conteo de facetas**, no reemplaza la fuente de
|
|
|
|
|
|
|
|
la jerarquía.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## 4. Diseño de sincronización
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Mismo patrón que `revalidate-storefront.ts`, no uno nuevo: un
|
|
|
|
|
|
|
|
subscriber adicional en `commerce-backend/src/subscribers/` (ej.
|
|
|
|
|
|
|
|
`sync-search-index.ts`) que reacciona a eventos y le pide al plugin
|
|
|
|
|
|
|
|
que reindexe el documento afectado — nunca Medusa escribiendo
|
|
|
|
|
|
|
|
directamente en Meilisearch fuera de este canal.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Eventos a escuchar, confirmados en la documentación oficial de
|
|
|
|
|
|
|
|
eventos de Medusa (`docs.medusajs.com/resources/events-reference`):
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
- `product.created` / `product.updated` / `product.deleted`
|
|
|
|
|
|
|
|
- `product-variant.created` / `product-variant.updated` / `product-variant.deleted`
|
|
|
|
|
|
|
|
(mismo set que ya usa `revalidate-storefront.ts`, hoy validado en
|
|
|
|
|
|
|
|
producción)
|
|
|
|
|
|
|
|
- **Nuevo respecto al subscriber actual:** `product-category.created`
|
|
|
|
|
|
|
|
/ `product-category.updated` / `product-category.deleted` — un
|
|
|
|
|
|
|
|
cambio de nombre o jerarquía de categoría debe refrescar las
|
|
|
|
|
|
|
|
facetas del índice, y hoy `revalidate-storefront.ts` no escucha
|
|
|
|
|
|
|
|
estos eventos porque el storefront los resuelve en cada request
|
|
|
|
|
|
|
|
contra `/store/product-categories` directamente, sin necesidad de
|
|
|
|
|
|
|
|
revalidación. El índice de búsqueda sí necesita enterarse.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
**Punto a verificar en vivo en Etapa 2, no asumido aquí:** existe al
|
|
|
|
|
|
|
|
menos un reporte público de un evento `product-collection.updated`
|
|
|
|
|
|
|
|
que no se disparaba de forma confiable en ciertas versiones de Medusa
|
|
|
|
|
|
|
|
(`github.com/medusajs/medusa/issues/11197`). Antes de dar por cerrado
|
|
|
|
|
|
|
|
el diseño de sincronización hay que repetir exactamente la validación
|
|
|
|
|
|
|
|
que ya se hizo para `revalidate-storefront.ts` en la auditoría de
|
|
|
|
|
|
|
|
Etapa 1 de Fase 5: crear/editar una categoría real vía Admin y
|
|
|
|
|
|
|
|
confirmar en vivo que el evento llega al subscriber, no confiar en que
|
|
|
|
|
|
|
|
la documentación coincide con el comportamiento real de `2.18.0`
|
|
|
|
|
|
|
|
(el mismo tipo de brecha que ya se encontró una vez con la relación
|
|
|
|
|
|
|
|
`+categories` de `/store/products`).
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## 5. Diseño de UI — filtros facetados y búsqueda
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
**Filtros facetados (sidebar de `/catalog`):** mismo layout que ya
|
|
|
|
|
|
|
|
construyó la sub-fase 3 (`catalog-client.tsx`, PR #40) — sidebar con
|
|
|
|
|
|
|
|
"Categoría"/"Marca"/"Edad" como pills con conteo entre paréntesis y
|
|
|
|
|
|
|
|
slider de precio máximo. Lo único que cambia es **de dónde sale el
|
|
|
|
|
|
|
|
conteo**: hoy `categoryCounts`/`brandCounts`/`ageCounts` se calculan
|
|
|
|
|
|
|
|
recorriendo el arreglo de productos ya traído en el cliente; con el
|
|
|
|
|
|
|
|
motor de búsqueda, esos conteos vienen del propio Meilisearch
|
|
|
|
|
|
|
|
(`facetDistribution`) en la misma respuesta que trae los productos
|
|
|
|
|
|
|
|
filtrados, sin recorrer nada en el navegador. El breadcrumb no cambia
|
|
|
|
|
|
|
|
— sigue resolviéndose contra la jerarquía real de categorías, no
|
|
|
|
|
|
|
|
contra el índice de búsqueda.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
Inicio > Mochilas y morrales > Marvel breadcrumb real (sin cambios)
|
|
|
|
|
|
|
|
┌────────────┬──────────────────────────────────────┐
|
|
|
|
|
|
|
|
│ Categoría │ [buscador con autocompletado] │ ← nuevo
|
|
|
|
|
|
|
|
│ ☐ Marvel(6)│ [card][card][card] │
|
|
|
|
|
|
|
|
│ ☐ Frozen(4)│ [card][card][card] │
|
|
|
|
|
|
|
|
│ Marca │ │
|
|
|
|
|
|
|
|
│ ☐ Disney(2)│ conteos ahora vienen de Meilisearch, │
|
|
|
|
|
|
|
|
│ Precio │ no de recorrer el arreglo en cliente │
|
|
|
|
|
|
|
|
│ [====○===] │ │
|
|
|
|
|
|
|
|
└────────────┴──────────────────────────────────────┘
|
|
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
**Búsqueda con autocompletado (barra del navbar):** hoy
|
|
|
|
|
|
|
|
`PredictiveSearch` (`components/search/predictive-search.tsx`) filtra
|
|
|
|
|
|
|
|
por substring sobre los productos ya cargados en memoria del cliente —
|
|
|
|
|
|
|
|
funciona a 26 productos, no escala y no tolera errores de tipeo (ADR
|
|
|
|
|
|
|
|
0004, hallazgo #5). Se reemplaza la fuente de datos del mismo
|
|
|
|
|
|
|
|
componente visual por `searchProducts(query)` del `CatalogProvider`
|
|
|
|
|
|
|
|
(interfaz que **ya existe** en `lib/headless/types.ts`, sin necesidad
|
|
|
|
|
|
|
|
de un tipo nuevo) apuntando a Meilisearch en vez de al `?q=` de
|
|
|
|
|
|
|
|
Medusa: tolerancia a typos y ranking por relevancia nativos del motor,
|
|
|
|
|
|
|
|
mismo contrato de datos que ya consume el dropdown de resultados —
|
|
|
|
|
|
|
|
cero cambio de componente React, solo de qué hay detrás de
|
|
|
|
|
|
|
|
`CatalogProvider.searchProducts`.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## Opciones consideradas
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| Opción | A favor | En contra |
|
|
|
|
|
|
|
|
|---|---|---|
|
|
|
|
|
|
|
|
| **Meilisearch autohospedado** (`@rokmohar/medusa-plugin-meilisearch@^1.4.1`, sincronizado por eventos) | Plugin comunitario activo y compatible con nuestra versión exacta de Medusa (2.18); menor huella de RAM que la alternativa evaluada; almacenamiento memory-mapped se degrada en vez de fallar bajo presión de memoria; mismo patrón de subscriber que `revalidate-storefront.ts`, cero riesgo para ese flujo | Ningún paquete "oficial" de Medusa (el plugin es comunitario, no del core); atado a la línea `v1` del plugin mientras no se suba Medusa a 2.19+; un servicio más para operar en un host ya con swap saturado |
|
|
|
|
|
|
|
|
| **Typesense autohospedado** | Autocompletado ~2x más rápido según benchmarks públicos | Cero integración con Medusa (habría que escribir la sincronización completa desde cero, igual esfuerzo que la guía "custom" de Meilisearch pero sin base comunitaria); ~30% más RAM que Meilisearch para el mismo dataset, el peor trade-off posible dado el estado actual del host |
|
|
|
|
|
|
|
|
| **Módulo custom desde cero** (seguir la guía oficial de Medusa, sin el plugin de rokmohar) | Sin dependencia de un paquete de terceros; control total | Reimplementa lo que el plugin comunitario ya resuelve (declaración de índices, reindexado atómico, tracking de escrituras); más superficie propia para mantener sin ganancia clara sobre usar el plugin ya compatible con 2.18 |
|
|
|
|
|
|
|
|
| **No adoptar motor de búsqueda todavía** (seguir con filtrado 100% client-side) | Cero riesgo, cero componente nuevo | No resuelve la deuda de facetas ya identificada en ADR 0004 y agravada por la sub-fase 3; el techo de complejidad de filtros llega antes que el de 100 productos si el catálogo sigue creciendo en categorías |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## Decisión
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
**Meilisearch autohospedado**, vía `@rokmohar/medusa-plugin-meilisearch@^1.4.1`
|
|
|
|
|
|
|
|
(no la última versión del plugin — esa exige Medusa 2.19+, fuera de
|
|
|
|
|
|
|
|
alcance), sincronizado por el mismo patrón de subscriber por eventos
|
|
|
|
|
|
|
|
que ya usa `revalidate-storefront.ts`, con `product-category.*` sumado
|
|
|
|
|
|
|
|
al set de eventos existente. Se despliega en k3d con límites de
|
|
|
|
|
|
|
|
recursos conservadores desde el primer día y se trata como
|
|
|
|
|
|
|
|
experimento a monitorear, no como capacidad garantizada, dado el
|
|
|
|
|
|
|
|
estado actual de memoria del host.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
**No se sube la versión de Medusa como parte de esta decisión** —
|
|
|
|
|
|
|
|
queda como deuda técnica documentada para cuando se justifique
|
|
|
|
|
|
|
|
migrar a la línea `v2` del plugin y al Search Module nativo de
|
|
|
|
|
|
|
|
Medusa.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## Consecuencias
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
- Se gana: conteo de facetas real (no client-side), búsqueda tolerante
|
|
|
|
|
|
|
|
a errores de tipeo con ranking por relevancia, y una arquitectura
|
|
|
|
|
|
|
|
que ya no depende del techo de 100 productos de `fetchProducts`.
|
|
|
|
|
|
|
|
- Se acepta como deuda técnica: quedar atado a la línea `v1` del
|
|
|
|
|
|
|
|
plugin de Meilisearch mientras Medusa siga en `2.18.x`; migrar a
|
|
|
|
|
|
|
|
Medusa `2.19+` es un proyecto aparte, no parte de esta Etapa 3.
|
|
|
|
|
|
|
|
- Riesgo operativo real y no resuelto todavía por este ADR: el host ya
|
|
|
|
|
|
|
|
corre con el swap saturado — el despliegue de Etapa 2 debe empezar
|
|
|
|
|
|
|
|
con límites de recursos conservadores y validación en vivo de
|
|
|
|
|
|
|
|
consumo real antes de confiar en el servicio, siguiendo la misma
|
|
|
|
|
|
|
|
lección que dejó el incidente de recursos de Gitea.
|
|
|
|
|
|
|
|
- Revertir esta decisión implica desinstalar el plugin y el
|
|
|
|
|
|
|
|
subscriber nuevo, y volver el buscador/filtrado a 100% client-side
|
|
|
|
|
|
|
|
— no hay migración de datos que deshacer, porque Meilisearch nunca
|
|
|
|
|
|
|
|
es la fuente de verdad, solo una copia de solo lectura indexada
|
|
|
|
|
|
|
|
desde Medusa.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
## Nota de implementación (2026-08-26, antes de escribir código de Etapa 2)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Antes de tocar código se verificó el código fuente real de
|
|
|
|
|
|
|
|
`@rokmohar/medusa-plugin-meilisearch` en el tag exacto `v1.4.1`
|
|
|
|
|
|
|
|
(`github.com/rokmohar/medusa-plugin-meilisearch`), no su README
|
|
|
|
|
|
|
|
genérico ni memoria de entrenamiento — misma disciplina que ya destapó
|
|
|
|
|
|
|
|
el bug de `+categories` en `/store/products`. Aparecieron tres
|
|
|
|
|
|
|
|
desviaciones reales respecto al diseño de §4/§5 de este ADR:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1. **No se escribe un subscriber nuevo.** El plugin trae sus propios
|
|
|
|
|
|
|
|
subscribers (`meilisearch-product-upsert.ts`,
|
|
|
|
|
|
|
|
`meilisearch-category-upsert.ts`, variantes/colección/inventario),
|
|
|
|
|
|
|
|
auto-registrados al instalar el paquete — el propio README dice
|
|
|
|
|
|
|
|
*"You don't need custom subscribers anymore, you can remove them."*
|
|
|
|
|
|
|
|
Escribir `sync-search-index.ts` como describe §4 causaría doble
|
|
|
|
|
|
|
|
indexado. La escucha de `product-category.*` que pedía §4 ya viene
|
|
|
|
|
|
|
|
incluida en los subscribers del plugin.
|
|
|
|
|
|
|
|
2. **Las rutas Store API del plugin no devuelven `facetDistribution`**,
|
|
|
|
|
|
|
|
pese a que §5 asume que sí. El servicio interno
|
|
|
|
|
|
|
|
(`MeiliSearchService.search()`) sí puede pedirlo vía
|
|
|
|
|
|
|
|
`additionalOptions: { facets: [...] }`, pero las rutas
|
|
|
|
|
|
|
|
empaquetadas nunca lo piden ni lo reenvían. Se agrega una ruta
|
|
|
|
|
|
|
|
Store API propia (`src/api/store/search/route.ts`) que resuelve el
|
|
|
|
|
|
|
|
servicio del plugin desde el container y arma
|
|
|
|
|
|
|
|
`{ products, facetDistribution }`. Consecuencia positiva no
|
|
|
|
|
|
|
|
buscada: Meilisearch queda 100% `ClusterIP`-only, sin Ingress, sin
|
|
|
|
|
|
|
|
key pública ni ruta de red navegador→Meilisearch — todo pasa por
|
|
|
|
|
|
|
|
Medusa.
|
|
|
|
|
|
|
|
3. **El plugin se conecta vía un array `plugins:` nuevo en
|
|
|
|
|
|
|
|
`medusa-config.ts`**, no otra entrada en `modules:` (el único
|
|
|
|
|
|
|
|
precedente en este repo, el file-provider S3, usa `modules:`), y
|
|
|
|
|
|
|
|
usa nombres de env var propios (`MEILISEARCH_HOST`/
|
|
|
|
|
|
|
|
`MEILISEARCH_API_KEY`, leídos por el plugin) distintos del env var
|
|
|
|
|
|
|
|
del servidor Meilisearch en sí (`MEILI_MASTER_KEY`, leído por el
|
|
|
|
|
|
|
|
contenedor `getmeili/meilisearch`) — mismo valor, dos nombres de
|
|
|
|
|
|
|
|
secret.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Ninguna de las tres cambia la decisión ni sus consecuencias — cambian
|
|
|
|
|
|
|
|
el cómo, no el qué. Plan de implementación completo, fase por fase,
|
|
|
|
|
|
|
|
en la sesión que aprobó esta Etapa 2 (2026-08-26).
|