No tienes acceso a esta clase

¡Continúa aprendiendo! Únete y comienza a potenciar tu carrera

Convierte tus certificados en títulos universitarios en USA

Antes: $249

Currency
$209

Paga en 4 cuotas sin intereses

Paga en 4 cuotas sin intereses
Suscríbete

Termina en:

18 Días
2 Hrs
5 Min
48 Seg

Agregando un filtro de rango para precios

16/24
Recursos

Los endpoints del tipo GET, además de paginar la información, pueden tener otro tipo de filtros más específicos para una regla de negocio. MongoDB permite crear cualquier tipo de filtro que necesite tu servicio.

Filtro de rango numérico

Crear un filtro entre dos números X e Y que podrían ser años o precios de productos es un caso de uso bastante habitual en la consulta de datos.

Paso 1: tipado de datos

Comienza creando el DTO para que el constructor reciba estos datos.

// products/products.dto.ts
import { ValidateIf } from 'class-validator';

export class FilterProductsDto {

  @IsOptional()
  @Min(0)
  minPrice: number;

  @ValidateIf((params) => params.minPrice)
  @IsPositive()
  maxPrice: number;
}

Utilizamos el decorador @ValidateIf() para validar el precio máximo solo si existe el mínimo y también es válido.

Paso 2: preparar el servicio para el filtro

Preparar el servicio para recibir estos nuevos datos y filtrar por rango mínimo y máximos un determinado campo del esquema.

// products/products.service.ts
import { Model, FilterQuery } from 'mongoose';

@Injectable()
export class ProductsService {

  findAll(params?: FilterProductsDto) {
    if (params) {
      const filters: FilterQuery<Product> = {};
      const { minPrice, maxPrice } = params;
      if (minPrice && maxPrice) {
        filters.price = { $gte: minPrice, $lte: maxPrice };
      }
      return this.productModel.find(filters).exec();
    }
    return this.productModel.find().exec();
  }
}

Para crear el filtro de rangos máximos y mínimos, MongoDB utiliza operadores de comparaciones especiales:

  • $gte equivalente a >=
  • $lte equivalente a <=

De esta manera, el servicio está preparado para realizar un filtro numérico si el usuario ingresa el rango. Recuerda que este tipo de filtros suelen ser opcionales, el endpoint debe seguir funcionando correctamente en el caso de que no se ingrese un filtro.

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


Código de ejemplo para filtro de rango de precios

// src/products/dtos/products.dtos.ts

import {
  ...,
  ValidateIf // 👈 new decorator
} from 'class-validator';

export class FilterProductsDto {
  ...
  @IsOptional()
  @Min(0)
  minPrice: number; // 👈 new field

  @ValidateIf((params) => params.minPrice)
  @IsPositive()
  maxPrice: number;  // 👈 new field
}
// src/products/services/products.service.ts
import { Model, FilterQuery } from 'mongoose';

@Injectable()
export class ProductsService {
  ... 

  findAll(params?: FilterProductsDto) {
    if (params) {
      const filters: FilterQuery<Product> = {}; // 👈 create filters
      const { limit, offset } = params;
      const { minPrice, maxPrice } = params; // 👈
      if (minPrice && maxPrice) {
        filters.price = { $gte: minPrice, $lte: maxPrice };
      }
      return this.productModel.find(filters).skip(offset).limit(limit).exec();
    }
    return this.productModel.find().exec();
  }
  ..
}

Aportes 9

Preguntas 6

Ordenar por:

¿Quieres ver más aportes, preguntas y respuestas de la comunidad?

En general los cursos de NestJs son excelentes me están gustando mucho espero creen más con todo el tema de los test.
El profesor lo explica todo muy claro.

Excelente clase, todo se entiende, todo funciona de lo más bien 😃

Cuando se didiregen a la ruta de docs para poder probar la documentación con swagger, me di cuenta que los parámetros están en

*required 

, cuando estos deberian ser opcionales. En el FilterDto le agregué esto para que no sean obligatorios pasarlos. ¿Será la única solución?

export class FilterProductsDto {
@ApiProperty({ required: false })
@IsOptional()
@IsPositive()
limit: number;

@ApiProperty({ required: false })
@IsOptional()
@Min(0)
offset: number;
} 

Se podría validad en ambos datos

@IsOptional()
  @ValidateIf((params) => params.maxPrice)
  @Min(0)
  minPrice: number;  

  @ValidateIf((params) => params.minPrice)
  @IsPositive()
  maxPrice: number;   
findAll(params?: FilterProductsDto) {
    if (params) {
      const filters: FilterQuery<Product> = {};
      const limit = params.limit || 10;
      const offset = params.offset || 0;
      const { minPrice, maxPrice } = params;
      if (minPrice && maxPrice) {
        filters.price = { $gte: minPrice, $lte: maxPrice };
      }
      return this.productModel
        .find(filters)
        .skip(offset * limit)
        .limit(limit)
        .exec();
    }
    return this.productModel.find().exec();
  }

Recuerden que no estamos validando que se pueda poner solamente maxPrice, el código nos deja, pero en nuestro product.service no tenemos nada de código para este caso, un ejemplo podría ser:

 findAll(params?: FilterProductsDto) {
    const filters: FilterQuery<Product> = {};
    if (params) {
      const { limit, offset, minPrice, maxPrice } = params;
      if (minPrice && maxPrice) {
        filters.price = { $gte: minPrice, $lte: maxPrice };
      } else if (maxPrice && !minPrice) {
        filters.price = { $lte: maxPrice };
      }
      return this.productModel.find(filters).skip(offset).limit(limit).exec();
    }
    return this.productModel.find().exec();
  }
Yo lo hice así para que se le pueda mandar solo un precio máximo y para que si le enviamos minPrice igual 0 no arroje false como resultado del if ```js async getAll(params?: FilterProductsDto) { if (params) { const filter: FilterQuery<Product> = {}; const { limit, offset, minPrice = 0, maxPrice } = params; if (minPrice >= 0 && maxPrice) { filter.price = { $gte: minPrice, $lte: maxPrice }; } return await this.productModel.find(filter).skip(offset).limit(limit); } return await this.productModel.find(); } ```

Recordar que cero es false en javascript, por lo tanto la condición debe ser:

if (minPrice >= 0 && maxPrice > 0) { }

excelente si en el query se envía un obejeto where para comparar con cualquier campo o atributo de la colección