Laravel Scout(Algolia)搜索结果自定义排序实现方案咨询
问题描述
我正在使用Laravel Algolia包实现搜索功能,希望在将结果返回视图前调整排序顺序:先搜索所有产品,将用户收藏的产品前置,最后进行分页。我尝试了以下代码:
$results = Product::search($search); //Then I would like to do this: $results = $results->sort(function($a, $b) { return $user->hasFavourite($a) < $user->hasFavourite($b) ? -1 : 1 }); return $results->paginate(48);
但我知道Laravel\Scout\Builder对象不支持该sort操作,想了解有没有解决办法?
解决办法
方法一:获取结果后再手动排序分页
先把搜索结果转为集合,完成排序后再手动实现分页逻辑:
// 获取所有搜索结果集合 $products = Product::search($search)->get(); // 排序:收藏产品前置 $sortedProducts = $products->sort(function ($a, $b) use ($user) { // 用太空船运算符简化排序逻辑,收藏的返回0,未收藏返回1,实现前置 return $user->hasFavourite($b) <=> $user->hasFavourite($a); }); // 手动构建分页实例 $perPage = 48; $currentPage = request()->get('page', 1); $paginatedProducts = new \Illuminate\Pagination\LengthAwarePaginator( $sortedProducts->slice(($currentPage - 1) * $perPage, $perPage)->values(), $sortedProducts->count(), $perPage, $currentPage, ['path' => request()->url(), 'query' => request()->query()] ); return $paginatedProducts;
注意:这种方式需要一次性拉取所有搜索结果,数据量大时会影响性能,适合小数据集场景。
方法二:利用Algolia服务端排序(推荐)
借助Algolia的自定义排序能力,直接在搜索请求中注入排序规则,性能更优:
// 获取用户收藏的产品ID列表 $favouriteIds = $user->favourites()->pluck('product_id')->toArray(); // 通过闭包自定义Algolia搜索参数,优先排序收藏产品 $results = Product::search($search, function ($algolia, $query, $options) use ($favouriteIds) { $options['sort'] = [ // 优先判断是否在收藏列表,收藏的排前面 function ($a, $b) use ($favouriteIds) { $aIsFav = in_array($a['objectID'], $favouriteIds); $bIsFav = in_array($b['objectID'], $favouriteIds); return $bIsFav <=> $aIsFav; }, // 其次按Algolia默认的搜索评分排序 '_score:desc' ]; return $algolia->search($query, $options); })->paginate(48); return $results;
这种方式把排序逻辑交给Algolia处理,无需拉取全量数据,适合大数据集场景。
方法三:分两次查询合并结果
分别查询收藏/非收藏的匹配产品,合并后再分页:
$favProductIds = $user->favourites()->pluck('product_id'); // 查询收藏且匹配搜索的产品 $favouriteMatches = Product::search($search) ->whereIn('id', $favProductIds) ->get(); // 查询非收藏且匹配搜索的产品 $nonFavouriteMatches = Product::search($search) ->whereNotIn('id', $favProductIds) ->get(); // 合并结果:收藏产品在前 $mergedProducts = $favouriteMatches->merge($nonFavouriteMatches); // 手动分页(同方法一的分页代码) $perPage = 48; $currentPage = request()->get('page', 1); $paginatedProducts = new \Illuminate\Pagination\LengthAwarePaginator( $mergedProducts->slice(($currentPage - 1) * $perPage, $perPage)->values(), $mergedProducts->count(), $perPage, $currentPage, ['path' => request()->url(), 'query' => request()->query()] ); return $paginatedProducts;
这种方式避免了全量结果排序,但需要发起两次搜索请求,适合中等数据集场景。
内容的提问来源于stack exchange,提问作者peirix
相关产品推荐
相关产品推荐

