TyperORM/NestJS:动态构建查询搜索的Where条件
通用动态筛选条件实现方案
场景与问题
我维护着一个包含多模型的服务,所有模型都支持前端调用筛选(比如State州和City城市实体),城市数据示例如下:
{ "id": 1, "name": "Afonso Cláudio", "state": { "id": 8, "name": "Espírito Santo", "abbreviation": "ES" } }
所有服务都继承自实现了find方法的AbstractService:
async find(query: FindDto): Promise<ResultDto> { try { if (!query.take) query.take = 20; if (query.page) query.skip = (query.page - 1) * query.take; const data = await this.repository.find(query); return httpSuccess(Constant.OK, data); } catch (error) { httpErrorHandling(Constant.CANNOT_RETURN_DATA, error); } }
FindDto结构定义:
export class FindDto { @ApiPropertyOptional() @IsNumber() @IsOptional() page: number; @ApiPropertyOptional() @IsNumber() @IsOptional() take: number; @Exclude() skip: number; @Exclude() where: any; }
当前需要实现一个通用方法,自动识别请求中除page、take、skip外的所有参数,构建repository.find可用的where条件,要求支持:
- 字符串部分匹配(比如
name=Afonso返回名称含该字符串的城市) - 关联实体筛选(比如按州ID/缩写筛选城市)
- 无需逐个字段硬编码处理
现有代码仅支持简单精确匹配,无法满足需求:
const entries = Object.entries(query); if (query) { for (const entry of entries) { if (entry[1] !== undefined && !['take', 'page', 'skip'].includes(entry[0])) { query.where = { [entry[0]]: entry[1] }; } } }
通用解决方案
1. 扩展FindDto支持动态参数
修改FindDto,允许接收任意筛选参数:
export class FindDto { @ApiPropertyOptional() @IsNumber() @IsOptional() page: number; @ApiPropertyOptional() @IsNumber() @IsOptional() take: number; @Exclude() skip: number; @Exclude() where: any; // 新增任意键值对,用于接收前端筛选参数 @Exclude() [key: string]: any; }
2. 编写动态Where条件构建工具
创建通用工具函数,解析参数生成符合TypeORM规范的查询条件:
import { Brackets } from 'typeorm'; /** * 解析查询参数生成TypeORM Where条件 * @param query 请求查询参数 */ export function buildWhereCondition(query: Record<string, any>) { const where: Record<string, any> = {}; const excludedKeys = ['page', 'take', 'skip']; for (const [key, value] of Object.entries(query)) { if (excludedKeys.includes(key) || value === undefined) continue; // 处理关联实体字段(如state_abbreviation) if (key.includes('_')) { const [relation, field] = key.split('_', 2); where[relation] = { ...(where[relation] || {}), [field]: value }; continue; } // 处理模糊匹配(如name_like) if (key.endsWith('_like')) { const field = key.slice(0, -5); where[field] = Brackets.create(qb => { qb.where(`LOWER(${field}) LIKE :value`, { value: `%${value.toLowerCase()}%` }); }); continue; } // 默认精确匹配 where[key] = value; } return where; }
3. 集成到AbstractService
修改AbstractService的find方法,调用工具函数生成条件:
async find(query: FindDto): Promise<ResultDto> { try { if (!query.take) query.take = 20; if (query.page) query.skip = (query.page - 1) * query.take; // 生成动态where条件 query.where = buildWhereCondition(query); const data = await this.repository.find(query); return httpSuccess(Constant.OK, data); } catch (error) { httpErrorHandling(Constant.CANNOT_RETURN_DATA, error); } }
4. 示例用法
- 模糊匹配城市名称:
{{URL}}city?page=1&name_like=Afonso - 筛选ES州的城市:
{{URL}}city?state_abbreviation=ES - 精确匹配城市ID:
{{URL}}city?id=1
扩展说明
- 可按需添加更多匹配规则:比如
_gt(大于)、_lt(小于)、_in(包含),只需在工具函数中补充对应逻辑 - 支持多层关联实体筛选:可扩展拆分逻辑处理
city.state.country_code这类嵌套字段 - 若使用TypeORM QueryBuilder,可基于此思路生成更复杂的联合查询
内容的提问来源于stack exchange,提问作者Ivonei Balena
相关产品推荐
相关产品推荐

