Los esquemas que contienen propiedades tipo Array deben manipularse de una forma especial. Haciendo los típicos push/pullpara agregar/quitar elementos, pero considerando la asincronía y que estos arrays se guardan en una base de datos.
Agregar y quitar elementos en un array
Veamos cómo es posible agregar/quitar elementos de un array que forma parte de un documento en MongoDB:
// users/services/orders.service.tsexportclassOrdersService{asyncaddProducts(id:string, productsIds:string[]){const order =awaitthis.orderModel.findById(id); productsIds.forEach((pId)=> order.products.push(pId));return order.save();}asyncremoveProduct(id:string, productId:string){const order =awaitthis.orderModel.findById(id); order.products.pull(productId);return order.save();}}
La función findById() devolverá la referencia del documento encontrado a través de su ID. Gracias a esto, puedes ejecutar otras acciones sobre ese mismo documento para agregar elementos a un array con push() o removerlos con pull(). Finalmente, la función save() actualizará en la base de datos el documento completo.
Del lado del controlador, haz una simple llamada a estas funciones que se encargarán de la manipulación de, en este caso, el array de productos.
Ten en cuenta que este tipo de operaciones son asíncronas, dependiendo de cómo armes tu controlador y el servicio encargado de realizar las modificaciones en la base de datos.
Operadores especiales de MongoDB
Si trabajas directamente con MongoDB, tienes que conocer los operadores que implementa para la manipulación de arrays. Estos son:
$pullAll para eliminar todos los items en un array
De esta manera, tu mismo puedes crear las consultas a tu base de datos necesarias para manipular un array dentro de un documento, sin necesidad de que herramientas como Mongoose implementen una capa de abstracción que facilite la tarea.
Contribución creada por: Kevin Fiorentino (Platzi Contributor).
Código de ejemplo para manipulación de arrays
// src/users/services/orders.service.tsexportclassOrdersService{...asyncremoveProduct(id:string, productId:string){// 👈 const order =awaitthis.orderModel.findById(id); order.products.pull(productId);return order.save();}asyncaddProducts(id:string, productsIds:string[]){// 👈 const order =awaitthis.orderModel.findById(id); productsIds.forEach((pId)=> order.products.push(pId));return order.save();}}
Para evitar duplicados en orders.productsIds podemos usar el operador de mongo $addToSet. Este operador agrega los elementos al array a menos que el valor ya se encuentre en el mismo.
// ProductsasyncaddProducts(idOrder: string,productsIds: string[]){const order =awaitthis.OrderModel.findByIdAndUpdate( idOrder,{$addToSet:{productos: productsIds }})if(!order){thrownewNotFoundException(`order ${idOrder} not found`);}returnawait order.save();}
Lo ideal seria crear una nueva logica que incluya la cantidad del producto. Asi, se podria enviar n veces el id del producto pero solo actualizariamos la cantidad del mismo
asyncaddProduct(id: string,productsIds: string[]){const order =awaitthis.orderModel.findById(id);if(order){ productsIds.forEach((element)=>{// order.products.push(element); order.products.addToSet(element);});return order.save();}}
Para el metodo push tambien se puede usar el spread operator de la siguiente manera
asyncaddProducts(id:string,productIds:string[]){const order =awaitthis.orderModel.findById(id) order.products.push(...productIds)return order.save()}}
Algo que no entiendo es ¿Por qué algunos métodos que usan mongoose lse les poné await pero a otros no?
Porque no se completó simplemente.
Estoy sufriendo un poco ya que estoy intentando hacer pull de una referencia que esta dentro de un array pero este a su vez esta dentro de otro objeto.
[{"_id":"618eb34fe87705119f14d35c","name":"Proyecto test","description":"Control de tanque de agua!","owner":"salvador@iotest.com","slug":"Proyecto-test","structure":{"datasets":["618f9459d5e03d172af0dff9"<---],"_id":"618eb34fe87705119f14d35e"},"data":[],"__v":0}]
VS Code me dice que el metodo pull esta disponible pero al ejecutar me aparece el siguiente error:
TypeError: project.structure.datasets.pull is not a function
async removeDataset(projectId: string, datasetId: string) {
const project = await this.ProjectModel.findById(projectId);
project.structure.datasets.pull(datasetId);
}
Alguna idea de que puede ser?
Hola, al parecer puede ser que el Schema esté mal definido, por ejemplo para definir los arrays debes usar algo similar a esto:
Sin embargo veo que tú no lo tienes como un array directo sino como un subDoc en structure y este tiene el array entonces hay que revisar como definiste el Schema. ¿Podrías compartirnos el Schema para verlo por favor?
Como se puede implementar para hacer los populates automáticos ya que tengo una relación con un documento que a su vez tiene relación a otro pero la ultima relación no me la resuelve ejemplo:
Quisiera obtener el name del modelo 3 pero no me hace el populate
tengo un problema con los datos que almaceno en los documentos en una base de datos de mongoDB, sólo se están almacenando hasta un histórido de lo guardado las últimas 24 horas y de ahí hacia atrás no se visualizan y pareciera que se borrarán, alguien sabe con que puede estar relacionado?
Uff esto es raro cuéntanos sobre tu ambiente es decir ¿Es una DB gratis en Mongo Atlas o Mongo corriendo dentro de un contenedor en tu local?
Como podría hacer si de la orden quiero modificar un solo producto, embebido así como los ejemplos de eliminar y adicionar.
se puede modificar solo uno producto?
o si quiero modificar un elemento del skill de customer como se podria realizar?
La API de Moongose con los arrays no darian varias formas, ejemplo:
orders.products.pull(id) => eliminar solo un producto del array
orders.products.pull(id) => agrega solo un producto al array
Saludos, en los schemas hay algun atributo para tener un objeto indefinido, es decir, pueda haber un objeto con 3 parametros otro con 5 parametros etc?
Puedes colocar en tu schema que hay parámetros opcionales con eso sabes que el Schema no te los va a pedir ciertos campos como obligatorios y ya en tu lógica puedes hacer las validaciones necesarias con esto logras tener documentos dentro de tu DB que tengan campos dinámicos.