如何用MongoDB的find查询替代数组过滤实现关联属性筛选?
能否用find方法直接筛选包含关联属性的MongoDB记录?
当前我通过先查询所有QuestionAnswer记录、再在内存中过滤关联question字段包含指定内容的方式实现需求,但这种方式效率较低。我已经了解到可以用聚合lookup方案实现数据库层面的筛选,想知道是否可以直接用find方法完成?
现有TypeScript路由代码
router.get("/slice", async (req, res) => { if (req.query.first && req.query.rowcount) { const first: number = parseInt(req.query.first as string); const rowcount: number = parseInt(req.query.rowcount as string); const question: string = req.query.question as string; if (question) { let result = await QuestionAnswer.QuestionAnswer.find().populate('question_id').exec(); let filtered: any[] = []; result.forEach((record) => { let q = record.get("question_id.question"); if (q.includes(question)) filtered.push(record); }); filtered = filtered.slice(first, first + rowcount); res.send(filtered); } else { let result = await QuestionAnswer.QuestionAnswer.find().skip(first).limit(rowcount).populate('question_id'); res.send(result); } } else { res.status(404).send(); } });
对应的Schema定义
const questionSchema = new Schema({ question: { type: String, required: true, }, topic_id: { type: Types.ObjectId, required: true, ref: 'topic', }, explanation: { type: String, required: true, }, }); const questionAnswerSchema = new Schema({ question_id: { type: Types.ObjectId, required: true, ref: 'question', }, answer: { type: String, required: true, }, isCorrect: { type: Boolean, required: true, }, });
解决方案:用两步find查询实现关联筛选
可以用find实现,不需要依赖聚合lookup。核心思路是先从关联的question集合中筛选出符合条件的文档ID,再用这些ID作为条件查询QuestionAnswer集合,具体步骤如下:
- 先查询
question集合,获取所有question字段包含指定字符串的文档的_id列表 - 用上述ID列表作为条件查询
QuestionAnswer集合,同时populate关联的question_id字段,再执行分页逻辑
优化后的代码如下:
router.get("/slice", async (req, res) => { if (req.query.first && req.query.rowcount) { const first: number = parseInt(req.query.first as string); const rowcount: number = parseInt(req.query.rowcount as string); const question: string = req.query.question as string; if (question) { // 第一步:找到符合条件的question的ID列表 const matchedQuestionIds = await Question.distinct('_id', { question: { $regex: question, $options: 'i' } // 正则匹配实现模糊查询,i表示忽略大小写 }); if (matchedQuestionIds.length === 0) { return res.send([]); } // 第二步:用匹配到的ID查询QuestionAnswer,同时分页并populate const result = await QuestionAnswer.QuestionAnswer.find({ question_id: { $in: matchedQuestionIds } }) .skip(first) .limit(rowcount) .populate('question_id'); res.send(result); } else { // 无筛选条件时的原有分页逻辑 const result = await QuestionAnswer.QuestionAnswer.find() .skip(first) .limit(rowcount) .populate('question_id'); res.send(result); } } else { res.status(404).send(); } });
说明
- 这种方式将筛选逻辑放在数据库层面完成,避免了全量查询后在内存中过滤的性能问题,数据量越大优势越明显
- 使用
$regex替代includes实现模糊匹配,$options: 'i'可实现大小写不敏感的查询(不需要的话可以去掉) - 通过
distinct直接获取符合条件的_id列表,减少数据传输量
内容的提问来源于stack exchange,提问作者Papp Zoltán
相关产品推荐
相关产品推荐

