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