No tienes acceso a esta clase

隆Contin煤a aprendiendo! 脷nete y comienza a potenciar tu carrera

Relaciones uno a muchos referenciadas

22/24
Recursos

Es crucial dise帽ar bien tu base de datos. Las relaciones uno a muchos pueden crecer indefinidamente y es importante considerar la escalabilidad de tu base de datos.

Relaciones uno a muchos embebidas vs. referenciadas

A lo igual que las relaciones uno a uno, las relaciones uno a muchos requieren contemplar la posible actualizaci贸n de todo ese array de documentos. Tener un array de objetos puede no ser escalable y una no muy buena decisi贸n de arquitectura de la base de datos.

Estos problemas se solucionan creando arrays de referencias. Un simple array de string que contendr谩 un item por cada ID al cual deseas hacer referencia dentro de un objeto principal.

Relaciones referenciadas

Veamos como puedes crear este tipo de relaci贸n donde un documento contendr谩 un array de IDs que hacen referencia a otros documentos de la base de datos.

Paso 1: preparaci贸n del esquema

Prepara la propiedad correspondiente en tu esquema que contendr谩 el array de referencias.

// users/order.entity.ts
import { Document, Types } from 'mongoose';
import { Product } from '../../products/entities/product.entity';

@Schema()
export class Order extends Document {

  @Prop({ type: [{ type: Types.ObjectId, ref: Product.name }] })
  products: Types.Array<Product>;
}

Observa que el decorador @Prop() recibe como tipo un Types.ObjectId que a su vez se encuentra encerrado por un array []. Tambi茅n, tienes que tipar la propiedad con Types.Array<> proveniente desde mongoose.
De esta simple manera, Mongoose sabe que la propiedad products contiene un array de MongoID.

Paso 2: preparar el DTO para la validaci贸n de datos

El DTO solo necesita recibir un array de string[] que es el equivalente para un array de MongoID.

// users/order.dto.ts
import { IsMongoId, IsNotEmpty, IsDate, IsArray } from 'class-validator';
import { OmitType, PartialType } from '@nestjs/swagger';

export class CreateOrderDto {

  @IsArray()
  @IsNotEmpty()
  readonly products: string[];
}

Utiliza el decorador @IsArray() para validar que efectivamente se trate de un array.

Paso 3: GET de documentos referenciados

Realizar un 鈥淛oin鈥 o, como se lo conoce en MongoDB, un 鈥淧opulate鈥 es muy sencillo. Basta con agregar la configuraci贸n despu茅s del m茅todo de b煤squeda indicando el nombre de la propiedad a popular.

// users/orders.service.ts

export class OrdersService {

  constructor(@InjectModel(Order.name) private orderModel: Model<Order>) {}

  findAll() {
    return this.orderModel
      .find()
      .populate('products')
      .exec();
  }
}

Mongoose sabr谩 a qu茅 colecci贸n ir a buscar los documentos al estar referenciado y tipado desde el esquema.

De esta manera, tu base de datos est谩 lista para manipular grandes vol煤menes de datos con los mejores tipos de relaciones que existen. Utiliza el tipo de relaci贸n m谩s apropiado para cada escenario.

Contribuci贸n creada por: Kevin Fiorentino (Platzi Contributor).


C贸digo de ejemplo de relaciones uno a muchos referenciadas

// src/users/entities/order.entity.ts
import { Document, Types } from 'mongoose';

import { Customer } from './customer.entity';
import { Product } from '../../products/entities/product.entity';

@Schema()
export class Order extends Document {
   ...
  @Prop({ type: Types.ObjectId, ref: Customer.name, required: true })
  customer: Customer | Types.ObjectId; // 馃憟 relation 1:1 customer

  @Prop({ type: [{ type: Types.ObjectId, ref: Product.name }] })
  products: Types.Array<Product>; // 馃憟 relation 1:N
}
// src/users/dtos/order.dto.ts
import { IsMongoId, IsNotEmpty, IsDate, IsArray } from 'class-validator';
import { OmitType, PartialType } from '@nestjs/swagger';  // 馃憟 use OmitType

export class CreateOrderDto {
  @IsNotEmpty()
  @IsMongoId()
  readonly customer: string;

  @IsDate()
  @IsNotEmpty()
  readonly date: Date;

  @IsArray()
  @IsNotEmpty()
  readonly products: string[];
}

export class UpdateOrderDto extends PartialType(
  OmitType(CreateOrderDto, ['products']),  // 馃憟 implement OmitType
) {}
// src/users/services/orders.service.ts

export class OrdersService {
  constructor(@InjectModel(Order.name) private orderModel: Model<Order>) {}

  findAll() {
    return this.orderModel
      .find()
      .populate('customer') // 馃憟 join customer 1:1
      .populate('products') // 馃憟 join products 1:N
      .exec();
  }
}

Aportes 14

Preguntas 5

Ordenar por:

驴Quieres ver m谩s aportes, preguntas y respuestas de la comunidad?

Excelente curso!!!

Para los que quieran popular, las skills tienen que usar un populate anidado

    return this.orderModel
      .find()
      .populate('productIds)
      .populate({
        path: 'customer',
        populate: {
          path: 'skills',
        },
      })
      .exec();

驴Qu茅 pasa si despu茅s de haber echo una Orden se modifica el precio de un producto?

Si se actualiza el precio de alguno de estos productos, al estar referenciado, la orden ya no tendr铆a el precio con el cual se compro el producto inicialmente.

Al d铆a de hoy ya no es necesario colocar el .exec()

Usando mongoose 8.3.3 y @nestjs/mongoose 10.0.6 no me funcionaba el `.populate('products')`. Si alguien tiene una soluci贸n similar a la de la clase me comenta, gracias de antemano. Por el momento me funcion贸 lo siguiente: 1\. Exportar MongooseModule para usar el modelo Product en OrdersService ```ts // src/products/products.module.ts exports: [ProductsService, MongooseModule], ``` 2\. Inyectar el modelo y modificar el m茅todo findAll() ```ts // src/users/services/orders.service.ts import { Product } from 'src/products/entities/product.entity'; // ... // ... constructor( @InjectModel(Order.name) private orderModel: Model<Order>, @InjectModel(Product.name) private productModel: Model<Product>, ) {} // ... // ... async findAll() { const orders = await this.orderModel.find(); for (const order of orders) { await Promise.all( order.products.map(async (productId, index) => { const product = await this.productModel.findById(productId); order.products[index] = product; }), ); } return orders; } ```
Usando mongoose 8.3.3 y @nestjs/mongoose 10.0.6 no funcionaba el populate('products'). Si alguien tiene una soluci贸n similar a la de la clase me comenta, gracias de antemano. Por el momento a mi me funcion贸 lo siguiente: 1\. Exportar MongooseModule para usar el modelo Product en OrdersService ```ts // src/products/products.module.ts exports: [ProductsService, MongooseModule], ``` 2\. Inyectar el modelo y modificar el m茅todo findAll() ```ts // src/users/services/orders.service.ts import { Product } from 'src/products/entities/product.entity'; // ... // ... constructor( @InjectModel(Order.name) private orderModel: Model<Order>, @InjectModel(Product.name) private productModel: Model<Product>, ) {} // ... // ... async findAll() { const orders = await this.orderModel.find(); for (const order of orders) { await Promise.all( order.products.map(async (productId, index) => { const product = await this.productModel.findById(productId); order.products[index] = product; }), ); } return orders; } ```
Usando mongoose 8.3.3 y @nestjs/mongoose 10.0.6 no funcionaba el populate('products'). Si alguien tiene una soluci贸n similar a la de la clase me comenta, gracias de antemano. A mi me funcion贸 lo siguiente: 1. Exportar MongooseModule para usar el modelo Product en OrdersService 2. Inyectar el modelo y modificar el m茅todo findAll() ```ts // src/products/products.module.ts exports: \[ProductsService, MongooseModule], ``` ```ts // src/users/services/orders.service.ts import { Product } from 'src/products/entities/product.entity'; // ... // ... constructor( @InjectModel(Order.name) private orderModel: Model\<Order\>, @InjectModel(Product.name) private productModel: Model\<Product\>, ) {} // ... // ... async findAll() { const orders = await this.orderModel.find(); for (const order of orders) { await Promise.all( order.products.map(async (productId, index) =\> { const product = await this.productModel.findById(productId); order.products\[index] = product; }), ); } return orders; } ```
```ts dsdas ``````ts ```
```ts sdsad ```sadasdas

Primera vez que me pasa esto en insomnia y no se a que se debe.

Excelente curso, enserio!!

Usando mongoose 8.3.3 y @nestjs/mongoose 10.0.6 no me funcionaba el .populate('products'). Si alguien tiene una soluci贸n similar a la de la clase me comenta, gracias de antemano. Por el momento me funcion贸 lo siguiente: 1\. Exportar MongooseModule para usar el modelo Product en OrdersService ```ts // src/users/services/orders.service.ts import { Product } from 'src/products/entities/product.entity'; ``` 2\. Inyectar el modelo y modificar el m茅todo findAll() ```ts // src/users/services/orders.service.ts import { Product } from 'src/products/entities/product.entity'; // ... // ... constructor( @InjectModel(Order.name) private orderModel: Model<Order>, @InjectModel(Product.name) private productModel: Model<Product>, ) {} // ... // ... async findAll() { const orders = await this.orderModel.find(); for (const order of orders) { await Promise.all( order.products.map(async (productId, index) => { const product = await this.productModel.findById(productId); order.products[index] = product; }), ); } return orders; } ```
Usando mongoose 8.3.3 y @nestjs/mongoose 10.0.6 no funcionaba el populate('products'). Si alguien tiene una soluci贸n similar a la de la clase me comenta, gracias de antemano. A mi me funcion贸 lo siguiente: 1. Exportar MongooseModule para usar el modelo Product en OrdersService 2. Inyectar el modelo y modificar el m茅todo findAll() ```ts // src/products/products.module.ts exports: [ProductsService, MongooseModule], ``````js // src/users/services/orders.service.ts import { Product } from 'src/products/entities/product.entity'; // ... // ... constructor( @InjectModel(Order.name) private orderModel: Model<Order>, @InjectModel(Product.name) private productModel: Model<Product>, ) {} // ... // ... async findAll() { const orders = await this.orderModel.find(); for (const order of orders) { await Promise.all( order.products.map(async (productId, index) => { const product = await this.productModel.findById(productId); order.products[index] = product; }), ); } return orders; } ```
Usando mongoose 8.3.3 y @nestjs/mongoose 10.0.6 no funcionaba el `populate('products')`. No s茅 si alguien encontr贸 la soluci贸n. A mi me funcion贸 lo siguiente: 1\. Exportar MongooseModule para usar el modelo Product en OrdersService ```ts // src/products/products.module.ts exports: [ProductsService, MongooseModule], ```2. Inyectar el modelo y modificar el m茅todo findAll() ```ts // src/users/services/orders.service.ts import { Product } from 'src/products/entities/product.entity'; // ... // ... constructor( @InjectModel(Order.name) private orderModel: Model<Order>, @InjectModel(Product.name) private productModel: Model<Product>, ) {} // ... // ... async findAll() { const orders = await this.orderModel.find(); for (const order of orders) { await Promise.all( order.products.map(async (productId, index) => { const product = await this.productModel.findById(productId); order.products[index] = product; }), ); } return orders; } ```Si alguien tiene una soluci贸n similar a la de la clase me comenta, gracias de antemano.