Laravel + Elasticsearch 實現中文搜索

Elasticsearch

Elasticsearch 是一個基於 Apache Lucene(TM) 的開源搜索引擎,不管在開源仍是專有領域,Lucene可 以被認爲是迄今爲止最早進、性能最好的、功能最全的搜索引擎庫。 php

可是,Lucene 只是一個庫。想要發揮其強大的做用,你需使用 Java 並要將其集成到你的應用中。Lucene 很是複雜,你須要深刻的瞭解檢索相關知識來理解它是如何工做的。 html

Elasticsearch 也是使用 Java 編寫並使用 Lucene 來創建索引並實現搜索功能,可是它的目的是經過簡單連貫的 RESTful API 讓全文搜索變得簡單並隱藏 Lucene 的複雜性。 node

不過,Elasticsearch 不只僅是 Lucene 和全文搜索引擎,它還提供:laravel

  • 分佈式的實時文件存儲,每一個字段都被索引並可被搜索
  • 實時分析的分佈式搜索引擎
  • 能夠擴展到上百臺服務器,處理PB級結構化或非結構化數據

並且,全部的這些功能被集成到一臺服務器,你的應用能夠經過簡單的 RESTful API、各類語言的客戶端甚至命令行與之交互。上手 Elasticsearch 很是簡單,它提供了許多合理的缺省值,並對初學者隱藏了複雜的搜索引擎理論。它開箱即用(安裝便可使用),只需不多的學習既可在生產環境中使用。git

Elasticsearch 在 Apache 2 license 下許可以使用,能夠免費下載、使用和修改。github

ElasticSearch 安裝

在 Laradock 中已經集成了 ElasticSearch。咱們能夠直接使用:docker

docker-compose up -d elasticsearch

若是須要安裝插件,執行命令:數據庫

docker-compose exec elasticsearch /usr/share/elasticsearch/bin/elasticsearch-plugin install {plugin-name}

// 重啓容器
docker-compose restart elasticsearch

注:json

  • The vm.max_map_count kernel setting must be set to at least 262144 for production use.

因爲我是 centos 7 環境,直接設置在系統設置:
sysctl -w vm.max_map_count=262144centos

  • 默認用戶名和密碼:「elastic」、「changeme」,端口號:9200

ElasticHQ

ElasticHQ is an open source application that offers a simplified interface for managing and monitoring Elasticsearch clusters.

Management and Monitoring for Elasticsearch.

http://www.elastichq.org/

  • Real-Time Monitoring
  • Full Cluster Management
  • Full Cluster Monitoring
  • Elasticsearch Version Agnostic
  • Easy Install - Always On
  • Works with X-Pack

輸入咱們的 Elasticsearch Host,便可進入後臺。

默認的建立了:

一個集羣 cluster:laradock-cluster
一個節點 node:laradock-node
一個索引 index:.elastichq

IK 分詞器安裝

ElasticSearch 主要是用於本身 blog 或者公衆號文章的搜索使用,因此須要選擇一箇中文分詞器配合使用,這裏剛開始推薦使用 IK 分詞器,下面開始安裝對應 ElasticSearch版本 (7.5.1) 一致的插件:

https://github.com/medcl/elasticsearch-analysis-ik/releases

// 安裝插件
docker-compose exec elasticsearch /usr/share/elasticsearch/bin/elasticsearch-plugin install https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v7.5.1/elasticsearch-analysis-ik-7.5.1.zip

注:能夠將 zip 文件先下載回來,而後再安裝,速度會快些。

檢驗分詞效果

根據 Elasticsearch API 測試,分詞的效果達到了:

~ curl -X POST "http://your_host/_analyze?pretty" -H 'Content-Type: application/json' -d'
{
  "analyzer": "ik_max_word",
  "text":     "我是中國人"
}
'

{
  "tokens" : [
    {
      "token" : "我",
      "start_offset" : 0,
      "end_offset" : 1,
      "type" : "CN_CHAR",
      "position" : 0
    },
    {
      "token" : "是",
      "start_offset" : 1,
      "end_offset" : 2,
      "type" : "CN_CHAR",
      "position" : 1
    },
    {
      "token" : "中國人",
      "start_offset" : 2,
      "end_offset" : 5,
      "type" : "CN_WORD",
      "position" : 2
    },
    {
      "token" : "中國",
      "start_offset" : 2,
      "end_offset" : 4,
      "type" : "CN_WORD",
      "position" : 3
    },
    {
      "token" : "國人",
      "start_offset" : 3,
      "end_offset" : 5,
      "type" : "CN_WORD",
      "position" : 4
    }
  ]
}

結合 Laravel

雖然 Elasticsearch 官方提供了對應的 PHP 版本的插件,但咱們仍是但願和 Laravel 結合的更緊密些,因此這裏選擇和 Scout 結合使用,具體用到了 tamayo/laravel-scout-elastic 插件。

composer require tamayo/laravel-scout-elastic
 
composer require laravel/scout
 
php artisan vendor:publish

選擇:Laravel\Scout\ScoutServiceProvider

修改驅動爲 elasticsearch

'driver' => env('SCOUT_DRIVER', 'elasticsearch'),

建立索引

建立索引有幾種方法,其中可使用 Ela 可視化工具 ElasticHQ 直接建立。

接下來咱們須要更新這個索引,補充 Mappings 這部分,能夠用 Postman。

另外一種方法是用 Laravel 自帶的 Artisan 命令行功能。

這裏咱們推薦使用 Artisan 命令行。
php artisan make:command ESOpenCommand

根據官網提示,咱們能夠在 ESOpenCommand 上向 Elasticsearch 服務器發送 PUT 請求,這裏藉助 Elasticsearch 提供的 PHP 插件,在咱們使用 tamayo/laravel-scout-elastic 插件時,已經安裝了 Elasticsearch PHP 插件:

下面就能夠藉助插件,建立咱們的 Index,直接看代碼:

public function handle()
    {
    $host = config('scout.elasticsearch.hosts');
    $index = config('scout.elasticsearch.index');
    $client = ClientBuilder::create()->setHosts($host)->build();

    if ($client->indices()->exists(['index' => $index])) {
        $this->warn("Index {$index} exists, deleting...");
        $client->indices()->delete(['index' => $index]);
    }

    $this->info("Creating index: {$index}");

    return $client->indices()->create([
        'index' => $index,
        'body' => [
            'settings' => [
                'number_of_shards' => 1,
                'number_of_replicas' => 0
            ],
            'mappings' => [
                '_source' => [
                    'enabled' => true
                ],
                'properties' => [
                    'id' => [
                        'type' => 'long'
                    ],
                    'title' => [
                        'type' => 'text',
                        'analyzer' => 'ik_max_word',
                        'search_analyzer' => 'ik_smart'
                    ],
                    'subtitle' => [
                        'type' => 'text',
                        'analyzer' => 'ik_max_word',
                        'search_analyzer' => 'ik_smart'
                    ],
                    'content' => [
                        'type' => 'text',
                        'analyzer' => 'ik_max_word',
                        'search_analyzer' => 'ik_smart'
                    ]
                ],
            ]
        ]
    ]);
}

好了,咱們執行 Kibana 看到咱們已經建立好了 Index:

注 Kibana 本地 Docker 安裝:

後續會重點說明 Kibana 如何使用

docker run -d --name kibana -e ELASTICSEARCH_HOSTS=http://elasticsearch_host -p 5601:5601 -e SERVER_NAME=ki.test kibana:7.5.2

爲了驗證 Index 是否可用,能夠插入一條數據看看:

curl -XPOST your_host/coding01_open/_create/1 -H 'Content-Type:application/json' -d'
{"content":"中韓漁警衝突調查:韓警平均天天扣1艘中國漁船"}

能夠經過瀏覽器看看對應的數據:

有了 Index,下一步咱們就能夠結合 Laravel,導入、更新、查詢等操做了。

Laravel Model 使用

Laravel 框架已經爲咱們推薦使用 Scout 全文搜索,咱們只須要在 Article Model 加上官方所說的內容便可,很簡單,推薦你們看 Scout 使用文檔:https://learnku.com/docs/laravel/6.x/scout/5191,下面直接上代碼:

<?php

namespace App;

use App\Tools\Markdowner;
use Illuminate\Database\Eloquent\Model;
use Illuminate\Database\Eloquent\SoftDeletes;
use Laravel\Scout\Searchable;

class Article extends Model
{
    use Searchable;

    protected $connection = 'blog';
    protected $table = 'articles';
    use SoftDeletes;

    /**
     * The attributes that should be mutated to dates.
     *
     * @var array
     */
    protected $dates = ['published_at', 'created_at', 'deleted_at'];

    /**
     * The attributes that are mass assignable.
     *
     * @var array
     */
    protected $fillable = [
        'user_id',
        'last_user_id',
        'category_id',
        'title',
        'subtitle',
        'slug',
        'page_image',
        'content',
        'meta_description',
        'is_draft',
        'is_original',
        'published_at',
        'wechat_url',
    ];

    protected $casts = [
        'content' => 'array'
    ];

    /**
     * Set the content attribute.
     *
     * @param $value
     */
    public function setContentAttribute($value)
    {
        $data = [
            'raw'  => $value,
            'html' => (new Markdowner)->convertMarkdownToHtml($value)
        ];

        $this->attributes['content'] = json_encode($data);
    }

    /**
     * 獲取模型的可搜索數據
     *
     * @return array
     */
    public function toSearchableArray()
    {
        $data = [
            'id' => $this->id,
            'title' => $this->title,
            'subtitle' => $this->subtitle,
            'content' => $this->content['html']
        ];

        return $data;
    }

    public function searchableAs()
    {
        return '_doc';
    }
}

Scout 提供了 Artisan 命令 import 用來導入全部已存在的記錄到搜索索引中。

php artisan scout:import "App\Article"

看看 Kibana,已存入 12 條數據,和數據庫條數吻合。

有了數據,咱們能夠測試看看能不能查詢到數據。

仍是同樣的,建立一個命令:

class ElasearchCommand extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'command:search {query}';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Command description';

    /**
     * Create a new command instance.
     *
     * @return void
     */
    public function __construct()
    {
        parent::__construct();
    }

    /**
     * Execute the console command.
     *
     * @return mixed
     */
    public function handle()
    {
        $article = Article::search($this->argument('query'))->first();
        $this->info($article->title);
    }
}

這是個人 titles,我隨便輸入一個關鍵字:「清單」,看是否能搜到。

總結

總體完成了:

  1. Elasticsearch 安裝;
  2. Elasticsearch IK 分詞器插件安裝;
  3. Elasticsearch 可視化工具 ElasticHQ 和 Kibana 的安裝和簡單使用;
  4. Scout 的使用;
  5. Elasticsearch 和 Scout 結合使用。

接下來就要將更多的內容存入 Elasticsearch 中,爲本身的 blog、公衆號、自動化搜索等場景提供全文搜索。

參考

相關文章
相關標籤/搜索