npm package: @karkinos/query-builder
Description: Una librería TypeScript flexible y type-safe para construir consultas complejas de manera fluida con drizzle ORM
Author: Karkinos Team
Latest version: 1.0.2

# @karkinos/query-builder

Una librería TypeScript flexible y type-safe para construir consultas complejas de manera fluida. Perfecta para aplicaciones que necesitan construir filtros dinámicos para APIs.

## 🚀 Características

- ✅ **Type-safe**: Completamente tipado con TypeScript
- 🔧 **Fluent API**: Sintaxis intuitiva y encadenable
- 🎯 **Flexible**: Soporta filtros complejos, ordenamiento y paginación

## 🏁 Instalación

```bash
npm install @karkinos/query-builder
# o
yarn add @karkinos/query-builder
# o
bun add @karkinos/query-builder
```

### Uso

```typescript
import { Q, F, Utils } from '@karkinos/query-builder';
// o importaciones específicas
import { FluentQueryBuilder, FilterFactory } from '@karkinos/query-builder';
```

## 📚 Uso Básico

### Query Builder Fluido

```typescript
import { Q } from '@karkinos/query-builder';

// Crear una consulta simple
const query = Q.create()
  .whereEquals('status', 'active')
  .whereGreaterThan('age', 18)
  .orderBy('createdAt', 'DESC')
  .paginate(1, 20)
  .build();

```
**Resultado:**
```json
{
  "filters": {
    "operator": "AND",
    "filters": [
      { "field": "status", "operator": "eq", "value": "active" },
      { "field": "age", "operator": "gt", "value": 18 }
    ]
  },
  "sort": { "createdAt": "DESC" },
  "page": 1,
  "limit": 20
}
```

### Factory de Filtros

```typescript
import { F } from '@karkinos/query-builder';

// Crear filtros individuales
const activeUsers = F.equals('status', 'active');
const adults = F.greaterThanOrEqual('age', 18);
const nameSearch = F.ilike('name', '%john%');
const validEmails = F.isNotNull('email');

// Rangos y fechas
const thisWeek = F.between('createdAt', startDate, endDate);
const dateRange = F.dateRange('updatedAt', new Date('2024-01-01'), new Date());
```

### Filtros Complejos con Grupos

```typescript
import { Q, F, FilterGroup } from '@karkinos/query-builder';

const query = Q.create()
  .setFilterGroup(
    FilterGroup.and(
      F.equals('status', 'active'),
      FilterGroup.or(
        F.equals('role', 'admin'),
        F.equals('role', 'moderator')
      ),
      F.greaterThan('lastLogin', thirtyDaysAgo)
    )
  )
  .build();
```

## 📋 API Reference

### FluentQueryBuilder (Q)

#### Métodos de Filtrado
- `whereEquals(field, value)` - Filtro de igualdad exacta
- `whereNotEquals(field, value)` - Filtro de no igualdad
- `whereGreaterThan(field, value)` - Mayor que
- `whereGreaterThanOrEqual(field, value)` - Mayor o igual que
- `whereLessThan(field, value)` - Menor que
- `whereLessThanOrEqual(field, value)` - Menor o igual que
- `whereLike(field, value)` - Búsqueda de texto (case-sensitive)
- `whereILike(field, value)` - Búsqueda de texto (case-insensitive)
- `whereIn(field, values[])` - Valor está en lista
- `whereNotIn(field, values[])` - Valor no está en lista
- `whereNull(field)` - Campo es null
- `whereNotNull(field)` - Campo no es null
- `whereBetween(field, min, max)` - Valor entre rango
- `whereDateRange(field, start, end)` - Rango de fechas
- `whereTextSearch(fields[], term)` - Búsqueda de texto en múltiples campos

#### Métodos de Ordenamiento
- `orderBy(field, direction)` - Ordenar por campo
- `orderByMultiple(sorts)` - Múltiples ordenamientos
- `clearOrderBy()` - Limpiar ordenamiento

#### Métodos de Paginación
- `page(num)` - Establecer página
- `limit(num)` - Establecer límite por página
- `paginate(page, limit)` - Paginación completa
- `clearPagination()` - Limpiar paginación

#### Métodos de Búsqueda
- `search(term)` - Término de búsqueda global
- `clearSearch()` - Limpiar búsqueda

#### Métodos de Utilidad
- `build()` - Construir query final
- `clone()` - Clonar builder
- `reset()` - Resetear a estado inicial
- `isEmpty()` - Verificar si está vacío

### FilterFactory (F)

#### Métodos de Creación
- `create(field, operator, value)` - Factory genérico
- `equals(field, value)` - Igualdad
- `notEquals(field, value)` - No igualdad
- `greaterThan(field, value)` - Mayor que
- `lessThan(field, value)` - Menor que
- `like(field, pattern)` - Pattern matching
- `in(field, values)` - Valor en lista
- `isNull(field)` - Es null
- `isNotNull(field)` - No es null

#### Métodos de Conveniencia
- `between(field, min, max)` - Rango entre valores
- `dateRange(field, start, end)` - Rango de fechas
- `textSearch(fields, term)` - Búsqueda en múltiples campos

### QueryUtils (Utils)

#### Utilidades de Alto Nivel
```typescript
import { Utils } from '@karkinos/query-builder';

// Query para tablas con paginación y búsqueda
const tableQuery = Utils.createTableQuery({
  page: 1,
  limit: 20,
  search: 'john',
  sortField: 'name',
  sortDirection: 'ASC'
});

// Filtros de estado comunes
const statusFilters = Utils.createStatusFilters(['active', 'pending']);

// Filtros de fechas predefinidos
const todayFilters = Utils.createDateFilters({
  field: 'createdAt',
  today: true
});

const thisMonthFilters = Utils.createDateFilters({
  field: 'updatedAt', 
  thisMonth: true
});
```

## 🎯 Ejemplos Avanzados

### Sistema de Usuarios Complejo

```typescript
import { Q, F, Utils } from '@karkinos/query-builder';

// Buscar usuarios activos con roles específicos
const activeAdmins = Q.create()
  .whereEquals('status', 'active')
  .whereIn('role', ['admin', 'super_admin'])
  .whereNotNull('lastLogin')
  .whereDateRange('createdAt', 
    new Date('2024-01-01'), 
    new Date()
  )
  .orderBy('lastLogin', 'DESC')
  .paginate(1, 50);

// Búsqueda de texto en múltiples campos
const userSearch = Q.create()
  .whereTextSearch(['name', 'email', 'username'], 'john')
  .whereEquals('status', 'active');

// Filtros de fecha usando utilidades
const recentUsers = Q.create()
  .and(Utils.createDateFilters({
    field: 'createdAt',
    thisWeek: true
  }))
  .whereNotNull('profileImage');
```

### E-commerce: Productos

```typescript
// Productos en stock con descuentos
const discountedProducts = Q.create()
  .whereGreaterThan('stock', 0)
  .whereBetween('discount', 10, 50)
  .whereIn('category', ['electronics', 'clothing'])
  .orderBy('discount', 'DESC')
  .orderBy('price', 'ASC');

// Productos más vendidos del mes
const topProducts = Q.create()
  .and(Utils.createDateFilters({
    field: 'lastSale',
    thisMonth: true
  }))
  .whereGreaterThan('salesCount', 10)
  .orderBy('salesCount', 'DESC')
  .limit(20);
```

### Reportes y Analytics

```typescript
// Reportes de ventas por período
const salesReport = Q.create()
  .whereDateRange('saleDate', startDate, endDate)
  .whereIn('status', ['completed', 'shipped'])
  .whereNotNull('customerId')
  .orderBy('saleDate', 'DESC');

// Usuarios más activos
const activeUsers = Q.create()
  .whereGreaterThan('loginCount', 5)
  .and(Utils.createDateFilters({
    field: 'lastActivity',
    thisMonth: true
  }))
  .orderBy('loginCount', 'DESC')
  .limit(100);
```

## 🔧 Extensibilidad

### Crear Filtros Personalizados

```typescript
import { FilterBase } from '@karkinos/query-builder';

class CustomRangeFilter extends FilterBase {
  constructor(field: string, min: any, max: any) {
    super({
      field,
      operator: 'between' as any,
      value: { min, max }
    });
  }

  static create(field: string, min: any, max: any) {
    return new CustomRangeFilter(field, min, max);
  }
}
```

### Extender QueryBuilder

```typescript
import { FluentQueryBuilder } from '@karkinos/query-builder';

class CustomQueryBuilder extends FluentQueryBuilder {
  whereActive(): this {
    return this.whereEquals('status', 'active');
  }

  whereUser(userId: string): this {
    return this.whereEquals('userId', userId);
  }

  recent(): this {
    const thirtyDaysAgo = new Date();
    thirtyDaysAgo.setDate(thirtyDaysAgo.getDate() - 30);
    return this.whereGreaterThan('createdAt', thirtyDaysAgo);
  }
}
```

## 🧪 Testing

```typescript
import { Q, F } from '@karkinos/query-builder';

describe('QueryBuilder', () => {
  it('should build basic query', () => {
    const query = Q.create()
      .whereEquals('status', 'active')
      .build();

    expect(query.filters).toEqual({
      operator: 'AND',
      filters: [
        { field: 'status', operator: 'eq', value: 'active' }
      ]
    });
  });

  it('should handle complex filters', () => {
    const query = Q.create()
      .whereBetween('age', 18, 65)
      .whereIn('role', ['user', 'admin'])
      .build();

    expect(query.filters.filters).toHaveLength(2);
  });
});
```

## 📝 Tipos TypeScript

```typescript
// Tipos principales exportados
export type FilterOperatorType = 
  | 'eq' | 'ne' | 'gt' | 'gte' | 'lt' | 'lte'
  | 'like' | 'ilike' | 'in' | 'notIn' 
  | 'isNull' | 'isNotNull';

export type LogicalOperator = 'AND' | 'OR';
export type SortDirection = 'ASC' | 'DESC' | 'asc' | 'desc';

export interface IQueryParams {
  filters?: IFilterGroup;
  search?: string;
  page?: number;
  limit?: number;
  sort?: Record<string, SortDirection>;
}
```

## 🤝 Contribuir

1. Mantén la estructura de carpetas organizada
2. Agrega tests para nuevas características
3. Documenta con JSDoc
4. Usa TypeScript estricto
5. Sigue los patrones existentes

## 🔗 Links Útiles

- [Documentación del Monorepo](../../README.md)
- [Guía de Contribución](../../CONTRIBUTING.md)
- [Ejemplos Adicionales](./examples/)