Laravel 5.8+scout7.0 使用 orderBy 排序失效解決方案

authcontroller發表於2019-09-06

最近在使用elasticSearch6.2.4做搜尋時發現,排序欄位失效,所以在這記錄下

先看下解決方案,話不多說,直接上程式碼

$list = Article::search($words)->orderBy('created_at','desc')->paginateRaw(10)->toArray();
$results = $list['data'];
if ($results['hits']['total'] === 0) {
    return $this->model->newCollection();
}
$builder =new Builder(new static(),$this->model->newModelQuery());
$keys = collect($results['hits']['hits'])->pluck('_id')->values()->all();
$query = $this->newQuery();
if ($builder->queryCallback) {
    call_user_func($builder->queryCallback, $query);
}
//查詢資料
$scoutModelsLists = $query->whereIn(
    $this->model->qualifyColumn($this->model->getKeyName()), $keys
)->orderBy('created_at','desc')->get();
//過濾資料
$scoutModelsLists->filter(function () use ($keys) {
    return in_array($this->model->getKey(), $keys);
});
//這裡為最終排序好的資料
$data = $scoutModelsLists;

問題分析

  1. 原來使用的查詢語句為

    $list = Article::search($words)->orderBy('created_at','desc')->paginate(10)->toArray();

    上面查詢語句雖然設定了排序欄位,但是最終輸出的時候卻沒有排序,經分析,在ES搜尋結果裡面的確是排了序,但最終輸出時,ES資料結構轉化為集合時,並未加上排序欄位

    程式碼分析

    檔案1: /vendor/laravel/scout/src/builder.php 約261行-305行
    仔細觀察這個檔案會有兩個方法 paginate、paginateRaw ,前一個返回laravel 集合,後一個返回es的原生查詢結構,
    這兩者程式碼的不同點在於這塊

    $results = $this->model->newCollection($engine->map(
            $this, $rawResults = $engine->paginate($this, $perPage, $page), $this->model
        )->all());

    檔案2:vendor/tamayo/laravel-scout-elastic/src/ElasticsearchEngine.php 211行,map方法,因為這裡我們使用的是ES引擎,如果使用別的,可能有所不同,程式碼:

    public function map(Builder $builder, $results, $model)
    {
    //無資料返回空集合
    if ($results['hits']['total'] === 0) {
        return $model->newCollection();
    }
    //獲取所有鍵為_id的ES資料
    //$keys = collect($results['hits']['hits'])->pluck('_id')->values()->all();
    //轉化ES資料並過濾
    return $model->getScoutModelsByIds(
            $builder, $keys
        )->filter(function ($model) use ($keys) {
            return in_array($model->getScoutKey(), $keys);
        });
    }

    從程式碼看來,es搜尋出來有資料,則轉化並過濾一下返回符合條件的集合,不滿足直接返回空
    檔案3: /vendor/laravel/scout/src/Searchable.php 約171行getScoutModelsByIds方法,程式碼

    public function getScoutModelsByIds(Builder $builder, array $ids)
    {
    //加入軟刪除
    $query = static::usesSoftDelete()
        ? $this->withTrashed() : $this->newQuery();
    
    if ($builder->queryCallback) {
        call_user_func($builder->queryCallback, $query);
    }
    // 重點這裡,自改程式碼
    // return $query->whereIn(
      //   $this->getScoutKeyName(), $ids
    // )->orderBy('orderBy','desc')->get();
    //官方程式碼
    return $query->whereIn(
        $this->getScoutKeyName(), $ids
    )->get();
    }

    這個檔案是重點,主要是這裡最後返回的時候並沒有加上orderBy排序欄位,所以最後輸出時雖然es排序了,這裡又重置了,為防止改元件後其他地方無法更新,所以在最後返回資料時加了排序處理,方案參考文章開頭。
    暫時告一段落,空了再完善。

相關文章