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
- 在模型中添加
Searchabletrait,定义可搜索字段:
// 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
相关产品推荐
相关产品推荐

