Yii ElasticSearch實戰以及優化點

前面分享了Xunserch 和 ElasticSearch的安裝,本來是要用Xunserch 的因爲官網項目部需要太強大的插件,但是安裝不給力,各種錯誤,最後用ElasticSearch。。。其實ElasticSearch並不太難,,, 不說了 直接上代碼

先說下我的設計模式:

後臺添加數據的同時王es裏面插入索引, es6.0版本後一個索引只能插入一個type,所以es每個索引對應數據表, es存放前端要顯示的字段即可,沒必要全部字段都存在裏面。 插入或者更新是調用一個公共方法。。。 搜索調用搜索接口

優化點 如果你的集羣只有一個節點,這不要設置副本數,不然健康值會變成yellow,設置爲0即可
在這裏插入圖片描述

阿里雲的dts可以自動把mysql數據庫的數據添加和更新索引到ES 但是需要付費。。。這個不做過多的說明

1.在 項目配置文件 web.php (高級模板main.php) 中的components中添加配置:

		//現使用 es
        'elasticsearch' => [
            'class' => 'yii\elasticsearch\Connection',
            //默認爲true   如果單節點  設爲false 不然會連接錯誤
            'autodetectCluster' => false,
            'nodes' => [
                ['http_address' => '127.0.0.1:9200'],
            ],
        ],

2.搜索接口

/**
     *  auth - rk
     *  獲取全站搜索數據
     *  支持分詞搜索和 多條件+多字段搜索
     */
 public function actionSearch(){

        if(Yii::$app->request->isPost){
            $searchData = Yii::$app->request->post('name','');
            $pageSize = Yii::$app->request->post('pageSize',5);
            $page = Yii::$app->request->post('page',1);
            $cate = Yii::$app->request->post('cate','');
            $offset = ($page-1)*$pageSize;
            $searchData =trim($searchData);
//            $searchData = str_replace(" ","",$searchData);
            if(empty($searchData)){
                $data['res_code']='200';
                $data['res_msg']='success';
                $data['res_data']=[];
                return $data;
            }
            //ElasticSearch
            $es = new ElasticSearch();
            //高亮數據
            $highlight = [
             #左標籤,配合前端.highlight這個類來實現高亮
            "pre_tags"=>['<span class="highlight">'],
            "post_tags"=>['</span>'],
            #在原生api中寫的是{}表示空對象,因此使用php的stdClass來表示空對象
            "fields"=>[
                "title"=>new \stdClass(),
                "subtitle"=>new \stdClass(),
                 ]
            ];

            $cat = ['product','cases','news','about_us'];
            if(!empty($cate)){
                if(in_array($cate,$cat)){
                    $cat = [$cate];
                }
            }
            $resArr = [];
            foreach ($cat as $val){
                $es::$index_db_name = $val;
                $es::$type_tb_name = $val;
                $where = ["is_delete"=>0,"status"=>1];
                if($val == 'about_us'){
                    $where = ["is_delete"=>0];
                }
                $must[] = [
                    "multi_match" => [//分詞多字段搜索
                        'query' => $searchData,//['fuzzy'=>$searchData]
                        'fields' => ['title','subtitle'],//搜索的字段
                    ],
                ];

                $query = [
                    'bool'=>[
                        'must' => $must,
                        'should' => [
                            ['match'=> ['is_delete' => 0]]
                        ],
                        'minimum_should_match' => 1,
                    ]
                ];

                $sort = ['sort' => ['order' => 'desc']];
                //->query($query)->orderBy($sort)->asArray()->all()   'terms'=> $where

                $esQuery = $es::find()->query($query);//->offset($offset)->limit($pageSize)->orderBy($sort)->highlight($highlight)->asArray()->all()
                $count = $es::find()->query($query)->search();
                $res = $esQuery->offset($offset)->limit($pageSize)->orderBy($sort)->highlight($highlight)->asArray()->all();
                $resArr[$val]['count'] = $count['hits']['total']['value'] ??0;
                $resArr[$val]['pageSize'] = $pageSize;
                $resArr[$val]['data'] = $res??[];
            }
            //搜索數據存儲到後臺
            $searchModel  = new Search();
            //先判斷是否存在搜索數據  存在更新次數
            $querySql = $searchModel::find()->where(['like','content',$searchData]);
            $count = $querySql->count();
            if($count !==0){
                $queryData = $querySql->asArray()->all();
                foreach ($queryData as $key => &$val){
                    $num = $val['num'] +=1;
                    $searchModel->updateAll(['num'=>$num],['id'=>$val['id']]);
                }
            }else{
                $searchModel->ip = Yii::$app->request->getUserIP();
                $searchModel->content  = $searchData;
                $searchModel->save();
            }

             //xunsearch
//            $db = \Yii::$app->xunsearch->getDatabase('mmc_uav',true);
//            $xs = $db->xs;
//            $search = $db->getSearch();
//            $index = $db->getIndex();
//            var_dump($search);die;
            $data['res_code']='200';
            $data['res_msg']='success';
            $data['host'] = Yii::$app->request->hostInfo;
            $data['res_data']=$resArr;
            return $data;
        }
        $data['res_code']='400';
        $data['res_msg']='請求方式錯誤';
        return $data;
    }

3.ElasticSearch模型

<?php
/**
 * Created by PhpStorm.
 * User: renkun
 * Date: 2020/4/9
 * Time: 10:09
 */

namespace app\models;


use yii\elasticsearch\ActiveRecord;

class ElasticSearch  extends ActiveRecord
{

    public static $index_db_name = '';
    public static $type_tb_name = '';
//    # 定義db鏈接
    public static function getDb()
    {
        return \Yii::$app->get('elasticsearch');
    }

    // 索引名相當於庫名
    public static function index(){
        return self::$index_db_name;
    }

    // 類別名相當於表名
     public static function type(){
        return self::$type_tb_name;
     }

    # 屬性
    public function attributes()
    {
        $mapConfig = self::mapConfig();
        return array_keys($mapConfig[self::$type_tb_name]);
    }
    # mapping配置
    public static function mapConfig(){
        return [
            'news' => [
                'id'  => ['type' => 'int'],
                'userId'  => ['type' => 'int'],
                'categoryId'  => ['type' => 'int'],
                'title'  => ['type' => 'string'],//默認分詞
                'subtitle'  => ['type' => 'string'],
                'content'      => ['type' => 'string'],
                'recommend'  => ['type' => 'int'],
                'publishTime'  => ['type' => 'int'],
                'auth'  => ['type' => 'string'],
                'cover_img'  => ['type' => 'string'],
                'origin'  => ['type' => 'string'],
                'origin_url'  => ['type' => 'string'],
                'status'  => ['type' => 'int'],
                'sort'  => ['type' => 'int'],
                'is_delete'  => ['type' => 'int'],
            ],
            'about_us' => [
                'id'  => ['type' => 'int'],
                'content'      => ['type' => 'string'],
                'addtime'  => ['type' => 'int'],
                'title'  => ['type' => 'string'],//默認分詞
                'description'  => ['type' => 'string'],
                'type'  => ['type' => 'int'],
                'sort'  => ['type' => 'int'],
                'is_delete'  => ['type' => 'int'],
            ],
            'cases' => [
                'id'  => ['type' => 'int'],
                'userId'  => ['type' => 'int'],
                'categoryId'  => ['type' => 'int'],
                'title'  => ['type' => 'string'],//默認分詞
                'subtitle'  => ['type' => 'string'],
                'auth'  => ['type' => 'string'],
                'device'  => ['type' => 'string'],
                'content'      => ['type' => 'string'],
                'recommend'  => ['type' => 'int'],
                'status'  => ['type' => 'int'],
                'cover_img'  => ['type' => 'string'],
                'create_at'  => ['type' => 'int'],
                'sort'  => ['type' => 'int'],
                'is_delete'  => ['type' => 'int'],
            ],
            'product' => [
                'id' => ['type' => 'int'],
                'product_type_id'  => ['type' => 'int'],
                'title'  => ['type' => 'string'],//默認分詞
                'description'  => ['type' => 'string'],
                'cover_img_url'  => ['type' => 'string'],
                'product_introduction'  => ['type' => 'string'],
                'extension'  => ['type' => 'string'],
                'images'  => ['type' => 'long'],
                'recommend'  => ['type' => 'string'],
                'addtime'  => ['type' => 'int'],
                'is_recommend'  => ['type' => 'int'],
                'user_id'      => ['type' => 'int'],
                'status'  => ['type' => 'int'],
                'sort'  => ['type' => 'int'],
                'is_delete'  => ['type' => 'int'],
            ]
        ];
    }

     // 映射
     public static function mapping(){
        $mapConfig = self::mapConfig();
        return [
            static::type() => $mapConfig[self::$type_tb_name]
        ];
    }
    // 更新映射
     public static function updateMapping(){
         $db = self::getDb();
         $command = $db->createCommand();
         if(!$command->indexExists(self::index())){
             $command->createIndex(self::index());
         }
         $res = $command->setMapping(self::index(), self::type(), self::mapping());
        return $res;
     }
     // 創建索引
     public static function createIndex(){
         $db = static::getDb();
         $command = $db->createCommand();
         if(!$command->indexExists(self::index())){
             $command->createIndex(static::index(), [
                 'settings' => [
                     'index' => [
                         // 指定新索引的時候不會立即生效,1s之後刷新生效  默認是1s
                         'refresh_interval'      => '1s',
                         // 索引分片個數  默認5片
                         'number_of_shards'      => 5,
                         // 每個分片副本個數  默認1個       單節點集羣如果副本不設置爲0   集羣健康值會變成黃色  因爲單點集羣無法存放副本
                         'number_of_replicas'    => 0,
                     ]
                 ],
//             'mappings' => static::mapping(),
                 //'warmers' => [ /* ... */ ],
                 //'aliases' => [ /* ... */ ],
                 //'creation_date' => '...'
             ]);
         }
     }

    // 刪除索引   (謹慎使用)
     public static function deleteIndex(){
         $db = static::getDb();
         $command = $db->createCommand();
         $res = $command->deleteIndex(static::index(), static::type());
         return $res;
     }


    //創建es索引 並賦值
    public static function es($index,$data=[],$action = 0){   //0 添加  1 創建
        $es = new self();
        $es::$index_db_name = $index;
        $es::$type_tb_name = $index;
        $attr = $es::mapping();

        $indexKey = array_keys($attr[$index]);
//        var_dump($data);die;
        $tmpData = $data;
        //只獲取主要字段
        if(!empty($data)){
            if($action == 1){
                $es = $es::findOne($data['id']); //更新
                if(empty($es)){
                    self::es($index,$tmpData,0);
                }
            }else{
                $es::createIndex();
                $es->primaryKey = $data['id'];
            }
            foreach ($data as $key => $val){
//                var_dump($indexKey);
//                var_dump($data);die;
                if(in_array($key,$indexKey)){
                    $es->$key = $val;
                }
            }
            if ($es->save() == false) {
                die("es數據出現錯誤,請聯繫管理員");
            }

        }
    }

}

  1. 手動添加索引和更新
		 if( $model->save()){
	              //同步到es
	              ElasticSearch::es('product',$model->attributes);
	              return $this->redirect(['view', 'id' => $model->id]);
	      }

//最終效果
在這裏插入圖片描述
在這裏插入圖片描述

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