You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在TypeScript DTO(结合class-validator)中使用MongoDB查询运算符?

解决MongoDB查询运算符在DTO中的使用限制问题

问题背景

现有QueryUserDto代码如下:

import { ApiHideProperty } from '@nestjs/swagger';
import { Transform } from 'class-transformer';
import {
  IsEmpty,
  IsEnum,
  IsMongoId,
  IsOptional,
  IsString,
} from 'class-validator';
import { QueryModifierDto } from 'src/common/dtos/query-modifier.dto';

export class QueryUserDto extends QueryModifierDto {
  @IsOptional()
  @IsMongoId()
  _id?: string;

  @IsOptional()
  @IsString()
  firstName?: string;

  @IsOptional()
  @IsString()
  lastName?: string;

  @IsOptional()
  @Transform(({ value }) => value.toLowerCase())
  @IsString()
  email?: string;

  @IsOptional()
  @IsEnum(['admin', 'user'], { message: 'Role must be either admin or user' })
  role?: string;

  @ApiHideProperty()
  @IsOptional()
  @IsEmpty()
  tokens?: string[];
}

该DTO无法支持包含MongoDB查询运算符的查询,例如:

const user = await this.usersService.findOne({
  tokens: { $in: [payload.token] },
});

需求是允许使用任意合法的MongoDB查询运算符,同时不影响客户端发起的常规查询。参考的QueryModifierDto代码:

import { IsNumber, IsOptional, IsPositive, IsString } from 'class-validator';

export class QueryModifierDto {
  @IsOptional()
  @IsPositive()
  @IsNumber()
  page?: number;

  @IsOptional()
  @IsPositive()
  @IsNumber()
  limit?: number;

  @IsOptional()
  @IsString()
  fields?: string;

  @IsOptional()
  @IsString()
  sort?: string;
}

解决方案

核心思路是给需要支持运算符的字段定义混合类型,通过条件验证区分普通值和运算符对象,既保留原有客户端查询的验证逻辑,又允许服务层使用MongoDB查询语法。

1. 修改字段验证规则

以tokens字段为例,调整验证逻辑,同时支持数组和MongoDB运算符对象:

import { ApiHideProperty } from '@nestjs/swagger';
import { Transform } from 'class-transformer';
import {
  IsEnum,
  IsMongoId,
  IsOptional,
  IsString,
  IsArray,
  ValidateIf,
  IsObject,
} from 'class-validator';
import { QueryModifierDto } from 'src/common/dtos/query-modifier.dto';

export class QueryUserDto extends QueryModifierDto {
  @IsOptional()
  @IsMongoId()
  _id?: string;

  @IsOptional()
  @IsString()
  firstName?: string;

  @IsOptional()
  @IsString()
  lastName?: string;

  @IsOptional()
  @Transform(({ value }) => {
    // 仅对普通字符串转小写,运算符对象不处理
    return typeof value === 'string' ? value.toLowerCase() : value;
  })
  @ValidateIf((obj, value) => typeof value === 'string')
  @IsString()
  @ValidateIf((obj, value) => typeof value === 'object' && !Array.isArray(value))
  @IsObject()
  email?: string | Record<string, any>;

  @IsOptional()
  @IsEnum(['admin', 'user'], { message: 'Role must be either admin or user' })
  role?: string;

  @ApiHideProperty()
  @IsOptional()
  // 条件验证:非对象时验证为数组,对象时验证为合法查询结构
  @ValidateIf((obj, value) => typeof value !== 'object' || Array.isArray(value))
  @IsArray()
  @ValidateIf((obj, value) => typeof value === 'object' && !Array.isArray(value))
  @IsObject()
  tokens?: string[] | Record<string, any>;
}

2. 服务层使用示例

修改后即可自由使用MongoDB查询运算符,同时客户端常规查询不受影响:

// 使用$in运算符查询
const user = await this.usersService.findOne({
  tokens: { $in: [payload.token] },
});

// 客户端传入普通值的查询依然有效
const activeUsers = await this.usersService.find({
  role: 'user',
});

3. 类型安全优化(可选)

为了获得TypeScript类型提示,可以定义MongoDB查询运算符的类型:

// 定义常用查询运算符类型
type MongoQueryOperator<T> = {
  $eq?: T;
  $ne?: T;
  $in?: T[];
  $nin?: T[];
  $regex?: RegExp | string;
  // 按需添加其他运算符
};

// 字段类型替换为普通值或运算符对象
tokens?: string[] | MongoQueryOperator<string[]>;
email?: string | MongoQueryOperator<string>;

内容的提问来源于stack exchange,提问作者Faizan Ashiq

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.29 12:42:40