elasticsearch服务类封装

 

1.服务类的调用

<?php  
  /**
     * es数据查询
     *
     * @return void
     */
    // public function esSeachData($keywords, $page = 1)
    public function esSeachData()
    {

        $page = 1; // 分页需要是数量的倍数
        $keywords = '真无线'; // 检索内容
        $configId = 1; // 检索配置id 1=搜索配置,2=文章推荐
        // 只有文章推荐需要筛选文章,也就是$configId = 2
        $searchTypeValue= 2; // $config['search_type']  检索配置 id为1传栏目、id为2传模型id

        // 检索配置:1=搜索配置,2=文章推荐
        $config = AddonsElasticsearchPlugConfig::build() -> where('id', $configId)->find();

        $keyName = 'content'; // 检索索引名
        $fieldName = 'title'; // 检索字段
        $pageSize = $config['search_article_nums']; // 文章数量
        $from = ($page - 1) * $pageSize; // 分页需要是数量的倍数

        // 检索范围:0=模糊搜索,1=精准匹配
        if ($config['search_scope']) {
            $fieldName = $fieldName.'.keyword';
        }


        // 检索排序规则:0=默认,1=发布时间倒序,1=发布时间升序
        switch ($config['search_sort']) {
            case 1:
                $sort['create_time'] = ['order' => 'desc'];
                break;
            case 2:
                $sort['create_time'] = ['order' => 'asc'];
                break;
            default:
                $sort = [];
                break;
        }

        // 返回的字段限制
        $includeFields = ['id','cid','mid','title'];

        // 检索类型:0=匹配即可,1=同栏目数据,1=同模型数据(只有文章推荐需要筛选文章、栏目)
        if ($configId == 2 && $config['search_type'] == 1) {
            $searchTypeKey = 'cid';
            $res  = ElasticsearchService::build() -> esSingleFieldWhereSearch($keyName, $fieldName, $keywords, $from, $pageSize, $sort, $includeFields, $searchTypeKey, $searchTypeValue);
        } elseif ($configId == 2 && $config['search_type'] == 2) {
            $searchTypeKey = 'mid';
            $res  = ElasticsearchService::build() -> esSingleFieldWhereSearch($keyName, $fieldName, $keywords, $from, $pageSize, $sort, $includeFields, $searchTypeKey, $searchTypeValue);
        }else{
            $res  = ElasticsearchService::build() -> esSingleFieldSearch($keyName, $fieldName, $keywords, $from, $pageSize, $sort, $includeFields);
        }

        $list['total'] = $res['pageNumber']; // 数据数量
        $list['per_page'] = $pageSize; // 页码数量
        $list['current_page'] = $page; // 页码
        $list['last_page'] = ceil($res['pageNumber']/$pageSize); // 最后页码
        $list['data'] = $res['list']; // 数据列表
        $data['list'] = $list;

        return $this->json_success('es数据查询',$data);
        // return $data;
    }

 

  

2.服务类的封装

<?php
/*
 * @Author: panzhide seanzhui@qq.com
 * @Date: 2023-03-29 16:48:56
 * @LastEditors: panzhide seanzhui@qq.com
 * @LastEditTime: 2023-04-03 11:35:37
 * @FilePath: \zhanzancms\addons\elasticsearchplug\model\ElasticsearchService.php
 * @Description: 这是默认设置,请设置`customMade`, 打开koroFileHeader查看配置 进行设置: https://github.com/OBKoro1/koro1FileHeader/wiki/%E9%85%8D%E7%BD%AE
 */
namespace addons\elasticsearchplug\service;
use Elasticsearch\ClientBuilder;
use think\App;
use think\facade\Db;
use addons\elasticsearchplug\model\BaseModel;
class ElasticsearchService extends BaseModel
{
    // 构造函数
    public function __construct()
    {
        $elasticsearch_hosts = ['elastic:seanzhui@198.12.116.62:9200'];
        $this->client = ClientBuilder::create()->setHosts($elasticsearch_hosts)->build();
        $this->index_name = 'zhanzancms'; // 索引名(项目名)
    }

    public static function build() {
        return new self();
    }



    /**
     * @desc 创建索引和映射,只能创建一次
     * ES可以自动创建索引,不过实际项目中,通常需要预先创建索引结构,明确指定数据类型,避免出现ES自动创建的字段类型不是你想要的类型
     * @paramstring $keyName  索引名
     * @returnarray|mixed|string
     */
    public function create_index($keyName) {
        $params = [
            'index' => $this->index_name .'_'. $keyName,
            'body' => [
                'settings' => [
                    'number_of_shards' => 5,   // 数据分片数,默认为5,有时候设置为3
                    'number_of_replicas' => 0  // 数据备份数,如果只有一台机器,设置为0
                ],
                'mappings'=>[
                    '_source' => [
                        'enabled' => true
                    ],
                    'properties' => [
                        // 以下为字段示例
                        'id' => [
                            'type' => 'integer',
                            'index' => false,  //index参数作用是控制当前字段是否被索引,默认为true,false表示不记录,即不可被搜索
                        ],
                        'title' => [
                            'type' => 'text', // text 类型的字符串是可以被全文检索的,它会被分词器作用,
                            'index' => true,
                            'analyzer' => 'ik_max_word' // 会将文本做最细粒度的拆分,比如会将“中华人民共和国国歌”拆分为“中华人民共和国,中华人民,中华,华人,人民共和国,人民,人,民,共和国,共和,和,国国,国歌”,会穷尽各种可能的组合,适合 Term Query
                        ],
                        'content' => [
                            'type' => 'text',
                            'index' => true,
                            'analyzer' => 'ik_max_word'
                        ],
                        'tags' => [   // 商品标签,字段被拆分后不具有意义,所以使用keyword 类型
                            'type' => 'keyword',
                        ],
                        'price' => [
                            'type' => 'float'
                        ],
                        'add_time' => [
                            'type' => 'integer',   // 时间戳
                        ],
                        'update_time' => [
                            'type' => 'integer',   // 时间戳
                        ]
                    ]
                ]
            ]
        ];

        try {
            $response =  $this->client->indices()->create($params);
            Db::commit();
        } catch (\Exception $e) {
            Db::rollback();
            throw new \think\Exception($e -> getMessage());
        }
        return $response;
    }


    /**
     * 删除索引
     * @paramstring $keyName  索引名
     * @returnvoid
     */
    public function deleteIndex(string $keyName)
    {
        Db::startTrans();
        try {
            $params = ['index' => $keyName];
            $response = $this->client->indices()->delete($params);
            Db::commit();
        } catch (\Exception $e) {
            Db::rollback();
            throw new \think\Exception($e -> getMessage());
        }
        return $response;
    }

    /**
     * 判断文档存在
     * @paramstring $keyName  索引名
     * @paramstring $id        索引id
     * @returnbool
     */
    public function existsDoc(string $keyName, string $id)
    {
        $params = [
            'index' => $this->index_name .'_'. $keyName,
            'id'    => $id
        ];
        return $this->client->exists($params);
    }

    /**
     * 添加文档(单条)
     * @paramstring $keyName  索引名
     * @paramstring $id        索引id
     * @paramarray $doc        跟创建文档结构时properties(es文档模板)的字段一致
     * @returnarray|callable
     */
    public function addDoc(string $keyName, string $id, array $doc)
    {
        $params = [
            'index' => $this->index_name .'_'. $keyName,
            'id'    => $id,
            'body'  => $doc
        ];
        return $this->client->index($params);
    }

    /**
     * 添加文档(批量),如果id已存在则为更新
     * @paramstring $keyName  索引名
     * @paramstring $id        索引id
     * @paramarray $list       跟创建文档结构时properties(es文档模板)的字段一致
     * @returnarray|callable
     */
    public function addDocAll(string $keyName, array $list)
    {
        $index_name = $this->index_name .'_'. $keyName;
        $docs = [];
        foreach ($list as $key => $value) {
            $docs['body'][] = ['index'=>['_index'=>$index_name, '_id'=>$value['id']]];
            $docs['body'][] = $value;
        }
        return $this->client->bulk($docs);
    }

    /**
     * 删除文档
     * @paramstring $keyName  索引名
     * @paramstring $id        文档id
     * @returnarray|callable
     */
    public function deleteDoc(string $keyName, string $id)
    {
        // 文档是否存在
        $res = $this->existsDoc($keyName, $id);
        if (!$res) {
            throw new \think\Exception('文档不存在');
        }

        Db::startTrans();
        try {
            $params = [
                'index' => $this->index_name .'_'. $keyName,
                'id'    => $id
            ];
            $response = $this->client->delete($params);
            Db::commit();
        } catch (\Exception $e) {
            Db::rollback();
            throw new \think\Exception($e -> getMessage());
        }
        return $response;
    }

    /**
     * 更新文档(字段内容)
     * @paramstring $keyName  索引名
     * @paramstring $id        文档id
     * @paramstring $key       更新的字段
     * @paramstring $value     更新的内容
     * @returnarray|callable
     */
    public function updateDoc(string $keyName, string $id, string $key, string $value)
    {
        // 文档是否存在
        $res = $this->existsDoc($keyName, $id);
        if (!$res) {
            throw new \think\Exception('文档不存在');
        }

        Db::startTrans();
        try {
            // 可以灵活添加新字段,最好不要乱添加
            $params = [
                'index' => $this->index_name .'_'. $keyName,
                'id'    => $id,
                'body'  => [
                    'doc' => [
                        $key => $value
                    ]
                ]
            ];
            $response =  $this->client->update($params);
            Db::commit();
        } catch (\Exception $e) {
            Db::rollback();
            throw new \think\Exception($e -> getMessage());
        }
        return $response;
    }

    /**
     * 更新文档(数组)
     * @paramstring $keyName  索引名
     * @paramstring $id        文档id
     * @paramarray $doc       更新的字段
     * @returnarray|callable
     */
    public function updateDocArray(string $keyName, string $id, array $doc)
    {
        // 文档是否存在
        $res = $this->existsDoc($keyName, $id);
        if (!$res) {
            throw new \think\Exception('文档不存在');
        }

        Db::startTrans();
        try {
            // 可以灵活添加新字段,最好不要乱添加
            $params = [
                'index' => $this->index_name .'_'. $keyName,
                'id'    => $id,
                'body'  => ['doc'=>$doc]
            ];
            $response = $this->client->update($params);
            Db::commit();
        } catch (\Exception $e) {
            Db::rollback();
            throw new \think\Exception($e -> getMessage());
        }
        return $response;
    }

    /**
     * 获取文档
     * @paramstring $keyName  索引名
     * @paramstring $id        文档id
     * @returnarray
     */
    public function getDoc(string $keyName, string $id){
        $params = [
            'index' => $this->index_name .'_'. $keyName,
            'id'    => $id
        ];
        $response = $this->client->get($params);
        return $response['_source'];
    }


    /**
     * es按条件批量删除数据
     * @paramstring $keyName    索引名
     * @paramstring $fieldName  字段名
     * @paramarray $where         排序
     * @returnvoid
     */
    public function esSingleFieldDel($keyName, $fieldName, $value)
    {
        $params = [
            'index' => $this->index_name .'_'. $keyName,
            // 如果出现版本冲突,如何处理?proceed表示继续更新,abort表示停止更新
            'conflicts' => 'proceed',
            'body' => [ // ES请求体内容
                'query' => [ // 设置查询条件,跟es查询语法一致
                    "term" => [
                        $fieldName => $value
                    ]
                ],
            ],
        ];
        $res = $this->client->deleteByQuery($params);
        return $res;
    }



    /**
     * es单字段复杂搜索
        $sort  3维数组  例:'sort' => [
            ['time' => ['order' => 'desc']],
            ['popularity' => ['order' => 'desc']]
        ]
     * @paramstring $keyName    索引名
     * @paramstring $fieldName.keyword  字段名  全文精确检索字段用 match加.keyword,其他非text字段匹配用term
     * @paramstring $keywords   关键词
     * @paramint $from          起始位置
     * @paramint $size          文档数量
     * @paramarray $sort        排序
     * @paramint $mid           复杂搜索
     * @paramarray $includeFields  返回的字段
     * @returnvoid
     */
    public function esSingleFieldWhereSearch($keyName, $fieldName, $keywords, $from=0, $size=0, $sort=[], $includeFields=[], $searchTypeKey, $searchTypeValue)
    {
        $searchTypeValueMin = $searchTypeValue - 1;
        $searchTypeValueMax = $searchTypeValue + 1;
        $params = [
            'index' => $this->index_name .'_'. $keyName,
            'body'  => [
                'query' => [
                    'bool' => [
                        // 必须匹配
                        'must' => [
                            'match' => [
                                $fieldName => $keywords
                            ],
                        ],
                        // // 应该匹配
                        // 'should' => [
                        //     ['match' => [
                        //         'mid' => [
                        //             'query' => '1',
                        //             'boost' => 10, // 权重
                        //         ]
                        //     ]],
                        //     // ['match' => [
                        //     //     $fieldName => [
                        //     //         'query' => $keywords,
                        //     //         'boost' => 2,
                        //     //     ]
                        //     // ]],
                        // ],

                        // 复杂的搜索,筛选出mid等于x的
                        'filter' => [
                            'range' => [
                                $searchTypeKey => [
                                    'gt'=> $searchTypeValueMin,
                                    'lt'=> $searchTypeValueMax
                                ]
                            ]
                        ],
                    ],
                ]
            ]
        ];

        // 获取页面数量
        $resList = $this->client->search($params);
        $data['pageNumber'] = count($resList);

        if(!empty($sort)){
            $params['body']['sort'] = $sort;
        }
        if($size != 0){
            $params['body']['from'] = $from;
            $params['body']['size'] = $size;
        }
        if(!empty($includeFields)){
            $params['body']["_source"] = $includeFields;
        }
        $res = $this->client->search($params);
        $data['list'] = array_column($res['hits']['hits'], '_source');
        return $data;
    }


    /**
     * es单字段模糊匹配
        $sort  3维数组  例:'sort' => [
            ['time' => ['order' => 'desc']],
            ['popularity' => ['order' => 'desc']]
        ]
     * @paramstring $keyName    索引名
     * @paramstring $fieldName  字段名
     * @paramstring $keywords   关键词
     * @paramint $from          起始位置
     * @paramint $size          文档数量
     * @paramarray $sort        排序
     * @paramarray $includeFields  返回的字段
     * @returnvoid
     */
    public function esSingleFieldSearch($keyName, $fieldName, $keywords, $from=0, $size=0, $sort=[], $includeFields=[])
    {
        $params = [
            'index' => $this->index_name .'_'. $keyName,
            'body'  => [
                'query' => [
                    'match' => [
                        $fieldName => $keywords
                    ]
                ]
            ]
        ];

        // 获取页面数量
        $resList = $this->client->search($params);
        $data['pageNumber'] = count($resList);

        if(!empty($sort)){
            $params['body']['sort'] = $sort;
        }
        if($size != 0){
            $params['body']['from'] = $from;
            $params['body']['size'] = $size;
        }
        if(!empty($includeFields)){
            $params['body']["_source"] = $includeFields;
        }

        $res = $this->client->search($params);
        $data['list'] = array_column($res['hits']['hits'], '_source');
        return $data;
    }


    /**
     * es多字段模糊匹配同一数据
     *  param $fieldName     1维数组
        param $includeFields 1维数组
        param $sort           3维数组  例:
            [
                ['time' => ['order' => 'desc']],
                ['popularity' => ['order' => 'desc']]
            ]
     * @paramstring $keyName       索引名
     * @paramarray $fieldName      字段名(多字段)
     * @paramstring $keywords      关键词
     * @paramint $from             起始位置
     * @paramint $size             文档数量
     * @paramarray $sort           排序
     * @paramarray $includeFields  返回的字段
     * @returnvoid
     */
    public function esMultiFieldSearch($keyName, $fieldName=[], $keywords, $from=0, $size=0, $sort=[], $includeFields=[])
    {
        $params = [
            'index' => $this->index_name .'_'. $keyName,
            'body'  => [
                "query"=>[
                    "multi_match"=> [
                        "query" => $keywords,
                        "fields" => $fieldName
                    ]
                ]
            ]
        ];

        // 获取页面数量
        $resList = $this->client->search($params);
        $data['pageNumber'] = count($resList);

        if(!empty($sort)){
            $params['body']['sort'] = $sort;
        }
        if($size!=0){
            $params['body']['from'] = $from;
            $params['body']['size'] = $size;
        }
        if(!empty($includeFields)){
            $params['body']["_source"] = $includeFields;
        }

        $res = $this->client->search($params);
        $data['list'] = array_column($res['hits']['hits'], '_source');
        return $data;
    }

    /**
     * 获取索引内所有数据
     * @param [type] $keyName 索引名
     * @paraminteger $from   起始位置
     * @paraminteger $size   文档数量
     * @paramarray $sort     排序
     * @returnvoid
     */
    public function getEsIndexAllData($keyName, $from=0, $size=0, $sort=[])
    {
        $params = [
            'index' => $this->index_name .'_'. $keyName,
            'body'  => [
                'query' => [
                    'match_all' => new \stdClass()
                ]
            ]
        ];
        if($size!=0){
            $params['body']['from'] = $from;
            $params['body']['size'] = $size;
        }
        if(!empty($sort)){
            $params['body']['sort'] = $sort;
        }
        $res = $this->client->search($params);
        $res = array_column($res['hits']['hits'], '_source');
        return $res;
    }



}
posted @ 2023-03-27 10:47  潘潘潘的博客  阅读(166)  评论(0编辑  收藏  举报