本文系统介绍 MongoDB Search 支持的索引类型、字段映射类型、分析器、检索操作符($search、$vectorSearch、$rankFusion)及参数细节,作为撰写检索查询和调优时的技术参考。
概述
MongoDB Search 功能通过在 MongoDB 实例中集成专用的搜索节点(mongot),提供与数据库无缝集成的全文检索和向量检索能力。无需管理外部搜索系统,即可使用 MongoDB 查询语言(MQL)完成多模态搜索任务,从而简化开发、降低运维负担。
关于 MongoDB Search 的适用范围、工作原理、开通和节点管理,请参见 MongoDB Search。本文聚焦索引定义和检索查询所需的算法参数与操作符。
Search 索引类型
全文检索索引
使用 createSearchIndex 方法创建,用于支持 $search 聚合阶段的全文检索查询。索引映射方式分为动态映射和静态映射。
动态映射(Dynamic Mapping)
dynamic: true 使 MongoDB 自动为集合中的所有字段建立索引,适合快速原型验证。
db.reviews.createSearchIndex({
name: "reviews_full_text_index",
definition: {
mappings: {
dynamic: true
}
}
});静态映射(Static Mapping)
手动指定需要索引的字段及其类型,适合生产环境。通过精确控制索引范围减少存储开销。
db.movies.createSearchIndex({
name: "movies_custom_index",
definition: {
mappings: {
dynamic: false,
fields: {
title: { type: "string", analyzer: "lucene.standard" },
genre: { type: "stringFacet" },
year: { type: "number" },
rating: { type: "number" },
plot: { type: "string", analyzer: "lucene.standard" }
}
}
}
});向量检索索引
使用三参数形式的 createSearchIndex 方法创建,类型为 "vectorSearch",用于支持 $vectorSearch 聚合阶段的相似性检索。
db.images.createSearchIndex(
"vector_index", // 索引名称
"vectorSearch", // 索引类型
{
fields: [
{
type: "vector",
path: "embedding", // 存储向量的字段
numDimensions: 1536, // 向量的维度
similarity: "dotProduct", // 相似度计算方法
quantization: "scalar" // 量化方式(可选)
}
]
}
);字段映射类型
在静态映射索引的 fields 中,为每个字段指定类型:
类型 | 说明 | 适用场景 |
| 全文索引字符串字段,经过分词处理 | 标题、描述、评论等需要全文检索的文本 |
| 字符串分面字段,不分词 | 分类、标签等用于聚合统计的字段 |
| 数值类型(int、long、float、double) | 价格、评分、年份等数值范围查询 |
| 日期类型 | 时间范围查询 |
| 布尔类型 | 状态过滤 |
| ObjectId 类型 | _id 或引用字段 |
| 地理坐标(GeoJSON) | 地理位置检索 |
| 不分词的精确匹配字符串 | 枚举值、ID、SKU 等精确匹配场景 |
| 自动补全类型 | 搜索建议、前缀匹配 |
| 嵌套文档 | 嵌套对象字段索引 |
| 向量类型(仅用于向量检索索引) | 向量相似性检索 |
分析器
分析器决定文本如何被分词和标准化,在 string 类型字段中通过 analyzer 参数指定。
内置分析器
分析器 | 说明 | 分词示例 |
| 标准分析器,按 Unicode 文本分段规则分词并转小写 | "The Quick Fox" → ["the", "quick", "fox"] |
| 按非字母字符分割并转小写 | "hello-world 123" → ["hello", "world"] |
| 仅按空白字符分割,不转小写 | "Hello World" → ["Hello", "World"] |
| 不分词,整个字段值作为一个 token | "New York" → ["New York"] |
| 英文分析器,支持词干提取和停用词过滤 | "running quickly" → ["run", "quick"] |
| 中文分析器,支持中文分词 | "全文搜索引擎" → ["全文", "搜索", "引擎"] |
自定义分析器
可通过组合 tokenizer、tokenFilters 和 charFilters 创建自定义分析器:
db.collection.createSearchIndex({
name: "custom_analyzer_index",
definition: {
mappings: {
dynamic: false,
fields: {
content: {
type: "string",
analyzer: "myCustomAnalyzer"
}
}
},
analyzers: [
{
name: "myCustomAnalyzer",
charFilters: [],
tokenizer: {
type: "standard"
},
tokenFilters: [
{ type: "lowercase" },
{ type: "stopword", tokens: ["the", "a", "an", "is"] }
]
}
]
}
});Tokenizer 类型
Tokenizer | 说明 |
| 按 Unicode 文本分段规则分词 |
| 按空白字符分割 |
| 整体作为单个 token |
| 正则捕获组分词 |
| 正则分隔符分割 |
| N-gram 分词 |
| 边缘 N-gram(前缀分词) |
| 识别并保留 URL 和 Email 地址 |
Token Filters
Filter | 说明 |
| 转小写 |
| 移除停用词 |
| 词干提取 |
| 同义词扩展 |
| 生成 N-gram 短语 |
| 去除首尾空白 |
| 按长度过滤 token |
| Unicode 折叠(去除重音符号等) |
| Unicode 规范化 |
全文检索操作符($search)
通过 $search 聚合阶段执行全文检索查询,以下为支持的操作符。
text
基础全文检索,对查询文本进行分词后匹配目标字段。
db.reviews.aggregate([
{
$search: {
index: "reviews_full_text_index",
text: {
query: "good quality", // 查询关键词
path: "comment" // 在 comment 字段中检索
}
}
},
{ $limit: 5 },
{ $project: { _id: 0, comment: 1, score: { $meta: "searchScore" } } }
]);参数说明:
query:检索关键词(字符串或字符串数组)path:检索字段(字符串或字符串数组)fuzzy:模糊匹配配置(可选)score:评分调整(可选)synonyms:同义词映射名称(可选)
compound
组合多个检索条件的复合查询,支持布尔逻辑组合。
db.movies.aggregate([
{
$search: {
index: "movies_custom_index",
compound: {
must: [
{ text: { query: "Drama", path: "genre" } }
],
mustNot: [
{ text: { query: "horror", path: "genre" } }
],
should: [
{ text: { query: "award", path: "plot" } }
],
filter: [
{ range: { path: "year", gte: 2000, lte: 2023 } }
]
}
}
},
{ $project: { title: 1, genre: 1, year: 1, _id: 0, score: { $meta: "searchScore" } } }
]);子句说明:
子句 | 含义 | 是否影响评分 |
| 文档必须匹配所有条件 | 是 |
| 文档必须不匹配任何条件 | 否 |
| 匹配的文档获得更高评分 | 是 |
| 文档必须匹配,但不计算评分 | 否(性能更优) |
phrase
短语检索,要求词语按顺序出现。通过 slop 参数控制允许的词间距。
db.movies.aggregate([
{
$search: {
index: "default",
phrase: {
query: "organized crime",
path: "plot",
slop: 0 // 允许的词间距(默认 0,严格相邻)
}
}
},
{ $project: { title: 1, _id: 0, score: { $meta: "searchScore" } } }
]);
// 匹配 The Godfather(plot 中包含 "organized crime" 短语)slop 值大于 0 时允许词语之间存在其他词:
// slop: 5 允许 "men" 和 "bond" 之间最多间隔 5 个词
{ phrase: { query: "men bond", path: "plot", slop: 5 } }
// 匹配 The Shawshank Redemption("men bond over a number of years")range
数值或日期范围查询。
{
$search: {
range: {
path: "rating",
gte: 8.5,
lt: 10
}
}
}支持的比较运算:gt(大于)、gte(大于等于)、lt(小于)、lte(小于等于)
fuzzy(模糊匹配)
在 text 操作符中通过 fuzzy 参数启用,容忍拼写错误。
db.movies.aggregate([
{
$search: {
index: "default",
text: {
query: "Godfater", // 拼写有误
path: "title",
fuzzy: {
maxEdits: 1, // 最大编辑距离(1 或 2)
prefixLength: 2, // 前缀不参与模糊匹配的字符数
maxExpansions: 50 // 最大扩展变体数
}
}
}
},
{ $project: { title: 1, _id: 0, score: { $meta: "searchScore" } } }
]);
// 匹配 "The Godfather"wildcard(通配符)
使用 *(零个或多个字符)和 ?(单个字符)进行模式匹配。匹配发生在分词后的 token 级别。
db.movies.aggregate([
{
$search: {
index: "default",
wildcard: {
query: "inter*",
path: "title",
allowAnalyzedField: true
}
}
},
{ $project: { title: 1, _id: 0, score: { $meta: "searchScore" } } }
]);
// 匹配 Interstellar(token "interstellar" 匹配 "inter*")参数说明:
query:包含通配符的模式(*匹配零或多个字符,?匹配单个字符)path:检索字段allowAnalyzedField:对已分词字段使用时必须设为true
regex(正则表达式)
使用正则表达式进行模式匹配。基于 Lucene 正则引擎,匹配发生在分词后的 token 级别。
db.movies.aggregate([
{
$search: {
index: "default",
regex: {
query: ".*tion",
path: "plot",
allowAnalyzedField: true
}
}
},
{ $project: { title: 1, _id: 0, score: { $meta: "searchScore" } } },
{ $limit: 5 }
]);
// 匹配 plot 中包含以 "tion" 结尾的 token 的文档(如 redemption、fiction)参数说明:
query:正则表达式模式(支持.、*、+、?、|、()、[ ]、{n,m}等语法)path:检索字段allowAnalyzedField:对已分词字段使用时必须设为true
autocomplete(自动补全)
需配合 autocomplete 字段类型使用,支持前缀匹配和搜索建议。
// 索引定义:为 title 字段配置 autocomplete 类型
db.movies.createSearchIndex({
name: "ac_index",
definition: {
mappings: {
dynamic: false,
fields: {
title: [
{ type: "autocomplete" },
{ type: "string", analyzer: "lucene.standard" }
]
}
}
}
});
// 查询:输入 "inter" 自动补全
db.movies.aggregate([
{
$search: {
index: "ac_index",
autocomplete: {
query: "inter",
path: "title"
}
}
},
{ $project: { title: 1, _id: 0, score: { $meta: "searchScore" } } }
]);
// 匹配 Interstellarexists(字段存在性检查)
检查文档中指定字段是否存在。
db.movies.aggregate([
{
$search: {
index: "default",
exists: {
path: "rating"
}
}
},
{ $project: { title: 1, rating: 1, _id: 0, score: { $meta: "searchScore" } } },
{ $limit: 3 }
]);向量检索($vectorSearch)
相似度算法
在创建向量检索索引时,通过 similarity 参数指定相似度计算方法:
算法 | 说明 | 适用场景 |
| 欧氏距离,距离越小越相似 | 特征向量空间中的绝对距离度量 |
| 余弦相似度,值越大越相似 | 文本嵌入、语义检索(最常用) |
| 点积,值越大越相似 | 已归一化的向量,性能优于 cosine |
量化方式
通过 quantization 参数指定向量的量化压缩方式,可降低内存占用:
量化方式 | 说明 |
| 标量量化,将浮点值映射为整型,降低存储和内存开销 |
不指定 | 使用原始浮点精度 |
向量检索查询
// 假设 QUERY_EMBEDDING 是由 AI 模型生成的向量
const QUERY_EMBEDDING = [0.12, 0.45, -0.23, ...];
db.images.aggregate([
{
$vectorSearch: {
index: "vector_index",
path: "embedding",
queryVector: QUERY_EMBEDDING,
numCandidates: 150, // 候选集大小(性能与召回率的权衡参数)
limit: 10 // 返回结果数量
}
},
{
$project: {
_id: 0,
title: 1,
score: { $meta: "vectorSearchScore" } // 获取向量检索相关性得分
}
}
]);参数说明:
参数 | 类型 | 说明 |
| string | 向量检索索引名称 |
| string | 向量字段路径 |
| array | 查询向量,维度必须与索引定义一致 |
| int | 候选集大小,增大可提高召回准确率但消耗更多资源 |
| int | 返回的最近邻结果数量 |
| string | 查询时使用的量化方式(可选) |
混合检索($rankFusion)
使用 $rankFusion 功能需在实例参数设置中将 setParameter.featureFlagRankFusionBasic 和 setParameter.featureFlagRankFusionFull 设置为 true,修改后实例会自动重启生效。
$rankFusion 聚合阶段用于将多个检索管道的结果通过 Reciprocal Rank Fusion(RRF)算法进行融合排序,适用于全文检索与向量检索的混合查询场景。
RRF 算法原理
RRF 算法根据文档在各子管道中的排名计算融合分数:
score = weight × (1 / (rank + 60))其中 rank 为文档在子管道结果中的排名(从 1 开始),常数 60 为 RRF 平滑参数。当文档出现在多个子管道结果中时,最终分数为各子管道分数之和。
语法与参数
db.collection.aggregate([
{
$rankFusion: {
input: {
pipelines: {
"<pipeline_name_1>": [ /* 子管道 1 */ ],
"<pipeline_name_2>": [ /* 子管道 2 */ ]
}
},
combination: { // 可选
weights: {
"<pipeline_name_1>": number,
"<pipeline_name_2>": number
}
},
scoreDetails: boolean // 可选,默认 false
}
}
]);参数说明:
参数 | 类型 | 必填 | 说明 |
| object | 是 | 命名的子管道映射,每个子管道必须是"有序管道"(以 |
| object | 否 | 各子管道的权重映射,未指定的管道权重默认为 1 |
| boolean | 否 | 是否在结果中包含各子管道的详细评分信息,默认 |
使用示例
全文检索 + 向量检索的混合查询
db.products.aggregate([
{
$rankFusion: {
input: {
pipelines: {
fullTextSearch: [
{ $search: { index: "default", text: { query: "wireless headphones", path: "description" } } },
{ $limit: 20 }
],
vectorSearch: [
{ $vectorSearch: { index: "vector_index", path: "embedding", queryVector: QUERY_VECTOR, numCandidates: 100, limit: 20 } }
]
}
},
combination: {
weights: {
fullTextSearch: 0.3,
vectorSearch: 0.7
}
}
}
},
{ $limit: 10 },
{ $project: { title: 1, score: { $meta: "searchScore" } } }
]);查看详细评分
db.products.aggregate([
{
$rankFusion: {
input: {
pipelines: {
textSearch: [
{ $search: { index: "default", text: { query: "laptop", path: "title" } } },
{ $limit: 10 }
],
ratingSort: [
{ $sort: { rating: -1 } },
{ $limit: 10 }
]
}
},
scoreDetails: true
}
},
{ $limit: 5 },
{ $project: { title: 1, score: { $meta: "searchScore" }, details: { $meta: "scoreDetails" } } }
]);子管道要求
每个子管道必须是"有序管道",满足以下条件之一:
以
$search阶段开头以
$vectorSearch阶段开头包含
$sort阶段
使用注意事项
各子管道的结果数量建议通过
$limit控制,避免处理过多候选文档。weights中未指定的管道默认权重为 1,所有权重值必须为正数。scoreDetails: true会在结果中包含各子管道的排名和分数信息,便于调试和评估融合效果。
scoreDetails 输出格式
当 scoreDetails: true 时,通过 { $meta: "scoreDetails" } 获取的详细评分信息结构如下:
// scoreDetails 输出示例
{
value: 0.03252, // 最终 RRF 融合分数
description: "value output by reciprocal rank fusion algorithm, computed as sum of (weight * (1 / (60 + rank))) across input pipelines...",
details: [
{
inputPipelineName: "textSearch", // 子管道名称
rank: 1, // 该文档在此管道中的排名(从 1 开始)
weight: 1, // 该管道的权重
value: 0.6887, // 该管道原始评分
details: []
},
{
inputPipelineName: "vectorSearch",
rank: 2,
weight: 1,
value: 0.9999,
details: []
}
]
}
// 最终分数计算:1 × (1/(60+1)) + 1 × (1/(60+2)) = 0.01639 + 0.01613 = 0.03252评分与结果处理
获取检索相关性得分
// 全文检索得分
{ $project: { score: { $meta: "searchScore" } } }
// 向量检索得分
{ $project: { score: { $meta: "vectorSearchScore" } } }评分修改器
在全文检索操作符中,可通过 score 参数调整评分:
// boost:按倍数提升原始评分
{ text: { query: "crime", path: "genre", score: { boost: { value: 5 } } } }
// 原始评分 0.575 → 提升后 2.874
// constant:设置固定评分值
{ text: { query: "crime", path: "genre", score: { constant: { value: 10 } } } }
// 所有匹配文档评分均为 10支持的评分修改方式:
boost:按倍数提升原始评分constant:设置固定评分值function:自定义函数评分(可引用字段值动态计算)
高亮(Highlight)
返回匹配文本的上下文片段,便于在搜索结果中展示命中关键词:
db.movies.aggregate([
{
$search: {
index: "default",
text: { query: "war", path: "plot" },
highlight: {
path: "plot",
maxCharsToExamine: 500,
maxNumPassages: 3
}
}
},
{
$project: {
title: 1,
_id: 0,
score: { $meta: "searchScore" },
highlights: { $meta: "searchHighlights" }
}
}
]);storedSource
在索引中存储原始字段值,查询时可直接返回,避免回查集合从而降低延迟。
索引定义:
db.collection.createSearchIndex({
name: "stored_index",
definition: {
mappings: { dynamic: true },
storedSource: {
include: ["title", "genre"]
}
}
});查询时使用 returnStoredSource:
db.movies.aggregate([
{
$search: {
index: "stored_index",
text: { query: "drama", path: "genre" },
returnStoredSource: true
}
},
{ $project: { title: 1, genre: 1, _id: 0, score: { $meta: "searchScore" } } },
{ $limit: 3 }
]);Facet 聚合($searchMeta)
使用 $searchMeta 聚合阶段,按分类统计文档数量,常用于搜索结果的分面导航:
db.movies.aggregate([
{
$searchMeta: {
index: "movies_custom_index",
facet: {
operator: {
range: { path: "rating", gte: 8.0 }
},
facets: {
genreFacet: {
type: "string",
path: "genre",
numBuckets: 10
},
yearFacet: {
type: "number",
path: "year",
boundaries: [1990, 2000, 2010, 2020]
}
}
}
}
}
]);说明:使用 $searchMeta 时,genre 字段需在索引中定义为 stringFacet 类型。
索引管理
操作 | 方法 |
创建全文检索索引 |
|
创建向量检索索引 |
|
查看索引列表 |
|
查看索引列表(简化) |
|
更新索引定义 |
|
删除索引 |
|
索引构建与状态
索引创建后,mongot 会在后台异步构建索引。索引的生命周期包含以下阶段:
索引定义注册:调用
createSearchIndex后,索引定义立即注册到mongot的索引目录中。此时通过$listSearchIndexes即可看到该索引(返回id、name、type、latestDefinition字段)。初始同步(Initial Sync):
mongot从mongod同步全量数据并构建索引。对于大集合,此过程可能持续数分钟到数十分钟。稳定状态(Steady State):初始同步完成后,索引进入稳定状态,此时索引可正常响应查询请求。
如何确认索引已就绪:
$listSearchIndexes和getSearchIndexes()在索引创建后立即返回索引信息,但这不代表索引已构建完成。当前版本的返回结果中不包含索引状态字段。确认索引就绪的可靠方式是尝试执行
$search查询:若索引尚未就绪,查询将返回错误;若查询成功返回结果,则表明索引已可用。也可通过 mongot 日志确认:当日志中出现
Transitioning from INITIAL_SYNC to STEADY_STATE消息时,表示该索引已完成构建并进入可查询状态。
索引更新
更新索引定义时,mongot 会在后台重建新索引,期间旧索引仍可支持查询。新索引构建完成后自动替换旧索引,整个过程对查询无感知。
注意事项
索引构建延迟:索引创建后需等待构建完成才能查询。通过
$listSearchIndexes能看到索引信息不代表索引已就绪,应通过实际执行$search查询或查看 mongot 日志来确认索引状态。索引更新:更新索引定义时,
mongot会在后台重建新索引,旧索引仍可支持查询,新索引完成构建后自动替换。数据同步延迟:数据写入后通常在秒级延迟内即可检索,延迟受实例写入负载和文档大小等因素影响。
静态映射推荐:生产环境建议使用静态映射控制索引范围,减少存储开销。
filter 性能优势:
compound查询中的filter子句不计算评分,性能优于must,适合精确过滤条件。向量维度一致性:向量检索索引的
numDimensions必须与实际嵌入模型输出维度一致。分析器命名格式:内置分析器使用
lucene.前缀格式(如lucene.standard)。wildcard/regex 注意事项:在
string类型(已分词)字段上使用时必须设置allowAnalyzedField: true,且匹配发生在 token 级别而非原始文本级别。备份恢复:通过备份恢复的新实例不包含 Search 索引,需在新实例上重新开通 Search 服务并重建索引。
数据迁移:使用 DTS 等工具迁移数据时,Search 索引不会被同步,需在目标实例手动创建。