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

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.27 22:05:13