如何在Mongoose中实现MongoDB数组的部分字符串匹配?
解决方案
1. 实现正确的URL修剪函数
首先需要把输入的完整URL处理成不带协议、www前缀、查询参数、哈希值的主机名+路径格式,这是匹配的基础:
function trim(findUrl: string): string { try { const urlObj = new URL(findUrl); // 移除www前缀(如果存在) const hostname = urlObj.hostname.replace(/^www\./, ''); // 拼接主机名和路径,移除末尾多余斜杠 let trimmed = `${hostname}${urlObj.pathname}`.replace(/\/$/, ''); return trimmed; } catch (error) { // 处理无效URL,直接返回空字符串避免错误查询 return ''; } }
2. MongoDB查询逻辑
核心需求是:找到sites文档中,urls数组里存在至少一个条目是修剪后URL的前缀(即修剪后URL以该条目开头)。
使用MongoDB的$expr表达式结合$anyElementTrue、$map和$indexOfBytes来实现这个逻辑:
public async lookup(findUrl: string) { const trimmed = trim(findUrl); if (!trimmed) return []; // 查询符合条件的站点 const matchedSites = await Site.find({ $expr: { $anyElementTrue: { $map: { input: "$urls", as: "siteUrl", in: { // 检查修剪后的URL是否以当前siteUrl开头(索引位置为0) $eq: [0, { $indexOfBytes: [trimmed, "$$siteUrl"] }] } } } } }).select('name').lean(); // 只返回需要的name字段,提升性能 // 提取站点名称返回,符合示例格式 return matchedSites.map(site => site.name); }
3. 各场景验证
- 场景1:修剪后的URL为
amazon.com/Coffee-Program-Ceramic-Makes-Programmers/dp/B07D2XJLLG,MySite的urls中amazon.com/Coffee是它的前缀,匹配成功,返回[MySite]。 - 场景2:修剪后的URL为
google.com/search,两个站点的urls都包含该条目,匹配成功,返回[MySite, OtherSite]。 - 场景3:修剪后的URL为
microsoft.com/en-us/surface,OtherSite的urls中microsoft.com/en-us是它的前缀,匹配成功,返回[OtherSite]。 - 场景4:修剪后的URL为
microsoft.com/nl-nl或microsoft.com,没有任何站点的urls条目是它们的前缀,返回[]。
性能优化建议
如果sites集合数据量较大,上述查询会触发全表扫描,可通过以下方式优化:
- 统一大小写:插入文档时将
urls条目转为小写,修剪函数也返回小写字符串,避免大小写不匹配的同时,可提升后续索引效率。 - 拆分存储路径:将
urls中的每个条目拆分为hostname和path两个字段单独存储,先通过hostname过滤缩小范围,再匹配路径前缀,减少需要处理的数据量。
内容的提问来源于stack exchange,提问作者Thimma
相关产品推荐
相关产品推荐

