如何在Node.js中使用Sequelize实现精准的动态查询生成函数
如何在Node.js中使用Sequelize实现精准的动态查询生成函数
看起来你已经搞定了排序和分页的动态处理逻辑,接下来要把动态查询条件也整合进去对吧?我来帮你扩展这个函数,让它能灵活处理各种通过query params传递的查询需求,比如age>5、name包含John这类场景,同时保持代码的安全性和可维护性。
扩展后的动态查询生成函数
这个函数会保留你原有的分页排序逻辑,同时新增查询条件的解析和构建功能,支持多种常见的查询操作符:
const { Op } = require('sequelize'); const buildUserQuery = (q) => { // 保留你原有的分页&排序逻辑 const limit = q?.limit * 1 || 200; const page = q?.page * 1 || 1; const skip = (page - 1) * limit; const sort = q?.sort || "createdAt"; const sortBy = q?.sortBy || "DESC"; // 初始化查询条件容器 const where = {}; // 定义允许的操作符映射(对应Sequelize的Op对象) const allowedOperators = { 'gt': Op.gt, // 大于 'lt': Op.lt, // 小于 'gte': Op.gte, // 大于等于 'lte': Op.lte, // 小于等于 'eq': Op.eq, // 精确匹配 'like': Op.like,// 模糊匹配 'in': Op.in // 包含在数组中 }; // 定义允许查询的字段白名单(防止非法字段查询,提升安全性) const allowedFields = ['name', 'age', 'email', 'createdAt']; // 遍历所有query参数,筛选出查询条件相关的字段 Object.keys(q).forEach(key => { // 跳过分页、排序相关的参数 if (['limit', 'page', 'sort', 'sortBy'].includes(key)) return; let field = key; let operator = Op.eq; // 默认使用精确匹配 let value = q[key]; // 处理第一种参数格式:`字段__操作符=值`(推荐,URL友好,比如age__gt=5) if (key.includes('__')) { const [fieldName, opKey] = key.split('__'); // 检查字段和操作符是否在白名单内 if (!allowedFields.includes(fieldName) || !allowedOperators[opKey]) return; field = fieldName; operator = allowedOperators[opKey]; } // 处理第二种参数格式:`字段=操作符:值`(比如age=gt:5) else if (value.includes(':')) { const [opKey, ...valueParts] = value.split(':'); if (!allowedFields.includes(field) || !allowedOperators[opKey]) return; operator = allowedOperators[opKey]; value = valueParts.join(':'); // 处理值中包含冒号的情况 } // 检查字段是否在白名单内 else if (!allowedFields.includes(field)) { return; } // 类型转换:根据操作符处理值的类型 switch (operator) { case Op.in: // 把逗号分隔的字符串转成数组,数字自动转Number类型 value = value.split(',').map(item => isNaN(item) ? item.trim() : Number(item.trim())); break; case Op.like: // 模糊查询自动添加%(如果需要前缀/后缀匹配,可以让用户自己传%) value = `%${value.trim()}%`; break; case Op.gt: case Op.lt: case Op.gte: case Op.lte: // 数字类型字段转Number if (!isNaN(value)) value = Number(value); break; case Op.eq: // 布尔值转换(比如isActive=true) if (value.toLowerCase() === 'true') value = true; if (value.toLowerCase() === 'false') value = false; // 数字转换 else if (!isNaN(value)) value = Number(value); break; } // 处理同一字段的多条件(比如age>5且age<10) if (where[field]) { if (Array.isArray(where[field])) { where[field].push({ [operator]: value }); } else { where[field] = [where[field], { [operator]: value }]; } } else { where[field] = { [operator]: value }; } }); // 组合最终的查询配置 const queryConfig = { order: [[sort, sortBy]], where }; // 若存在limit参数,添加分页配置 if (q?.limit) { queryConfig.limit = limit; queryConfig.offset = skip; } return queryConfig; };
函数使用示例
在你的路由中调用这个函数,直接传入req.query即可生成Sequelize所需的查询配置:
// 假设你已经定义了User模型 const User = require('./models/User'); app.get('/users', async (req, res) => { try { const queryOptions = buildUserQuery(req.query); const users = await User.findAll(queryOptions); res.status(200).json({ data: users, pagination: { page: req.query.page || 1, limit: req.query.limit || 200, total: await User.count({ where: queryOptions.where }) } }); } catch (error) { res.status(500).json({ error: error.message }); } });
常见查询场景示例
你可以通过不同的query params组合来实现各种查询需求:
- 精确匹配:
/users?name=John→ 查询name等于John的用户 - 范围查询:
/users?age__gt=18&age__lt=30→ 查询年龄18到30之间的用户 - 模糊查询:
/users?name__like=ohn→ 查询name包含ohn的用户 - IN查询:
/users?id__in=1,3,5→ 查询id为1、3、5的用户 - 布尔查询:
/users?isActive=true→ 查询激活状态的用户 - 分页+排序+查询:
/users?age__gte=20&sort=age&sortBy=ASC&page=2&limit=10→ 分页查询年龄≥20的用户,按年龄升序排列,取第2页,每页10条
额外注意事项
- 字段白名单:一定要维护
allowedFields数组,防止用户查询敏感字段(比如密码)或者模型中不存在的字段,避免报错或信息泄露。 - 操作符限制:
allowedOperators只开放你需要的操作符,减少SQL注入风险。 - 日期处理:如果需要查询日期字段(比如
createdAt),可以在类型转换部分添加日期转换逻辑:if (['createdAt', 'updatedAt'].includes(field)) { value = new Date(value); } - 空值处理:如果参数值为空字符串,可以在遍历的时候跳过,避免生成无效的查询条件。
备注:内容来源于stack exchange,提问作者Hiren Kalariya
相关产品推荐
相关产品推荐

