@teamnet/ic-orm
Version:
Database Management System for Total.js v4 and standalone
829 lines (643 loc) • 16.9 kB
Markdown
# 🔍 Query Builder Avanzado - MongoDB con icorm
Guía completa del Query Builder para construir consultas complejas de forma fluida y encadenable.
## 📋 Tabla de Contenidos
- [WHERE - Condiciones](#where---condiciones)
- [IN/NOTIN - Arrays de Valores](#innotin---arrays-de-valores)
- [BETWEEN - Rangos](#between---rangos)
- [SEARCH - Búsqueda de Texto](#search---búsqueda-de-texto)
- [SORT - Ordenamiento](#sort---ordenamiento)
- [FIELDS - Proyección de Campos](#fields---proyección-de-campos)
- [TAKE/SKIP - Paginación](#takeskip---paginación)
- [OR - Condiciones OR](#or---condiciones-or)
- [Métodos de Encadenamiento](#métodos-de-encadenamiento)
- [Ejemplos Complejos](#ejemplos-complejos)
---
## 🎯 WHERE - Condiciones
Filtra documentos por condiciones específicas.
### Sintaxis
```javascript
.where(field, value)
.where(field, operator, value)
```
### Operadores Soportados
| Operador | MongoDB | Descripción |
|----------|---------|-------------|
| `=` o `==` | `$eq` | Igual a |
| `!=` o `<>` | `$ne` | Diferente de |
| `>` | `$gt` | Mayor que |
| `>=` | `$gte` | Mayor o igual que |
| `<` | `$lt` | Menor que |
| `<=` | `$lte` | Menor o igual que |
### Ejemplos
#### Igualdad Simple
```javascript
// WHERE active = true
var users = await DBMS()
.find('users')
.where('active', true)
.promise();
// WHERE email = 'john@example.com'
var user = await DBMS()
.one('users')
.where('email', 'john@example.com')
.promise();
```
#### Comparaciones Numéricas
```javascript
// WHERE age > 18
var adults = await DBMS()
.find('users')
.where('age', '>', 18)
.promise();
// WHERE age >= 18 AND age <= 65
var workingAge = await DBMS()
.find('users')
.where('age', '>=', 18)
.where('age', '<=', 65)
.promise();
// WHERE price < 100
var cheap = await DBMS()
.find('products')
.where('price', '<', 100)
.promise();
```
#### Búsqueda por _id
```javascript
// WHERE _id = ObjectId('...')
var user = await DBMS()
.one('users')
.where('_id', '507f1f77bcf86cd799439011')
.promise();
// icorm convierte automáticamente el string a ObjectId
```
#### Múltiples Condiciones (AND)
```javascript
// WHERE active = true AND age >= 18 AND verified = true
var users = await DBMS()
.find('users')
.where('active', true)
.where('age', '>=', 18)
.where('verified', true)
.promise();
// Todas las condiciones se unen con AND automáticamente
```
#### Condiciones con null/undefined
```javascript
// Buscar documentos donde email es null
var noEmail = await DBMS()
.find('users')
.where('email', null)
.promise();
// Buscar documentos donde deletedAt NO es null
var deleted = await DBMS()
.find('users')
.where('deletedAt', '!=', null)
.promise();
```
---
## 📋 IN/NOTIN - Arrays de Valores
Filtra por múltiples valores posibles.
### Sintaxis
```javascript
.in(field, arrayOfValues)
.notin(field, arrayOfValues)
```
### Ejemplos
#### IN Básico
```javascript
// WHERE category IN ('electronics', 'furniture', 'clothing')
var products = await DBMS()
.find('products')
.in('category', ['electronics', 'furniture', 'clothing'])
.promise();
// WHERE status IN ('active', 'pending')
var users = await DBMS()
.find('users')
.in('status', ['active', 'pending'])
.promise();
```
#### IN con _id (ObjectIds)
```javascript
// Buscar múltiples usuarios por ID
var userIds = [
'507f1f77bcf86cd799439011',
'507f1f77bcf86cd799439012',
'507f1f77bcf86cd799439013'
];
var users = await DBMS()
.find('users')
.in('_id', userIds)
.promise();
// icorm convierte automáticamente los strings a ObjectIds
```
#### NOTIN (excluir valores)
```javascript
// WHERE status NOT IN ('deleted', 'banned')
var validUsers = await DBMS()
.find('users')
.notin('status', ['deleted', 'banned'])
.promise();
// WHERE category NOT IN ('archived')
var activeProducts = await DBMS()
.find('products')
.notin('category', ['archived'])
.promise();
```
#### IN con Array Vacío
```javascript
// Array vacío = no encuentra nada (comportamiento esperado)
var users = await DBMS()
.find('users')
.in('_id', []) // No retorna documentos
.promise();
// users = []
```
#### Combinar IN con WHERE
```javascript
// WHERE category IN (...) AND active = true
var products = await DBMS()
.find('products')
.in('category', ['electronics', 'furniture'])
.where('active', true)
.where('stock', '>', 0)
.promise();
```
---
## 📏 BETWEEN - Rangos
Filtra por valores dentro de un rango.
### Sintaxis
```javascript
.between(field, min, max)
```
### Ejemplos
```javascript
// WHERE age BETWEEN 18 AND 65
var users = await DBMS()
.find('users')
.between('age', 18, 65)
.promise();
// WHERE price BETWEEN 100 AND 1000
var products = await DBMS()
.find('products')
.between('price', 100, 1000)
.promise();
// WHERE created BETWEEN '2024-01-01' AND '2024-12-31'
var thisYear = await DBMS()
.find('orders')
.between('created', new Date('2024-01-01'), new Date('2024-12-31'))
.promise();
```
---
## 🔎 SEARCH - Búsqueda de Texto
Búsqueda de texto con RegEx (case insensitive).
### Sintaxis
```javascript
.search(field, text, [compare])
```
**Compare:**
- `'*'` - Contiene (por defecto)
- `'beg'` - Comienza con
- `'end'` - Termina con
### Ejemplos
#### Search Básico (Contiene)
```javascript
// Buscar usuarios cuyo nombre contenga 'john' (case insensitive)
var users = await DBMS()
.find('users')
.search('name', 'john')
.promise();
// Encuentra: 'John', 'JOHN', 'Johnny', 'john smith', etc.
```
#### Search con Compare
```javascript
// Nombre que EMPIEZA con 'john'
var users = await DBMS()
.find('users')
.search('name', 'john', 'beg')
.promise();
// Encuentra: 'John Smith', 'Johnny', 'john'
// No encuentra: 'Mary John'
// Nombre que TERMINA con 'smith'
var users = await DBMS()
.find('users')
.search('name', 'smith', 'end')
.promise();
// Encuentra: 'John Smith', 'jane smith'
// No encuentra: 'Smith John'
```
#### Search en Múltiples Campos
```javascript
// Buscar en nombre O descripción
var products = await DBMS()
.find('products')
.or(function(builder) {
builder.search('name', 'laptop');
builder.search('description', 'laptop');
})
.promise();
```
#### Search con Acentos
```javascript
// icorm maneja automáticamente variaciones de acentos
var users = await DBMS()
.find('users')
.search('name', 'jose')
.promise();
// Encuentra: 'José', 'jose', 'JOSÉ', 'Jose'
```
---
## 🔀 SORT - Ordenamiento
Ordena los resultados.
### Sintaxis
```javascript
.sort(field, [descending])
```
**Descending:**
- `false` o no especificar - Ascendente (A-Z, 0-9)
- `true` - Descendente (Z-A, 9-0)
### Ejemplos
#### Sort Ascendente
```javascript
// ORDER BY name ASC
var users = await DBMS()
.find('users')
.sort('name')
.promise();
// ORDER BY created ASC (más antiguos primero)
var oldest = await DBMS()
.find('users')
.sort('created')
.promise();
```
#### Sort Descendente
```javascript
// ORDER BY age DESC (mayores primero)
var users = await DBMS()
.find('users')
.sort('age', true)
.promise();
// ORDER BY price DESC (más caros primero)
var expensive = await DBMS()
.find('products')
.sort('price', true)
.promise();
// ORDER BY created DESC (más recientes primero)
var recent = await DBMS()
.find('posts')
.sort('created', true)
.promise();
```
#### Sort Múltiple
```javascript
// ORDER BY active DESC, name ASC
var users = await DBMS()
.find('users')
.sort('active', true) // Activos primero
.sort('name') // Luego por nombre
.promise();
// ORDER BY category ASC, price DESC
var products = await DBMS()
.find('products')
.sort('category') // Agrupar por categoría
.sort('price', true) // Más caros primero en cada categoría
.promise();
```
---
## 📦 FIELDS - Proyección de Campos
Selecciona qué campos incluir o excluir en los resultados.
### Sintaxis
```javascript
.fields(field1, field2, ...)
.fields('-excludedField1', '-excludedField2', ...)
```
### Ejemplos
#### Incluir Campos Específicos
```javascript
// SELECT _id, name, email FROM users
var users = await DBMS()
.find('users')
.fields('name', 'email')
.promise();
// Resultado:
// [
// { _id: ObjectId(...), name: 'John', email: 'john@...' },
// { _id: ObjectId(...), name: 'Jane', email: 'jane@...' }
// ]
// Nota: _id siempre se incluye a menos que se excluya explícitamente
```
#### Excluir Campos
```javascript
// SELECT * EXCEPT (password, tokens) FROM users
var users = await DBMS()
.find('users')
.fields('-password', '-tokens')
.promise();
// Retorna todos los campos excepto password y tokens
```
#### Solo Traer IDs
```javascript
// Solo _id (para verificar existencia o mapear IDs)
var users = await DBMS()
.find('users')
.fields('_id')
.where('active', true)
.promise();
// Resultado: [{ _id: ObjectId(...) }, { _id: ObjectId(...) }, ...]
```
#### Campos de Objetos Anidados
```javascript
// Traer solo campos específicos de objetos anidados
var products = await DBMS()
.find('products')
.fields('name', 'price', 'specs.cpu', 'specs.ram')
.promise();
// Resultado:
// [
// {
// _id: ObjectId(...),
// name: 'Laptop',
// price: 1299,
// specs: { cpu: 'Intel i7', ram: '16GB' }
// }
// ]
```
---
## 📄 TAKE/SKIP - Paginación
Controla cuántos documentos retornar y desde dónde empezar.
### Sintaxis
```javascript
.take(limit) // LIMIT
.skip(offset) // OFFSET
```
### Ejemplos
#### Limitar Resultados
```javascript
// Primeros 10 usuarios
var users = await DBMS()
.find('users')
.take(10)
.promise();
// Primer usuario
var firstUser = await DBMS()
.find('users')
.sort('created')
.take(1)
.promise();
```
#### Paginación Simple
```javascript
// Página 1 (usuarios 1-10)
var page1 = await DBMS()
.find('users')
.take(10)
.skip(0)
.promise();
// Página 2 (usuarios 11-20)
var page2 = await DBMS()
.find('users')
.take(10)
.skip(10)
.promise();
// Página 3 (usuarios 21-30)
var page3 = await DBMS()
.find('users')
.take(10)
.skip(20)
.promise();
```
#### Paginación Dinámica
```javascript
function paginate(page, limit) {
return DBMS()
.find('users')
.sort('name')
.take(limit)
.skip((page - 1) * limit)
.promise();
}
// Uso
var page1 = await paginate(1, 20); // Usuarios 1-20
var page2 = await paginate(2, 20); // Usuarios 21-40
var page5 = await paginate(5, 20); // Usuarios 81-100
```
---
## 🔀 OR - Condiciones OR
Agrupa condiciones con operador OR.
### Sintaxis
```javascript
.or(function(builder) {
builder.where(...);
builder.where(...);
})
```
### Ejemplos
#### OR Simple
```javascript
// WHERE name = 'John' OR name = 'Jane'
var users = await DBMS()
.find('users')
.or(function(builder) {
builder.where('name', 'John');
builder.where('name', 'Jane');
})
.promise();
```
#### OR con Diferentes Campos
```javascript
// WHERE (age >= 65 OR status = 'premium')
var users = await DBMS()
.find('users')
.or(function(builder) {
builder.where('age', '>=', 65);
builder.where('status', 'premium');
})
.promise();
```
#### OR con AND
```javascript
// WHERE active = true AND (age >= 65 OR status = 'premium')
var users = await DBMS()
.find('users')
.where('active', true)
.or(function(builder) {
builder.where('age', '>=', 65);
builder.where('status', 'premium');
})
.promise();
```
#### OR con SEARCH
```javascript
// WHERE (name LIKE '%john%' OR email LIKE '%john%')
var users = await DBMS()
.find('users')
.or(function(builder) {
builder.search('name', 'john');
builder.search('email', 'john');
})
.promise();
```
---
## ⛓️ Métodos de Encadenamiento
Métodos adicionales para controlar la ejecución de queries.
### DATA - Procesar Resultados
```javascript
var users = await DBMS()
.find('users')
.where('active', true)
.data(function(users) {
// Transformar datos antes de continuar
users.forEach(function(user) {
user.displayName = user.name + ' (' + user.email + ')';
});
})
.promise();
```
### CALLBACK - Usar Callbacks
```javascript
DBMS()
.find('users')
.where('active', true)
.callback(function(err, users) {
if (err) {
console.error('Error:', err);
return;
}
console.log('Users:', users);
});
```
### SET - Asignar a Variable
```javascript
var db = DBMS();
await db.find('users')
.where('active', true)
.set('activeUsers') // Asigna resultado a db.$output.activeUsers
.promise();
console.log(db.$output.activeUsers);
```
### DEBUG - Activar Logging
```javascript
var users = await DBMS()
.debug() // Activa logging de la query
.find('users')
.where('active', true)
.promise();
// Output en consola:
// ICORM ---> { collection: 'mydb.users', condition: { active: true }, options: {} }
```
---
## 🎯 Ejemplos Complejos
### Búsqueda Avanzada de Productos
```javascript
async function searchProducts(filters) {
var db = DBMS().find('products');
// Filtro por categorías
if (filters.categories && filters.categories.length > 0) {
db.in('category', filters.categories);
}
// Rango de precios
if (filters.minPrice || filters.maxPrice) {
if (filters.minPrice)
db.where('price', '>=', filters.minPrice);
if (filters.maxPrice)
db.where('price', '<=', filters.maxPrice);
}
// Búsqueda de texto
if (filters.search) {
db.or(function(builder) {
builder.search('name', filters.search);
builder.search('description', filters.search);
builder.search('sku', filters.search);
});
}
// Solo activos con stock
db.where('active', true);
db.where('stock', '>', 0);
// Excluir campos sensibles
db.fields('-cost', '-supplier');
// Ordenamiento
if (filters.sortBy === 'price-asc') {
db.sort('price');
} else if (filters.sortBy === 'price-desc') {
db.sort('price', true);
} else if (filters.sortBy === 'name') {
db.sort('name');
} else {
db.sort('created', true); // Más recientes primero
}
// Paginación
var page = filters.page || 1;
var limit = filters.limit || 20;
db.take(limit);
db.skip((page - 1) * limit);
return await db.promise();
}
// Uso
var products = await searchProducts({
categories: ['electronics', 'computers'],
minPrice: 500,
maxPrice: 2000,
search: 'laptop',
sortBy: 'price-asc',
page: 1,
limit: 20
});
```
### Query Builder Reutilizable
```javascript
function createUserQuery(baseFilters) {
var db = DBMS().find('users');
// Filtros base siempre aplicados
db.where('deleted', false);
if (baseFilters.active !== undefined) {
db.where('active', baseFilters.active);
}
return db;
}
// Uso
var activeUsers = await createUserQuery({ active: true })
.where('age', '>=', 18)
.sort('name')
.promise();
var inactiveAdmins = await createUserQuery({ active: false })
.where('role', 'admin')
.sort('created', true)
.promise();
```
### Búsqueda con Múltiples Filtros Opcionales
```javascript
async function findUsers(criteria) {
var db = DBMS().find('users');
// WHERE dinámico
Object.keys(criteria).forEach(function(key) {
var value = criteria[key];
if (value !== undefined && value !== null && value !== '') {
if (key === 'search') {
// Búsqueda de texto en múltiples campos
db.or(function(builder) {
builder.search('name', value);
builder.search('email', value);
});
} else if (key === 'minAge') {
db.where('age', '>=', value);
} else if (key === 'maxAge') {
db.where('age', '<=', value);
} else if (key === 'roles' && Array.isArray(value)) {
db.in('role', value);
} else {
db.where(key, value);
}
}
});
return await db.promise();
}
// Uso
var results = await findUsers({
active: true,
minAge: 18,
maxAge: 65,
search: 'john',
roles: ['admin', 'moderator']
});
```
---
[← Volver al Índice](./README.md) | [Siguiente: Operadores →](./mongodb-operators.md)