@teamnet/ic-orm
Version:
Database Management System for Total.js v4 and standalone
660 lines (533 loc) • 16.2 kB
Markdown
# 💡 Ejemplos Prácticos - MongoDB con icorm
Casos de uso reales y ejemplos completos para aplicaciones con MongoDB e icorm en Total.js v4.
## 📋 Tabla de Contenidos
- [Sistema de Usuarios Completo](#sistema-de-usuarios-completo)
- [Catálogo de Productos](#catálogo-de-productos)
- [Sistema de Pedidos](#sistema-de-pedidos)
- [Blog con Comentarios](#blog-con-comentarios)
- [Alta Concurrencia](#alta-concurrencia)
---
## 👥 Sistema de Usuarios Completo
### Registro y Login
```javascript
// Controller: controllers/auth.js
// Registrar nuevo usuario
async function register() {
var self = this;
try {
// Verificar si el email ya existe
var exists = await DBMS()
.one('users')
.where('email', self.body.email.toLowerCase())
.fields('_id')
.promise();
if (exists) {
return self.invalid('El email ya está registrado');
}
// Hashear password
var bcrypt = require('bcrypt');
var hashedPassword = await bcrypt.hash(self.body.password, 10);
// Crear usuario
var user = await DBMS()
.insert('users', {
email: self.body.email.toLowerCase(),
password: hashedPassword,
name: self.body.name,
role: 'user',
active: true,
verified: false,
loginCount: 0,
created: new Date()
})
.promise();
// No retornar password
delete user.password;
self.json(user);
} catch(err) {
self.invalid(err);
}
}
// Login de usuario
async function login() {
var self = this;
try {
var user = await DBMS()
.one('users')
.where('email', self.body.email.toLowerCase())
.promise();
if (!user || !user.active) {
return self.invalid('Credenciales inválidas');
}
// Verificar password
var bcrypt = require('bcrypt');
var validPassword = await bcrypt.compare(self.body.password, user.password);
if (!validPassword) {
return self.invalid('Credenciales inválidas');
}
// Actualizar estadísticas
await DBMS()
.modify('users', {
lastLogin: new Date(),
'+loginCount': 1
})
.where('_id', user._id)
.promise();
delete user.password;
self.json({ success: true, user: user });
} catch(err) {
self.invalid(err);
}
}
```
### Perfil de Usuario
```javascript
// Obtener perfil
async function getProfile() {
var self = this;
try {
var user = await DBMS()
.one('users')
.where('_id', self.user.id)
.fields('-password')
.promise();
self.json(user);
} catch(err) {
self.invalid(err);
}
}
// Actualizar perfil
async function updateProfile() {
var self = this;
try {
await DBMS()
.modify('users', {
name: self.body.name,
phone: self.body.phone,
avatar: self.body.avatar,
updated: new Date()
})
.where('_id', self.user.id)
.promise();
self.json({ success: true });
} catch(err) {
self.invalid(err);
}
}
```
---
## 🛍️ Catálogo de Productos
### Listado con Filtros
```javascript
async function listProducts() {
var self = this;
try {
var page = +self.query.page || 1;
var limit = +self.query.limit || 20;
var db = DBMS().find('products');
// Filtros
if (self.query.category)
db.where('category', self.query.category);
if (self.query.minPrice)
db.where('price', '>=', +self.query.minPrice);
if (self.query.maxPrice)
db.where('price', '<=', +self.query.maxPrice);
// Búsqueda
if (self.query.search) {
db.or(function(builder) {
builder.search('name', self.query.search);
builder.search('description', self.query.search);
});
}
// Solo activos con stock
db.where('active', true);
db.where('stock', '>', 0);
// Ordenamiento
if (self.query.sortBy === 'price-asc')
db.sort('price');
else if (self.query.sortBy === 'price-desc')
db.sort('price', true);
else
db.sort('created', true);
// Paginación
db.take(limit);
db.skip((page - 1) * limit);
var products = await db.promise();
self.json({ items: products });
} catch(err) {
self.invalid(err);
}
}
```
### Detalle de Producto
```javascript
async function getProduct() {
var self = this;
try {
var product = await DBMS()
.one('products')
.where('slug', self.params.slug)
.where('active', true)
.promise();
if (!product)
return self.invalid('Producto no encontrado');
// Incrementar vistas (asíncrono)
DBMS()
.modify('products', { '+views': 1 })
.where('_id', product._id)
.callback(function() {});
self.json(product);
} catch(err) {
self.invalid(err);
}
}
```
---
## 📦 Sistema de Pedidos
### Crear Pedido
```javascript
async function createOrder() {
var self = this;
try {
var items = self.body.items; // [{productId, quantity, price}, ...]
// Verificar stock de cada item
for (var item of items) {
var product = await DBMS()
.one('products')
.where('_id', item.productId)
.fields('stock', 'price')
.promise();
if (!product || product.stock < item.quantity) {
return self.invalid('Stock insuficiente para ' + item.productId);
}
}
// Calcular totales
var subtotal = items.reduce((sum, item) => sum + (item.price * item.quantity), 0);
var tax = subtotal * 0.16;
var total = subtotal + tax;
// Crear pedido
var order = await DBMS()
.insert('orders', {
userId: self.user.id,
orderNumber: 'ORD-' + Date.now(),
items: items,
subtotal: subtotal,
tax: tax,
total: total,
status: 'pending',
shippingAddress: self.body.shippingAddress,
created: new Date()
})
.promise();
// Actualizar stock de cada producto
for (var item of items) {
await DBMS()
.modify('products', {
'-stock': item.quantity,
'+sold': item.quantity
})
.where('_id', item.productId)
.promise();
}
self.json(order);
} catch(err) {
self.invalid(err);
}
}
```
### Consultar Pedidos
```javascript
async function getOrders() {
var self = this;
try {
var orders = await DBMS()
.find('orders')
.where('userId', self.user.id)
.sort('created', true)
.promise();
self.json(orders);
} catch(err) {
self.invalid(err);
}
}
```
---
## 📝 Blog con Comentarios
### Crear Post
```javascript
async function createPost() {
var self = this;
try {
var slug = self.body.title
.toLowerCase()
.replace(/[^a-z0-9]+/g, '-')
.replace(/^-+|-+$/g, '');
var post = await DBMS()
.insert('posts', {
title: self.body.title,
slug: slug,
content: self.body.content,
authorId: self.user.id,
authorName: self.user.name,
published: false,
views: 0,
likes: 0,
commentsCount: 0,
tags: self.body.tags || [],
created: new Date()
})
.promise();
self.json(post);
} catch(err) {
self.invalid(err);
}
}
```
### Agregar Comentario
```javascript
async function addComment() {
var self = this;
try {
// Crear comentario
var comment = await DBMS()
.insert('comments', {
postId: self.params.postId,
userId: self.user.id,
userName: self.user.name,
content: self.body.content,
likes: 0,
created: new Date()
})
.promise();
// Incrementar contador de comentarios en el post
await DBMS()
.modify('posts', {
'+commentsCount': 1
})
.where('_id', self.params.postId)
.promise();
self.json(comment);
} catch(err) {
self.invalid(err);
}
}
```
### Listar Posts con Paginación
```javascript
async function listPosts() {
var self = this;
try {
var page = +self.query.page || 1;
var limit = 10;
var result = await new Promise((resolve, reject) => {
DBMS()
.list('posts')
.where('published', true)
.sort('created', true)
.take(limit)
.skip((page - 1) * limit)
.callback(function(err, items, count) {
if (err) reject(err);
else resolve({
items,
count,
page,
pages: Math.ceil(count / limit)
});
});
});
self.json(result);
} catch(err) {
self.invalid(err);
}
}
```
---
## ⚡ Alta Concurrencia
### Sistema de Likes Thread-Safe
```javascript
// Agregar like (atómico)
async function addLike() {
var self = this;
try {
var postId = self.params.postId;
var userId = self.user.id;
// Verificar si ya le dio like
var exists = await DBMS()
.one('likes')
.where('postId', postId)
.where('userId', userId)
.fields('_id')
.promise();
if (exists) {
return self.invalid('Ya le diste like');
}
// Registrar like
await DBMS()
.insert('likes', {
postId: postId,
userId: userId,
created: new Date()
})
.promise();
// Incrementar contador (atómico)
await DBMS()
.modify('posts', {
'+likes': 1
})
.where('_id', postId)
.promise();
self.json({ success: true });
} catch(err) {
self.invalid(err);
}
}
// Quitar like
async function removeLike() {
var self = this;
try {
var postId = self.params.postId;
var userId = self.user.id;
// Eliminar like
var count = await DBMS()
.remove('likes')
.where('postId', postId)
.where('userId', userId)
.promise();
if (count === 0) {
return self.invalid('No habías dado like');
}
// Decrementar contador (atómico)
await DBMS()
.modify('posts', {
'-likes': 1
})
.where('_id', postId)
.promise();
self.json({ success: true });
} catch(err) {
self.invalid(err);
}
}
```
### Procesamiento Batch
```javascript
// Procesar grandes volúmenes de datos
async function processLargeDataset() {
var batchSize = 1000;
var skip = 0;
var processed = 0;
while (true) {
// Obtener batch
var records = await DBMS()
.find('logs')
.where('processed', false)
.take(batchSize)
.skip(skip)
.promise();
if (records.length === 0)
break;
// Procesar cada registro
for (var record of records) {
// Hacer algo con el registro
await processRecord(record);
// Marcar como procesado
await DBMS()
.modify('logs', {
processed: true,
processedAt: new Date()
})
.where('_id', record._id)
.promise();
}
processed += records.length;
console.log(`Procesados ${processed} registros...`);
skip += batchSize;
}
console.log(`Total procesados: ${processed}`);
}
```
### Estadísticas en Tiempo Real
```javascript
async function getDashboardStats() {
try {
// Ejecutar múltiples queries en paralelo
var [totalUsers, activeUsers, totalOrders, todayRevenue] = await Promise.all([
// Total de usuarios
new Promise((resolve, reject) => {
DBMS()
.scalar('users', 'count')
.callback((err, result) => err ? reject(err) : resolve(result));
}),
// Usuarios activos
new Promise((resolve, reject) => {
DBMS()
.scalar('users', 'count')
.where('active', true)
.callback((err, result) => err ? reject(err) : resolve(result));
}),
// Total de pedidos
new Promise((resolve, reject) => {
DBMS()
.scalar('orders', 'count')
.callback((err, result) => err ? reject(err) : resolve(result));
}),
// Ingresos de hoy
new Promise((resolve, reject) => {
var today = new Date();
today.setHours(0, 0, 0, 0);
DBMS()
.scalar('orders', 'sum', 'total')
.where('created', '>=', today)
.where('status', 'completed')
.callback((err, result) => err ? reject(err) : resolve(result || 0));
})
]);
return {
totalUsers,
activeUsers,
totalOrders,
todayRevenue
};
} catch(err) {
throw err;
}
}
```
---
## 🔧 Utilidades Comunes
### Función de Búsqueda Reutilizable
```javascript
function createSearchQuery(collection, filters) {
var db = DBMS().find(collection);
// Aplicar filtros dinámicamente
Object.keys(filters).forEach(function(key) {
var value = filters[key];
if (value === undefined || value === null || value === '')
return;
if (key === 'search') {
db.or(function(builder) {
builder.search('name', value);
builder.search('description', value);
});
} else if (key === 'minPrice') {
db.where('price', '>=', value);
} else if (key === 'maxPrice') {
db.where('price', '<=', value);
} else if (Array.isArray(value)) {
db.in(key, value);
} else {
db.where(key, value);
}
});
return db;
}
// Uso
var products = await createSearchQuery('products', {
category: 'electronics',
minPrice: 100,
maxPrice: 1000,
search: 'laptop'
}).promise();
```
---
[← Volver al Índice](./README.md)