MongoDB单查询跨多集合多字段搜索问题(用aggregate与populate)
问题描述
我们拥有Ad和Company两个MongoDB集合,一个公司可关联多个广告。需要开发API接口,支持通过关键词在公司名称、primaryText、headline、description字段中搜索匹配的广告,要求使用aggregate与populate函数。
提供的Mongoose Schema如下:
Company Schema
const CompanySchema = new mongoose.Schema({ name: { type: String, required: true, }, url: { type: String, required: true, }, }); const Company = mongoose.model("company", CompanySchema);
Ad Schema
const AdSchema = new mongoose.Schema({ primaryText: { type: String, required: true, }, companyId: { type: mongoose.Schema.Types.ObjectId, ref: "company", required: true, }, headline: { type: String, required: true, }, description: { type: String, required: false, default: "", }, CTA: { type: String, required: true, }, imageUrl: { type: String, required: true, }, }); AdSchema.index({ primaryText: 'text', headline: 'text', description: 'text' }); const Ad = mongoose.model("ad", AdSchema);
现有实现代码无法通过公司名称搜索,代码如下:
router.get("/search", async function (req, res, next) { const query = req.query.query; const ads = await Ad.aggregate([ { $match: { $text: { $search: query, }, }, }, { $lookup: { from: "companies", localField: "companyId", foreignField: "_id", as: "company", }, }, { $unwind: "$company", }, ]); res.json(ads); });
现有代码的问题
- 文本索引覆盖不全:仅在Ad集合的
primaryText、headline、description字段创建了文本索引,未包含关联Company集合的name字段,导致$text搜索无法匹配公司名称。 - 搜索时机错误:
$match阶段先过滤Ad自身字段,此时尚未关联Company数据,无法基于公司名称筛选结果。
正确实现方式
方案一:纯聚合流程实现
先关联Company数据,再同时匹配Ad自身字段和公司名称,若要对公司名称使用文本搜索,需先给Company集合添加文本索引:
// 先为Company集合添加文本索引 CompanySchema.index({ name: 'text' }); router.get("/search", async function (req, res, next) { const query = req.query.query; // 先搜索匹配关键词的公司ID const matchedCompanies = await Company.find( { $text: { $search: query } }, { _id: 1 } ); const companyIds = matchedCompanies.map(c => c._id); // 聚合筛选Ad自身字段匹配,或关联公司ID匹配的广告,再关联公司数据 const ads = await Ad.aggregate([ { $match: { $or: [ { $text: { $search: query } }, { companyId: { $in: companyIds } } ] } }, { $lookup: { from: "companies", localField: "companyId", foreignField: "_id", as: "company", } }, { $unwind: "$company" } ]); res.json(ads); });
方案二:结合aggregate与populate实现
先用aggregate筛选符合条件的Ad,再通过populate关联Company数据,满足题目要求的两个函数使用:
// 先为Company集合添加文本索引 CompanySchema.index({ name: 'text' }); router.get("/search", async function (req, res, next) { const query = req.query.query; // 搜索匹配关键词的公司ID const matchedCompanies = await Company.find( { $text: { $search: query } }, { _id: 1 } ); const companyIds = matchedCompanies.map(c => c._id); // 聚合筛选Ad,再用populate关联公司数据 const ads = await Ad.aggregate([ { $match: { $or: [ { $text: { $search: query } }, { companyId: { $in: companyIds } } ] } } ]).populate({ path: 'companyId', model: Company }); // 格式化字段名(可选,将companyId替换为company) const formattedAds = ads.map(ad => { const { companyId, ...rest } = ad; return { ...rest, company: companyId }; }); res.json(formattedAds); });
内容的提问来源于stack exchange,提问作者Qazi Amaan
相关产品推荐
相关产品推荐

