搜索接口優化方案——elasticsearch分佈式搜索引擎的基本使用

前言:

    在開發項目中一般都會有搜索功能。如果是面向C端的搜索功能,往往都特別考驗性能。比如普通的商城系統中的商品搜索或者一些資源的站內搜索。

    可能以前的做法就是對商品表做一個按名稱或商品描述做模糊查詢。更好一點的是對搜索關鍵字進行分詞,並且專門建一個搜索詞庫表。不過前期需要對搜索詞進行拆解然後冪集組合並於商品ID關聯,搜索字與詞庫表的字以完全匹配的方式查詢並找到商品ID。

    雖然建詞庫表也是不錯的解決方法,但是還要拆解存庫建索引,相對比較麻煩。所以也是在網上查詢瞭解到了elasticsearch,打算以後做站內搜索用ES,下面就簡單介紹一下他的基本使用方法。

 

ES介紹:

     ElasticSearch是一個基於Lucene的搜索服務器。它提供了一個分佈式多用戶能力的全文搜索引擎,基於RESTful web接口。Elasticsearch是用Java開發的,並作爲Apache許可條款下的開放源碼發佈,是當前流行的企業級搜索引擎。設計用於雲計算中,能夠達到實時搜索,穩定,可靠,快速,安裝使用方便。

 

下載安裝:

1. 找到官網,按自己的系統下載對應版本,目前200多M。

    

2. 解壓後打開目錄中的bin裏的"elasticsearch.bat"就可以啓動。

3. 啓動時間相對慢一點,以下只演示單機單節點使用,分佈式可以後期配置。

 

編碼使用:

以下是以ThinkPHP6.0.2進行演示使用,所以在ES服務開啓後,先打開瀏覽器訪問127.0.0.1:9200,如果返回下面表示成功開啓。

1. ThinkPHP6.0中通過composer install elasticsearch/elasticsearch 下載ES依賴包,如果composer慢可以試一下下載方法後再安裝。

 

並且每次要本地使用ES時都要先開啓elasticsearch.bat文件,不然就會出現下面的提示。

 

2. 在使用前需要理解一下ES的幾個名詞:索引,Type,文檔,字段,映射。並且ES以restful風格進行查詢,所以可以將前面的名詞可以和MySQL的數據庫,表,列,SQL關聯一下。比如索引相當於MySQL的數據庫,文檔相當於表中的一條記錄restful方式請求就類似於SQL語句。比如GET 索引名/Type/ID 可以獲取文檔數據,POST,PUT,DELETE等等創建索引,創建/修改/刪除文檔等可以查詢官方文檔,然後使用POSTMAN用不同的請求127.0.0.1:9200。

 

3. 現在回到ThinkPHP6中,在安裝ES依賴後。可以封裝一個ES基本方法工具類。

<?php


namespace app\common\helper;

use Elasticsearch\ClientBuilder as ESClientBuilder;
class Elasticsearch
{
    protected $config = [
        "127.0.0.1:9200",
    ];
    protected $es;

    /*
     * @Title:  構造函數
     * @Author: 北橋蘇
     * @Times:  2020/5/14 17:35
     * */
    public function __construct()
    {
        $this->es = $this->init();
    }

    /*
     * @Title ElasticSearch服務初始化
     * @Return object ES實例
     * */
    private function init()
    {
        // 從配置文件讀取 Elasticsearch 服務器列表
        $builder = ESClientBuilder::create()->setHosts($this->config);

        // 應用調試模式
//        if (env('APP_DEBUG')) {
//            // 配置日誌,Elasticsearch 的請求和返回數據將打印到日誌文件
//            $builder->setLogger(app('log')->error());
//        }
        return $builder->build();
    }

    /*
     * @Title: 獲取ES服務信息
     * @Return: ES服務的基本信息
     * */
    public function info()
    {
        return $this->es->info();
    }

    /*
     * @Titles: 創建索引
     * @Param: string $name 索引名
     * @Param: array $mappings 字段映射
     * @Return: boolean 創建結果
     * */
    public function index($name, $mappings=[])
    {
        $params = [
            'index' => $name,
            'body' => [
                'settings' => [
                    'number_of_shards' => 1,                // 索引分片(一個索引可以分多個分片在不同的節點上)
                    'number_of_replicas' => 0,              // 索引分片副本
                ]
            ]
        ];
        // 創建索引設置字段以及字段類型分詞器等
        if($mappings) {
            $params['body']['mappings']['properties'] = $mappings;
        }

        return $this->es->indices()->create($params);
    }

    /*
     * @Title: 獲取索引配置,可以獲取單個索引,不傳參數是節點上的所有索引
     * @Param: array $name 索引名數組
     * @Return:array 索引的名稱分片副本數等數據
     * */
    public function getIndexConfig($name=[])
    {
        $params = [];
        if(!empty($name)) {
            $params = [
                "index" => $name
            ];
        }
        return $this->es->indices()->getSettings($params);
    }

    /*
     * @Title: 創建/更改映射(創建索引時也可以指定映射-其實就是存儲數據的字段類型)
     * @Param: string $name 索引名
     * @Param: array $data 字段設置
     * @Return boolean 修改結果
     * */
    public function putMapping($name, $data)
    {
        $params = [
            'index' => $name,
            'body' => [
                '_source' => [
                    'enabled' => true
                ],
                'properties' => $data
            ]
        ];

        return $this->es->indices()->putMapping($params);
    }

    /*
     * @Tile: 獲取索引設置的字段映射
     * @Param: string $name
     * @Return: array
     * */
    public function getMapping($name)
    {
        $params = ['index' => $name];
        return $this->es->indices()->getMapping($params);
    }

    /**
     * @Notes: 獲取索引中的文檔
     * @param $name 索引名稱
     * @param $id 編號
     * @Return: 文檔數據
     */
    public function getIndex($name, $id)
    {
        return $this->es->get(['index' => $name, 'id' => $id]);
    }

    /**
     * @Notes: 刪除索引
     * @param $name 索引名稱
     * @return array
     */
    public function deleteIndex($name)
    {
        return $this->es->indices()->delete(['index' => $name]);
    }


    /***********************文檔部分********************************************************/

    /*
     * @Tiele: 在某索引中創建一個文檔(如果索引未設置映射,添加文檔會自動創建字段類型等)
     * @Param: string $name 索引名
     * @Param: array $data 文檔數據
     * @Return array
     * */
    public function createDoc($name, $data)
    {
        $params = [
            'index' => $name,
            'id'    => $data['id'],
            'body'  => $data
        ];
        $response = $this->es->index($params);
        return $response;
    }

    /**
     * @Notes: 更新文檔
     * @param $index 索引
     * @param $id 文檔 id
     * @param $data 字段
     * @author: 北橋蘇
     * @Time: 2020/4/21 9:51
     */
    public function updateDoc($index, $id, array $data)
    {
        $params = [
            'index' => $index,
            'id' => $id,
            //'type' => '_doc',
            'body' => [
                'doc' => $data
            ]
        ];

        $this->es->update($params);
    }

    // 刪除一個文檔
    /*
     * @Tile: 刪除一個文檔
     * @Param: array $param 刪除索引名,ID
     * @Return array
     * */
    public function delDoc($param)
    {
        $this->es->delete($param);
    }

    // 批量創建文檔
    public function bulk($params)
    {
        return $this->es->bulk($params);
    }

    // 搜索
    public function queryData($index,$kye, $from = 0, $size = 10)
    {
        $params = [
            'index' => $index,
            'body'  => [
                'from'  => $from,
                'size'  => $size,
                'query' => [
                    'match' => [
                        'class_name'  => '版',
                    ],
                ]
            ],
        ];

        return $this->es->search($params);
    }

    /*
     * @Tile: 多功能搜索
     * @Param string $index 索引名稱
     * @Param array $where 條件數組
     * @Param array $sort 排序數組
     * @Param int $from 開始查詢的位置
     * @Param int $size 一共查詢的條數
     * @Return array 查詢結果
     * */
    public function query($index,$where,$sort='',$from=0,$size=1500)
    {
        $params = [
            "index" => $index,
            "body" => [
                "from" => $from,
                "size" => $size,
                "query" => $where
            ]
        ];

        if($sort) {
            $params['body']['sort'] = $sort;
        }

        return $this->es->search($params);
    }



}

4. 業務使用代碼塊。

以下app('es')在provider.php註冊到容器中

<?php
namespace app\admin\controller;

use app\BaseController;

class Test extends BaseController
{
	
    public function index()
    {
    	var_dump(2323);die;
    }

    // 測試ElasticSearch
    public function esTest()
    {
        $res = app('es')->info();
        dump($res);die;
    }

    // 創建一個索引
    public function createIndex()
    {
        $res = app('es')->index("goods");
        var_dump($res);die;
    }

    // 創建文檔
    public function createDoc()
    {

        $goodsList = \app\common\model\Goods::page(0,1500)->select()->toArray();

        foreach ($goodsList as $val) {
            $res = app('es')->createDoc('goods',$val);
        }

        dump($res);die;

        $goods = \app\common\model\Goods::find(1);
        var_dump($goods);die;
    }

    // 搜索
    public function queryDoc()
    {
        // 獲取所有
        $where = [
            'match_all' => new \stdClass(),                 // 空對象,PHP5.3  或者     (object)[]    //也是給出空數組的方式
        ];

        // 單字段完全匹配
//        $where = [
//            'match_phrase' => [
//                'class_name' => '班',
//            ]
//        ];

        // 單字段模糊匹配
//        $where = [
//            'match' => [
//                'goods_name' => '蘋果'
//            ]
//        ];

        // 多字段匹配
//        $where = [
//            'multi_match' => [
//                'query' => '蘋果',
//                'fields' => ['goods_name'],     //這裏添加多字段
//                'type' => 'best_fields'         //most_fields多字段匹配更高,best_fields完全匹配佔比更高
//            ]
//        ];

        // 聯合搜索must聯合必須,should聯合可以,must_not聯合不必須, 聯合搜索類似SQL的多字段查詢(就使用must)
//        $where = [
//            'bool' => [
//                'must' => [
//                    'match' => [
//                        'goods_name' => '蘋果'
//                    ]
//                ],
//                'should' => [
//                    'match' => [
//                        'level' => '3'
//                    ]
//                ]
//            ]
//        ];

        // 排序非必填
        $sort = [
            "sort" => "asc"
        ];

        $res = app('es')->query('goods',$where,$sort,0,3000);
        dump($res);die;

//        $res = app('es')->queryData('class','class_name',0,20);
//        dump($res);die;
    }

    // 獲取索引(索引中的某個文檔)
    public function getDoc()
    {
        $res = app('es')->getIndex('goods',1500);
        dump($res);die;
    }

    // 獲取的索引配置
    public function getIndexConfig()
    {
        $res = app('es')->getIndexConfig('class,user,goods');
        dump($res);die;
    }


    
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章