UNPKG

@teamnet/ic-orm

Version:

Database Management System for Total.js v4 and standalone

660 lines (533 loc) 16.2 kB
# 💡 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)