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

Laravel搭配MySQL使用全文搜索时附加查询条件不生效问题

问题描述

在Laravel框架下基于MySQL实现全文搜索功能时,基础搜索运行正常,但添加非全文索引字段筛选时返回结果不符合预期:涉及的status、type字段均为enum存储类型,预期筛选type = image且status = 1的商品,实际返回了所有类型、任意状态下匹配全文搜索规则的商品。

原有业务查询代码

Product::accepted()
            ->select('products.id as id','products.type','products.display_image as display_path',
                'products.watermark',
                DB::raw("concat('[', group_concat(JSON_OBJECT('id', keywords.id , 'keyword',keywords.keyword)
                                    order by keywords.id separator ','), ']') as keywords"))
            ->join('products_keywords','products_keywords.product_id','products.id')
            ->join('keywords','keywords.id','products_keywords.keyword_id')
            ->whereRaw(
                "products.type = ?
                 AND MATCH(keywords.keyword) AGAINST(? IN BOOLEAN MODE)
                  OR MATCH(products.description) AGAINST(? IN BOOLEAN MODE)
                   Or MATCH(products.description_en) AGAINST(? IN BOOLEAN MODE)",
                ['image',$searchTerms,$searchTerms,$searchTerms])
            ->groupBy('products.id')->groupBy('products.display_image')
            ->groupBy('products.type')->groupBy('products.watermark')
            ->paginate(50);

scope说明

accepted是Product模型中定义的本地查询作用域,用于实现status = 1的筛选,实现代码如下:

public function scopeAccepted($query)
{
    return $this->where('products.status','1');
}
问题根因

SQL逻辑运算符存在优先级差异:AND的运算优先级高于OR,当前whereRaw内的条件没有做逻辑分组,实际执行逻辑为:

  • 满足products.type = 'image'且keywords.keyword匹配搜索词,或者
  • products.description匹配搜索词,或者
  • products.description_en匹配搜索词

只要满足后两个条件任意一条,无论type、status字段值是什么,都会被纳入结果集,该问题和字段的enum存储类型无关。

修复方案

核心是给三个全文匹配的OR条件整体添加括号,明确逻辑边界:要求必须同时满足type = image、status = 1,且三个全文匹配条件至少命中一个。

方案1:修正Raw SQL的括号

直接修改whereRaw内的SQL语句,给或条件加分组:

->whereRaw(
    "products.type = ?
     AND (
        MATCH(keywords.keyword) AGAINST(? IN BOOLEAN MODE)
        OR MATCH(products.description) AGAINST(? IN BOOLEAN MODE)
        OR MATCH(products.description_en) AGAINST(? IN BOOLEAN MODE)
    )",
    ['image',$searchTerms,$searchTerms,$searchTerms])

方案2:使用Laravel条件分组(推荐)

通过闭包包裹全文搜索的或条件,Laravel会自动生成带正确括号的SQL,避免手写Raw SQL的优先级错误,参数绑定也更清晰:

Product::accepted()
    ->select(
        'products.id as id',
        'products.type',
        'products.display_image as display_path',
        'products.watermark',
        DB::raw("concat('[', group_concat(JSON_OBJECT('id', keywords.id , 'keyword',keywords.keyword)
                            order by keywords.id separator ','), ']') as keywords")
    )
    ->join('products_keywords','products_keywords.product_id','products.id')
    ->join('keywords','keywords.id','products_keywords.keyword_id')
    ->where('products.type', 'image')
    ->where(function ($query) use ($searchTerms) {
        $query->whereRaw('MATCH(keywords.keyword) AGAINST(? IN BOOLEAN MODE)', [$searchTerms])
            ->orWhereRaw('MATCH(products.description) AGAINST(? IN BOOLEAN MODE)', [$searchTerms])
            ->orWhereRaw('MATCH(products.description_en) AGAINST(? IN BOOLEAN MODE)', [$searchTerms]);
    })
    ->groupBy('products.id', 'products.display_image', 'products.type', 'products.watermark')
    ->paginate(50);

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 22:15:46