跳转到主要内容

简介

Laravel Scout 是一个基于驱动的简洁方案,用于给 Eloquent 模型添加全文检索能力。它通过模型观察者,自动同步 Eloquent 记录与搜索索引。 Scout 内置了 database 引擎,使用 MySQL / PostgreSQL 的全文索引和 LIKE 子句直接搜索数据库,无需外部服务。大规模生产环境中若需要拼写容错、分面搜索、地理搜索,则可以选择外部引擎。

支持的引擎一览

引擎特点适用场景
database使用 MySQL / PostgreSQL 全文索引大多数应用
collection在 PHP 内过滤(含 SQLite 也可)本地开发、测试
Meilisearch开源,支持拼写容错,速度快生产环境
Algolia云端 SaaS,功能丰富生产环境
Typesense开源,支持向量检索生产环境

安装

通过 Composer 安装。
composer require laravel/scout
安装后,使用 vendor:publish 命令发布配置文件,会生成 config/scout.php
php artisan vendor:publish --provider="Laravel\Scout\ScoutServiceProvider"
最后在需要可搜索的模型上添加 Laravel\Scout\Searchable trait。该 trait 会注册模型观察者,实现与搜索驱动的自动同步。
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Laravel\Scout\Searchable;

class Post extends Model
{
    use Searchable;
}

配置队列

使用 databasecollection 之外的引擎时,强烈建议先配置好队列驱动。启动队列 worker 后,索引同步会在后台执行,Web 界面的响应速度将大幅提升。 config/scout.php 中的 queue 设为 true
'queue' => true,
也可以指定连接和队列名。
'queue' => [
    'connection' => 'redis',
    'queue' => 'scout'
],
配置好后启动专用的队列 worker。
php artisan queue:work redis --queue=scout

使用唯一任务

在写入密集的应用中,你可能希望避免同一模型记录的重复索引任务进入队列。在 config/scout.php 中注册 MakeSearchableUniquelyRemoveFromSearchUniquely 任务类,就能使用唯一化的索引任务。通常在服务提供者的 boot 方法中设置。
use Laravel\Scout\Jobs\MakeSearchableUniquely;
use Laravel\Scout\Jobs\RemoveFromSearchUniquely;
use Laravel\Scout\Scout;

Scout::makeSearchableUsing(MakeSearchableUniquely::class);
Scout::removeFromSearchUsing(RemoveFromSearchUniquely::class);
这些任务使用 Laravel 的唯一任务锁,防止已在队列中的同一模型记录被重复派发。

各驱动的准备工作

Algolia

使用 Algolia 驱动时,请在 config/scout.php 中配置 idsecret,并安装 Algolia PHP SDK。
composer require algolia/algoliasearch-client-php
.env 中配置凭证。
ALGOLIA_APP_ID=your-app-id
ALGOLIA_SECRET=your-secret-key

索引配置

Algolia 可以通过 config/scout.php 管理索引设置。
use App\Models\User;

'algolia' => [
    'id' => env('ALGOLIA_APP_ID', ''),
    'secret' => env('ALGOLIA_SECRET', ''),
    'index-settings' => [
        User::class => [
            'searchableAttributes' => ['id', 'name', 'email'],
            'attributesForFaceting' => ['filterOnly(email)'],
        ],
    ],
],
配置完毕后执行 scout:sync-index-settings 命令,让 Algolia 生效。
php artisan scout:sync-index-settings

Meilisearch

Meilisearch 是一款高性能的开源搜索引擎。本地开发中最简单的方法是使用 Laravel Sail 中的 Docker 服务。
# 使用 Laravel Sail
./vendor/bin/sail up -d meilisearch
不使用 Sail 时,可以直接用 Docker 启动。
docker run -it --rm \
    -p 7700:7700 \
    getmeili/meilisearch:latest \
    meilisearch --master-key="masterKey"
安装 Meilisearch PHP SDK。
composer require meilisearch/meilisearch-php http-interop/http-factory-guzzle
.env 中配置驱动与主机。
SCOUT_DRIVER=meilisearch
MEILISEARCH_HOST=http://127.0.0.1:7700
MEILISEARCH_KEY=masterKey
升级 Scout 时请务必查阅 Meilisearch 本身的破坏性变更

索引配置(Meilisearch)

在 Meilisearch 中,用于 where() 过滤的字段必须提前注册到 filterableAttributes,用于 orderBy() 排序的字段则注册到 sortableAttributes
use App\Models\User;

'meilisearch' => [
    'host' => env('MEILISEARCH_HOST', 'http://localhost:7700'),
    'key' => env('MEILISEARCH_KEY', null),
    'index-settings' => [
        User::class => [
            'filterableAttributes' => ['id', 'name', 'email'],
            'sortableAttributes' => ['created_at'],
        ],
    ],
],
注意数值列的数据类型。Meilisearch 只会对类型正确的字段执行 >< 之类的过滤操作。
public function toSearchableArray(): array
{
    return [
        'id' => (int) $this->id,
        'name' => $this->name,
        'price' => (float) $this->price,
    ];
}
设置完毕后执行 scout:sync-index-settings
php artisan scout:sync-index-settings

Typesense

Typesense 是一款高性能的开源搜索引擎,支持关键字、语义、地理及向量检索。
composer require typesense/typesense-php
.env 中配置连接信息。
SCOUT_DRIVER=typesense
TYPESENSE_API_KEY=masterKey
TYPESENSE_HOST=localhost
TYPESENSE_PORT=8108
TYPESENSE_PATH=
TYPESENSE_PROTOCOL=http
使用 Typesense 时,toSearchableArray 需要把模型主键 cast 为字符串,把创建时间 cast 为 UNIX 时间戳。
public function toSearchableArray(): array
{
    return array_merge($this->toArray(), [
        'id' => (string) $this->id,
        'created_at' => $this->created_at->timestamp,
    ]);
}

database / collection 引擎

如果希望在不引入外部服务的情况下添加搜索功能,这两种就是最合适的选项。 database 引擎 使用 MySQL / PostgreSQL 的全文索引和 LIKE 子句。多数应用足够使用。
SCOUT_DRIVER=database
collection 引擎 在 PHP 中过滤,因此可在 Laravel 支持的所有数据库(包括 SQLite)上运行。适合本地开发、测试或小数据集。
SCOUT_DRIVER=collection
数据库引擎不像外部引擎那样需要手动管理索引,它直接搜索数据库表。

Searchable trait

自定义 toSearchableArray()

默认情况下,模型的 toArray() 全部数据都会写入搜索索引。要自定义同步到索引的数据,可以重写 toSearchableArray 方法。
<?php

namespace App\Models;

use Illuminate\Database\Eloquent\Model;
use Laravel\Scout\Searchable;

class Post extends Model
{
    use Searchable;

    /**
     * 获取要索引的数据数组。
     *
     * @return array<string, mixed>
     */
    public function toSearchableArray(): array
    {
        $array = $this->toArray();

        // 自定义数据...

        return $array;
    }
}

自定义索引名

默认情况下模型的表名(复数)会被用作索引名。可以通过重写 searchableAs 来自定义。
public function searchableAs(): string
{
    return 'posts_index';
}

database 引擎的搜索策略

在 database 引擎中,可以通过 PHP Attribute 为每列指定高效的搜索策略。
use Laravel\Scout\Attributes\SearchUsingFullText;
use Laravel\Scout\Attributes\SearchUsingPrefix;

#[SearchUsingPrefix(['id', 'email'])]
#[SearchUsingFullText(['bio'])]
public function toSearchableArray(): array
{
    return [
        'id' => $this->id,
        'name' => $this->name,
        'email' => $this->email,
        'bio' => $this->bio,
    ];
}
使用 SearchUsingFullText 之前,请确认目标字段已建立全文索引

有条件地允许搜索

若只想在特定条件下让模型可被搜索,可以定义 shouldBeSearchable 方法。
/**
 * 判断该模型是否应可被搜索。
 */
public function shouldBeSearchable(): bool
{
    return $this->isPublished();
}
shouldBeSearchable 对 database 引擎无效。要在 database 引擎上实现类似效果,请使用where 子句

索引管理

本节命令主要针对 Algolia、Meilisearch、Typesense 等外部引擎。database 引擎无需管理索引。

导入已有记录

在已有项目中引入 Scout 时,可以使用 scout:import 命令把已有记录导入索引。
php artisan scout:import "App\Models\Post"
也可以通过队列在后台导入。
php artisan scout:queue-import "App\Models\Post" --chunk=500

清空索引

要从索引中删除某模型的全部记录,可以使用 scout:flush
php artisan scout:flush "App\Models\Post"

暂停索引同步

想在一段 Eloquent 操作期间临时停止与索引的同步,使用 withoutSyncingToSearch
use App\Models\Order;

Order::withoutSyncingToSearch(function () {
    // 内部的模型操作都不会同步到搜索索引
});

手动添加 / 删除记录

可以基于查询把一批模型加入索引。
// 加入查询结果
Order::where('price', '>', 100)->searchable();

// 通过关联加入
$user->orders()->searchable();
从索引中移除记录使用 unsearchable
Order::where('price', '>', 100)->unsearchable();
模型 delete 时会自动从索引中删除。

搜索

search 方法用来搜索模型。再链上 get 就可以得到 Eloquent 模型集合。
use App\Models\Order;

$orders = Order::search('Star Trek')->get();
在控制器或路由中直接返回时会自动转 JSON。
use Illuminate\Http\Request;

Route::get('/search', function (Request $request) {
    return Order::search($request->search)->get();
});
如需拿到原始搜索结果,使用 raw 方法。
$orders = Order::search('Star Trek')->raw();

分页

paginate 方法用来对搜索结果做分页,用法与普通 Eloquent 查询一致。
$orders = Order::search('Star Trek')->paginate();

// 指定每页条数
$orders = Order::search('Star Trek')->paginate(15);
在 database 引擎下也可以使用 simplePaginate,因为它不计算总数,对大数据集更高效。
$orders = Order::search('Star Trek')->simplePaginate(15);
Blade 模板中的展示示例:
<div class="container">
    @foreach ($orders as $order)
        {{ $order->price }}
    @endforeach
</div>

{{ $orders->links() }}

过滤与排序

where 方法可以给搜索查询添加过滤条件。
use App\Models\Order;

// 相等过滤
$orders = Order::search('Star Trek')->where('user_id', 1)->get();

// 在数组内任一匹配
$orders = Order::search('Star Trek')->whereIn('status', ['open', 'paid'])->get();

// 不在数组内
$orders = Order::search('Star Trek')->whereNotIn('status', ['closed'])->get();
使用 Meilisearch 时,在使用 where 前必须配置可过滤字段
query 方法用来自定义 Eloquent 查询。
use Illuminate\Database\Eloquent\Builder;

$orders = Order::search('Star Trek')
    ->query(fn (Builder $query) => $query->with('invoices'))
    ->get();

Eager Loading

Scout 会先从搜索引擎获得 ID 列表,再用 Eloquent 取回模型。要避免 N+1 问题,可以在 query 方法内使用 with() 提前加载。
use App\Models\Order;
use Illuminate\Database\Eloquent\Builder;

$orders = Order::search('Star Trek')
    ->query(fn (Builder $query) => $query->with(['invoices', 'user']))
    ->get();
批量导入时若也需要 Eager Load 关联,可以定义 makeAllSearchableUsing
use Illuminate\Database\Eloquent\Builder;

protected function makeAllSearchableUsing(Builder $query): Builder
{
    return $query->with('author');
}
makeAllSearchableUsing 在通过队列进行批量导入时可能不生效。队列任务处理模型集合时不会恢复关联。

软删除

如果被索引的模型使用了软删除,并希望在搜索时也能搜到已删除的模型,请把 config/scout.phpsoft_delete 设为 true
'soft_delete' => true,
启用后可以通过 withTrashedonlyTrashed 搜索已删除记录。
// 包含已删除
$orders = Order::search('Star Trek')->withTrashed()->get();

// 仅搜索已删除
$orders = Order::search('Star Trek')->onlyTrashed()->get();

自定义引擎

若内置引擎不能满足需求,可以自行实现自定义引擎。自定义引擎需继承 Laravel\Scout\Engines\Engine 抽象类,并实现下面 8 个方法。
use Laravel\Scout\Builder;

abstract public function update($models);
abstract public function delete($models);
abstract public function search(Builder $builder);
abstract public function paginate(Builder $builder, $perPage, $page);
abstract public function mapIds($results);
abstract public function map(Builder $builder, $results, $model);
abstract public function getTotalCount($results);
abstract public function flush($model);
实现时可参考 Laravel\Scout\Engines\AlgoliaEngine 类。 创建的自定义引擎需要在 App\Providers\AppServiceProviderboot 方法中注册。
use App\ScoutExtensions\MySqlSearchEngine;
use Laravel\Scout\EngineManager;

public function boot(): void
{
    resolve(EngineManager::class)->extend('mysql', function () {
        return new MySqlSearchEngine;
    });
}
注册后在 config/scout.php 中作为驱动使用。
'driver' => 'mysql',

相关页面

Eloquent ORM

了解 Eloquent 模型的基础用法。

Eloquent 关联

了解关联定义与 Eager Loading。

队列

Scout 可以配合队列在后台异步同步索引。
最后修改于 2026年7月13日