MongoDB 全文检索(Full-Text Search)完全指南
核心结论:$text 索引 + 聚合管道 是 MongoDB 内置全文检索的 唯一推荐方式,支持 多语言、权重、排序、短语、高亮,性能媲美 Elasticsearch(中小规模)。
一、$text 索引 vs 其他方案
| 方案 | 推荐度 | 适用场景 |
|---|---|---|
$text 索引 | 5 stars | 中小规模(< 1000万文档)、快速集成 |
| Atlas Search | 5 stars | 大规模、复杂搜索、云原生 |
| Elasticsearch | 4 stars | 超大规模、实时分析、复杂查询 |
正则 $regex | 1 star | 仅简单匹配,性能差 |
二、创建 $text 索引(一键开启)
// 单字段
db.articles.createIndex({ title: "text" })
// 多字段 + 权重
db.articles.createIndex(
{
title: "text",
content: "text",
tags: "text"
},
{
weights: {
title: 10,
content: 5,
tags: 1
},
name: "article_text_index",
default_language: "chinese" // 或 english, spanish 等
}
)
权重:
title匹配得分是content的 2 倍
三、基本搜索语法
| 需求 | 查询写法 |
|---|---|
| 关键词 | { $text: { $search: "MongoDB 性能" } } |
| 短语 | { $text: { $search: "\"快速入门\"" } } |
| 排除 | { $text: { $search: "MongoDB -备份" } } |
| 多词 OR | { $text: { $search: "索引 优化" } } |
// 搜索包含 "MongoDB" 但不包含 "备份"
db.articles.find({
$text: { $search: "MongoDB -备份" }
})
四、得分排序 + 投影(必须)
db.articles.find(
{ $text: { $search: "性能优化" } },
{
score: { $meta: "textScore" }, // 获取匹配得分
title: 1,
excerpt: 1,
_id: 0
}
)
.sort({ score: { $meta: "textScore" } }) // 按相关度排序
.limit(10)
输出:
{
"title": "MongoDB 索引优化实战",
"excerpt": "通过覆盖索引提升 100 倍性能...",
"score": 2.5
}
五、高级搜索技巧
1. 多语言支持
// 创建索引时指定语言
default_language: "chinese"
// 覆盖多语言字段
db.articles.createIndex({
title_en: "text",
title_zh: "text"
}, { default_language: "none" })
2. 短语 + 模糊
// 精确短语
{ $text: { $search: "\"MongoDB 教程\"" } }
// 模糊(自动分词)
{ $text: { $search: "MongoDB 教程" } }
3. 大小写与标点
$text忽略大小写、标点,支持 词干提取(stemming)
// 以下都匹配
"mongodb", "MongoDB", "MONGODB"
"running", "run", "ran"
六、结合聚合管道(推荐)
db.articles.aggregate([
// 1. 全文搜索
{ $match: { $text: { $search: "性能优化" } } },
// 2. 投影得分
{
$project: {
title: 1,
excerpt: { $substr: ["$content", 0, 200] },
score: { $meta: "textScore" }
}
},
// 3. 排序
{ $sort: { score: { $meta: "textScore" } } },
// 4. 分页
{ $skip: 0 },
{ $limit: 10 }
])
七、搜索高亮(自定义实现)
// 服务端高亮函数
function highlight(text, keyword) {
const regex = new RegExp(`(${keyword})`, 'gi');
return text.replace(regex, '<mark>$1</mark>');
}
// 查询 + 高亮
const results = await db.articles.find(
{ $text: { $search: "MongoDB" } },
{ title: 1, content: 1, score: { $meta: "textScore" } }
)
.sort({ score: { $meta: "textScore" } })
.limit(5)
.toArray();
results.forEach(doc => {
doc.highlight = highlight(doc.content, "MongoDB");
});
八、性能优化
| 优化点 | 建议 |
|---|---|
| 索引字段选择 | 只索引 title, content, tags |
| 权重合理 | 高权重字段放高分 |
| 避免全表扫描 | 结合其他条件 $match |
| 覆盖索引 | 投影字段在索引中 |
// 覆盖查询(totalDocsExamined: 0)
db.articles.find(
{ $text: { $search: "索引" } },
{ title: 1, _id: 0, score: { $meta: "textScore" } }
)
.sort({ score: { $meta: "textScore" } })
.explain()
九、限制与注意事项
| 限制 | 说明 |
|---|---|
每个集合只有 1 个 $text 索引 | 需合并字段 |
| 不支持部分匹配 | 如 mongo* → 用 $regex |
| 不支持排序字段 | 不能在 sort 中用 $text |
| 不实时 | 写入后稍有延迟 |
十、Node.js + Mongoose 实现
// Schema
const articleSchema = new Schema({
title: String,
content: String,
tags: [String],
lang: { type: String, default: 'chinese' }
});
// 创建索引
articleSchema.index(
{ title: "text", content: "text", tags: "text" },
{ weights: { title: 10, content: 5 }, default_language: "chinese" }
);
// 查询
const results = await Article.find(
{ $text: { $search: "性能优化" } },
{ score: { $meta: "textScore" } }
)
.sort({ score: { $meta: "textScore" } })
.limit(10);
十一、Atlas Search(云原生增强版)
// Atlas 专用 $search 阶段
db.articles.aggregate([
{
$search: {
index: "default",
text: {
query: "MongoDB 性能",
path: ["title", "content"]
},
highlight: { path: "content" }
}
},
{ $limit: 10 },
{ $project: { title: 1, highlight: { $meta: "searchHighlights" } } }
])
优势:支持 同义词、自动补全、facet、地理搜索
十二、终极案例:博客搜索系统
// 1. 创建索引
db.posts.createIndex(
{
title: "text",
content: "text",
tags: "text"
},
{
weights: { title: 10, content: 3, tags: 5 },
default_language: "chinese"
}
)
// 2. 搜索 API
app.get('/search', async (req, res) => {
const { q, page = 1, limit = 10 } = req.query;
const skip = (page - 1) * limit;
const results = await db.posts.aggregate([
{ $match: { $text: { $search: q } } },
{
$project: {
title: 1,
excerpt: { $substrCP: ["$content", 0, 200] },
tags: 1,
score: { $meta: "textScore" }
}
},
{ $sort: { score: { $meta: "textScore" } } },
{ $skip: skip },
{ $limit: parseInt(limit) }
]).toArray();
res.json({ results, total: results.length });
});
总结:全文检索黄金三步
| 步骤 | 命令 |
|---|---|
| 1. 创建索引 | createIndex({ field: "text" }, { weights }) |
| 2. 搜索 | { $text: { $search: "关键词" } } |
| 3. 排序 | .sort({ score: { $meta: "textScore" } }) |
你现在可以:
// 一行开启全文搜索
db.collection.createIndex({ content: "text" })
把你的搜索需求告诉我,我帮你写完整方案:
- 搜索字段(标题、内容、标签?)
- 语言(中文?英文?)
- 是否需要高亮、分页、同义词?
回复 3 个关键词,我生成:索引 + 查询 + 高亮 API!