袁来如此的工作笔记
袁来如此的工作笔记
竹杖芒鞋轻胜马,谁怕? 一蓑烟雨任平生。

PHP类使用ElasticSearch-php

浏览量:93

使用步骤:
 
一、composer下载elasticsearch-php扩展   
1、composer require elasticsearch/elasticsearch --ignore-platform-reqs
2、扩展地址:https://github.com/elastic/elasticsearch-php/
 
二、use App\Handlers\ElasticHandler;加载该类
三、设置对应hosts,调用对应方法
代码:php通用扩展类
<?php
/**
 *
 * +------------------------------------------------------------+
 * @category Elastic
 * +------------------------------------------------------------+
 * Elastic类
 * +------------------------------------------------------------+
 *
 * @version 1.0
 *
 * Created at : 2021-4-15 15:36:43
 *
 * 使用步骤:
 * 一、composer下载elasticsearch-php扩展 https://github.com/elastic/elasticsearch-php/
 * 二、use App\Handlers\ElasticHandler;加载该类
 * 三、设置对应hosts,调用对应方法
 *
 */
namespace App\Handlers;
 
use Elasticsearch\ClientBuilder;
use Faker\Generator as Faker; //伪造数据
 
class ElasticHandler
{
    /**
     * 配置
     * @var array
     */
    private $hosts = [
        // 方案一
        // 'elastic:sChyNxdeDFL4BQUZFzgM@27.18.10.26:900', //账号密码的写法
        '11.214.114.422:17001', //集群那台
        // '192.168.1.1:9200',         // IP + 端口
        // '192.168.1.2',              // 仅 IP
        // 'mydomain.server.com:9201', // 域名 + 端口
        // 'mydomain2.server.com',     // 仅域名
        // 'https://localhost',        // 对 localhost 使用 SSL
        // 'https://192.168.1.3:9200'  // 对 IP + 端口 使用 SSL
 
        // 方案二
        // // 等价于内联主机配置中使用 "https://username:password!#$?*abc@foo.com:9200/"
        // [
        //     'host' => 'foo.com',
        //     'port' => '9200', //默认9200
        //     'scheme' => 'https', //默认http
        //     'user' => 'username',
        //     'pass' => 'password!#$?*abc'
        // ],
    ];
 
    /**
     * es连接对象
     * @access private
     * @var object
     */
    private $EsClient = null;
 
    /**
     * 伪造类
     */
    private $faker = null;
 
    /**
     * 索引 类似库
     * @access private
     * @var string
     */
    private $index = 'api_log';
 
    /**
     * 类型 类似表  *es9后面会被弃用 弃用原因:同一索引下,不同type的数据存储其他type的field大量空值,造成资源浪费
     * @access private
     * @var string
     */
    private $type = 'table';
 
    /**
     * 从哪一条开始
     * @access private
     * @var int
     */
    private $from = 0;
 
    /**
     * 条数
     * @access private
     * @var int //默认10 最高10000
     */
    private $size = 0;
 
    /**
     * 排序
     * @access private
     * @var array
     */
    private $sort = [];
 
    /**
     * 字段
     * @access private
     * @var string
     */
    private $field;
 
    /**
     * 条件
     * @access private
     * @var string
     */
    private $query;
 
    /**
     * 配置重试次数
     * @access private
     * @var string
     */
    private $retries = 1;
 
    /**
     * 数据库错误信息
     * @access private
     * @var string
     */
    private $error = '';
 
    /**
     * 错误的输出方式 1直接返回 2报错异常
     * @access private
     * @var int
     */
    private $errorType = 1;
 
    /**
     * 忽略错误
     * @access private
     * @var array
     */
    private $ignore = [400, 404, 503];
 
    /**
     * 超时时间
     * @access private
     * @var int
     */
    private $timeout = 10;
 
    /**
     * 连接超时时间
     * @access private
     * @var int
     */
    private $connect_timeout = 10;
 
 
    public function __construct($index = 'database')
    {
        /**
         * 实例化 ES 客户端
         */
        $this->EsClient = ClientBuilder::create() // 实例化 ClientBuilder
                            ->setHosts($this->hosts) // 设置主机信息
                            ->setRetries($this->retries) // 配置重试次数
                            ->build(); // 构建客户端对象
 
        // 默认索引
        $this->index = config('other.elastic_environment') . '_' . $this->index; //辨别对应库,以及正式测试环境
 
        /**
         * 这是一个数据生成库,详细信息可以参考网络
         */
        $this->faker = app (Faker::class);
    }
 
    /**
     * 限制条数
     * @access public
     * @param  int $rows 记录行
     * @param  int $offset 偏移量
     * @return object
     */
    public function limit(int $rows, int $offset = 0)
    {
        $this->size = $rows;
 
        if ($offset == 0) {
            $this->from = 0;
        } else {
            $this->from = $rows * $offset;
        }
 
        return $this;
    }
 
    /**
     * 排序
     * @access public
     * @param  string $field 字段
     * @param  string $sort 排序
     * @return object
     */
    public function order($field, $sort = 'ASC')
    {
        $this->sort = [
            'sort' => [ // 排序
                [
                    $field => [
                        'order' => $sort, //对字段进行降序排序
                    ],
                ]
            ]
        ];
 
        return $this;
    }
 
    /**
     * 直接打印错误 无效?
     * @access public
     * @return string
     */
    public function fetchError($errorType = 1)
    {
        $this->errorType = $errorType;
        return $this;
    }
 
    /**
     * 获取最后一次执行错误
     * @access public
     * @return string
     */
    public function getError()
    {
        return $this->error;
    }
 
    /**
     * 设置错误
     * @access private
     * @param $msg string 错误信息
     * @param $type string 错误信息类别
     * @return void
     */
    private function setError($msg = '', $type = 1)
    {
        if ($type == 2) {
            $msg = 'error';
        }
 
        if ($this->errorType == 1) {
            $this->error = $msg;
        } else {
            // throw new \Exception($msg);
            echo $msg; exit();
        }
    }
 
    /**
     * 字段
     * @access public
     * @param  string $field 请求指定的字段,只显示对应的字段
     * @return object
     */
    public function field($field)
    {
        if (empty($field) || $field == '*') {
            $this->field = [];
        } else {
            $field = explode(',', $field);
            $this->field = $field;
        }
 
 
        return $this;
    }
 
    /**
     * 条件
     * @access public
     * @param  array $condition 条件
     * @return object
     */
    public function where($condition = [])
    {
        $this->query =  [
            'query' => [
                'bool' => $condition
            ]
        ];
        return $this;
    }
 
    /**
     * 配置重试次数
     * @access public
     * @param  int $retries
     * @return object
     */
    public function retries($retries = [])
    {
        $this->retries = $retries;
        return $this;
    }
 
    /**
     * 连接信息
     * @access public
     * @param  array $hosts
     * @return object
     */
    public function hosts($hosts = [])
    {
        $this->hosts = $hosts;
        return $this;
    }
 
    /**
     * 设置忽略错误
     * @access public
     * @param $ignore array 错误状态
     * @return void
     */
    public function setIgnore($ignore = [])
    {
        $this->ignore = $ignore;
        return $this;
    }
 
    /**
     * 设置超时时间
     * @access public
     * @param $timeout int
     * @return void
     */
    public function setTimeout($timeout = 10)
    {
        $this->timeout = $timeout;
        return $this;
    }
 
    /**
     * 设置连接超时时间
     * @access public
     * @param $connect_timeout int
     * @return void
     */
    public function setConnectTimeout($connect_timeout = 10)
    {
        $this->connect_timeout = $connect_timeout;
        return $this;
    }
 
    /**
     * 索引
     * @access public
     * @param  array $index 索引
     * @return object
     */
    public function index($index)
    {
        $this->index = config('other.elastic_environment') . '_' . $index;
        return $this;
    }
 
    /**
     * 查询多行记录
     * @access public
     * @return array
     */
    public function select()
    {
        $result = $this->search();
        // dd($result, $this->getError());
 
        $list = $result['hits']['hits'] ?? [];
        $source_list = [];
        foreach ($list as $key => $vo) {
            $source_list[] = $vo['_source'];
        }
 
        return $source_list;
    }
 
    /**
     * 查询行数 超过10000条会失败需要配置其他参数
     * @access public
     * @return int
     */
    public function count()
    {
        $this->query = array_merge($this->query, [
            'size' => 0,
            'track_total_hits' => true, //实际数量,不设置最大为10000
        ]);
        $result = $this->search();
        // dd($result);
 
        $count = $result['hits']['total']['value'] ?? 0;
 
        return $count;
    }
 
    /**
     * 添加一个文档到 Index 的Type中
     * @param array $body
     * @return void
     *          -(Y)_index:索引
     *          -(Y)_id:id值
     *          -(Y)_version:版本
     *          -(Y)result:结果
     *          -(Y)_shards:分片
     *              -(Y)total:总计
     *              -(Y)successful:成功
     *              -(Y)failed:失败
     *          -(Y)_seq_no:序列号(第几条数据)
     *          -(Y)_primary_term:初级术语??
     */
    public function insert($body = [])
    {
        $params = [
            'index' => $this->index, //索引
            'type'  => $this->type, //表
            // 'id' => 1, #可以手动指定id,也可以不指定随机生成
            'body'  => $body,
            'client' => [
                'ignore'          => $this->ignore,
                'timeout'         => $this->timeout,
                'connect_timeout' => $this->connect_timeout,
            ]
        ];
 
        return $this->EsClient->index($params);
    }
 
    /**
     * 批量添加文档到 Index 的Type中
     * @param array $body
     * @return void
     */
    public function insertAll($body = [])
    {
        $params = [];
        foreach ($body as $key => $vo) {
            $params['body'][] = [
                'index' => [
                    '_index' => $this->index, //索引
                    '_type'  => $this->type, //表
                ]
            ];
 
            $params['body'][] = $vo;
        }
 
        $params['client'] = [
            'ignore'          => $this->ignore,
            'timeout'         => $this->timeout,
            'connect_timeout' => $this->connect_timeout,
        ];
        // dd($params['body']);
 
        return $this->EsClient->bulk($params);
    }
 
    /**
     * 直接检索该字段
     * @param $id id值
     * @return array 该条记录的数据
     */
    public function find($id)
    {
        $params = [
            'index' => $this->index,
            'id'    => $id,
            '_source' => $this->field,
            'client' => [
                'ignore'          => $this->ignore,
                // 'verbose' => true, //增加响应的冗长
                'timeout'         => $this->timeout,
                'connect_timeout' => $this->connect_timeout,
                // 'future' => 'lazy', //启用 Future 模式  lazy、true
                // 'verify' => 'path/to/cacert.pem'      // SSL加密,使用自签名证书
            ]
        ];
 
        $result = $this->EsClient->getSource($params);
        if (isset($result['error'])) {
            if (isset($result['error']['reason'])) {
                $this->setError($result['error']['reason']);
            } else {
                $this->setError($result);
            }
            $result = [];
        }
 
        return $result;
    }
 
    /**
     * 删除一个文档
     * @param $id
     * @return bool
     * @return array
     *          -(Y)_index:索引
     *          -(Y)_type:??
     *          -(Y)_id:id值
     *          -(Y)_version:版本
     *          -(Y)result:结果
     *          -(Y)_shards:分片
     *              -(Y)total:总计
     *              -(Y)successful:成功
     *              -(Y)failed:失败
     *          -(Y)_seq_no:序列号(第几条数据)
     *          -(Y)_primary_term:初级术语??
     */
    public function delete($id)
    {
        $params = [
            'index' => $this->index,
            // 'type'  => $this->type,
            'id'    => $id,
            'client' => [
                'ignore'          => $this->ignore,
                'timeout'         => $this->timeout,
                'connect_timeout' => $this->connect_timeout,
            ]
        ];
 
        $result =  $this->EsClient->delete($params);
        // dd($result);
        if (isset($result) && $result['result'] != 'not_found') {
            return true;
        } else {
            $this->setError('not_found');
 
            return false;
        }
    }
 
 
    /**
     * 获取单个文档
     * @param $id id值
     * @return array
     *          -(Y)_index:索引名
     *          -(Y)_type:_doc 文档类型??
     *          -(Y)_id:id值
     *          -(Y)_version:版本
     *          -(Y)_seq_no:序列号(第几条数据)
     *          -(Y)_primary_term:初级术语??
     *          -(Y)found:是否找到
     *          -(Y)_source:该条记录的数据
     *              -(Y)
     */
    public function getDoc($id)
    {
        $params = [
            'index'  => $this->index,
            'type'   => $this->type,
            'id'     => $id,
            '_source' => $this->field,
            'client' => [
                'ignore'          => $this->ignore,
                'timeout'         => $this->timeout,
                'connect_timeout' => $this->connect_timeout,
            ]
        ];
 
        return $this->EsClient->get($params);
    }
 
    /**
     * 搜索文档,query是查询条件
     * @param array $query
     * @return array
     *          -(Y)took:??
     *          -(Y)timed_out:是否超时
     *          -(Y)_shards:分片
     *              -(Y)total:总计
     *              -(Y)successful:成功
     *              -(Y)skipped:???
     *              -(Y)failed:失败
     *          -(Y)hits:命中情况
     *              -(Y)total:总计
     *                  -(Y)value:命中数
     *                  -(Y)relation:关联
     *              -(Y)max_score:最大匹配值
     *                  -(Y)hits:命中情况
     *                      -(Y)_index:索引
     *                      -(Y)_type:初级术语??
     *                      -(Y)_id:id
     *                      -(Y)_score:分值
     *                      -(Y)_source:该条记录的数据
     *                      -(Y)sort:排序字段的情况
     */
    public function search()
    {
        // $query = [
        //     'query' => [
        //          //filtered //嵌套布尔过滤器
        //
        //          //一个 bool 过滤器的每个部分都是可选的(例如,我们可以只有一个 must 语句),而且每个部分内部可以只有一个或一组过滤器
        //         'bool' => [ //布尔过滤器
        //             'must'   => [ //必须匹配
        //                 [
        //                     'match' => [
        //                         'host_name' => '倪时鸿',
        //                     ],
        //                 ],
        //                 [
        //                     'match' => [
        //                         'input_type' => '6002',
        //                     ],
        //                 ],
        //             ],
        //             'should'   => [ //至少有一个语句要匹配
        //                 [
        //                     'match' => [
        //                         'host_name' => '倪时鸿',
        //                     ],
        //                 ],
        //                 [
        //                     'match' => [
        //                         'input_type' => '6002',
        //                     ],
        //                 ],
        //             ],
        //             'must_not'   => [ //不能匹配
        //                 [
        //                     'match' => [
        //                         'input_type' => '6002',
        //                     ]
        //                 ]
        //             ],
        //             'filter' => [
        //                 [
        //                     'range' => [
        //                         'type' => [
        //                             'gt' => 0
        //                         ]
        //                     ],
        //                 ],
        //                 [
        //                     'range' => [
        //                         'created_at' => [
        //                             'gte'    => "2021-04-26 00:00:00", //开始时间
        //                             'lte'    => "2021-04-27 00:00:00", //结束时间
        //                             // 'time_zone' => '+08:00', //时间区间
        //                             'format' => 'yyyy-MM-dd HH:mm:ss',
        //                         ]
        //                     ]
        //                 ]
        //             ]
        //         ]
        //     ],
        // ];
 
        $params = [
            'index'   => $this->index,
            // 'index' => 'm*', #index 和 type 是可以模糊匹配的,甚至这两个参数都是可选的
            'type'    => $this->type,
            '_source' => $this->field, // 请求指定的字段,只显示对应的字段
            'body'    => array_merge([
                'from' => $this->from,
                'size' => $this->size,
            ], $this->query, $this->sort)
        ];
 
        try {
            return $this->EsClient->search($params);
        } catch (\Exception $e) {
            $this->setError($e->getMessage());
 
            return false;
        }
    }
 
    /**
     * 删除一个Index
     * @return void
     *          -(Y)acknowledged:结果
     */
    public function delIndex()
    {
        $params = [
            'index' => $this->index
        ];
        if ($this->checkIndexExists()) {
            return $this->EsClient->indices()->delete($params);
        }
    }
 
    /**
     * 检查Index 是否存在
     * @return bool
     */
    public function checkIndexExists()
    {
        $params = [
            'index' => $this->index
        ];
        return $this->EsClient->indices()->exists($params);
    }
 
    /**
     * 获取 ES 的状态信息,包括index 列表
     * @return array
     */
    public function esStatus()
    {
        return $this->EsClient->indices()->stats();
    }
 
    /**
     * 批量生成文档
     * @param $num
     */
    public function generateDoc($num = 100)
    {
        $params = [];
        foreach (range(1, $num) as $item) {
            // $this->insert([
            //     'first_name' => $this->faker->name,
            //     'last_name'  => $this->faker->name,
            //     'age'        => $this->faker->numberBetween(20, 80),
            //     '@timestamp' => date('Y-m-d H:i:s')
            // ]);
            $params[] = [
                'first_name' => $this->faker->name,
                'last_name'  => $this->faker->name,
                'age'        => $this->faker->numberBetween(20, 80),
                '@timestamp' => date('Y-m-d H:i:s')
            ];
        }
 
        $this->insertAll($params);
    }
 
    /**
     * 获取Index的文档模板信息
     * @return array
     */
    public function getMapping()
    {
        $params = [
            'index' => $this->index
        ];
        return $this->EsClient->indices()->getMapping($params);
    }
 
    /**
     * 删除所有的 Index 谨慎操作!!!
     */
    public function delAllIndex()
    {
        echo '谨慎操作!!!!';exit();
 
        $indexList = $this->esStatus()['indices'];
        foreach ($indexList as $item => $index) {
            $this->delIndex();
        }
    }
 
    /**
     * 一次获取多个文档
     * @param $ids
     * @return array
     */
    public function getDocs($ids)
    {
        $params = [
            'index' => $this->index,
            'type'  => $this->type,
            'body'  => ['ids' => $ids]
        ];
        return $this->EsClient->mget($params);
    }
 
    /**
     * 更新一个文档
     * @param $id
     * @return array
     */
    public function update($id, $doc)
    {
        $params = [
            'index' => $this->index,
            'type'  => $this->type,
            'id'    => $id,
            'body'  => [
                'doc' => $doc
            ],
            'client' => [
                'ignore'          => $this->ignore,
                'timeout'         => $this->timeout,
                'connect_timeout' => $this->connect_timeout,
            ]
        ];
        $result = $this->EsClient->update($params);
 
        if (isset($result['error'])) {
            if (isset($result['error']['reason'])) {
                $this->setError($result['error']['reason']);
            } else {
                $this->setError($result);
            }
            $result = [];
        }
 
        return $result;
    }
 
    /**
     * 创建索引文档模板
     * @return void
     */
    public function createMapping($properties = [])
    {
        $this->createIndex();
        $params = [
            'index' => $this->index,
            'type'  => $this->type,
            'include_type_name' => true,
            'body'  => [
                $this->type => [
                    '_source'    => [
                        'enabled' => true
                    ],
                    'properties' => $properties,
                    // 'properties' => [
                    //     'ip'         => [
                    //         'type' => 'text'
                    //         //'index' => 'analyzed', // 全文搜索
                    //     ],
                    //     'input'      => [
                    //         'type' => 'text'
                    //     ],
                    //     'type'       => [
                    //         'type' => 'integer',
                    //     ],
                    //     'host_name'  => [
                    //         'type' => 'keyword',
                    //     ],
                    //     'input_type' => [
                    //         'type' => 'integer',
                    //     ],
                    //     'result'     => [
                    //         'type' => 'text',
                    //     ],
                    //     'key'        => [
                    //         'type' => 'text',
                    //     ],
                    //     'created_at' => [
                    //         'type'   => 'date', //需要先设置字段以及格式,才可以做排序
                    //         'format' => 'yyyy-MM-dd HH:mm:ss',
                    //     ],
                    //     '@timestamp' => [
                    //         'type'   => 'date',
                    //         'format' => 'yyyy-MM-dd HH:mm:ss',
                    //     ],
                    // ],
                ]
            ]
        ];
        $result = $this->EsClient->indices()->putMapping($params);
 
        return $result;
    }
 
    /**
     * 创建一个索引 Index (非关系型数据库里面那个索引,而是关系型数据里面的数据库的意思)
     * @return void
     *          -(Y)acknowledged:结果
     *          -(Y)shards_acknowledged:分片结果
     *          -(Y)index:索引
     */
    public function createIndex()
    {
        //先删除
        // $this->delIndex();
 
        $params = [
            'index' => $this->index,
            'body'  => [
                'settings' => [ //包含有关索引(分片数量等)以及分析器的配置
                    'number_of_shards'   => 5, //设置主分片
                    'number_of_replicas' => 3 //副分片
                ],
            ]
        ];
 
        return $this->EsClient->indices()->create($params);
    }
}
打赏