# SLM NeoDB -- Quickstart: Ejemplo Bancario Completo

Esta guia crea un sistema bancario minimo con usuarios y transacciones. Al final tendras documentos, busquedas, aggregations, una API key con permisos limitados y field masking funcionando.

**Tiempo estimado:** 15 minutos.

---

## 1. Compilar e iniciar el servidor

```bash
# Compilar en modo release
cargo build --release -p neodb-bin

# Crear directorio de datos
mkdir -p ./bank-data

# Iniciar el servidor
./target/release/neodb --data-dir ./bank-data --bind 127.0.0.1:7700
```

En el primer arranque veras algo asi en la terminal:

```
SLM NeoDB SLMTR1 starting...
storage opened data_dir=./bank-data
security initialized
SLM NeoDB listening bind=127.0.0.1:7700
Engine: SLMTR1 | Status: READY
```

**Importante:** En el primer arranque, el motor genera una master key y la imprime en stdout. Copia el `key_secret` -- lo necesitas para todas las llamadas.

Para los ejemplos de esta guia, usaremos esta variable de entorno:

```bash
export NEODB_KEY="master:TU_MASTER_SECRET_AQUI"
```

---

## 2. Verificar que el servidor esta corriendo

```bash
curl -s http://localhost:7700/_health | python3 -m json.tool
```

```json
{
    "status": "OK",
    "engine": "SLMTR1",
    "version": "1.0.1",
    "uptime_seconds": 86400,
    "timestamp": 1774226788183
}
```

El endpoint `/_health` no requiere autenticacion. `uptime_seconds` indica cuantos segundos lleva arriba el proceso (el mismo valor esta en `/_metrics` como `neodb_uptime_seconds`).

---

## 3. Crear el schema de usuarios

```bash
curl -s -X POST http://localhost:7700/bank/user/_schema \
  -H "SLM-KEY: $NEODB_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "strict_mode": true,
    "fields": {
      "nombre": "text",
      "email": "keyword",
      "telefono": "keyword",
      "pin": "blind",
      "saldo": "number[2]",
      "fecha_apertura": "datetime",
      "activo": "boolean",
      "sucursal": "keyword",
      "ubicacion": "geo"
    }
  }' | python3 -m json.tool
```

Notas sobre los tipos usados:

- `"text"` -- `nombre` es full-text searchable (se puede buscar por partes del nombre).
- `"keyword"` -- `email` y `telefono` son match exacto.
- `"blind"` -- `pin` se almacena como hash BLAKE3. El valor original **nunca** toca el disco. Solo se puede buscar por igualdad exacta.
- `"number[2]"` -- `saldo` tiene precision exacta de 2 decimales. El motor **nunca** redondea. Los campos `number` soportan aggregations automaticamente.
- `"datetime"` -- `fecha_apertura` con aggregations temporales habilitadas automaticamente.
- `"geo"` -- `ubicacion` para queries geoespaciales.

---

## 4. Crear el schema de transacciones

```bash
curl -s -X POST http://localhost:7700/bank/transaction/_schema \
  -H "SLM-KEY: $NEODB_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "strict_mode": true,
    "fields": {
      "wallet_id": "keyword",
      "tipo": "keyword",
      "monto": "number[2]",
      "concepto": "text",
      "fecha": "datetime",
      "status": "keyword"
    }
  }' | python3 -m json.tool
```

---

## 5. Crear usuarios

### Usuario 1: Gonzalo

```bash
curl -s -X POST http://localhost:7700/bank/user \
  -H "SLM-KEY: $NEODB_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "_id": "user-gonzalo",
    "nombre": "Gonzalo Araujo",
    "email": "gonzalo@slm.cloud",
    "telefono": "+52-55-1234-5678",
    "pin": "7741",
    "saldo": 50000.00,
    "fecha_apertura": "2025-01-15T10:00:00Z",
    "activo": true,
    "sucursal": "CDMX-REFORMA",
    "ubicacion": { "lat": 19.4326, "lon": -99.1332 }
  }' | python3 -m json.tool
```

### Usuario 2: Maria

```bash
curl -s -X POST http://localhost:7700/bank/user \
  -H "SLM-KEY: $NEODB_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "_id": "user-maria",
    "nombre": "Maria Lopez Garcia",
    "email": "maria@ejemplo.com",
    "telefono": "+52-55-9876-5432",
    "pin": "3389",
    "saldo": 125000.50,
    "fecha_apertura": "2024-06-01T14:30:00Z",
    "activo": true,
    "sucursal": "CDMX-POLANCO",
    "ubicacion": { "lat": 19.4362, "lon": -99.1928 }
  }' | python3 -m json.tool
```

### Usuario 3: Juan

```bash
curl -s -X POST http://localhost:7700/bank/user \
  -H "SLM-KEY: $NEODB_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "_id": "user-juan",
    "nombre": "Juan Perez Martinez",
    "email": "juan@ejemplo.com",
    "telefono": "+52-33-5555-0001",
    "pin": "1122",
    "saldo": 8500.75,
    "fecha_apertura": "2026-02-20T09:00:00Z",
    "activo": true,
    "sucursal": "GDL-CENTRO",
    "ubicacion": { "lat": 20.6597, "lon": -103.3496 }
  }' | python3 -m json.tool
```

---

## 6. Leer un usuario

```bash
curl -s http://localhost:7700/bank/user/user-gonzalo \
  -H "SLM-KEY: $NEODB_KEY" | python3 -m json.tool
```

Observa que el campo `pin` contiene un hash BLAKE3 de 64 caracteres, no el valor original `"7741"`.

---

## 7. Crear transacciones con Bulk

```bash
curl -s -X POST http://localhost:7700/bank/transaction/_bulk \
  -H "SLM-KEY: $NEODB_KEY" \
  -H "Content-Type: application/json" \
  -d '{"wallet_id": "user-gonzalo", "tipo": "deposito", "monto": 10000.00, "concepto": "Deposito nomina marzo", "fecha": "2026-03-01T09:00:00Z", "status": "completado"}
{"wallet_id": "user-gonzalo", "tipo": "retiro", "monto": 2500.00, "concepto": "Retiro cajero automatico", "fecha": "2026-03-05T14:30:00Z", "status": "completado"}
{"wallet_id": "user-gonzalo", "tipo": "transferencia", "monto": 5000.00, "concepto": "Pago renta departamento", "fecha": "2026-03-10T08:00:00Z", "status": "completado"}
{"wallet_id": "user-maria", "tipo": "deposito", "monto": 25000.00, "concepto": "Deposito nomina marzo", "fecha": "2026-03-01T09:00:00Z", "status": "completado"}
{"wallet_id": "user-maria", "tipo": "transferencia", "monto": 3500.50, "concepto": "Pago servicio internet", "fecha": "2026-03-08T11:00:00Z", "status": "completado"}
{"wallet_id": "user-maria", "tipo": "transferencia", "monto": 15000.00, "concepto": "Transferencia a cuenta de ahorro", "fecha": "2026-03-15T16:00:00Z", "status": "completado"}
{"wallet_id": "user-juan", "tipo": "deposito", "monto": 3000.00, "concepto": "Deposito efectivo sucursal", "fecha": "2026-03-02T10:30:00Z", "status": "completado"}
{"wallet_id": "user-juan", "tipo": "retiro", "monto": 500.00, "concepto": "Retiro cajero", "fecha": "2026-03-12T19:00:00Z", "status": "completado"}' | python3 -m json.tool
```

Respuesta esperada:
```json
{
    "total": 8,
    "created": 8,
    "updated": 0,
    "errors": 0,
    "items": [...]
}
```

> **Seed/import idempotente:** agrega `?mode=upsert` (`POST /bank/transaction/_bulk?mode=upsert`).
> Los `_id` que ya existen se **reemplazan** (`updated`) y los nuevos se crean
> (`created`), en una sola llamada — puedes re-correr el seed las veces que quieras
> sin "borrar antes" ni manejar `DOCUMENT_EXISTS`. El default `mode=create` falla
> si el `_id` ya existe.

---

## 8. Buscar transacciones de un usuario

```bash
curl -s -X POST http://localhost:7700/bank/transaction/_search \
  -H "SLM-KEY: $NEODB_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "query": { "term": { "wallet_id": "user-gonzalo" } },
    "sort": [{ "fecha": "desc" }],
    "size": 50
  }' | python3 -m json.tool
```

---

## 9. Buscar transacciones por concepto (full-text)

```bash
curl -s -X POST http://localhost:7700/bank/transaction/_search \
  -H "SLM-KEY: $NEODB_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "query": { "match": { "concepto": "nomina" } }
  }' | python3 -m json.tool
```

Esto retorna todas las transacciones cuyo concepto contiene la palabra "nomina", independientemente de mayusculas.

---

## 10. Busqueda combinada con bool

Transacciones de Gonzalo mayores a 3000:

```bash
curl -s -X POST http://localhost:7700/bank/transaction/_search \
  -H "SLM-KEY: $NEODB_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "query": {
      "bool": {
        "must": [
          { "term": { "wallet_id": "user-gonzalo" } },
          { "range": { "monto": { "gte": 3000.00 } } }
        ]
      }
    }
  }' | python3 -m json.tool
```

---

## 11. Busqueda por rango de fechas

Transacciones de la primera semana de marzo:

```bash
curl -s -X POST http://localhost:7700/bank/transaction/_search \
  -H "SLM-KEY: $NEODB_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "query": {
      "range": {
        "fecha": {
          "gte": "2026-03-01T00:00:00Z",
          "lte": "2026-03-07T23:59:59Z"
        }
      }
    },
    "sort": [{ "fecha": "asc" }]
  }' | python3 -m json.tool
```

---

## 12. Aggregations: totales de transacciones

```bash
curl -s -X POST http://localhost:7700/bank/transaction/_search \
  -H "SLM-KEY: $NEODB_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "query": {},
    "aggs": {
      "monto_total": { "sum": { "field": "monto" } },
      "monto_promedio": { "avg": { "field": "monto" } },
      "monto_maximo": { "max": { "field": "monto" } },
      "monto_minimo": { "min": { "field": "monto" } },
      "total_transacciones": { "count": { "field": "_id" } }
    },
    "size": 0
  }' | python3 -m json.tool
```

---

## 13. Aggregation por tipo de transaccion

```bash
curl -s -X POST http://localhost:7700/bank/transaction/_search \
  -H "SLM-KEY: $NEODB_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "query": {},
    "aggs": {
      "por_tipo": { "terms": { "field": "tipo", "size": 10 } }
    },
    "size": 0
  }' | python3 -m json.tool
```

Retorna buckets con el conteo por tipo (deposito, retiro, transferencia).

---

## 14. Histograma por dia

```bash
curl -s -X POST http://localhost:7700/bank/transaction/_search \
  -H "SLM-KEY: $NEODB_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "query": {},
    "aggs": {
      "por_dia": {
        "date_histogram": {
          "field": "fecha",
          "interval": "day",
          "timezone": "America/Mexico_City"
        }
      }
    },
    "size": 0
  }' | python3 -m json.tool
```

---

## 15. Aggregation de saldos de usuarios

```bash
curl -s -X POST http://localhost:7700/bank/user/_search \
  -H "SLM-KEY: $NEODB_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "query": {},
    "aggs": {
      "saldo_total": { "sum": { "field": "saldo" } },
      "saldo_promedio": { "avg": { "field": "saldo" } },
      "por_sucursal": { "terms": { "field": "sucursal", "size": 10 } }
    },
    "size": 0
  }' | python3 -m json.tool
```

---

## 16. Busqueda geoespacial: usuarios cerca de Reforma

Usuarios dentro de 5 km de Paseo de la Reforma, CDMX:

```bash
curl -s -X POST http://localhost:7700/bank/user/_search \
  -H "SLM-KEY: $NEODB_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "query": {
      "geo_distance": {
        "field": "ubicacion",
        "lat": 19.4326,
        "lon": -99.1332,
        "distance": "5km"
      }
    }
  }' | python3 -m json.tool
```

---

## 17. Crear una API key con permisos limitados

Crea una key para el backend de la app movil que solo puede leer usuarios y transacciones, pero no ver el campo `pin` ni el `saldo`:

```bash
curl -s -X POST http://localhost:7700/_security/keys \
  -H "SLM-KEY: $NEODB_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "name": "mobile-backend",
    "networks": ["0.0.0.0/0"],
    "permissions": [
      {
        "index": "bank",
        "types": ["user", "transaction"],
        "actions": ["read"],
        "denied_fields": ["pin", "saldo"]
      }
    ],
    "quota": { "rps": 50 }
  }' | python3 -m json.tool
```

La respuesta incluye `key_id` y `key_secret`. Guarda el `key_secret` -- es la unica vez que se muestra.

```bash
# Usa la nueva key
export MOBILE_KEY="KEY_ID_RETORNADO:KEY_SECRET_RETORNADO"
```

---

## 18. Demostrar field masking

Ahora lee un usuario con la key limitada:

```bash
curl -s http://localhost:7700/bank/user/user-gonzalo \
  -H "SLM-KEY: $MOBILE_KEY" | python3 -m json.tool
```

Observa que la respuesta **no contiene** los campos `pin` ni `saldo`. El field masking se aplica en la serializacion final, antes de enviar por la red. Los datos en disco no se modifican.

---

## 19. Verificar que la key limitada no puede escribir

```bash
curl -s -X POST http://localhost:7700/bank/user \
  -H "SLM-KEY: $MOBILE_KEY" \
  -H "Content-Type: application/json" \
  -d '{"nombre": "Test", "email": "test@test.com", "saldo": 0, "activo": true}'
```

La conexion se cierra silenciosamente sin respuesta HTTP. Esto es por diseno: los accesos no autorizados no reciben informacion sobre el motivo del rechazo.

---

## 20. Idempotencia con SLM-OPERATION-ID

Para garantizar que una operacion no se ejecute dos veces (por ejemplo, ante reintentos de red), envia el header `SLM-OPERATION-ID`:

```bash
curl -s -X POST http://localhost:7700/bank/transaction \
  -H "SLM-KEY: $NEODB_KEY" \
  -H "SLM-OPERATION-ID: 550e8400-e29b-41d4-a716-446655440000" \
  -H "Content-Type: application/json" \
  -d '{
    "wallet_id": "user-gonzalo",
    "tipo": "transferencia",
    "monto": 1000.00,
    "concepto": "Pago unico con idempotencia",
    "fecha": "2026-03-20T12:00:00Z",
    "status": "completado"
  }' | python3 -m json.tool
```

Si envias el mismo request con el mismo `SLM-OPERATION-ID`, obtendras el mismo resultado sin crear un documento duplicado.

---

## 21. Control de concurrencia optimista

Para evitar sobrescrituras accidentales, usa el parametro `version`:

```bash
# Leer la version actual
curl -s http://localhost:7700/bank/user/user-gonzalo \
  -H "SLM-KEY: $NEODB_KEY" | python3 -m json.tool
# Nota: la respuesta incluye "_version": 1

# Actualizar solo si la version es 1
curl -s -X PUT "http://localhost:7700/bank/user/user-gonzalo?version=1" \
  -H "SLM-KEY: $NEODB_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "saldo": 52500.00 }' | python3 -m json.tool

# Si otro proceso ya actualizo el documento, obtendras:
# { "status": 409, "error": "VERSION_CONFLICT", ... }
```

---

## 22. Backup

```bash
curl -s -X POST http://localhost:7700/_backup/run \
  -H "SLM-KEY: $NEODB_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "destination": "/tmp/bank-backup" }' | python3 -m json.tool
```

El backup usa RocksDB Checkpoint API -- es un snapshot consistente en un momento puntual.

---

## 23. CDC (Change Data Capture)

Obtener los cambios recientes en transacciones:

```bash
curl -s "http://localhost:7700/bank/transaction/_changes?since=1774226788183" \
  -H "SLM-KEY: $NEODB_KEY" | python3 -m json.tool
```

Retorna todos los documentos creados, actualizados o eliminados despues del timestamp. Util para sincronizar con sistemas externos.

---

## 24. TTL (Time To Live)

Crear un documento que expira automaticamente despues de 1 hora:

```bash
curl -s -X POST http://localhost:7700/bank/session \
  -H "SLM-KEY: $NEODB_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "user_id": "user-gonzalo",
    "token": "session-token-abc",
    "_ttl": 3600
  }' | python3 -m json.tool
```

El valor de `_ttl` es en segundos. Despues de expirar, el documento se marca automaticamente como deleted.

---

## Resumen

En esta guia completaste:

1. Compilar e iniciar SLM NeoDB
2. Crear schemas con Strict Mode (text, keyword, blind, number, datetime, geo)
3. Crear documentos individuales y via bulk (NDJSON)
4. Busquedas: full-text (match), exactas (term), rango (range), combinadas (bool), geoespaciales (geo_distance)
5. Aggregations: sum, avg, max, min, count, terms, date_histogram
6. Crear una API key con permisos limitados y CIDR binding
7. Field masking: campos sensibles eliminados de la respuesta
8. Idempotencia con SLM-OPERATION-ID
9. Control de concurrencia optimista con version
10. Backup via RocksDB checkpoint

Para la referencia completa del API y el Query DSL, consulta [`spec.md`](spec.md).
