在Laravel專案中使用Elasticsearch

oliver-l發表於2020-08-10

Elasticsearch概念

Elasticsearch是一個基於Lucene的搜尋伺服器。它提供了一個分散式多使用者能力的全文搜尋引擎,基於RESTful web介面。Elasticsearch是用Java語言開發的,並作為Apache許可條款下的開放原始碼釋出,是一種流行的企業級搜尋引擎。Elasticsearch用於雲端計算中,能夠達到實時搜尋,穩定,可靠,快速,安裝使用方便。官方客戶端在Java、.NET(C#)、PHP、Python、Apache Groovy、Ruby和許多其他語言中都是可用的。

Elastic 的底層是開源庫 Lucene。但是,你沒法直接用 Lucene,必須自己寫程式碼去呼叫它的介面。Elastic 是 Lucene 的封裝,提供了 REST API 的操作介面,開箱即用。

安裝Elasticsearch

參考安裝Elasticsearch
我使用centos伺服器,安裝命令如下

# Download and install the public signing key:
$ rpm --import https://artifacts.elastic.co/GPG-KEY-elasticsearch

# 新增 yum 源
$ vim /etc/yum.repos.d/elasticsearch-7.x.repo
# 要新增的內容 ##################################################################
[elasticsearch-7.x] 
name=Elasticsearch repository for  7.x packages
baseurl=https://artifacts.elastic.co/packages/7.x/yum
gpgcheck=1 
gpgkey=https://artifacts.elastic.co/GPG-KEY-elasticsearch
enabled=1 
autorefresh=1 
type=rpm-md
###############################################################################

# 安裝 es
$ yum install -y elasticsearch

# 管理 es
$ systemctl start elasticsearch.service
$ systemctl stop elasticsearch.service

# 測試 elasticsearch
# 參考 https://www.elastic.co/guide/cn/elasticsearch/guide/current/running-elasticsearch.html
$ curl http://127.0.0.1:9200?pretty 

外網訪問Elasticsearch

參考這兩篇文章elasticsearch外網訪問設定elasticsearch安裝及啟動異常解決

預設情況下安裝elasticsearch之後是無法進行外網訪問的,可以通過設定來完成這一目的

1、編輯elasticsearch配置檔案:vim /etc/config/elasticsearch.yml

找到network.host這一行,更改為network.host: 0.0.0.0

2、重啟服務,發現報錯

ERROR: [1] bootstrap checks failed
[1]: max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144]

3、以root賬戶更改/etc/sysctl.conf檔案,新增如下內容

vm.max_map_count=655360

4、以root賬戶執行下面命令

[***@elk01 ~]$ sudo sysctl -p
vm.max_map_count = 655360

5、再次啟動服務,開放9200埠。依然會報錯ERROR: [1] bootstrap checks failed
[1]: the default discovery settings are unsuitable for production use; at least one of [discovery.seed_hosts, discovery.seed_providers, cluster.initial_master_nodes] must be configured

6、在 elasticsearch.yml中新增配置項:

bootstrap.memory_lock: false
bootstrap.system_call_filter: false

cluster.initial_master_nodes: [“node-1”]
這個的話,這裡的node-1是上面一個預設的記得開啟就可以了

7、再次重啟服務,成功開啟es服務

在我的Laravel專案中使用Elasticsearch

通過網址也可以直接訪問elasticsearch,表示我們已經可以成功從外網訪問,當然在正式環境中,還是需要配置ip限制訪問的,我這邊是方便測試,所有暫不限制。

在我的Laravel專案中使用Elasticsearch

Elasticsearch基礎概念和使用

內容參考Elasticsearch基礎概念全文搜尋引擎 Elasticsearch入門教程
Elasticsearch 本質上是一個資料庫,但並不是 MySQL 這種關係型資料庫,查詢語言也不是 SQL,而是 Elasticsearch 自己的一套查詢語言。

既然是資料庫,有一些概念是互通的,如下表:

MySQL Elasticsearch
資料庫(Database) 索引(Index)
表(Table) 型別(Type)
記錄(Row) 文件(Document)
欄位(Column) 欄位(Fields)

建立索引

建立名為test的索引

curl -X PUT http://localhost:9200/test

伺服器返回一個 JSON 物件,裡面的acknowledged欄位表示操作成功。

{"acknowledged":true,"shards_acknowledged":true,"index":"test"}

刪除索引

curl -X DELETE 'localhost:9200/test'

伺服器返回值為,裡面的acknowledged欄位表示操作成功

{"acknowledged": true}

檢視索引

curl -X GET 'http://localhost:9200/_cat/indices?v'

伺服器會列出所有索引

health status index uuid                   pri rep docs.count docs.deleted store.size pri.store.size
yellow open   test  y6f8oZitQEKE3blusrH_7g   1   1          0            0       208b           208b

新增資料

向指定的 /Index/Type 傳送 PUT 請求,就可以在 Index 裡面新增一條記錄。比如,向/accounts/person傳送請求,就可以新增一條人員記錄。

curl -H 'Content-Type: application/json' -X PUT 'localhost:9200/accounts/person/1' -d '
{
  "user": "張三",
  "title": "工程師",
  "desc": "資料庫管理"
}'

伺服器響應後返回json物件如下:會給出 Index、Type、Id、Version 等資訊

{
    "_index":"accounts",
    "_type":"person",
    "_id":"1",
    "_version":1,
    "result":"created",
    "_shards":{"total":2,"successful":1,"failed":0},
    "_seq_no":0,
    "_primary_term":1
}

如果你仔細看,會發現請求路徑是/accounts/person/1,最後的1是該條記錄的 Id。它不一定是數字,任意字串(比如abc)都可以。

新增記錄的時候,也可以不指定 Id,這時要改成 POST 請求。

curl -H 'Content-Type: application/json' -X POST 'localhost:9200/accounts/person' -d '
{
  "user": "李四",
  "title": "工程師",
  "desc": "系統管理"
}'

伺服器返回json如下:_id欄位就是一個隨機字串

{
    "_index":"accounts",
    "_type":"person",
    "_id":"0JO3x3MBEaLkjdo6ypYV",
    "_version":1,
    "result":"created",
    "_shards":{"total":2,"successful":1,"failed":0},
    "_seq_no":1,
    "_primary_term":1
}

檢視記錄

/Index/Type/Id發出 GET 請求,就可以檢視這條記錄。

curl 'localhost:9200/accounts/person/1?pretty=true'

伺服器返回json物件如下:URL 的引數pretty=true表示以易讀的格式返回。

返回的資料中,found欄位表示查詢成功,_source欄位返回原始記錄。

{
  "_index" : "accounts",
  "_type" : "person",
  "_id" : "1",
  "_version" : 1,
  "_seq_no" : 0,
  "_primary_term" : 1,
  "found" : true,
  "_source" : {
    "user" : "張三",
    "title" : "工程師",
    "desc" : "資料庫管理"
  }
}

如果 Id 不正確,就查不到資料,found欄位就是false

curl 'localhost:9200/accounts/person/2?pretty=true'

伺服器返回json物件如下:

{
  "_index" : "accounts",
  "_type" : "person",
  "_id" : "2",
  "found" : false
}

刪除記錄

curl -X DELETE 'localhost:9200/accounts/person/0JO3x3MBEaLkjdo6ypYV'

伺服器返回json物件如下:

{
    "_index":"accounts",
    "_type":"person",
    "_id":"0JO3x3MBEaLkjdo6ypYV",
    "_version":2,
    "result":"deleted",
    "_shards":{"total":2,"successful":1,"failed":0},
    "_seq_no":2,
    "_primary_term":1
}

若刪除的資料不存在,我們再執行一次上面的命令

curl -X DELETE 'localhost:9200/accounts/person/0JO3x3MBEaLkjdo6ypYV'

伺服器返回json物件如下:result返回結果為not_found

{
    "_index":"accounts",
    "_type":"person",
    "_id":"0JO3x3MBEaLkjdo6ypYV",
    "_version":1,
    "result":"not_found",
    "_shards":{"total":2,"successful":1,"failed":0},
    "_seq_no":3,
    "_primary_term":1
}

更新資料

更新記錄就是使用 PUT 請求,重新傳送一次資料。

curl -H 'Content-Type: application/json' -X PUT 'localhost:9200/accounts/person/1' -d '
{
  "user": "張三",
  "title": "工程師",
  "desc": "資料庫管理,軟體開發"
}' 

伺服器響應後返回json物件如下:result結果返回updated,表示更新資料成功

{
    "_index":"accounts",
    "_type":"person",
    "_id":"1",
    "_version":2,
    "result":"updated",
    "_shards":{"total":2,"successful":1,"failed":0},
    "_seq_no":4,
    "_primary_term":1
}

資料查詢

返回所有記錄

curl 'localhost:9200/accounts/person/_search?pretty=true'

伺服器會返回索引型別下的所有資料,返回json如下:

{
  "took" : 7,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 1,
      "relation" : "eq"
    },
    "max_score" : 1.0,
    "hits" : [
      {
        "_index" : "accounts",
        "_type" : "person",
        "_id" : "1",
        "_score" : 1.0,
        "_source" : {
          "user" : "張三",
          "title" : "工程師",
          "desc" : "資料庫管理,軟體開發"
        }
      }
    ]
  }
}

上面程式碼中,返回結果的 took欄位表示該操作的耗時(單位為毫秒),timed_out欄位表示是否超時,hits欄位表示命中的記錄,裡面子欄位的含義如下。
返回的記錄中,每條記錄都有一個_score欄位,表示匹配的程式,預設是按照這個欄位降序排列。

全文搜尋

Elastic 的查詢非常特別,使用自己的查詢語法,要求 GET 請求帶有資料體。

curl -H 'Content-Type: application/json' 'localhost:9200/accounts/person/_search?pretty=true'  -d '
{
  "query" : { "match" : { "desc" : "軟體" }}
}'

伺服器返回json物件如下:

{
  "took" : 3,
  "timed_out" : false,
  "_shards" : {
    "total" : 1,
    "successful" : 1,
    "skipped" : 0,
    "failed" : 0
  },
  "hits" : {
    "total" : {
      "value" : 1,
      "relation" : "eq"
    },
    "max_score" : 0.5753642,
    "hits" : [
      {
        "_index" : "accounts",
        "_type" : "person",
        "_id" : "1",
        "_score" : 0.5753642,
        "_source" : {
          "user" : "張三",
          "title" : "工程師",
          "desc" : "資料庫管理,軟體開發"
        }
      }
    ]
  }
}

上面程式碼使用 Match 查詢,指定的匹配條件是desc欄位裡面包含”軟體”這個詞。返回結果如下。

Elastic 預設一次返回10條結果,可以通過size欄位改變這個設定。

 $ curl -H 'Content-Type: application/json' 'localhost:9200/accounts/person/_search'  -d '
 {
   "query" : { "match" : { "desc" : "管理" }},
   "size": 1
 }'

上面程式碼指定,每次只返回一條結果。

還可以通過from欄位,指定位移。

 $ curl -H 'Content-Type: application/json' 'localhost:9200/accounts/person/_search'  -d '
 {
   "query" : { "match" : { "desc" : "管理" }},
   "from": 1,
   "size": 1
 }'

上面程式碼指定,從位置1開始(預設是從位置0開始),只返回一條結果。

如果有多個搜尋關鍵字, Elastic 認為它們是or關係。

 $ curl -H 'Content-Type: application/json' 'localhost:9200/accounts/person/_search'  -d '
 {
   "query" : { "match" : { "desc" : "軟體 系統" }}
 }'

上面程式碼搜尋的是軟體 or 系統

如果要執行多個關鍵詞的and搜尋,必須使用布林查詢

 $ curl -H 'Content-Type: application/json' 'localhost:9200/accounts/person/_search'  -d '
 {
   "query": {
     "bool": {
       "must": [
         { "match": { "desc": "軟體" } },
         { "match": { "desc": "系統" } }
       ]
     }
   }
 }'

在laravel中使用Elasticsearch

通過composer引入使用Elasticsearch第三方包

composer require babenkoivan/scout-elasticsearch-driver

Laravel 提供了一個擴充套件包 Scout 用來提供全文搜尋的功能,通過安裝擴充套件包我們是可以使用 Elasticsearch 作為驅動的。

文件可檢視這裡babenkoivan/scout-elasticsearch-driver,在使用擴充套件包之前,需要進行相關配置

配置

首先將Scout和擴充套件包的配置檔案釋出出來。

php artisan vendor:publish --provider="Laravel\Scout\ScoutServiceProvider"
php artisan vendor:publish --provider="ScoutElastic\ScoutElasticServiceProvider"

修改一下配置,可以配置到 env 中。

SCOUT_DRIVER=elastic 
SCOUT_ELASTIC_HOST=elasticsearch:9200

使用

建立索引

該命令會建立一個GoodsIndexConfigurator.php檔案,在檔案中可以指定索引名稱和設定

php artisan make:index-configurator Es/GoodsIndexConfigurator

該命令會在elasticsearch中建立相應的索引名,相當於建立在mysql中建立表

php artisan elastic:create-index App\\Es\\GoodsIndexConfigurator

注意,每個可搜尋模型都需要有自己的索引配置器。

在Elasticsearch 6.0.0或更高版本中建立的索引只能包含一個對映型別。在5.x中建立的具有多種對映型別的索引將繼續像在Elasticsearch 6.x中一樣工作。對映型別將在Elasticsearch 7.0.0中完全刪除。

修改商品模型程式碼如下

<?php

namespace App\Models;

use App\Es\GoodsIndexConfigurator;
use Illuminate\Database\Eloquent\Model;
use ScoutElastic\Searchable;

class HGoodsEs extends Model
{
    use Searchable;

    protected $table='goods';
    public $timestamps = true;

    //使用es配置
    protected $indexConfigurator = GoodsIndexConfigurator::class;

    protected $mapping = [
        'properties' => [
            'title' => [
                'type' => 'text',
            ],
            'content' => [
                'type' => 'text',
            ],
        ]
    ];

    public function toSearchableArray()
    {
        return [
            'title'=> $this->title,
            'content' => strip_tags($this->content),
        ];
    }

}

資料匯入

執行以下命令,匯入商品資料

php artisan scout:import "App\Models\Goods"

在我的Laravel專案中使用Elasticsearch

匯入完成後,我們直接在網頁檢視elasticsearch:9200/goods/goods/_sea...
可檢視到如下:

說明資料已成功匯入elasticsearch
在我的Laravel專案中使用Elasticsearch

測試搜尋

使用tinker測試

Goods::search('boy')->get()

執行結果如下,說明已成功使用elasticsearch進行全文搜尋,其餘就根據業務需要去執行相應的查詢就好了

在Laravel專案中使用Elasticsearch

本作品採用《CC 協議》,轉載必須註明作者和本文連結

相關文章