MongoDB Search 算法参数

更新时间:
复制为 MD 格式

本文系统介绍 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 中,为每个字段指定类型:

类型

说明

适用场景

string

全文索引字符串字段,经过分词处理

标题、描述、评论等需要全文检索的文本

stringFacet

字符串分面字段,不分词

分类、标签等用于聚合统计的字段

number

数值类型(int、long、float、double)

价格、评分、年份等数值范围查询

date

日期类型

时间范围查询

boolean

布尔类型

状态过滤

objectId

ObjectId 类型

_id 或引用字段

geo

地理坐标(GeoJSON)

地理位置检索

token

不分词的精确匹配字符串

枚举值、ID、SKU 等精确匹配场景

autocomplete

自动补全类型

搜索建议、前缀匹配

document

嵌套文档

嵌套对象字段索引

vector

向量类型(仅用于向量检索索引)

向量相似性检索

分析器

分析器决定文本如何被分词和标准化,在 string 类型字段中通过 analyzer 参数指定。

内置分析器

分析器

说明

分词示例

lucene.standard

标准分析器,按 Unicode 文本分段规则分词并转小写

"The Quick Fox" → ["the", "quick", "fox"]

lucene.simple

按非字母字符分割并转小写

"hello-world 123" → ["hello", "world"]

lucene.whitespace

仅按空白字符分割,不转小写

"Hello World" → ["Hello", "World"]

lucene.keyword

不分词,整个字段值作为一个 token

"New York" → ["New York"]

lucene.english

英文分析器,支持词干提取和停用词过滤

"running quickly" → ["run", "quick"]

lucene.chinese

中文分析器,支持中文分词

"全文搜索引擎" → ["全文", "搜索", "引擎"]

自定义分析器

可通过组合 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

说明

standard

按 Unicode 文本分段规则分词

whitespace

按空白字符分割

keyword

整体作为单个 token

regexCaptureGroup

正则捕获组分词

regexSplit

正则分隔符分割

nGram

N-gram 分词

edgeGram

边缘 N-gram(前缀分词)

uaxUrlEmail

识别并保留 URL 和 Email 地址

Token Filters

Filter

说明

lowercase

转小写

stopword

移除停用词

stemming

词干提取

synonym

同义词扩展

shingle

生成 N-gram 短语

trim

去除首尾空白

length

按长度过滤 token

icuFolding

Unicode 折叠(去除重音符号等)

icuNormalizer

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" } } }
]);

子句说明:

子句

含义

是否影响评分

must

文档必须匹配所有条件

mustNot

文档必须不匹配任何条件

should

匹配的文档获得更高评分

filter

文档必须匹配,但不计算评分

否(性能更优)

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" } } }
]);
// 匹配 Interstellar

exists(字段存在性检查)

检查文档中指定字段是否存在。

db.movies.aggregate([
  {
    $search: {
      index: "default",
      exists: {
        path: "rating"
      }
    }
  },
  { $project: { title: 1, rating: 1, _id: 0, score: { $meta: "searchScore" } } },
  { $limit: 3 }
]);

混合检索($rankFusion)

说明

使用 $rankFusion 功能需在实例参数设置中将 setParameter.featureFlagRankFusionBasicsetParameter.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
    }
  }
]);

参数说明:

参数

类型

必填

说明

input.pipelines

object

命名的子管道映射,每个子管道必须是"有序管道"(以 $search$vectorSearch 开头,或包含 $sort

combination.weights

object

各子管道的权重映射,未指定的管道权重默认为 1

scoreDetails

boolean

是否在结果中包含各子管道的详细评分信息,默认 false

使用示例

全文检索 + 向量检索的混合查询

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 类型。

索引管理

操作

方法

创建全文检索索引

db.collection.createSearchIndex({ name, definition })

创建向量检索索引

db.collection.createSearchIndex(name, "vectorSearch", { fields })

查看索引列表

db.collection.aggregate([{ $listSearchIndexes: {} }])

查看索引列表(简化)

db.collection.getSearchIndexes()

更新索引定义

db.collection.updateSearchIndex(name, definition)

删除索引

db.collection.dropSearchIndex(name)

索引构建与状态

索引创建后,mongot 会在后台异步构建索引。索引的生命周期包含以下阶段:

  1. 索引定义注册:调用 createSearchIndex 后,索引定义立即注册到 mongot 的索引目录中。此时通过 $listSearchIndexes 即可看到该索引(返回 idnametypelatestDefinition 字段)。

  2. 初始同步(Initial Sync)mongotmongod 同步全量数据并构建索引。对于大集合,此过程可能持续数分钟到数十分钟。

  3. 稳定状态(Steady State):初始同步完成后,索引进入稳定状态,此时索引可正常响应查询请求。

如何确认索引已就绪:

  • $listSearchIndexesgetSearchIndexes() 在索引创建后立即返回索引信息,但这不代表索引已构建完成。当前版本的返回结果中不包含索引状态字段。

  • 确认索引就绪的可靠方式是尝试执行 $search 查询:若索引尚未就绪,查询将返回错误;若查询成功返回结果,则表明索引已可用。

  • 也可通过 mongot 日志确认:当日志中出现 Transitioning from INITIAL_SYNC to STEADY_STATE 消息时,表示该索引已完成构建并进入可查询状态。

索引更新

更新索引定义时,mongot 会在后台重建新索引,期间旧索引仍可支持查询。新索引构建完成后自动替换旧索引,整个过程对查询无感知。

注意事项

  1. 索引构建延迟:索引创建后需等待构建完成才能查询。通过 $listSearchIndexes 能看到索引信息不代表索引已就绪,应通过实际执行 $search 查询或查看 mongot 日志来确认索引状态。

  2. 索引更新:更新索引定义时,mongot 会在后台重建新索引,旧索引仍可支持查询,新索引完成构建后自动替换。

  3. 数据同步延迟:数据写入后通常在秒级延迟内即可检索,延迟受实例写入负载和文档大小等因素影响。

  4. 静态映射推荐:生产环境建议使用静态映射控制索引范围,减少存储开销。

  5. filter 性能优势compound 查询中的 filter 子句不计算评分,性能优于 must,适合精确过滤条件。

  6. 向量维度一致性:向量检索索引的 numDimensions 必须与实际嵌入模型输出维度一致。

  7. 分析器命名格式:内置分析器使用 lucene. 前缀格式(如 lucene.standard)。

  8. wildcard/regex 注意事项:在 string 类型(已分词)字段上使用时必须设置 allowAnalyzedField: true,且匹配发生在 token 级别而非原始文本级别。

  9. 备份恢复:通过备份恢复的新实例不包含 Search 索引,需在新实例上重新开通 Search 服务并重建索引。

  10. 数据迁移:使用 DTS 等工具迁移数据时,Search 索引不会被同步,需在目标实例手动创建。