Node.js下MongoDB关联双集合实现记者姓名与报道内容多条件搜索
Node.js + MongoDB 跨集合搜索实现方案
核心逻辑
通过联表查询关联记者和报道两个集合,同时匹配记者姓名、报道标题、报道描述三个维度的关键词,返回符合条件的报道信息。
代码实现(基于Mongoose,最常用的MongoDB Node.js工具库)
1. 定义数据模型
// 引入依赖 const mongoose = require('mongoose'); // 记者集合模型 const journalistSchema = new mongoose.Schema({ journalist_FirstName: String, journalist_LastName: String, // 其他自定义字段 }); const Journalist = mongoose.model('Journalist', journalistSchema); // 报道集合模型 const storySchema = new mongoose.Schema({ journalistId: { type: mongoose.Schema.Types.ObjectId, ref: 'Journalist' }, storyTitle: String, briefDescription: String, publishDate: Date, // 其他自定义字段 }); const Story = mongoose.model('Story', storySchema);
2. 核心搜索逻辑
async function searchStories(keyword) { // 构造不区分大小写的正则匹配规则 const searchRegex = new RegExp(keyword, 'i'); // 调用聚合管道一次性完成联表+匹配+字段筛选 const result = await Story.aggregate([ // 关联记者集合,拿到对应记者的姓名信息 { $lookup: { from: 'journalists', // 此处为MongoDB中记者集合的实际名称,默认是模型名小写复数 localField: 'journalistId', foreignField: '_id', as: 'journalist' } }, // 将关联返回的数组格式转为单个对象 { $unwind: '$journalist' }, // 匹配所有符合关键词的记录 { $match: { $or: [ { 'journalist.journalist_FirstName': searchRegex }, { 'journalist.journalist_LastName': searchRegex }, { storyTitle: searchRegex }, { briefDescription: searchRegex } ] } }, // 只返回需要的字段,可按需调整 { $project: { storyTitle: 1, briefDescription: 1, publishDate: 1, _id: 0 } } ]); // 格式化日期为示例要求的格式 return result.map(item => ({ storyTitle: item.storyTitle, briefDescription: item.briefDescription, publishDate: new Intl.DateTimeFormat('zh-CN', { year: 'numeric', month: 'long', day: 'numeric' }).format(item.publishDate) })); }
3. 调用示例
// 用户输入关键词Kabita时调用 searchStories('Kabita').then(res => { console.log(res); /* 输出示例: [ { storyTitle: 'Joining of two tables', briefDescription: 'MongoDB joining of tables', publishDate: '2021年11月21日' } ] */ });
性能优化建议
如果数据量较大,建议给搜索字段建立文本索引替代正则查询,性能提升明显:
// 给记者姓名字段建文本索引 await Journalist.collection.createIndex({ journalist_FirstName: 'text', journalist_LastName: 'text' }); // 给报道标题、描述字段建文本索引 await Story.collection.createIndex({ storyTitle: 'text', briefDescription: 'text' });
简化版实现(两次查询,写法更易懂)
如果不想写聚合管道,也可以分两次查询实现,小数据量下性能差异不大:
async function searchStoriesSimple(keyword) { const searchRegex = new RegExp(keyword, 'i'); // 第一步:查询所有姓名匹配关键词的记者ID const matchJournalistIds = (await Journalist.find({ $or: [ { journalist_FirstName: searchRegex }, { journalist_LastName: searchRegex } ] }, { _id: 1 })).map(item => item._id); // 第二步:查询符合条件的报道 const stories = await Story.find({ $or: [ { journalistId: { $in: matchJournalistIds } }, { storyTitle: searchRegex }, { briefDescription: searchRegex } ] }, { storyTitle: 1, briefDescription: 1, publishDate: 1, _id: 0 }); // 日期格式化逻辑同上 return stories.map(item => ({ ...item, publishDate: new Intl.DateTimeFormat('zh-CN', { year: 'numeric', month: 'long', day: 'numeric' }).format(item.publishDate) })); }
内容的提问来源于stack exchange,提问作者Nilachandan Mohanty
相关产品推荐
相关产品推荐

