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
相关产品推荐
相关产品推荐

