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

如何让Laravel Scout数据库驱动为FULLTEXT索引搜索添加后缀通配符?

问题描述

不想依赖Elasticsearch、Algolia这类专用搜索API,打算通过MySQL的FULLTEXT索引提升搜索性能,但遇到以下问题:

  • 搜索单词的部分内容(例如输入8字符单词中的7个)时,始终返回0条结果,仅能匹配完整单词
  • 对比手动执行的查询发现,Laravel Scout生成的SQL未给关键词添加末尾通配符*,手动添加后可得到预期结果,且性能优于WHERE LIKE "%...%"

需求:让Laravel Scout自动为搜索关键词添加末尾通配符,适配MySQL FULLTEXT的布尔模式搜索。

已做配置:

  1. 为Products表的page_title字段添加FULLTEXT索引
  2. Product模型配置如下:
/**
 * Get the indexable data array for the model.
 *
 * @return array<string, mixed>
 */
#[SearchUsingPrefix(['code'])]
#[SearchUsingFullText(['page_title'])]
public function toSearchableArray()
{
    return [
        'page_title'    => $this->page_title,
        'code'          => $this->code,
    ];
}
  1. 尝试添加#[SearchUsingPrefix(['page_title'])],无效果

测试场景:

  • 使用Scout搜索:输入"fitte"(目标匹配"fitted")返回0条结果
    Product::search($q)->query(function($query){
        $query->select(['id', 'page_title as text', 'code as subtext'])->limit(5);
    })->get()
    
  • 手动执行FULLTEXT布尔模式查询:相同关键词可得到预期结果
    Product::whereRaw("MATCH(page_title) AGAINST('$q*' IN BOOLEAN MODE)")
        ->select(['id', 'page_title as text', 'code as subtext'])->limit(5)
        ->get()
    

环境:XAMPP运行MySQL

解决方法

方法1:手动处理搜索关键词

在调用Scout的search方法前,直接给关键词添加末尾通配符。如果是多关键词,需为每个词单独加*:

// 单关键词
$q = $request->input('q') . '*';

// 多关键词(拆分后逐个加*)
$q = collect(explode(' ', $request->input('q')))
    ->map(fn($word) => $word . '*')
    ->implode(' ');

// 执行搜索
Product::search($q)->query(function($query){
    $query->select(['id', 'page_title as text', 'code as subtext'])->limit(5);
})->get();

方法2:自定义Scout搜索Builder

通过继承默认Builder类,全局修改搜索关键词处理逻辑:

  1. 创建自定义Builder类
namespace App\Scout;

use Laravel\Scout\Builder;

class MySqlFulltextBuilder extends Builder
{
    public function buildQuery()
    {
        // 仅针对FULLTEXT字段的搜索添加通配符
        if ($this->query && property_exists($this->model, 'searchableFullTextColumns')) {
            // 为每个关键词添加末尾*
            $this->query = preg_replace('/(\S+)/', '$1*', $this->query);
        }
        
        return parent::buildQuery();
    }
}
  1. 在Product模型中重写search方法,使用自定义Builder
use App\Scout\MySqlFulltextBuilder;

public static function search($query = '', $callback = null)
{
    return new MySqlFulltextBuilder(new static, $query, $callback);
}

方法3:自定义MySQL Scout驱动

如果需要全局适配所有模型的FULLTEXT搜索,可自定义驱动:

  1. 创建自定义引擎类
namespace App\Scout;

use Laravel\Scout\Engines\MySqlEngine;
use Laravel\Scout\Builder;

class CustomMySqlEngine extends MySqlEngine
{
    public function search(Builder $builder)
    {
        // 处理关键词,为每个词添加末尾*
        if ($builder->query) {
            $builder->query = preg_replace('/(\w+)/', '$1*', $builder->query);
        }
        
        return parent::search($builder);
    }
}
  1. 在config/scout.php中替换默认MySQL引擎
'engines' => [
    // 其他引擎...
    'mysql' => App\Scout\CustomMySqlEngine::class,
],

关于#[SearchUsingPrefix]无效的原因

#[SearchUsingPrefix]注解是Scout用来生成LIKE 'xxx%'前缀匹配查询的,仅适用于普通字段的前缀搜索,并不适配MySQL FULLTEXT索引的布尔模式通配符逻辑,因此对page_title这类FULLTEXT字段无效。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 07:55:09