Laravel多关键词多列按规则排序搜索及滚动加载实现求助
Laravel 多关键词搜索排序+滚动加载解决方案
一、查询排序核心实现
针对你要的匹配优先级(全3个关键词>前2个>仅第1个),可以通过权重计算+排序来实现,同时保证结果不重复:
1. 控制器查询代码
public function search() { $word1 = request()->input('word1'); $word2 = request()->input('word2'); $word3 = request()->input('word3'); // 构造带权重计算的查询 $query = DB::table('your_table') // 替换成你的表名 ->select('*', DB::raw(" CASE // 匹配全部3个关键词,权重3 WHEN (meta_name LIKE ? OR meta_description LIKE ?) AND (meta_name LIKE ? OR meta_description LIKE ?) AND (meta_name LIKE ? OR meta_description LIKE ?) THEN 3 // 匹配前2个关键词,权重2 WHEN (meta_name LIKE ? OR meta_description LIKE ?) AND (meta_name LIKE ? OR meta_description LIKE ?) THEN 2 // 仅匹配第1个关键词,权重1 WHEN (meta_name LIKE ? OR meta_description LIKE ?) THEN 1 ELSE 0 END AS match_weight ", [ "%{$word1}%", "%{$word1}%", "%{$word2}%", "%{$word2}%", "%{$word3}%", "%{$word3}%", "%{$word1}%", "%{$word1}%", "%{$word2}%", "%{$word2}%", "%{$word1}%", "%{$word1}%" ])) // 过滤至少匹配第1个关键词的记录 ->where(function ($q) use ($word1) { $q->where('meta_name', 'like', "%{$word1}%") ->orWhere('meta_description', 'like', "%{$word1}%"); }) // 只保留有权重的记录(排除完全不匹配的) ->having('match_weight', '>', 0) // 按权重降序排序,权重相同的可以再加主键排序保证稳定 ->orderBy('match_weight', 'desc') ->orderBy('id', 'asc') // 去重(用主键groupBy比distinct更稳妥,避免同一条记录因多列匹配重复出现) ->groupBy('id'); // 分页处理(用于滚动加载) $results = $query->paginate(10); // 区分AJAX请求和初始页面请求 if (request()->wantsJson()) { return response()->json($results->items()); } return view('search', compact('results')); }
关键说明
- 用
CASE语句给不同匹配程度的记录分配权重,直接在数据库层面完成排序逻辑,效率更高 groupBy('id')确保同一条记录不会因为同时在meta_name和meta_description匹配关键词而重复出现- 外层
where保证只返回至少包含第一个关键词的结果,避免无效数据
二、滚动加载实现
1. 前端视图(Blade)
<div id="search-results"> @foreach($results as $item) <div class="result-card"> <h3>{{ $item->meta_name }}</h3> <p>{{ $item->meta_description }}</p> </div> @endforeach </div> <div id="loading" style="display: none; text-align: center; padding: 1rem;">加载中...</div> <div id="no-more" style="display: none; text-align: center; padding: 1rem; color: #999;">没有更多内容了</div>
2. 滚动监听JS代码
let currentPage = 2; let isLoading = false; let hasMore = true; // 监听滚动事件 window.addEventListener('scroll', () => { if (isLoading || !hasMore) return; // 判断是否滚动到页面底部(留100px提前触发加载) if (window.innerHeight + window.scrollY >= document.body.offsetHeight - 100) { loadMoreResults(); } }); function loadMoreResults() { isLoading = true; document.getElementById('loading').style.display = 'block'; // 携带搜索关键词和当前页码请求数据 fetch(`/search?word1={{ request()->word1 }}&word2={{ request()->word2 }}&word3={{ request()->word3 }}&page=${currentPage}`) .then(res => res.json()) .then(data => { isLoading = false; document.getElementById('loading').style.display = 'none'; if (data.length === 0) { hasMore = false; document.getElementById('no-more').style.display = 'block'; return; } // 追加新数据到结果容器 const container = document.getElementById('search-results'); data.forEach(item => { const card = document.createElement('div'); card.className = 'result-card'; card.innerHTML = ` <h3>${item.meta_name}</h3> <p>${item.meta_description}</p> `; container.appendChild(card); }); currentPage++; }) .catch(err => { isLoading = false; document.getElementById('loading').style.display = 'none'; console.error('加载失败:', err); }); }
3. 路由配置
Route::get('/search', [SearchController::class, 'search']);
内容的提问来源于stack exchange,提问作者Istiaque Ahmed
相关产品推荐
相关产品推荐

