ExpressJS路由中MongoDB数字类型手机号前缀匹配搜索问题
Number类型手机号前缀匹配搜索解决方案
问题背景
JobApplication集合中mobile_no字段为Number类型,当前仅支持精确匹配完整手机号,无法通过前缀(如"67")搜索到以该前缀开头的号码(如67890),需保留Number类型实现前缀搜索。
方法1:使用$expr实现实时字符串转换匹配
适合数据量较小的场景,通过MongoDB聚合表达式将Number类型转为字符串后做前缀匹配:
修改路由代码
router.get('/job_applications', async (req, res) => { try { let query = {}; const { name, email, mobile_no } = req.query; if (name) { query.name = { $regex: name, $options: 'i' }; } if (email) { query.email = { $regex: email, $options: 'i' }; } // 修改mobile_no的查询逻辑 if (mobile_no && !isNaN(Number(mobile_no))) { const prefix = mobile_no.toString(); query.$expr = { $regexMatch: { input: { $toString: "$mobile_no" }, regex: `^${prefix}`, options: '' } }; } const job_applications = await JobApplication.find(query); res.render('./admin/job_applications/job_app_home', { job_applications, noResults: job_applications.length === 0, params: req.query }); } catch (err) { console.error(err); res.status(500).send('Server Error'); } });
方法2:新增字符串副本字段(性能优先推荐)
数据量较大时,方法1会触发全表扫描,性能低下。可新增字符串类型字段存储手机号的字符串形式,专门用于搜索:
1. 修改JobApplication Schema
const jobApplicationSchema = new mongoose.Schema({ name: { type: String, required: true }, email: { type: String, required: true }, mobile_no: { type: Number, required: true }, // 新增字符串类型的手机号字段 mobile_str: { type: String, required: true }, resume: { type: String, required: true }, job_title: { type: String } }, { timestamps: true });
2. 添加预保存中间件自动同步字段
在Schema定义后添加中间件,确保新增/修改数据时自动同步mobile_str:
jobApplicationSchema.pre('save', function(next) { this.mobile_str = this.mobile_no.toString(); next(); });
3. 迁移已有数据
执行以下脚本更新已有文档的mobile_str字段:
// 在项目中执行一次该脚本 const JobApplication = require('./path/to/your/models').JobApplication; async function migrateMobileNumbers() { try { await JobApplication.updateMany( {}, [ { $set: { mobile_str: { $toString: "$mobile_no" } } } ] ); console.log('数据迁移完成'); } catch (err) { console.error(err); } } migrateMobileNumbers();
4. 修改路由查询逻辑
router.get('/job_applications', async (req, res) => { try { let query = {}; const { name, email, mobile_no } = req.query; if (name) { query.name = { $regex: name, $options: 'i' }; } if (email) { query.email = { $regex: email, $options: 'i' }; } if (mobile_no && !isNaN(Number(mobile_no))) { const prefix = mobile_no.toString(); query.mobile_str = { $regex: `^${prefix}`, $options: '' }; } const job_applications = await JobApplication.find(query); res.render('./admin/job_applications/job_app_home', { job_applications, noResults: job_applications.length === 0, params: req.query }); } catch (err) { console.error(err); res.status(500).send('Server Error'); } });
注意事项
- EJS模板代码无需修改,保持原有的表单和列表渲染逻辑即可
- 方法2中可给
mobile_str字段添加索引,进一步提升搜索性能:jobApplicationSchema.index({ mobile_str: 1 });
内容的提问来源于stack exchange,提问作者Afzal Hamdulay
相关产品推荐
相关产品推荐

