搜索
简介
几乎每个应用程序都需要搜索功能。无论您的用户是在知识库中搜索相关文章、探索产品目录,还是针对文档语料库提出自然语言问题,Laravel 都提供了内置工具来处理这些场景——通常您不需要任何外部服务即可实现。
大多数应用程序会发现,Laravel 提供的内置数据库驱动选项已经足够满足需求——只有当您需要容错纠错、分面过滤或大规模地理搜索等功能时,才需要外部搜索服务。
全文搜索
当您需要关键词相关性排名(数据库根据搜索词的匹配程度对结果进行评分和排序)时,Laravel 的 whereFullText 查询构建器方法利用了 MariaDB、MySQL 和 PostgreSQL 上的原生全文索引。全文搜索可以理解词边界和词干提取,因此搜索“running”可以匹配包含“run”的记录。无需任何外部服务。
语义 / 向量搜索
对于通过含义而非精确关键词匹配结果的 AI 驱动语义搜索,whereVectorSimilarTo 查询构建器方法使用了存储在带有 pgvector 扩展的 PostgreSQL 中的向量嵌入。例如,搜索“Napa Valley 最好的酒庄”可以呈现出一篇题为“值得一游的顶级葡萄园”的文章——尽管它们没有任何单词重叠。向量搜索需要带有 pgvector 扩展的 PostgreSQL 和 Laravel AI SDK。
重排序 (Reranking)
Laravel 的 AI SDK 提供了重排序功能,该功能使用 AI 模型根据语义相关性对任何结果集进行重新排序。在执行全文搜索等快速初始检索步骤后,作为第二阶段使用,重排序尤为强大——为您提供速度和语义准确性双重保证。
Laravel Scout 搜索
对于那些希望使用 Searchable 特性自动保持搜索索引与 Eloquent 模型同步的应用程序,Laravel Scout 提供了内置的数据库引擎以及用于 Algolia、Meilisearch 和 Typesense 等第三方服务的驱动程序。
全文搜索
虽然 LIKE 查询对于简单的子字符串匹配效果良好,但它们无法理解语言。对“running”进行 LIKE 搜索无法找到包含“run”的记录,且结果不会按相关性排序——它们仅仅按照数据库发现它们的顺序返回。全文搜索通过使用理解词边界、词干提取和相关性评分的专用索引解决了这两个问题,使数据库能够首先返回最相关的结果。
MariaDB、MySQL 和 PostgreSQL 内置了快速全文搜索功能——无需外部搜索服务。您只需将全文索引添加到要搜索的列中,然后使用 whereFullText 查询构建器方法进行搜索即可。
全文搜索目前由 MariaDB、MySQL 和 PostgreSQL 支持。
添加全文索引
要使用全文搜索,首先向要搜索的列添加全文索引。您可以将索引添加到单个列,或传递一个列数组来创建可同时搜索多个字段的复合索引。
1Schema::create('articles', function (Blueprint $table) {2 $table->id();3 $table->string('title');4 $table->text('body');5 $table->timestamps();6 7 $table->fullText(['title', 'body']);8});
在 PostgreSQL 上,您可以为索引指定语言配置,这可以控制词干提取的方式。
1$table->fullText('body')->language('english');
有关创建索引的更多信息,请参阅迁移文档。
运行全文查询
索引就绪后,使用 whereFullText 查询构建器方法进行搜索。Laravel 将为您的数据库驱动生成合适的 SQL——例如,MariaDB 和 MySQL 上的 MATCH(...) AGAINST(...),以及 PostgreSQL 上的 to_tsvector(...) @@ plainto_tsquery(...)。
1$articles = Article::whereFullText('body', 'web developer')->get();
使用 MariaDB 和 MySQL 时,结果会自动按相关性得分排序。在 PostgreSQL 上,whereFullText 会过滤匹配的记录,但不会按相关性对其进行排序——如果您需要 PostgreSQL 上的自动相关性排序,请考虑使用 Scout 的数据库引擎,它会自动为您处理此问题。
如果您创建了跨多个列的复合全文索引,则可以通过将相同的列数组传递给 whereFullText 来对所有列进行搜索。
1$articles = Article::whereFullText(2 ['title', 'body'], 'web developer'3)->get();
orWhereFullText 方法可用于添加“或”条件的全文搜索子句。有关完整详细信息,请参阅查询构建器文档。
语义 / 向量搜索
全文搜索依赖于关键词匹配——查询中的单词必须(以某种形式)出现在数据中。语义搜索采取了根本不同的方法:它使用 AI 生成的向量嵌入将文本的含义表示为数字数组,然后查找含义与查询最相似的结果。例如,搜索“Napa Valley 最好的酒庄”可以呈现出一篇题为“值得一游的顶级葡萄园”的文章——尽管它们在单词上完全没有重叠。
向量搜索的基本工作流程是:为每条内容生成一个嵌入(数字数组)并将其与数据一起存储;然后在搜索时,为用户的查询生成一个嵌入,并找到向量空间中与该嵌入最接近的已存储嵌入。
向量搜索需要带有 pgvector 扩展的 PostgreSQL 数据库和 Laravel AI SDK。所有 Laravel Cloud 无服务器 Postgres 数据库都已经包含了 pgvector。
生成嵌入 (Embeddings)
嵌入是一个高维数字数组(通常包含数百或数千个数字),它代表了一段文本的语义含义。您可以使用 Laravel Stringable 类上提供的 toEmbeddings 方法为字符串生成嵌入。
1use Illuminate\Support\Str;2 3$embedding = Str::of('Napa Valley has great wine.')->toEmbeddings();
要同时为多个输入生成嵌入——这比一次生成一个更有效率,因为它只需要对嵌入提供程序进行一次 API 调用——请使用 Embeddings 类。
1use Laravel\Ai\Embeddings;2 3$response = Embeddings::for([4 'Napa Valley has great wine.',5 'Laravel is a PHP framework.',6])->generate();7 8$response->embeddings; // [[0.123, 0.456, ...], [0.789, 0.012, ...]]
有关配置嵌入提供程序、自定义维度和缓存的更多详细信息,请参阅 AI SDK 文档。
存储与索引向量
要存储向量嵌入,请在迁移中定义一个 vector 列,指定与嵌入提供程序的输出匹配的维度数(例如,OpenAI 的 text-embedding-3-small 模型为 1536)。您还应该在列上调用 index 以创建 HNSW(分层可导航小世界)索引,这会极大地加速大型数据集上的相似度搜索。
1Schema::ensureVectorExtensionExists();2 3Schema::create('documents', function (Blueprint $table) {4 $table->id();5 $table->string('title');6 $table->text('content');7 $table->vector('embedding', dimensions: 1536)->index();8 $table->timestamps();9});
Schema::ensureVectorExtensionExists 方法会在创建表之前确保您的 PostgreSQL 数据库上启用了 pgvector 扩展。
在您的 Eloquent 模型上,将向量列强制转换为 array,以便 Laravel 自动处理 PHP 数组与数据库向量格式之间的转换。
1protected function casts(): array2{3 return [4 'embedding' => 'array',5 ];6}
有关向量列和索引的更多详细信息,请参阅 迁移文档。
相似度查询
存储内容嵌入后,您可以使用 whereVectorSimilarTo 方法搜索相似记录。此方法使用余弦相似度将给定的嵌入与存储的向量进行比较,过滤掉低于 minSimilarity 阈值的结果,并自动按相关性对结果进行排序——最相似的记录排在最前面。阈值应为 0.0 到 1.0 之间的值,其中 1.0 表示向量完全相同。
1$documents = Document::query()2 ->whereVectorSimilarTo('embedding', $queryEmbedding, minSimilarity: 0.4)3 ->limit(10)4 ->get();
为方便起见,当提供的是纯字符串而不是嵌入数组时,Laravel 将自动使用您配置的嵌入提供程序为您生成嵌入。这意味着您可以直接传递用户的搜索查询,而无需先手动将其转换为嵌入。
1$documents = Document::query()2 ->whereVectorSimilarTo('embedding', 'best wineries in Napa Valley')3 ->limit(10)4 ->get();
对于向量查询的底层控制,还可以使用 whereVectorDistanceLessThan、selectVectorDistance 和 orderByVectorDistance 方法。这些方法让您可以直接处理距离值而不是相似度得分,将计算出的距离作为结果中的一列进行选择,或者手动控制排序。有关完整详细信息,请参阅 查询构建器文档 和 AI SDK 文档。
重排序结果
重排序是一种 AI 模型根据每个结果与给定查询的语义相关性对结果集进行重新排序的技术。与需要预先计算和存储嵌入的向量搜索不同,重排序适用于任何文本集合——它将原始内容和查询作为输入,并返回按相关性排序的项目。
重排序作为快速初始检索步骤后的第二阶段尤为强大。例如,您可以使用全文搜索快速将数千条记录缩小到前 50 个候选者,然后使用重排序将最相关的结果放在顶部。这种“先检索后重排序”的模式为您提供了速度和语义准确性。
您可以使用 Reranking 类对字符串数组进行重排序。
1use Laravel\Ai\Reranking;2 3$response = Reranking::of([4 'Django is a Python web framework.',5 'Laravel is a PHP web application framework.',6 'React is a JavaScript library for building user interfaces.',7])->rerank('PHP frameworks');8 9$response->first()->document; // "Laravel is a PHP web application framework."
Laravel 集合还提供了一个 rerank 宏,它接受字段名称(或闭包)和查询,从而可以轻松对 Eloquent 结果进行重排序。
1$articles = Article::all()2 ->rerank('body', 'Laravel tutorials');
有关配置重排序提供程序和可用选项的完整详细信息,请参阅 AI SDK 文档。
Laravel Scout
上述搜索技术都是您在代码中直接调用的查询构建器方法。Laravel Scout 采取了不同的方法:它提供了一个 Searchable 特性,您可以将其添加到 Eloquent 模型中,Scout 会在记录创建、更新和删除时自动使您的搜索索引保持同步。当您希望模型始终可搜索而无需手动管理索引更新时,这特别方便。
数据库引擎
Scout 的内置数据库引擎会对现有数据库执行全文搜索和 LIKE 搜索——无需外部服务或额外的基础设施。只需将 Searchable 特性添加到您的模型中,并定义一个返回您希望可搜索列的 toSearchableArray 方法即可。
您可以使用 PHP 属性来控制每一列的搜索策略。SearchUsingFullText 将使用数据库的全文索引,SearchUsingPrefix 将仅匹配字符串的开头 (example%),而任何没有属性的列都将使用默认的 LIKE 策略(两边都有通配符:%example%)。
1<?php 2 3namespace App\Models; 4 5use Illuminate\Database\Eloquent\Model; 6use Laravel\Scout\Attributes\SearchUsingFullText; 7use Laravel\Scout\Attributes\SearchUsingPrefix; 8use Laravel\Scout\Searchable; 9 10class Article extends Model11{12 use Searchable;13 14 #[SearchUsingPrefix(['id'])]15 #[SearchUsingFullText(['title', 'body'])]16 public function toSearchableArray(): array17 {18 return [19 'id' => $this->id,20 'title' => $this->title,21 'body' => $this->body,22 ];23 }24}
在指定列应使用全文查询约束之前,请确保该列已分配了全文索引。
添加该特性后,您可以使用 Scout 的 search 方法搜索您的模型。即使在 PostgreSQL 上,Scout 的数据库引擎也会自动按相关性对结果进行排序。
1$articles = Article::search('Laravel')->get();
当您的搜索需求适中,且希望获得 Scout 自动索引同步的便利性而又不想部署外部服务时,数据库引擎是一个不错的选择。它很好地处理了最常见的搜索用例,包括过滤、分页和软删除记录处理。有关完整详细信息,请参阅 Scout 文档。
第三方引擎
Scout 还支持第三方搜索引擎,如 Algolia、Meilisearch 和 Typesense。这些专用搜索服务提供高级功能,如容错纠错、分面过滤、地理搜索和自定义排名规则——这些功能在超大规模或当您需要高度优化的“输入即搜索”体验时变得非常重要。
由于 Scout 在其所有驱动程序中提供统一的 API,因此以后从数据库引擎切换到第三方引擎所需的代码更改极少。您可以从数据库引擎开始,仅在应用程序的需求超过数据库所能提供的能力时才迁移到第三方服务。
有关配置第三方引擎的完整详细信息,请参阅 Scout 文档。
许多应用程序根本不需要外部搜索引擎。本页介绍的内置技术涵盖了绝大多数用例。
组合技术
本页介绍的搜索技术并非互斥——组合使用它们通常会产生最佳结果。以下是展示这些工具如何协同工作的两种常见模式。
全文检索 + 重排序
使用全文搜索快速将大型数据集缩小到候选集,然后应用重排序按语义相关性对这些候选者进行排序。这为您提供了数据库原生全文搜索的速度以及 AI 驱动的相关性评分的准确性。
1$articles = Article::query()2 ->whereFullText('body', $request->input('query'))3 ->limit(50)4 ->get()5 ->rerank('body', $request->input('query'), limit: 10);
向量搜索 + 传统过滤器
将向量相似度与标准的 where 子句相结合,将语义搜索范围限制在记录的子集中。当您需要基于含义的搜索,但需要按所有权、类别或任何其他属性限制结果时,这非常有用。
1$documents = Document::query()2 ->where('team_id', $user->team_id)3 ->whereVectorSimilarTo('embedding', $request->input('query'))4 ->limit(10)5 ->get();