npm package: @hemia/db-connector
Description: Hemia Database Conector
Author: Hemia Technologies
Latest version: 0.0.14

# @hemia/db-connector

Conector unificado para bases de datos SQL, NoSQL y OLAP, diseñado para facilitar operaciones comunes como conexión, transacciones, queries y manipulación de datos. Ideal para aplicaciones Node.js que requieren una interfaz abstracta para distintos motores de base de datos.

---

## Instalación

```bash
npm install @hemia/db-connector
```

---

## Bases de Datos Soportadas

### 🗄️ SQL (OLTP - Online Transaction Processing)
Bases de datos relacionales optimizadas para transacciones y operaciones CRUD.

* ✅ **MySQL** - Base de datos relacional de código abierto (Sequelize)
* ✅ **PostgreSQL** - Base de datos relacional avanzada con soporte JSON
  - **Sequelize** - ORM tradicional
  - **Drizzle ORM** - ORM moderno con TypeScript nativo y máximo rendimiento
* ✅ **Microsoft SQL Server (MSSQL)** - Base de datos empresarial de Microsoft (Sequelize)

**Casos de uso:**
- Sistemas transaccionales (e-commerce, banking)
- Aplicaciones empresariales (ERP, CRM)
- Gestión de usuarios y autenticación
- Aplicaciones que requieren integridad referencial

---

### 📄 NoSQL (Document Store)
Bases de datos orientadas a documentos para datos no estructurados.

* ✅ **MongoDB** - Base de datos de documentos JSON flexible y escalable

**Casos de uso:**
- Aplicaciones con esquemas flexibles
- Gestión de contenido (CMS)
- Perfiles de usuario complejos
- Catálogos de productos
- Logs y eventos en formato JSON
- Aplicaciones en tiempo real

---

### 📊 OLAP (Online Analytical Processing)
Bases de datos columnares optimizadas para análisis y consultas agregadas.

* ✅ **ClickHouse** - Base de datos columnar de alta velocidad para análisis en tiempo real

**Casos de uso:**
- Análisis de logs y trazas (APM, Observability)
- Métricas y monitoreo en tiempo real
- Business Intelligence y Data Warehousing
- Análisis de eventos y comportamiento de usuarios
- Procesamiento de grandes volúmenes de datos
- Dashboards analíticos de alto rendimiento

---

## Uso rápido

### MongoDB

```ts
import { MongoDBConnector } from '@hemia/db-connector';

const mongo = new MongoDBConnector({
  host: 'localhost',
  user: 'admin',
  password: '1234',
  database: 'mydb',
});

await mongo.connect();
const docs = await mongo.find(MyModel, { status: 'active' });
```

### SQL (MySQL/MSSQL/PostgreSQL)

```ts
import { DBConnector } from '@hemia/db-connector';
import { DBSQLType } from '@hemia/db-connector/types/DBSQLTypes';

const dbConnector = DBConnector.getInstance();
const sql = dbConnector.getEngine(DBSQLType.MySQL, {
  host: 'localhost',
  user: 'root',
  password: 'pass',
  database: 'mydb',
  dialect: 'mysql'
});

await sql.connect();
const rows = await sql.select('users', { where: { active: true } });

// Transacciones
await sql.withTransaction(async (transaction) => {
  await sql.insert('orders', { userId: 1, total: 100 }, { transaction });
  await sql.update('users', { id: 1 }, { balance: 500 }, { transaction });
});
```

### PostgreSQL con Drizzle ORM (Recomendado)

```ts
import { 
  DrizzlePostgresConnector,
  pgTable, 
  serial, 
  varchar, 
  integer,
  eq, 
  and, 
  gt 
} from '@hemia/db-connector';

// 1. Definir schema con tipos
const users = pgTable('users', {
  id: serial('id').primaryKey(),
  name: varchar('name', { length: 255 }).notNull(),
  email: varchar('email', { length: 255 }).notNull().unique(),
  age: integer('age')
});

// 2. Conectar y registrar schema
const connector = new DrizzlePostgresConnector({
  host: 'localhost',
  port: 5432,
  database: 'mydb',
  user: 'postgres',
  password: 'password',
  dialect: 'postgresql'
});

await connector.connect();
connector.registerTableSchema('users', users);

// 3. Usar API nativa de Drizzle (tipado completo)
const db = connector.getDb();

// SELECT con tipos inferidos
const activeAdults = await db
  .select()
  .from(users)
  .where(and(
    eq(users.status, 'active'),
    gt(users.age, 18)
  ));

// INSERT con autocompletado
const newUser = await db
  .insert(users)
  .values({
    name: 'John Doe',
    email: 'john@example.com',
    age: 30
  })
  .returning();

// UPDATE
await db
  .update(users)
  .set({ name: 'Jane Doe' })
  .where(eq(users.id, 1));

// O usar API abstracta (compatible con Sequelize)
const users = await connector.select('users', {
  where: { age: { $gt: 18 } },
  limit: 10
});
```

**Operadores soportados:**
- `eq(column, value)` - Igual a
- `ne(column, value)` - No igual a
- `gt(column, value)` - Mayor que / `gte` - Mayor o igual
- `lt(column, value)` - Menor que / `lte` - Menor o igual  
- `like(column, pattern)` - LIKE / `ilike` - ILIKE (case-insensitive)
- `inArray(column, values)` - IN (lista)
- `isNull(column)` - IS NULL
- `and(...conditions)` - AND lógico / `or` - OR lógico
- `not(condition)` - NOT lógico

**Ventajas de Drizzle sobre Sequelize:**
- ⚡ **3-4x más rápido** en queries complejas
- 🎯 **Type-safety 100%** sin decoradores ni boilerplate
- 📦 **Bundle 50% más pequeño** que Sequelize
- 🔧 **Migrations automáticas** con Drizzle Kit
- 🚀 **Zero overhead** - todo compile-time

> 📖 **Nota técnica**: DrizzlePostgresConnector usa una arquitectura independiente (`DrizzleSqlConnector`) sin dependencias de Sequelize. Ver [DRIZZLE_REFACTOR.md](./DRIZZLE_REFACTOR.md) para detalles de implementación.

### ClickHouse (OLAP)

```ts
import { DBConnector } from '@hemia/db-connector';
import { DBOLAPType } from '@hemia/db-connector/types/OLAPTypes';

const dbConnector = DBConnector.getInstance();
const clickhouse = dbConnector.getEngine(DBOLAPType.ClickHouse, {
  host: 'http://localhost:8123',
  user: 'default',
  password: '',
  database: 'analytics',
});

await clickhouse.connect();

// Insertar datos (JSONEachRow por defecto)
await clickhouse.insert({
  table: 'traces',
  values: [
    { trace_id: '123', service_name: 'api', duration_ms: 45 },
    { trace_id: '124', service_name: 'db', duration_ms: 120 }
  ]
});

// Insertar con formato CSV
await clickhouse.insert({
  table: 'traces',
  values: '125,"cache",30\n126,"queue",85',
  format: 'CSV'
});

// Buscar con filtros
const traces = await clickhouse.find('traces', { 
  service_name: 'api',
  error: false 
});

// Query personalizada para análisis
const stats = await clickhouse.query(`
  SELECT 
    service_name,
    count() as total_requests,
    avg(duration_ms) as avg_duration,
    max(duration_ms) as max_duration
  FROM traces
  WHERE start_time >= now() - INTERVAL 1 HOUR
  GROUP BY service_name
  ORDER BY total_requests DESC
`);
```

---

## Uso con DBConnector (Recomendado)

El patrón recomendado es usar `DBConnector` como punto de entrada único:

```ts
import { DBConnector } from '@hemia/db-connector';
import { DBSQLType, DBNoSQLType, DBOLAPType } from '@hemia/db-connector';

const dbConnector = DBConnector.getInstance();

// Obtener motor SQL
const mysql = dbConnector.getEngine(DBSQLType.MySQL, credentials);

// Obtener motor NoSQL  
const mongo = dbConnector.getEngine(DBNoSQLType.MongoDB, credentials);

// Obtener motor OLAP
const clickhouse = dbConnector.getEngine(DBOLAPType.ClickHouse, credentials);
```

---

## Características destacadas

### MongoDBConnector (NoSQL)

* Conexión y desconexión automáticas
* Transacciones con rollback (`withTransaction`)
* CRUD genérico (`create`, `find`, `findOne`, `update`, `delete`, etc.)
* Operaciones avanzadas: `aggregate`, `findAllFromMultipleModels`
* Soporte para modelos Mongoose
* Manejo automático de sesiones

**Ejemplo de uso avanzado:**
```ts
// Aggregate pipeline
const results = await mongo.aggregate(UserModel, [
  { $match: { active: true } },
  { $group: { _id: '$country', total: { $sum: 1 } } }
]);

// Transacciones
await mongo.withTransaction(async (session) => {
  await mongo.create(UserModel, { name: 'John' }, session);
  await mongo.update(UserModel, { _id: userId }, { status: 'active' }, session);
});
```

---

### SequelizeSqlConnector (SQL)

* Operaciones CRUD: `select`, `insert`, `update`, `delete`
* Transacciones con soporte completo de Sequelize (`withTransaction`)
* Query SQL libre con `query()` para consultas complejas
* Manejo de errores y mapeo de códigos SQLSTATE (`createDatabaseError`)
* Soporte para stored procedures
* Conexión a MySQL, PostgreSQL, MSSQL

**Ejemplo de uso avanzado:**
```ts
// Query con stored procedure
const result = await sql.query('CALL get_user_stats(:userId)', {
  replacements: { userId: 123 },
  type: QueryTypes.SELECT
});

// Transacciones
await sql.withTransaction(async (transaction) => {
  await sql.insert('orders', orderData, { transaction });
  await sql.update('inventory', { productId: 1 }, { stock: 10 }, { transaction });
});
```

---

### ClickHouseConnector (OLAP)

* Conexión optimizada para análisis de alto rendimiento
* Múltiples formatos de inserción: `JSONEachRow`, `CSV`, `TabSeparated`, etc.
* Método `find()` con filtros para búsquedas rápidas
* Query SQL libre para análisis complejos
* Ideal para procesamiento de grandes volúmenes de datos
* Soporte para columnas calculadas (MATERIALIZED)

**Ejemplo de uso avanzado:**
```ts
// Insertar millones de registros eficientemente
await clickhouse.insert({
  table: 'events',
  values: largeDataArray, // Array con millones de registros
  format: 'JSONEachRow'
});

// Análisis en tiempo real
const metrics = await clickhouse.query(`
  SELECT 
    toStartOfMinute(timestamp) as minute,
    countIf(status = 200) as success,
    countIf(status >= 400) as errors,
    quantile(0.95)(response_time) as p95_latency
  FROM http_logs
  WHERE timestamp >= now() - INTERVAL 1 HOUR
  GROUP BY minute
  ORDER BY minute DESC
`);

// Búsqueda con filtros múltiples
const errorTraces = await clickhouse.find('traces', {
  service_name: 'payment-api',
  error: true,
  duration_ms: 1000 // Mayor a 1 segundo
});
```

---

## Tipos esperados de configuración

```ts
interface CredentialsConnection {
  host: string;
  port?: number;
  user?: string;
  password?: string;
  database: string;
  dialect?: 'mysql' | 'mssql' | 'postgres';
  logging?: boolean;
}
```

---

## Arquitectura y Patrones de Uso

### Lambda Architecture: Combinando SQL, NoSQL y OLAP

Una arquitectura común es usar diferentes bases de datos para diferentes propósitos:

```ts
// 1. OLTP (MySQL/PostgreSQL) - Transacciones en tiempo real
const mysql = dbConnector.getEngine(DBSQLType.MySQL, mysqlCredentials);
await mysql.insert('orders', { userId: 123, amount: 99.99 });

// 2. NoSQL (MongoDB) - Datos flexibles y documentos
const mongo = dbConnector.getEngine(DBNoSQLType.MongoDB, mongoCredentials);
await mongo.create(ProductModel, { 
  name: 'Laptop',
  specs: { ram: '16GB', cpu: 'i7' } // Esquema flexible
});

// 3. OLAP (ClickHouse) - Análisis y reportes
const clickhouse = dbConnector.getEngine(DBOLAPType.ClickHouse, clickhouseCredentials);
await clickhouse.insert({
  table: 'order_analytics',
  values: [{ 
    order_id: orderId,
    user_id: 123,
    amount: 99.99,
    timestamp: new Date()
  }]
});
```

### Casos de Uso por Tipo de Base de Datos

#### 🗄️ SQL (MySQL/PostgreSQL/MSSQL)
**Cuándo usar:**
- Necesitas transacciones ACID
- Relaciones complejas entre entidades
- Integridad referencial crítica
- Consultas con JOINs

**Ejemplos:**
- Sistema de facturación
- Gestión de inventario
- Sistema de usuarios y permisos
- Carrito de compras

#### 📄 NoSQL (MongoDB)
**Cuándo usar:**
- Esquemas variables o en evolución
- Necesitas escalar horizontalmente
- Documentos JSON complejos
- Alto volumen de escrituras

**Ejemplos:**
- Catálogo de productos con atributos variables
- Perfiles de usuario con datos personalizados
- Sistema de logs y auditoría
- Gestión de contenido (CMS)

#### 📊 OLAP (ClickHouse)
**Cuándo usar:**
- Análisis de grandes volúmenes de datos
- Consultas agregadas complejas
- Necesitas latencias de milisegundos en análisis
- Datos de series temporales

**Ejemplos:**
- Observabilidad y APM (traces, logs, métricas)
- Análisis de comportamiento de usuarios
- Dashboards de BI en tiempo real
- Análisis de eventos y clickstream
- Monitoreo de infraestructura

---

## Ejemplo Completo: Sistema de E-commerce con Observabilidad

```ts
import { DBConnector, DBSQLType, DBNoSQLType, DBOLAPType } from '@hemia/db-connector';

const connector = DBConnector.getInstance();

// 1. Base de datos transaccional (MySQL)
const mysql = connector.getEngine(DBSQLType.MySQL, {
  host: 'localhost',
  database: 'ecommerce',
  user: 'root',
  password: 'pass'
});

// 2. Base de datos de documentos (MongoDB)
const mongo = connector.getEngine(DBNoSQLType.MongoDB, {
  host: 'localhost',
  database: 'ecommerce_content'
});

// 3. Base de datos analítica (ClickHouse)
const clickhouse = connector.getEngine(DBOLAPType.ClickHouse, {
  host: 'http://localhost:8123',
  database: 'analytics'
});

// Procesar orden
async function processOrder(orderData) {
  const startTime = Date.now();
  
  try {
    // 1. Guardar orden en MySQL (transaccional)
    await mysql.withTransaction(async (tx) => {
      const order = await mysql.insert('orders', orderData, { transaction: tx });
      await mysql.update('inventory', 
        { productId: orderData.productId }, 
        { stock: { decrement: 1 } },
        { transaction: tx }
      );
    });
    
    // 2. Guardar detalles enriquecidos en MongoDB
    await mongo.create(OrderDetailsModel, {
      orderId: orderData.id,
      userPreferences: orderData.preferences,
      metadata: orderData.metadata
    });
    
    // 3. Registrar evento en ClickHouse para análisis
    await clickhouse.insert({
      table: 'order_events',
      values: [{
        event_type: 'order_created',
        order_id: orderData.id,
        user_id: orderData.userId,
        amount: orderData.amount,
        duration_ms: Date.now() - startTime,
        timestamp: new Date(),
        success: true
      }]
    });
    
  } catch (error) {
    // Registrar error en ClickHouse
    await clickhouse.insert({
      table: 'order_events',
      values: [{
        event_type: 'order_failed',
        order_id: orderData.id,
        error_message: error.message,
        duration_ms: Date.now() - startTime,
        timestamp: new Date(),
        success: false
      }]
    });
    throw error;
  }
}

// Análisis de órdenes en tiempo real
async function getOrderStats() {
  return await clickhouse.query(`
    SELECT 
      toStartOfHour(timestamp) as hour,
      countIf(success = true) as successful_orders,
      countIf(success = false) as failed_orders,
      avg(amount) as avg_order_value,
      quantile(0.95)(duration_ms) as p95_duration
    FROM order_events
    WHERE timestamp >= now() - INTERVAL 24 HOUR
    GROUP BY hour
    ORDER BY hour DESC
  `);
}
```

---

## Licencia

MIT

---

## Autor

Hemia Technologies