You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Laravel多字段搜索优化:如何简化代码并提升查询效率?

Laravel模糊搜索:代码简化与提速方案

你的代码功能没问题,但重复的like逻辑确实冗余,而且%xxx%的模糊搜索在数据量大时速度堪忧,下面给你具体的优化方案:

一、简化重复的Like逻辑

1. 自定义模型查询范围(Scope)

把多字段和关联模型的搜索逻辑封装到Game模型的查询范围里,控制器代码会清爽很多:

// app/Models/Game.php
public function scopeSearch($query, $searchStr)
{
    if (empty($searchStr)) {
        return $query;
    }
    
    $pattern = "%{$searchStr}%";
    return $query->where('title', 'like', $pattern)
                 ->orWhere('description', 'like', $pattern)
                 ->orWhereHas('user', fn ($subQuery) => $subQuery->where('username', 'like', $pattern));
}

控制器里直接调用这个scope,配合when处理空搜索的情况:

public function index(Request $request)
{
    $queryStr = $request->query('search');

    $games = Game::withTrashed()
        ->with('user')
        ->when($queryStr, fn ($query) => $query->search($queryStr))
        ->get();

    return view('gameList', compact('games'));
}

2. 批量处理多字段(扩展友好)

如果以后要加更多搜索字段,把字段名放到数组里循环处理,不用逐行加orWhere:

// Game.php 中的scopeSearch优化
public function scopeSearch($query, $searchStr)
{
    if (empty($searchStr)) {
        return $query;
    }
    
    $pattern = "%{$searchStr}%";
    // 用where包裹当前模型的字段搜索,避免和关联模型的orWhere逻辑冲突
    $query->where(function ($q) use ($pattern) {
        foreach (['title', 'description'] as $field) {
            $q->orWhere($field, 'like', $pattern);
        }
    });
    
    return $query->orWhereHas('user', fn ($subQuery) => $subQuery->where('username', 'like', $pattern));
}

二、提升搜索速度的核心方案

当前的%xxx%前缀通配符查询无法利用数据库索引,数据量上去后会非常慢,推荐以下几种优化方式:

1. 数据库全文索引(低成本方案)

MySQL 全文索引

给需要搜索的字段创建全文索引,之后用全文搜索语法替代like:

  • 先通过迁移创建索引:
// 给games表的title、description加全文索引
Schema::table('games', function (Blueprint $table) {
    $table->fullText(['title', 'description']);
});

// 给users表的username加全文索引
Schema::table('users', function (Blueprint $table) {
    $table->fullText('username');
});
  • 修改模型的scopeSearch为全文搜索:
public function scopeSearch($query, $searchStr)
{
    if (empty($searchStr)) {
        return $query;
    }
    
    $query->whereRaw("MATCH(title, description) AGAINST(? IN BOOLEAN MODE)", [$searchStr]);
    
    return $query->orWhereHas('user', function ($subQuery) use ($searchStr) {
        $subQuery->whereRaw("MATCH(username) AGAINST(? IN BOOLEAN MODE)", [$searchStr]);
    });
}

注意:MySQL全文索引对中文支持有限,如果是中文搜索,可能需要额外配置或者换用PostgreSQL。

PostgreSQL 模糊搜索优化

PostgreSQL可以用pg_trgm扩展创建索引,完美支持%xxx%的模糊搜索且能用上索引:

  • 先在数据库中启用扩展:
CREATE EXTENSION IF NOT EXISTS pg_trgm;
  • 创建gin索引:
CREATE INDEX idx_games_title_description_trgm ON games USING gin (title gin_trgm_ops, description gin_trgm_ops);
CREATE INDEX idx_users_username_trgm ON users USING gin (username gin_trgm_ops);

之后原来的like查询会自动利用这个索引,速度提升明显。

2. Laravel Scout + 专业搜索引擎(大流量/复杂需求)

如果数据量很大或者需要分词、高亮、排序等高级功能,直接上Laravel Scout配合Meilisearch/Algolia/Elasticsearch:

  • 安装依赖:
composer require laravel/scout
# 以Meilisearch为例,安装驱动
composer require meilisearch/meilisearch-php http-interop/http-factory-guzzle
  • 在模型中添加Searchable trait,定义可搜索字段:
// Game.php
use Laravel\Scout\Searchable;

class Game extends Model
{
    use Searchable;
    
    public function toSearchableArray()
    {
        return [
            'id' => $this->id,
            'title' => $this->title,
            'description' => $this->description,
            'username' => $this->user->username,
        ];
    }
}
  • 控制器里的搜索代码简化到极致:
public function index(Request $request)
{
    $queryStr = $request->query('search');
    
    $games = empty($queryStr) 
        ? Game::withTrashed()->with('user')->get()
        : Game::search($queryStr)->withTrashed()->with('user')->get();
    
    return view('gameList', compact('games'));
}

Scout会自动同步数据到搜索引擎,搜索速度比数据库快几个量级,还支持各种高级搜索特性。

3. 限制通配符位置(业务允许的话)

如果业务能接受前缀匹配(比如搜索标题以关键词开头),把通配符放到后面{$searchStr}%,这样普通的B树索引就能生效,速度也会快很多,不过这种方式牺牲了搜索灵活性,需要看业务需求。

内容的提问来源于stack exchange,提问作者LaravelLover

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.17 18:57:54