资讯动态

Mongoose 如何用 $rankFusion 把 $vectorSearch 与 $search 组合成混合搜索?

发布时间:2026/9/13 15:37:57 来源:尧图企业网站定制
Mongoose 如何用 $rankFusion 把 $vectorSearch 与 $search 组合成混合搜索【免费下载链接】mongooseMongoDB object modeling designed to work in an asynchronous environment.项目地址: https://gitcode.com/GitHub_Trending/mo/mongoose如果你用 Mongoose 做搜索单纯的关键字匹配$search抓不住语义相近但用词不同的内容单纯的向量相似度搜索$vectorSearch又容易漏掉精确匹配标题或关键词的文档。Mongoose 官方文档给出的做法是用$rankFusion把这两种阶段拆成两条子管道再用 Reciprocal Rank FusionRRF把两路结果合并排序。本文基于仓库中的 Atlas Search 文档 和 Atlas Vector Search 文档给出一条从建索引到跑通混合查询的连续操作路径。适用前提集群支持 Atlas Vector Search文档以 MongoDB Atlas 上的向量搜索为准示例数据基于 MongoDB 的 mflix 样本数据集你需要自己生成 embedding。文档明确说明 Mongoose doesnt generate embeddings for you需要在保存文档或发起查询前生成向量。第一步在 Schema 中定义两个搜索索引混合搜索需要两个索引一个 Atlas Search 文本索引示例中叫movie_search和一个向量搜索索引示例中叫vector_index。两者都通过schema.searchIndex()声明再用Model.createSearchIndexes()创建。文本索引的声明来自 docs/atlas-search.mdmovieSchema.searchIndex({ name: movie_search, definition: { mappings: { dynamic: false, fields: { title: { type: string }, fullplot: { type: string }, cast: { type: string }, year: { type: number } } } } });文档建议生产环境不要用dynamic: true——它会索引所有字段带来不必要的存储开销。需要更细的分词控制时可以给字段加analyzer例如lucene.standard按空白和标点分词、lucene.english带词干化、lucene.keyword整值精确匹配。向量索引的声明来自 docs/atlas-vector-search.mdembedding 用Buffer存成 binDataconst movieSchema new mongoose.Schema({ title: String, plot: String, year: Number, genres: [String], // Field to store the embeddings for the plot. It can be whatever you choose. plot_embedding_voyage_3_large: { type: Buffer, // embeddings are stored as binData (BSON binary vector, float32) validate: { // A 2048-dim float32 binary vector is 8194 bytes: a 2-byte vector header 2048 x 4 bytes validator: v v null || v.length 8194, message: plot_embedding must be nullish or a 2048-dimension float32 binary vector } } }); // Define a vector search index movieSchema.searchIndex({ name: vector_index, type: vectorSearch, definition: { fields: [{ type: vector, path: plot_embedding_voyage_3_large, // Where the embeddings are stored numDimensions: 2048, // Must match your embedding models dimensions similarity: cosine // Similarity function to use }] } }); const Movie mongoose.model(Movie, movieSchema); await Movie.createSearchIndexes(); // Create the index向量索引定义的几个关键字段numDimensions必须和 embedding 模型的输出维度一致similarity可选cosine、euclidean或dotProduct。如果应用侧用第三方模型生成向量保存前要先用Binary.fromFloat32Array(new Float32Array(embeddings))把浮点数组转成 binData文档给出的示例函数见 docs/atlas-vector-search.md 的 Using Third-Party Embedding Models 一节。文档还提供了另一种可选路径Atlas Automated Embeddings。在 Atlas 上以及自管理部署的 MongoDB 8.2可以定义type: autoEmbed的索引由 Atlas 自动生成和管理 embeddingschema 里不需要 embedding 字段查询时直接传纯文本query。如果你走这条路向量子管道里的$vectorSearch就不需要queryVector。第二步确认索引已就绪向量搜索索引创建后需要时间构建不能建完就立刻查。用Model.listSearchIndexes()检查状态const indexes await Movie.listSearchIndexes(); const vectorIndex indexes.find(idx idx.name vector_index); if (vectorIndex?.queryable) { console.log(Vector search index is ready!); } else { console.log(Index is still building...); }queryable: true是文档给出的就绪判断标准。文档的最佳实践里也强调创建后总要检查listSearchIndexes()确认索引是queryable: true。第三步用 $rankFusion 组合两条子管道核心查询如下出自 docs/atlas-search.md 的 Hybrid Search 一节// See the Vector Search guide for details on generating embeddings const queryEmbedding await generateEmbedding(charming animals with adventurous tone); const results await Movie.aggregate([ { $rankFusion: { input: { pipelines: { // Semantic search subpipeline vector: [ { $vectorSearch: { index: vector_index, // Name of your vector search index path: plot_embedding_voyage_3_large, // Name of the field containing the embeddings queryVector: queryEmbedding, numCandidates: 100, limit: 50 } } ], // Keyword search subpipeline text: [ { $search: { index: movie_search, text: { query: adventure animals, path: fullplot } } }, { $limit: 50 } ] } }, combination: { weights: { vector: 0.7, // 70% weight to semantic relevance text: 0.3 // 30% weight to keyword relevance } } } }, { $limit: 10 } ]);结构上需要理解三点两条子管道是并列的pipelines.vector和pipelines.text各自独立执行$rankFusion用 RRF 把两路的排名结果融合。$search必须位于其子管道的第一阶段所以它不能直接放在$vectorSearch之后的同一条管道里——这正是必须用$rankFusion拆子管道的原因。combination.weights决定两路贡献比例上例中vector: 0.7、text: 0.3是文档示例给出的值即语义相关性占 70%、关键词相关性占 30%。向量侧的参数约束numCandidates是考察的候选文档数必须大于等于limit文档建议设为limit的 10–20 倍以获得更好的准确度。上面示例就是numCandidates: 100配limit: 50。其中generateEmbedding()是向量搜索指南里给出的示例函数需要VOYAGE_API_KEY环境变量通过fetch调 embedding 服务并把返回的浮点数组取出。注意文档的两条硬性要求索引和查询必须用同一个 embedding 模型如果用dotProduct相似度向量必须归一化。需要留意的限制先查索引状态再发查询queryable为 false 时索引还在构建中此时查询不能当作正常失败处理应等构建完成。$search的位置约束在主管道里$match放在$search之前会直接抛错要在搜索时过滤应该用compound里的filter子句。子管道内部同样要遵守$search在第一位的规则。索引管理方式开发环境可以打开 schema 选项autoSearchIndex: true该选项默认是false见 docs/guide.md 的 autoSearchIndex 一节让 Mongoose 在模型初始化时自动创建 schema 里声明的搜索索引文档建议生产环境关闭它改由Model.createSearchIndexes()、Atlas UI 或部署脚本手动管理避免应用部署时引入意外的索引变更。减少下游数据量在$rankFusion之后尽早加$limit需要时在结果里用$project只取需要的字段。跑通后的判断依据就是查询能正常返回融合排序后的文档且此前listSearchIndexes()已确认两个索引都处于queryable: true。文档没有给出固定的正确输出示例结果是否符合预期需要基于你自己数据集上观察到的排名来判断如果要调整语义和关键词的权重改combination.weights后重跑同一条聚合对比即可。想深入了解 RRF 的融合细节文档指向了 MongoDB Atlas Hybrid Search 文档仓库内的延伸阅读是 docs/atlas-vector-search.mdembedding 生成与$vectorSearch参数和 docs/atlas-search.md$search复合查询与索引管理。【免费下载链接】mongooseMongoDB object modeling designed to work in an asynchronous environment.项目地址: https://gitcode.com/GitHub_Trending/mo/mongoose创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价