必须先为字符串字段创建text索引,否则$text查询报错;支持多字段联合索引、通配符索引,但不可混用其他类型;检索支持OR、NOT、短语匹配;需投影并排序$meta:"textScore"实现相关性排序;中文需ICU配置且能力有限,推荐关键字数组或$search替代。

先建text索引,否则$text查询直接报错
必须对目标字符串字段显式创建text索引,否则执行{$text: {$search: "xxx"}}时会立即返回error: text index required。MongoDB不会自动为你补上索引,也不会静默降级为全表扫描。
db.articles.createIndex({ title: "text", content: "text" })——这是最常用写法,两个字段共用一个text索引,所有字符串值会被统一分词、去停用词、转小写处理。
db.articles.createIndex({ "$**": "text" })——通配符索引,自动覆盖集合中所有string类型字段,适合schema不固定或字段名动态生成的场景,但【索引体积可能数倍于原始数据,写入延迟明显上升】。
不能混用其他索引类型:{ title: "text", createdAt: 1 }是非法语法,text索引必须独占一个createIndex调用。
多关键词组合检索的三种写法
方法一:空格分隔 → OR逻辑匹配
db.articles.find({ $text: { $search: "mongodb tutorial" } })——命中含"mongodb"或"tutorial"任一词的文档,不分先后顺序,也不要求共现。
方法二:减号排除 → NOT逻辑控制
db.articles.find({ $text: { $search: "mongodb -tutorial" } })——必须含"mongodb",且不能含"tutorial"。注意减号前必须有空格,写成"mongodb-tutorial"会被当做一个完整词处理。
方法三:双引号包裹 → 精确短语匹配
db.articles.find({ $text: { $search: "\"nosql database\"" } })——要求两个词严格相邻、顺序一致、中间仅允许空格或标准标点。这一步操作起来很简单,但必须用英文双引号包裹,单引号或中文引号均无效。
让结果按相关性排序的关键步骤
第一步:投影出textScore字段
db.articles.find({ $text: { $search: "index" } }, { score: { $meta: "textScore" } })——【不加这个投影,score字段根本不存在,后续排序必然失效】。
第二步:在sort中显式引用$meta
.sort({ score: { $meta: "textScore" } })——必须写成{ $meta: "textScore" },只写{ score: 1 }会静默退化为无序返回。
第三步:聚合管道中保持结构一致
在$match阶段放$text,在$sort阶段用{ score: { $meta: "textScore" } },不要试图在$project里伪造score字段,MongoDB会忽略它。
避开中文全文检索的硬伤
MongoDB原生text索引默认使用英文分词器,对中文不做切词,而是把整个字符串当一个词索引。比如存入“季度报告”,搜“季度”或“报告”都查不到。
若MongoDB版本≥4.2且已启用ICU库,可强制指定中文分词:db.fs.files.createIndex({ filename: "text" }, { default_language: "zh", language_override: "lang" })。
但即便如此,仍不支持拼音转换、错别字容错、同义词扩展。搜索“北京”无法命中“bei jing”,搜“管理员”找不到“admin”。
真正需要中文模糊匹配时,应放弃$text,改用应用层预提取关键字数组 + 普通索引 + $in查询,或同步元数据到Elasticsearch。
替代$text的高性能模糊方案
方法一:关键字数组预处理
在写入文档时,用jieba等成熟分词库把标题/内容切分为keywords数组,如["季度", "报告", "2024"],再存入文档;然后建普通索引db.articles.createIndex({ keywords: 1 })。
查询时用db.articles.find({ keywords: { $in: ["季度", "报告"] } })——走B-tree索引,毫秒级响应,且完全可控切分粒度。
方法二:前缀字段冗余存储
对name字段,额外存一个name_prefix_3字段,值为name.substr(0,3),再建索引{name_prefix_3: 1};查“zha”开头的名字就变成{name_prefix_3: "zha"},避免$regex全表扫。
方法三:迁移到$search聚合阶段
MongoDB 6.0+ 已明确建议用$search替代$text:它支持拼写纠错、同义词映射、语言权重、高亮片段,且能对接Atlas Search服务。执行db.articles.aggregate([ { $search: { index: "default", text: { query: "mongdb", path: "title" } } } ])即可启用。











