# Datos: colecciones e ítems

Una **colección** es una tabla de datos estructurados del workspace; sus registros son **ítems**. Es el almacén que tu Met puede leer como fuente de datos y que tú operas por API para guardar, buscar y actualizar información. Trabaja server-side con tu key:

```ts
const met = new Met(key, { workspaceId });
```

## Colecciones

```ts
const col = await met.collections.create({ name: 'Preferencias' });

await met.collections.list();          // Collection[]
await met.collections.retrieve(col.id);
await met.collections.update(col.id, { name: 'Preferencias del cliente' });
await met.collections.delete(col.id);
```

`create` acepta `name` y un `folder_id?` opcional (scopes `collections:read`/`collections:write`). El `id` de una colección es **numérico**.

> **Ocultar una colección al Met sin borrarla:** ponla en `expose_to_agent: false` (default `true`). El Met deja de verla como fuente de datos, pero tú la sigues operando por API. Reexponla con `update(col.id, { expose_to_agent: true })`. Más contexto en [Mets y herramientas](mets-y-herramientas.html).

## Ítems

El primer argumento de casi todo es el `collectionId` **numérico**:

```ts
const item = await met.items.create(col.id, { nombre: 'Ana', canal: 'preferido' });

await met.items.list(col.id);                     // una página { data, has_more }
for await (const it of met.items.iterate(col.id)) { /* todas las páginas */ }

await met.items.retrieve(item.id);
await met.items.update(col.id, item.id, { canal: 'whatsapp' });
await met.items.patchField(col.id, item.id, 'canal', 'email'); // un solo campo
await met.items.setStatus(item.id, 'archivado');
await met.items.delete(item.id);
```

Los datos del ítem viven en `item.data`. Si un campo es una fórmula (`"=…"`), su resultado resuelto aparece en `item.computed[campo]` — `data` conserva la fórmula cruda; escribe siempre sobre `data`. Para un ítem sin colección (a nivel workspace) usa `met.items.createOrphan(body)`.

## Búsqueda

`met.items.search(query)` devuelve un `Item[]` con las coincidencias en todo el workspace:

```ts
const encontrados = await met.items.search('descuento anual');
```

Es **búsqueda léxica por texto**: coincide por las palabras que aparecen en los campos del ítem. **No es semántica ni vectorial** — no hay embeddings ni ranking por significado, así que busca por los términos literales que esperas encontrar. Para acotar por otro campo, **filtra el resultado en tu código**:

```ts
const soloDeAna = (await met.items.search('preferencia'))
  .filter((it) => it.data?.contact_id === 42);
```

## Ejemplo: memoria por cliente

Una colección de preferencias, un ítem por contacto, y recuperación por búsqueda + filtro:

```ts
const prefs = await met.collections.create({ name: 'preferencias' });

await met.items.create(prefs.id, {
  contact_id: 42,
  nota: 'Prefiere que le escriban por la mañana, tono cercano.',
});

// Más tarde: recuperar lo que sabemos de ese contacto
const memoria = (await met.items.search('prefiere'))
  .filter((it) => it.data?.contact_id === 42);
```

Como `search` es léxica, guarda en el texto del ítem las palabras por las que luego querrás encontrarlo.

## Carpetas

Cuando el workspace pasa de unas diez colecciones, el panel las agrupa en carpetas. Es organización de la vista, no del dato: mover una colección de carpeta no cambia sus ítems ni rompe ninguna referencia.

```ts
const carpetas = await met.folders.list();
const arbol = await met.folders.hierarchy();
```

`list()` devuelve las carpetas planas; `hierarchy()` las devuelve anidadas, que es lo que quieres para pintar un árbol sin reconstruir la relación padre-hijo a mano.

```ts
const f = await met.folders.create({ name: 'Operación' });
await met.folders.move(f.id, null);        // null = raíz
await met.folders.reorder([f.id, otra.id]);
```

`move` acepta `null` como padre para sacar una carpeta a la raíz. El orden es explícito y persistente: `reorder` recibe los ids en el orden que quieres, y `reorderCollectionsInFolder` hace lo mismo con las colecciones de una carpeta. No hay orden alfabético automático — si no reordenas, quedan como se crearon.

## Slugs: URLs legibles en vez de ids

Un ítem y una colección tienen id numérico, y además pueden tener un **slug**. Sirve para que tu integración no tenga que guardar ids nuestros:

```ts
await met.slugs.updateCollectionSlug(collection.id, 'facturas');
await met.slugs.updateItemSlug(item.id, 'inv-001');

const col = await met.slugs.resolveCollectionBySlug('facturas');
const inv = await met.slugs.resolveItemBySlug(col.id, 'inv-001');
```

El slug de un ítem es único **dentro de su colección**, no en todo el workspace: por eso `resolveItemBySlug` pide las dos cosas. El de una colección sí es único en el workspace.

Hay dos formas más de llegar a algo, y la diferencia importa:

```ts
// Por ruta legible: carpeta/colección
const r = await met.slugs.resolvePath('operacion/facturas');

// Por código corto del ítem (el que muestra el panel)
const x = await met.slugs.resolveByCode('AB/12');
```

`resolvePath` es para construir URLs que un humano lee y edita. `resolveByCode` es para el camino inverso: alguien te dicta el código que ve en pantalla y lo tienes que encontrar. El código lo genera Meteor y **no cambia**; el slug lo pones tú y puede cambiar, así que no lo uses como identificador estable en tu base — para eso está el id, o el código.
