Laravel多关键词搜索如何提升结果相关性 按匹配词数排序
实现方案
你当前的查询仅通过orWhere实现了OR逻辑匹配,没有对匹配度做量化计分,要实现「命中关键词越多的记录排序越靠前」的效果,核心是给每条符合筛选条件的商品计算匹配得分,再按得分倒序排列即可。
核心实现逻辑:
- 拆分搜索词时先过滤空值,避免用户输入多空格、首尾空格时拆出无效空字符串干扰匹配结果
- 保留原有OR匹配的筛选规则,保证命中任意一个关键词的商品都能被返回
- 逐词逐字段判断是否命中,每命中一个字段就给对应记录累加对应分值
- 最终按总匹配得分倒序排序,得分相同的场景可补充主键倒序规则,保证分页结果顺序稳定
调整后的完整代码如下:
public function search() { $data = [ 'title'=> $description = 'Recherche sur '.config('app.name'), 'description'=> $description, 'heading'=> config('app.name'), ]; $q = trim(request()->input('q', '')); // 拆分关键词并过滤空值 $words = array_filter(explode(' ', $q), fn($word) => trim($word) !== ''); $query = Product::query(); // 封装OR筛选条件,避免和后续全局查询条件产生逻辑冲突 $query->where(function ($subQuery) use ($words) { foreach ($words as $word) { $subQuery->orWhere('title','like',"%$word%") ->orWhere('subtitle','like',"%$word%") ->orWhere('description','like',"%$word%"); } }); // 拼接匹配度计分规则 $scoreSegments = []; $bindings = []; foreach ($words as $word) { // 三个字段分别判断命中,每命中1个字段加1分,可根据需求调整不同字段权重 $scoreSegments[] = "CASE WHEN title LIKE ? THEN 1 ELSE 0 END"; $scoreSegments[] = "CASE WHEN subtitle LIKE ? THEN 1 ELSE 0 END"; $scoreSegments[] = "CASE WHEN description LIKE ? THEN 1 ELSE 0 END"; // 填充参数绑定值,避免SQL注入风险 $bindings[] = "%$word%"; $bindings[] = "%$word%"; $bindings[] = "%$word%"; } $scoreExpression = implode(' + ', $scoreSegments); // 按匹配得分倒序,同得分下按商品ID倒序保证分页稳定 $query->orderByRaw("$scoreExpression DESC", $bindings) ->orderBy('id', 'DESC'); $products = $query->paginate(); return view('products.search', $data)->with('products', $products); }
上述计分规则下,搜索
blue red时,同时在任意字段命中两个词的商品得分至少为2,仅命中其中一个词的商品得分最高为1,自然会排在更靠前的位置。如果需要调整字段权重,比如希望title命中的优先级更高,只需要把title对应CASE语句里的加分值从1改成更高的数字(比如3)即可。
内容的提问来源于stack exchange,提问作者Chocogab
相关产品推荐
相关产品推荐

