程式碼:https://github.com/lijinma/laravel-scout-e... ,歡迎 Star
接著第一篇:部落格:[教程一] 寫一個搜尋:使用 Laravel Scout,Elasticsearch,ik 分詞 ,在這篇文章中,我要實現“搜尋結果高亮“。
你先看看搜尋結果高亮的效果:
http://scout.lijinma.com/search?query=%E8%...
我們知道 Laravel Scout 的 search 結果直接是一個一個物件,並沒有提供搜尋結果高亮功能,這個時候我們有兩條路可以解決我們的問題:
- 搜尋的時候不使用 Scout 提供的 search 方法,直接呼叫原生的 ElasticSearch 介面來做,搜尋後自己組裝需要的屬性,這肯定是一條路,在我們沒有 Laravel Scout 的時候確實也是這麼做的,但這樣寫雖然帶來了靈活性,但是我們要自己寫的程式碼還是有點多。
- 第二條路,就是我們想辦法修改 Scout ElasticSearch Engine,來滿足我們的高亮需求。
我選擇了第二條路:
1. 自定義 ElasticSearch Engine
首先,建立檔案:app/Libraries/EsEngine.php
繼承 ElasticsearchEngine,按照我們的需求新增或修改程式碼:
<?php namespace App\Libraries;
use Laravel\Scout\Builder;
use ScoutEngines\Elasticsearch\ElasticsearchEngine;
class EsEngine extends ElasticsearchEngine
{
public function search(Builder $builder)
{
$result = $this->performSearch($builder, array_filter([
'numericFilters' => $this->filters($builder),
'size' => $builder->limit,
]));
return $result;
}
/**
* Perform the given search on the engine.
*
* @param Builder $builder
* @return mixed
*/
protected function performSearch(Builder $builder, array $options = [])
{
$params = [
'index' => $this->index,
'type' => $builder->model->searchableAs(),
'body' => [
'query' => [
'bool' => [
'must' => [
[
'query_string' => [
'query' => "{$builder->query}",
]
]
]
]
],
]
];
/**
* 這裡使用了 highlight 的配置
*/
if ($builder->model->searchSettings
&& isset($builder->model->searchSettings['attributesToHighlight'])
) {
$attributes = $builder->model->searchSettings['attributesToHighlight'];
foreach ($attributes as $attribute) {
$params['body']['highlight']['fields'][$attribute] = new \stdClass();
}
}
if (isset($options['from'])) {
$params['body']['from'] = $options['from'];
}
if (isset($options['size'])) {
$params['body']['size'] = $options['size'];
}
if (isset($options['numericFilters']) && count($options['numericFilters'])) {
$params['body']['query']['bool']['must'] = array_merge($params['body']['query']['bool']['must'],
$options['numericFilters']);
}
return $this->elastic->search($params);
}
/**
* Map the given results to instances of the given model.
*
* @param mixed $results
* @param \Illuminate\Database\Eloquent\Model $model
* @return Collection
*/
public function map($results, $model)
{
if (count($results['hits']['total']) === 0) {
return Collection::make();
}
$keys = collect($results['hits']['hits'])
->pluck('_id')->values()->all();
$models = $model->whereIn(
$model->getKeyName(), $keys
)->get()->keyBy($model->getKeyName());
return collect($results['hits']['hits'])->map(function ($hit) use ($model, $models) {
$one = $models[$hit['_id']];
/**
* 這裡返回的資料,如果有 highlight,就把對應的 highlight 設定到物件上面
*/
if (isset($hit['highlight'])) {
$one->highlight = $hit['highlight'];
}
return $one;
});
}
}
2. 替換掉 Scout 的 Engine 為我們建立的 EsEngine
修改 app/Providers/AppServiceProvider.php
use App\Libraries\EsEngine;
use Laravel\Scout\EngineManager;
use Elasticsearch\ClientBuilder as ElasticBuilder;
public function boot()
{
resolve(EngineManager::class)->extend('es', function($app) {
return new EsEngine(ElasticBuilder::create()
->setHosts(config('scout.elasticsearch.hosts'))
->build(),
config('scout.elasticsearch.index')
);
});
}
3. 新增 Model 需要的屬性
為了方便一個 Model 是否在搜尋的時候使用高亮,我們把這些程式碼抽出來寫一個 Trait,使用的時候再 use
建立 Trait app/Libraries/EsSearchable.php
<?php namespace App\Libraries;
trait EsSearchable
{
public $searchSettings = [
'attributesToHighlight' => [
'*'
]
];
public $highlight = [];
}
Post.php Model 中使用 trait:
use Searchable, EsSearchable;
4. 修改 view 滿足我們的顯示
修改 resources/views/search.blade.php ,檢視高亮。
@extends('layouts.main')
@section('content')
<div class="row">
<div class="col-md-12">
<form action="/search">
<div class="input-group">
<input type="text" class="form-control h50" name="query" placeholder="關鍵字..." value="{{ $q }}">
<span class="input-group-btn"><button class="btn btn-default h50" type="submit" type="button"><span class="glyphicon glyphicon-search"></span></button></span>
</div>
</form>
</div>
</div>
@if($q)
<div class="row">
<div class="col-md-12">
<div class="panel panel-default list-panel search-results">
<div class="panel-heading">
<h3 class="panel-title ">
<i class="fa fa-search"></i> 關於 “<span class="highlight">{{ $q }}</span>” 的搜尋結果, 共 {{ $paginator->total() }} 條
</h3>
</div>
<div class="panel-body ">
[@foreach](https://learnku.com/users/5651)($paginator as $post)
<div class="result">
<h2 class="title">
<a href="{{ $post->url }}" target="_blank">
@if (isset($post->highlight['title']))
[@foreach](https://learnku.com/users/5651) ($post->highlight['title'] as $item)
{!! $item !!}
@endforeach
@else
{{ $post->title }}
@endif
</a>
</h2>
<div class="info">
</div>
<div class="desc">
@if (isset($post->highlight['content']))
[@foreach](https://learnku.com/users/5651) ($post->highlight['content'] as $item)
......{!! $item !!}......
@endforeach
@else
{{ mb_substr($post->content, 0, 150) }}......
@endif
</div>
<hr>
</div>
@endforeach
</div>
{{ $paginator->links() }}
</div>
</div>
</div>
@else
<div class="row text-center">
<div class="col-md-12">
<br>
<h2>你會搜尋到什麼?</h2>
<br>
<p>學習學習再學習公眾號所有文章</p>
</div>
</div>
@endif
@endsection
樣式可以抄一抄 Laravel China 的搜尋結果...
public/css/main.css
#app {
margin-top: 20px;
}
.h50 {
height: 50px;
}
.search-results {
margin-top: 20px;
padding: 20px;
line-height: 25px;
}
.search-results .panel-heading h3 {
color: #696969;
font-size: 15px;
margin-bottom: 12px;
}
.search-results a {
color: #333;
}
.search-results .result {
margin-bottom: 20px;
}
.search-results .user.result {
margin-top: 8px;
margin-bottom: 0px;
}
.search-results .result em {
color: #EB5424;
font-style: normal;
}
.search-results .result .title {
font-size: 18px;
}
.search-results .result .title .badge {
background: #EBEDEE;
color: #9A9DA0;
font-weight: normal;
font-size: 12px;
margin-left: 4px;
}
.search-results .result .info {
margin-bottom: 6px;
font-size: 14px;
}
.search-results .result .info .url a {
color: #23863F;
}
.search-results .result .info .date {
color: #999;
margin-left: 8px;
}
.search-results .result .desc {
color: #666;
font-size: 14px;
word-break: break-all;
}
.search-results .result .desc em {
color: #F86334;
}
.search-results .user .info {
margin-top: 4px;
font-size: 14px;
}
.search-results .user .info.number {
color: #666;
font-size: 13px;
}
.search-results em {
color: #e07b7a;
}
.search-results .role-label {
display: inline-block;
position: absolute;
}
.search-results .role-label a.label {
font-size: 85%;
font-weight: 100;
padding: 0.2em 1em .2em;
position: relative;
margin: 8px;
color: #fff;
}
.search-results .user-info {
padding-top: 8px;
padding-left: 8px;
}
.search-results hr {
margin-top: 15px;
margin-bottom: 15px;
}
.search-results .list-panel .panel-body {
padding: 0px;
}
好了,這樣就解決了高亮的問題了。
別忘記 Star 我的 demo 哦,我要儘可能寫的清楚。https://github.com/lijinma/laravel-scout-e...
我為什麼這麼做?
因為我看別人也這麼做: https://github.com/laravel/scout/pull/19
本作品採用《CC 協議》,轉載必須註明作者和本文連結