推薦18-Laravel scout 與 elasticsearch 案例

  • 2019 年 10 月 7 日
  • 筆記

$ wget https://artifacts.elastic.co/downloads/elasticsearch/elasticsearch-5.5.1.zip  $ unzip elasticsearch-5.5.1.zip  $ cd elasticsearch-5.5.1/

接著,進入解壓後的目錄,運行以下的命令,嘗試啟動 elastic

$ ./bin/elasticsearch

注意:

初次安裝,非常有可能會出現以下報錯:

max virtual memory areas vm.maxmapcount [65530] is too low

運行以下命令即可解決:

$ sudo sysctl -w vm.max_map_count=262144

如果一切正常,elastic 默認會在本機的 9200 埠運行,請求該埠,會獲得以下

$ curl localhost:9200    {    "name" : "atntrTf",    "cluster_name" : "elasticsearch",    "cluster_uuid" : "tf9250XhQ6ee4h7YI11anA",    "version" : {      "number" : "5.5.1",      "build_hash" : "19c13d0",      "build_date" : "2017-07-18T20:44:24.823Z",      "build_snapshot" : false,      "lucene_version" : "6.6.0"    },    "tagline" : "You Know, for Search"  }

默認情況下,elastic 只允許本機訪問,如果需要遠程訪問許可權,需要修改 elastic 安裝目錄的 config/elasticserach.yml 文件,去掉 netword.host 的注釋,將它的值 改為 0.0.0.0 ,然後重啟 elastic

network.host: 0.0.0.0

上面程式碼中,設成 0.0.0.0 讓任何人都可以訪問。線上服務不要這樣設置,要設成具體的 IP

基本概念

elastic 本質上是一個分散式資料庫,允許多台伺服器協同工作,每台伺服器可以允許多個 elastic 實例,單個 elastic 實例稱為一個節點,一組節點構成一個集群

它有一些重要的概念

  1. Index(索引)
  2. Type(類型)
  3. Document(文檔)
  4. Shards(分片)
  5. Replicasedit(副本)

分片和副本暫時用不到,就簡單的說明一下

- 副本是乘法,越多越浪費,但也越保險。  - 分片是除法,分片越多,單分片數據就越少也越分散。

由於裡面的概念內容比較多,貼出兩個講解的非常好的部落格:

  1. 阮一峰的講解
  2. ElastSearch 的技術分析

看完了之後,我們可以用一個對比來了解一下其中重要的概念

- 關係型資料庫 -> Databases(庫) -> Tables(表) -> Rows(行) -> Columns(列)。  - Elasticsearch -> Indeces(索引) -> Types(類型) -> Documents(文檔) -> Fields(屬性)。

Elasticsearch 集群可以包含多個索引(indices)(資料庫),每一個索引可以包含多個類型 (Types)(表),每一個類型包含多個文檔(documents)(行),然後每個文檔包含多個欄位(Fields)(列)。

雖然這麼類比,但是畢竟是兩個差異化的產品,而且上面也說過在以後的版本中類型 (Types) 可能會被刪除,所以一般我們創建索引都是一個種類對應一個索引。生鮮就創建商品的索引,生活用品就創建生活用品的索引,而不會說創建一個商品的索引,裡面既包含生鮮的類型,又包含生活用品的類型。

Laravel scout 與 es

先安裝 scout 包

composer require laravel/scout

再生成配置文件

php artisan vendor:publish --provider="LaravelScoutScoutServiceProvider"

config/app.php 的 provider 中,添加

LaravelScoutScoutServiceProvider::class,  ScoutEnginesElasticsearchElasticsearchProvider::class,

然後我們還需要在 scout.php 中,添加 es 的配置資訊,在 algolia 後添加

'elasticsearch' => [          'index' => env('ELASTICSEARCH_INDEX', 'dongdianyi'),          'hosts' => [              env('ELASTICSEARCH_HOST', 'http://127.0.0.1:9200'),          ],  ],

我們還需要使用到 GuzzleHttp

安裝一下

composer require guzzlehttp/guzzle

開始寫程式碼,需要先使用 command ,讓 ES 初始化一些數據

php artisan make:command InitEs

貼出我的程式碼

<?php    namespace AppConsoleCommands;    use GuzzleHttpClient;  use IlluminateConsoleCommand;    class ESInit extends Command  {        protected $signature = 'es:init';        protected $description = '初始化 es庫';        public function __construct()      {          parent::__construct();      }        public function handle()      {          $client = new Client();          try {              $this->createTemplate($client);              $this->createIndex($client);          } catch (Exception $ex) {              $ex->message();          }      }        public function createTemplate(Client $client)      {          $url = config('scout.elasticsearch.hosts')[0] . '/_template/tmp';          // 保證 template 不存在          // $client->delete($url);          $param = [              'json' => [                  'template' => '*',                  'settings' => [                      'number_of_shards' => 1,                  ],                  'mappings' => [                      '_default_' => [                          '_all'              => [                              'enabled' => true,                          ],                          'dynamic_templates' => [                              [                                  'strings' => [                                      'match_mapping_type' => 'string',                                      'mapping'            => [                                          'type'         => 'text',                                          'analyzer'     => 'ik_smart',                                          'ignore_above' => 256,                                          'fields'       => [                                              'keyword' => [                                                  'type' => 'keyword',                                              ],                                          ],                                      ],                                  ],                              ],                          ],                      ],                  ],              ],          ];          $client->put($url, $param);          $this->info('elasticsearch template created done');      }        public function createIndex(Client $client)      {          // 創建 index          $url = config('scout.elasticsearch.hosts')[0] . '/' . config('scout.elasticsearch.index');          // 保證 index 不存在          // $client->delete($url);          $param = [              'json' => [                  'settings' => [                      'refresh_interval'   => '5s',                      'number_of_shards'   => 1,                      'number_of_replicas' => 0,                  ],                  'mappings' => [                      '_default_' => [                          '_all' => [                              'enabled' => false,                          ],                      ],                  ],              ],          ];          $client->put($url, $param);          $this->info('elasticsearch index created done');      }  }

然後,我們要規定,是那個模型需要被搜索

<?php    namespace App;    use IlluminateDatabaseEloquentModel;  use LaravelScoutSearchable;      class Article extends Model  {      use Searchable;      protected $table = 'posts';        protected $fillable = [          'url',          'author',          'title',          'content',          'post_date'      ];        public function toSearchableArray()      {          return [              'title' => $this->title,              'content' => $this->content          ];      }  }

在模型裡面,使用 Searchable 和重載 toSearchableArray 函數就可以了

然後使用命令

php artisan scout:import "AppArticle"

將目前資料庫中的數據,按照 toSearchableArray 的規則導入,導入完成就可以了

驗證結果

es 和 scout 的步驟已經走完了,接下來就可以使用了

先定義 graphql 介面

searchArticles(keyWord: String!): [Article!]! @paginate(defaultCount: 10, builder: "App\Article@searchArticles")

然後再解析

public function searchArticles($rootValue, array $args, GraphQLContext $context, ResolveInfo $resolveInfo)      {          $query = $args['keyWord'];          return Article::search($query);      }

完成!,這裡例子中我使用的是 ik_smart ,會自動按照中文分詞的最大粒度去匹配