☰
laravel-mongodb 语义检索实战:Atlas Vector Search 索引、查询与混合排序全解析
2026/10/15 18:05:43 网站建设 项目流程
  • 后端
  • 数据库

【免费下载链接】laravel-mongodb

A MongoDB based Eloquent model and Query builder for Laravel (Moloquent)

项目地址:https://gitcode.com/gh_mirrors/la/laravel-mongodb
点击查看免费下载

导读

本文基于 laravel-mongodb 官方技能文档《Vector Search》展开,系统讲解如何在 Laravel + MongoDB Atlas 场景下基于语义相似度检索文档:通过autoEmbed让 Atlas 自动生成并维护向量、或自行调用嵌入模型手工写入向量,并最终用vectorSearch()一行查询完成语义召回;同时覆盖$rankFusion混合检索(全文 + 向量)与索引生命周期管理。读完本文你将掌握从建索引、写入数据到执行语义查询的完整链路,并理解其底层在 src/Query/Builder.php 与 src/Schema/Blueprint.php 中的真实实现。

一、两种向量检索方案总览

Atlas Vector Search 允许你使用存储在数据旁的 embedding 向量,按语义相似度查询文档。laravel-mongodb 支持两种截然不同的接入方式:

方案适用场景
Auto-embedding(自动嵌入)文档是纯文本字段;由 Atlas 自动生成并维护向量——无需编写任何 PHP 嵌入代码
Manual embedding(手工嵌入)需要自己掌控嵌入模型、需要自定义维度,或要处理非文本数据(如图片、音频)

两者在索引定义与查询入参上截然不同:自动嵌入用autoEmbed索引类型 + 字符串query;手工嵌入用vector索引类型 + 浮点数组queryVector。下面分别展开。

二、Auto-embedding:文本自动向量化(文本场景推荐)

Atlas 通过autoEmbed索引类型自动生成并维护向量。不需要任何 PHP 代码生成或存储向量——Atlas 在插入(insert)和更新(update)时自动完成嵌入。

2.1 创建 autoEmbed 向量搜索索引

在迁移文件中通过 Laravel Schema 的vectorSearchIndex()创建索引:

<?php use Illuminate\Database\Migrations\Migration; use Illuminate\Support\Facades\Schema; return new class extends Migration { public function up(): void { Schema::connection('mongodb')->table('products', function ($collection): void { $collection->vectorSearchIndex([ 'fields' => [ [ 'type' => 'autoEmbed', 'modality' => 'text', 'path' => 'description', // source text field 'model' => 'voyage-4', ], ['type' => 'filter', 'path' => 'category'], ], ], 'products_vector'); }); } };

关键参数说明:

  • 可用模型:voyage-4、voyage-4-large以及其他 MongoDB 支持提供商提供的模型。
  • 可选字段:
    • numDimensions:向量维度;
    • quantization:量化方式,取值为float|scalar|binary|binaryNoRescore;
    • similarity:相似度度量,取值为euclidean|cosine|dotProduct;
    • indexingMethod:索引算法,取值为flat|hnsw;
    • hnswOptions:HNSW 算法专属选项。
  • 索引中的filter类型字段(如category)用于后续查询的元数据过滤,不参与向量计算。

从源码看,src/Schema/Blueprint.php 中vectorSearchIndex(array $definition, string $name = 'default')的实现是:

$this->collection->createSearchIndex($definition, ['name' => $name, 'type' => 'vectorSearch']);

也就是说,该迁移本质上通过createSearchIndexes命令创建了一个type: "vectorSearch"的 Atlas 搜索索引。测试 tests/SchemaTest.php 已验证索引的type为vectorSearch、名称与latestDefinition.fields[0].type均正确。

2.2 正常插入文档——无需嵌入步骤

创建索引后,像平时一样用 Eloquent 写入数据即可,Atlas 会在插入时自动为description字段生成向量:

<?php use App\Models\Product; // Atlas generates the embedding for 'description' automatically on insert Product::create([ 'name' => 'Wireless headphones', 'description' => 'Over-ear noise-cancelling headphones with 30h battery', 'price' => 149.99, ]);

2.3 用文本直接查询(Atlas 自动嵌入查询)

<?php use App\Models\Product; $results = Product::vectorSearch( index: 'products_vector', path: 'description', query: 'noise-cancelling headphones for travel', numCandidates: 100, limit: 10, );
  • 索引使用autoEmbed时传字符串query;手工提供向量时传浮点数组queryVector,两者互斥。
  • 可选的model参数会覆盖索引上配置的模型(例如用更轻量的模型执行查询嵌入)。
  • numCandidates控制候选集规模(近似检索时参与排序的候选数量),limit控制最终返回条数。

源码印证:vectorSearch()的完整签名在 src/Query/Builder.php:

public function vectorSearch( string $index, string $path, array|null $queryVector = null, int $limit = 10, bool $exact = false, QueryInterface|array|null $filter = null, int|null $numCandidates = null, string|null $query = null, string|null $model = null, ): Collection

两个值得注意的实现细节:

  1. 版本门槛:query/model参数要求mongodb/mongodb2.4+。若未满足,src/Query/Builder.php 会抛出BadMethodCallException('The "query" and "model" parameters of vectorSearch() require mongodb/mongodb 2.4+.')。包本身在 composer.json 声明"mongodb/mongodb": "^1.21.2|^2.1.1",因此使用自动嵌入前请确认实际安装版本不低于 2.4。
  2. 自动打分:查询链路会追加addFields(vectorSearchScore: ['$meta' => 'vectorSearchScore']),即每条结果都会携带vectorSearchScore字段(见 src/Query/Builder.php),便于排序与展示相关性分数。

测试 tests/AtlasSearchTest.php 中testEloquentBuilderVectorSearchAutoEmbedding展示了完整实践:索引模型用voyage-4-large,查询时用model: 'voyage-4-lite'覆盖(同系列向量相互兼容),并断言结果模型为Book且vectorSearchScore为 float。该测试需要 Atlas 集群配置VOYAGE_API_KEY,否则会跳过。

三、Manual embedding:手工生成向量

当你自行生成向量(如使用 Laravel AI SDK、OpenAI PHP 等)并存入文档时,采用此方案。完整流程始终包含三步:创建索引 → 写入时存储 embedding → 按向量查询。

3.1 创建 vector 类型索引

<?php use Illuminate\Database\Migrations\Migration; use Illuminate\Support\Facades\Schema; return new class extends Migration { public function up(): void { Schema::connection('mongodb')->table('products', function ($collection): void { $collection->vectorSearchIndex([ 'fields' => [ [ 'type' => 'vector', 'path' => 'embedding', 'numDimensions' => 1536, 'similarity' => 'cosine', ], ['type' => 'filter', 'path' => 'category'], ], ], 'products_vector'); }); } };

numDimensions必须与你嵌入模型的输出维度一致(示例中的1536对应 OpenAItext-embedding-3-small);similarity决定向量空间的距离度量(euclidean/cosine/dotProduct),应与查询语义匹配。

3.2 写入时存储 embedding

<?php use App\Models\Product; use OpenAI\Laravel\Facades\OpenAI; $response = OpenAI::embeddings()->create([ 'model' => 'text-embedding-3-small', 'input' => $description, ]); Product::create([ 'name' => 'Wireless headphones', 'description' => $description, 'price' => 149.99, 'embedding' => $response->embeddings[0]->embedding, ]);

这里把模型返回的向量数组直接作为文档的embedding字段存储,字段路径必须与索引定义中的path完全一致。

3.3 按向量查询

<?php use App\Models\Product; use OpenAI\Laravel\Facades\OpenAI; $queryVector = OpenAI::embeddings()->create([ 'model' => 'text-embedding-3-small', 'input' => $userQuery, ])->embeddings[0]->embedding; $results = Product::vectorSearch( index: 'products_vector', path: 'embedding', queryVector: $queryVector, numCandidates: 100, limit: 10, );

查询参数进阶(均已在vectorSearch()签名中体现,可结合 tests/AtlasSearchTest.php 理解):

  • exact: true:关闭近似检索(ANN),执行精确暴力扫描。测试testDatabaseBuilderVectorSearch以exact: true传入完全匹配的向量,断言首位结果vectorSearchScore === 1.0。
  • filter:支持QueryInterface|array元数据过滤。测试testEloquentBuilderVectorSearch用Query::query(title: Query::ne('...'))排除精确匹配项,并断言vectorSearchScore处于(0.9, 1.0)区间,证明过滤与评分可以同时生效。
  • 结果统一由vectorSearchScore字段携带相关性分数。

Eloquent 层的方法位于 src/Eloquent/Builder.php,它会调用底层 Query Builder 的vectorSearch(),再通过hydrate()将结果水合为模型实例(返回Collection<int, TModel>),因此Product::vectorSearch(...)返回的仍是 Eloquent 模型集合。

四、混合检索:全文 + 向量($rankFusion)

把 Atlas Search 的全文检索($search)与向量检索($vectorSearch)放进同一聚合管道,用$rankFusion做加权融合排序,即可同时兼顾关键词命中与语义相关:

<?php use App\Models\Product; $results = Product::raw(fn ($c) => $c->aggregate([ ['$rankFusion' => [ 'input' => [ 'pipelines' => [ 'fullText' => [ ['$search' => [ 'index' => 'default', 'text' => ['query' => $query, 'path' => 'description'], ]], ], 'semantic' => [ ['$vectorSearch' => [ 'index' => 'products_vector', 'path' => 'embedding', 'queryVector' => $queryVector, 'numCandidates' => 100, 'limit' => 20, ]], ], ], ], 'combination' => ['weights' => ['fullText' => 0.4, 'semantic' => 0.6]], ]], ['$limit' => 10], ]));

要点:

  • input.pipelines中每个命名管道(fullText/semantic)各自产出一个子结果集,$rankFusion会按combination.weights加权合并排序;权重之和不必为 1,但比例决定两路信号的强弱。
  • 全文管道$search需要独立的 Atlas Search 索引(示例中名为default,可通过searchIndex()创建);向量管道$vectorSearch使用本文创建的products_vector。
  • 外层再叠加$limit控制最终返回条数。
  • 为什么用raw()而不是vectorSearch()?因为$rankFusion属于较新的聚合阶段。laravel-mongodb 的 src/Query/AggregationBuilder.php 基于mongodb/mongodb的FluentFactoryTrait提供流畅的阶段构建,并提供了addRawStage()兜底——对于库尚未内置的阶段,可直接以原始管道数组的方式拼接,文档示例正是这一用法。

五、索引生命周期管理

除了创建,laravel-mongodb 还提供完整的 Atlas 搜索索引管理能力(均位于 src/Schema/Blueprint.php):

  • searchIndex(array $definition, string $name = 'default'):创建 Atlas全文Search 索引,底层调用createSearchIndex($definition, ['name' => ..., 'type' => 'search']),适用于$search阶段的全文检索场景。
  • vectorSearchIndex(array $definition, string $name = 'default'):创建向量索引(type: 'vectorSearch'),即本文核心。
  • dropSearchIndex(string $name):删除索引。测试 tests/SchemaTest.php 与 tests/AtlasSearchTest.php 均演示了建索引后dropSearchIndex()清理,避免共享夹具残留。

六、运行前提与注意事项

  • 集群规格:需要 AtlasM10+集群,Vector Search 在免费/共享层(Free/Shared tiers)不可用。
  • 嵌入提供商密钥:auto-embedding 要求先在 Atlas 中配置好嵌入提供商的 API key(例如VOYAGE_API_KEY),否则索引创建或查询会失败;测试 tests/AtlasSearchTest.php 通过检测VOYAGE_API_KEY环境变量决定是否跳过。
  • 索引构建时间:执行php artisan migrate后,Atlas 需要几分钟构建索引,请等待索引进入 Ready 状态后再查询(测试中对应waitForSearchIndexesReady()辅助方法)。
  • 仅限 Atlas:$vectorSearch只对 MongoDB Atlas 集群可用,不适用于自托管部署(见 src/Query/Builder.php 的源码注释;src/Schema/Builder.php 也记录了 MongoDB 4~6 / 7-community 不存在createSearchIndexes命令的事实)。
  • auto-embedding 的 PHP 端版本要求:query/model参数需要mongodb/mongodb2.4+(src/Query/Builder.php 会做运行时校验),使用前请确认依赖版本。

附:速查对照

能力写法
建 autoEmbed 索引Schema::table(...)内$collection->vectorSearchIndex([...autoEmbed...], 'products_vector')
建 vector 索引vectorSearchIndex字段type: 'vector'+numDimensions+similarity
文本语义查询Product::vectorSearch(index:, path:, query: '...', numCandidates:, limit:)
向量查询Product::vectorSearch(index:, path:, queryVector: $vec, numCandidates:, limit:)
覆盖嵌入模型model: 'voyage-4-lite'(作用于 autoEmbed 索引)
精确检索 / 过滤exact: true、filter: Query::query(...)
混合排序Product::raw(fn ($c) => $c->aggregate([...'$rankFusion'...]))
删索引Schema::table(...)内$collection->dropSearchIndex('products_vector')

以上全部操作均以当前仓库 src 下源码与 tests/AtlasSearchTest.php、tests/SchemaTest.php 的真实实现与测试用例为佐证,可直接在项目中对照验证。

  • 后端
  • 数据库

【免费下载链接】laravel-mongodb

A MongoDB based Eloquent model and Query builder for Laravel (Moloquent)

项目地址:https://gitcode.com/gh_mirrors/la/laravel-mongodb
点击查看免费下载

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询