yii2 composer安装Es和yii2的Elasticsearch操作类

1.在yii2的项目目录中的composer.json的require中加入:

"elasticsearch/elasticsearch": "~6.0" 

image.png

2.然后执行composer update

3.config/params.php中添加

'elasticsearch' => [
    '192.168.163.128:9200'
],
image.png

4.新建一个EsService.php

<?php
namespace frontend\service;

use yii;
use Elasticsearch\ClientBuilder;
class EsService
{

    public  $index;
    public  $type;
    public  $client;
    public   function __construct($index, $type){
        $this->index = $index;
        $this->type = $type;
        $hosts = yii::$app->params['elasticsearch'];
        $this->client = ClientBuilder::create()->setHosts($hosts)->build();
    }

    /**
     * 创建索引
     */
    public function createIndex(){
        $params = [
            'index' => $this->index,
            'body' => [
                'settings' => [
                    'number_of_shards' => 2,    //分片数
                    'number_of_replicas' => 0   //副本数
                ]
            ]
        ];

        $response = $this->client->indices()->create($params);
        return $response;
    }

    /**
     * 删除索引
     */
    public function deleteIndex(){
        $params = ['index' => $this->index];
        $response = $this->client->indices()->delete($params);
        return $response;

    }

    /**
     * 添加文档
     */
    public function createDocument($id, $data){
        $params = [
            'index' => $this->index,
            'type' => $this->type,
            'id' => $id,
            'body' => $data
        ];

        $response = $this->client->index($params);
        return $response;
    }

    /**
     * 批量添加文档
     */
    public function batchCreateDoc($data){
        foreach ($data as  $value){
            $params = [
                'index' => $this->index,
                'type' => $this->type,
                'body' => $value
            ];
            $responses = $this->client->index($params);
        }

        return $responses;

    }

    /**
     * 查询文档
     */
    public function getDocument($id){
        $params = [
            'index' => $this->index,
            'type' => $this->type,
            'id' => $id,
            'client' => [ 'ignore' => [400, 404] ],
        ];

        $response = $this->client->get($params);
        return $response;
    }

    /**
     * 搜索文档
     */
    public function searchDocument($param){

        $tmp = [];
        foreach ($param as $key => $value) {
            $tmp[$key] = [
                "query"=> $value,
                "minimum_should_match"=> "50%",
            ];
        }

        $params = [
            'index' => $this->index,
            'type' => $this->type,
            'body' => [
                'query' => [
                    'match' =>  $tmp
                ]
            ]
        ];

        $response = $this->client->search($params);
        return $response;
    }

    /**
     * @param $param
     * @desc 关键词搜索文档
     */
    public function keywordSearch($keyword){
        $params = [
            'index' => $this->index,
            'type' => $this->type,
            'body' => [
                'query' => [
                    'multi_match' =>  [
                        'query'=>$keyword,
                    ],
                ]
            ]
        ];
        $response = $this->client->search($params);
        return $response;
    }
}

//在控制器里面操作
<?php
namespace frontend\controllers;

use yii\web\Controller;
use frontend\service\EsService;

/**
 * Es controller
 */
class EsController extends Controller
{
    /**
     * Displays homepage.
     *
     * @return mixed
     */
    public function actionIndex(){

        $EsService = new EsService('mall', 'product');
        $result = $EsService->createIndex();
        $data = [
            [
                'name' => 'mongodb',
                'nike_name' => 'NoSql',
                'age' => '10',
                'about' => 'this is mongodb and NoSql',
                'interests' => 'sports music 量子专家',
            ],
            [
                'name' => 'MySql',
                'nike_name' => 'Sql',
                'age' => '22',
                'about' => 'this is MySql and Sql',
                'interests' => '作者是茂名量子专家',
            ],
            [
                'name' => '弗南永州锤子帮',
                'nike_name' => '强小妮',
                'age' => '32',
                'about' => 'this is MySql and Sql',
                'interests' => '强小妮是锤子帮大哥',
            ],
            [
                'name' => '弗北热干面',
                'nike_name' => '热干面',
                'age' => '32',
                'about' => 'this is MySql and Sql',
                'interests' => '包子加热干面法力无边',
            ],
        ];

        //$result = $EsService->createDocument('', $ins);
        //$result = $EsService->batchCreateDoc($data);
        //$result = $EsService->keywordSearch('包');

        echo "<pre>";
        print_r($result);
        echo "</pre>";
        exit;
        //return $this->render('index');
    }

}

5.yii2还提供了一个操作Es的类 yii2-elasticsearch
首先引入 yii2-elasticsearch 操作类
composer.json的require中添加:

"yiisoft/yii2-elasticsearch": "~2.0.0"
image.png

然后执行:composer update
config/web.php的components中添加:

'components' => [
     ...
        'elasticsearch' => [
            'class' => 'yii\elasticsearch\Connection',
            'nodes' => [
                ['http_address' => '127.0.0.1:9200']
            ],
            'autodetectCluster' => false
        ],
    ],

6.参考某个大佬写了一个公共的类 common\Elasticsearch.php

<?php

namespace app\common;

use yii;
use yii\elasticsearch\ActiveRecord;
use yii\data\Pagination;


class Elasticsearch extends ActiveRecord{

    /**
     * 使用的数据库连接
     */
    public static function getDb(){
        return \Yii::$app->get('elasticsearch');
    }

    /**
     * [mapConfig mapping配置]
     * 返回这个模型的映射
     *
     * properties配置说明
     *
     * 属性
     * 数据类型 string(已废弃) byte short integer long float double boolean date binary(二进制) ip(以字符换格式存IPv4地址) token_count(存储索引的字数信息)
     *
     * 公共属性
     * index            -> analyzed(被分析,编入索引,产生的token能被搜索到) not_analyzed(不被分析,使用原始值编入索引,在索引中作为单个词) no(不编入索引,无法搜索该字段)
     * store            -> 指定是否将字段的原始值写入索引,默认值是no,字段值被分析,能够被搜索,但是,字段值不会存储,这意味着,该字段能够被查询,但是不会存储字段的原始值  通常情况字段值已经是_source字段的一部分,可以只检索需要的
     * boost            -> 字段级别的助推,默认值是1,定义了字段在文档中的重要性/权重
     * include_in_all   -> 该属性指定当前字段是否包括在_all字段中,默认值是true
     * copy_to          -> 该属性指定一个字段名称,ElasticSearch引擎将当前字段的值复制到该属性指定的字段中
     * doc_values       -> 文档值是存储在硬盘上的索引时(indexing time)数据结构,对于not_analyzed字段,默认值是true,analyzed string字段不支持文档值;如果您确定不需要对字段进行排序或聚合,或者从脚本访问字段值,则可以禁用doc值以节省磁盘空间
     * fielddata        -> 字段数据是存储在内存中的查询时(querying time)数据结构,只支持analyzed string字段
     * null_value       -> 该属性指定一个值,当字段的值为NULL时,该字段使用null_value代替NULL值;在ElasticSearch中,NULL 值不能被索引和搜索,当一个字段设置为NULL值,ElasticSearch引擎认为该字段没有任何值,使用该属性为NULL字段设置一个指定的值,使该字段能够被索引和搜索。
     *
     * 常用其他属性
     * analyzer         -> 该属性定义用于建立索引和搜索的分析器名称,默认值是全局定义的分析器名称,该属性可以引用在配置结点(settings)中自定义的分析器
     * search_analyzer  -> 该属性定义的分析器,用于处理发送到特定字段的查询字符串
     * ignore_above     -> 该属性指定一个整数值,当字符串字段(analyzed string field)的字节数量大于该数值之后,超过长度的部分字符数据将不能被analyzer处理,不能被编入索引;对于 not analyzed string字段,超过长度的部分字符将被忽略,不会被编入索引。默认值是0,禁用该属性;
     * position_increment_gap   -> 该属性指定在相同词的位置上增加的gap,默认值是100;
     * index_options    -> 索引选项控制添加到倒排索引(Inverted Index)的信息
     * [
     *      docs        -> 只索引文档编号(Doc Number)
     *      freqs       -> 索引文档编号和词频率(term frequency)
     *      positions   -> 索引文档编号,词频率和词位置(序号)
     *      offsets     -> 索引文档编号,词频率,词偏移量(开始和结束位置)和词位置(序号)
     *      默认情况下,被分析的字符串(analyzed string)字段使用positions,其他字段使用docs
     * ]
     *
     * 数值类型的其他属性
     * precision_step   -> 该属性指定为数值字段每个值生成的term数量,值越低,产生的term数量越高,范围查询越快,索引越大,默认值是4
     * ignore_malformed -> 忽略格式错误的数值,默认值是false,不忽略错误格式,对整个文档不处理,并且抛出异常
     * coerce           -> 默认值是true,尝试将字符串转换为数值,如果字段类型是整数,那么将小数取整
     *
     * 日期类型的其他属性
     * format           -> 指定日期的格式,例如:“yyyy-MM-dd hh:mm:ss”
     * ignore_malformed -> 忽略错误格式,默认值是false,不忽略错误格式
     *
     * fields           -> 在fields属性中定义一个或多个字段,该字段的值和当前字段值相同,可以设置一个字段用于搜索,一个字段用于排序等
     *      "properties":
     *      {
     *          "id":{
     *              "type":"long",
     *              "fields":{
     *                  "id2":{"type":"long","index":"not_analyzed"}
     *              }
     *          }
     *      }
     * _all             -> 表示存储其他字段的数据以便搜索,默认情况下,_all字段是启用的,包含了索引中所有字段的数据,然而这一字段使索引变大,如果不需要,请禁用该字段,或排除某些字段
     * "_all":{"enabled":false}
     * _source          -> 表示在生成索引的过程中,存储发送到ElasticSearch的原始JSON文档,默认情况下,该字段会被启用,因为索引的局部更新功能依赖该字段。
     * _routing         -> 路由字段 公式:shard_num = hash(_routing) % num_primary_shards  使用的默认字段是_id,设置required为true,表示路由字段在进行索引的CRUD操作时必需显式赋值。
     *
     * 不可配置的元字段
     * _index           -> 返回文档所属的索引
     * _uid             -> 返回文档的type和id
     * _type            -> 返回文档类型(type)
     * _id              -> 返回文档的ID;
     * _size            -> 返回文档的_source字段中函数的字节数量;
     * _field_names     -> 返回文档中不包含null值的字段名称;
     *
     */
    public static function mapConfig(){
        return [
            // 根据type来获取model->attributes
            'member' => [
                'properties' => [
                    'user_id'    => ['type' => 'integer'],
                    'user_name'     => ['type' => 'text'],
                    'email'         => ['type' => 'keyword'],
                    'qq'            => ['type' => 'keyword'],
                    'edit_time'     => ['type' => 'date', 'format' => 'yyyy-MM-dd HH:mm:ss||yyyy-MM-dd']
                ]
            ],
            // 根据type来获取model->attributes
            'member2' => [
                'properties' => [
                     'user_id'      => ['type' => 'integer'],
                     'user_name'        => ['type' => 'text'],                'edit_time'        => ['type' => 'date', 'format' => 'yyyy-MM-dd HH:mm:ss||yyyy-MM-dd']
                ]
            ]
        ];
    }

    /**
     * 获取群集中所有索引映射
     */
    public static function getMapping(){
        $db = self::getDb();
        $command = $db->createCommand();
        return $command->getMapping();
    }

    /**
     * 根据索引主键获取相关信息
     * 支持单个或多个
     */
    public function getByKey($id){
        if(is_array($id)){
            $res = self::mget($id);
        }else{
            $res = self::get($id);
        }
        return $res;
    }

    /**
     * 单个
     * 默认返回object对象 返回数组 添加->asArray()
     */
    public function getOne($query = []){
        $es_query = self::find();

        // 匹配查询
        if($query && !empty($query)){
            $es_query->query($query);
        }
        // 分组
        $res = $es_query->one();

        return $res;
    }

    /**
     * 列表
     * 默认返回object对象 返回数组 添加->asArray()
     * search 与 all 区别在于 all是在search基础上处理再拿出结果
     */
    public function getList($query = [], $order = [], $offset = 0, $limit = 20){
        $es_query = self::find();

        // 匹配查询
        if($query && !empty($query)){
            $es_query->query($query);
        }
        // 排序
        if($order && !empty($order)){
            $es_query->orderby($order);
        }
        // 分组
        $res = $es_query->offset($offset)->limit($limit)->asArray()->all();
        $list = array_column($res, '_source');

        return $list;
    }

    /**
     * 分页列表
     * 默认返回object对象 返回数组 添加->asArray()
     * search 与 all 区别在于 all是在search基础上处理再拿出结果
     */
    public function getPageList($query = [], $order = []){
        $es_query = self::find();

        // 匹配查询
        if($query && !empty($query)){
            $es_query->query($query);
        }
        // 排序
        if($order && !empty($order)){
            $es_query->orderby($order);
        }

        // 分组
        $count = $es_query->search();
        // 分页
        $pages = new Pagination(['totalCount' => $count['hits']['total']]);
        // 分组
        $res = $es_query->offset($pages->offset)->limit($pages->limit)->asArray()->all();
        $list = array_column($res, '_source');

        return ['list' => $list, 'pages' => $pages];
    }

    /**
     * 列表
     * 默认返回object对象 返回数组 添加->asArray()
     * search 与 all 区别在于 all是在search基础上处理再拿出结果
     */
    public function getFilterList($filter_arr = [], $query = [], $order = [], $offset = 0, $limit = 20){
        $es_query = self::find();

        // 过滤器
        if($filter_arr && !empty($filter_arr)){
            $es_query->postFilter($filter_arr);
        }
        // 匹配查询
        if($query && !empty($query)){
            $es_query->query($query);
        }
        // 排序
        if($order && !empty($order)){
            $es_query->orderby($order);
        }
        // 分组
        $res = $es_query->offset($offset)->limit($limit)->all();

        return $res;
    }

    /**
     * 获取高亮列表
     * 默认返回object对象 返回数组 添加->asArray()
     * search 与 all 区别在于 all是在search基础上处理再拿出结果
     * 循环使用$v->highlight获取高亮列表
     */
    public function getHighLightList($highlight_arr = [], $query = [], $offset = 0, $limit = 20){
        $es_query = self::find();

        // 高亮
        if($highlight_arr && !empty($highlight_arr)){
            $es_query->highlight($highlight_arr);
        }
        // 匹配查询
        if($query && !empty($query)){
            $es_query->query($query);
        }
        // 分组
        $res = $es_query->offset($offset)->limit($limit)->all();

        return $res;
    }

    /**
     * 获取聚合列表
     * 默认返回object对象 返回数组 添加->asArray()
     * search 与 all 区别在于 all是在search基础上处理再拿出结果
     */
    public function getAggList($aggregate_name, $addAggregate_arr = [], $query = [], $offset = 0, $limit = 20){
        $es_query = self::find();

        // 聚合
        if($addAggregate_arr && !empty($addAggregate_arr)){
            $es_query->addAggregate($aggregate_name, $addAggregate_arr);
        }
        // 匹配查询
        if($query && !empty($query)){
            $es_query->query($query);
        }
        // 分组
        $res = $es_query->offset($offset)->limit($limit)->search();

        return ['list' => $res['hits']['hits'], $aggregate_name => $res['aggregations'][$aggregate_name]];
    }
}

7.新建一个model model/EsModel

<?php

namespace app\models;

use app\common\Elasticsearch;

class EsModel extends Elasticsearch{

    // _index _type _id 定义
    // https://www.elastic.co/guide/cn/elasticsearch/guide/current/_Document_Metadata.html
    public static $index_db_name = '';
    public static $type_tb_name = '';

    // 索引名相当于库名
    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]['properties']);
    }

    // 映射
    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();
        $command->createIndex(static::index(), [
            'settings' => [
                'index' => [
                    // 指定新索引的时候不会立即生效,1s之后刷新生效  默认是1s
                    'refresh_interval'      => '1s',
                    // 索引分片个数  默认5片
                    'number_of_shards'      => 5,
                    // 每个分片副本个数  默认1个
                    'number_of_replicas'    => 1
                ]
            ],
            '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;
    }
}

8.controller调用

$model = new \app\models\EsModel();
$model::$index_db_name = 'index_name';
$model::$type_tb_name = 'type_name';
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,245评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,749评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,960评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,575评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,668评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,670评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,664评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,422评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,864评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,178评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,340评论 1 344
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,015评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,646评论 3 323
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,265评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,494评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,261评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,206评论 2 352

推荐阅读更多精彩内容