client = ClientBuilder::create()->setHosts([self::ES_HOST_NAME])->build(); } private function __clone() { } public static function getInstance(){ if (is_null(self::$instance)) { self::$instance = new self(); } return self::$instance; } /** * 查询条件 * @param $params * @return $this */ public function select($params = '') { $this->queryParams['select'] = str_replace(' ', '', trim($params, " ,\t\r\n")); return $this; } /** * 分页 * @param $number * @return $this */ public function from($number = 0) { $this->queryParams['from'] = $number ? ($number-1)*10 : 0; $this->queryParams['current_page'] = $number; return $this; } /** * 页码 * @param $number * @return $this */ public function size($number = 10) { $this->queryParams['size'] = $number; return $this; } /** * 批量查询 * @param $params * @return $this */ public function equal($params = []) { $this->queryParams['equal'] = $params; return $this; } /** * 批量查询 * @param $params * @return $this */ public function in($params = []) { $this->queryParams['in'] = $params; return $this; } /** * 范围查找 */ public function between($params = []) { $this->queryParams['between'] = $params; return $this; } /** * 模糊查询 * @param $params * @return $this */ public function like($params = []) { //$this->queryParams['like'][] = $params; $this->queryParams['like'] = $params; return $this; } public function orlike($params = []) { //$this->queryParams['orlike'][] = $params; $this->queryParams['orlike'] = $params; return $this; } /** * has_parent 查询(父查子) * @param $params * @return $this */ public function hasParent($params = []) { $this->queryParams['has_parent'][] = $params; return $this; } /** * has_child 查询(子查父) * @param $params * @return $this */ public function hasChild($params, $child_params = []) { $this->queryParams['has_child'] = $params; return $this; } public function order($params = []) { $this->queryParams['order'] = $params; return $this; } /** * 根据条件删除文档 * @param array $params * @return array */ public function deleteDoc($params = []) { return $this->client->deleteByQuery($params); } public function batchDelete($bulkBody = []){ $params = [ 'index' => $this->index, 'body' => $bulkBody, ]; return $this->client->bulk($params); } public function existsIndex(String $index_name) { $params = [ 'index' => $index_name ]; return $this->client->indices()->exists($params); } /** * 创建索引 */ public function createIndex(String $index_name, array $mapping = []): array { if ($this->existsIndex($index_name)) { throw new Exception('索引已存在'. $index_name); } //只能创建一次 $params = [ 'index' => $index_name, 'body' => [ 'settings' => [ 'number_of_shards' => 5, 'number_of_replicas' => 1 ], 'mappings' => $mapping ] ]; return $this->client->indices()->create($params); } /** * 删除索引 */ public function deleteIndex(String $index_name = ''): array{ $params = ['index' => $index_name]; return $this->client->indices()->delete($params); } /** * 根据id删除文档 * @param int $id * @return array */ public function deleteDocById($id = 0) { $params = [ 'index' => $this->index, 'id' => $id ]; $response = $this->client->delete($params); return $response; } /** * 组装查询条件 * @param array $params * @return $this */ private function parseQueryParams() { $queryParams = [ 'index' => $this->index, 'body' => [ 'query' => [ 'bool' => [ 'must' => [], 'filter' => [] ] ], ], 'from' => $this->queryParams['from'] ?? 0, 'size' => $this->queryParams['size'] ?? 10, 'current_page' => $this->queryParams['current_page'] ?? 1, 'track_total_hits' => true, ]; $filter = $must = $orFilter = []; if (!empty($this->queryParams['select'])) { $queryParams['_source'] = explode(',', $this->queryParams['select']); } if (!empty($this->queryParams['equal'])) { foreach ($this->queryParams['equal'] as $key => $row) { foreach ($row as $filed => $value) { $filter[] = [ 'term' => [$filed => $value] ]; } } } if (!empty($this->queryParams['in'])) { foreach ($this->queryParams['in'] as $key => $row) { foreach ($row as $filed => $value) { $filter[] = [ 'terms' => [$filed => array_values(array_unique(array_filter($value)))] ]; } } } if (!empty($this->queryParams['like'])) { foreach ($this->queryParams['like'] as $key => $row) { foreach ($row as $filed => $value) { /*$must[] = [ 'wildcard' => [$filed => '*'. $value. '*'] ];*/ $must[] = [ 'match' => [$filed => $value, ''] // 'match_phrase' => [$filed => $value] ]; } } $queryParams['body']['query']['bool']['must'] = $must; } if (!empty($this->queryParams['orlike'])) { foreach ($this->queryParams['orlike'] as $key => $row) { foreach ($row as $filed => $value) { // $orlike[] = [ // 'match_phrase' => [$filed => $value] // ]; $orlike[] = [ 'match' => [$filed => ['query' => $value, 'fuzziness' => "AUTO", 'operator' => 'or']] ]; } } $queryParams['body']['query']['bool']['must']['bool']['should'] = $orlike; } //has_child 查询(子查父) if (!empty($this->queryParams['has_child'])) { $should = []; foreach ($this->queryParams['has_child'] as $key => $row) { $queryMust = []; foreach ($row as $name => $value) { if ($name == 'category_id') { $queryMust[] = [ 'has_child' => [ 'type' => 'category', 'query' => [ 'bool' => [ 'must' => [ 'terms' => [ 'category_id' => $value ] ] ] ], ], ]; } if ($name == 'profit') { $queryMust[] = [ 'range' => [ 'profit' => [ 'gte' => $value, ] ] ]; } if ($name == 'profit_rate') { $queryMust[] = [ 'range' => [ 'profit_rate' => [ 'gte' => $value, ] ] ]; } } $should[] = [ 'bool' => [ 'must' => $queryMust ] ]; } $filter[] = [ 'bool' => [ 'should' => $should ] ]; } //has_parent 查询(父查子) if (!empty($this->queryParams['has_parent'])) { foreach ($this->queryParams['has_parent'] as $key => $row) { $queryParams['body']['query']['bool']['must'][] = [ 'has_parent' => $row ]; } } if (!empty($this->queryParams['order'])) { foreach ($this->queryParams['order'] as $key => $row) { $queryParams['body']['sort'][] = [ key($row) => [ 'order' => current($row) ] ]; } } $queryParams['body']['query']['bool']['filter'] = $filter; $this->queryParams = $queryParams; return $this; } /** * @param bool $isTotal isTotal=true时, 返回总数 * @return array|string */ public function query($isTotal = false) { try { $this->parseQueryParams(); $this->queryParams['body']['highlight'] = [ "fields"=>[ "goods_name" => new \stdClass(), "goods_no" => new \stdClass(), ], "pre_tags"=>"", "post_tags"=>"" ]; //dd($this->queryParams); if ($this->debug) { return \GuzzleHttp\json_encode($this->queryParams); } if ($isTotal === true) { unset( $this->queryParams['from'], $this->queryParams['size'], $this->queryParams['_source'] ); $count = $this->client->count($this->queryParams); return (int)$count['count']; } if (!empty($this->queryParams)) { //高亮 $result = $this->client->search($this->queryParams); $data = [ 'current_page' => 1, 'data' => [], 'last_page' => 0, 'per_page' => $this->queryParams['size'], 'total' => 0, ]; if (isset($result['hits']) && !empty($result['hits'])) { $list = $result['hits']['hits']; // echo "
";
                    // print_r($list);
                    // exit();
                    $arr = [];
                    foreach ($list as $key => &$value) {
                        $value['_source']['highlight_goods_name'] = isset($value['highlight']['goods_name'][0]) ? $value['highlight']['goods_name'][0] :  (isset($value['highlight']['goods_no'][0]) ? $value['highlight']['goods_no'][0] : "");
                        $arr[] = $value['_source'];
                    }
                    $data['current_page'] = $this->queryParams['current_page'];
                    $data['data'] = $arr;
                    $data['last_page'] = ceil($result['hits']['total']['value'] / $this->queryParams['size']);
                    $data['per_page'] = $this->queryParams['size'];
                    $data['total'] = $result['hits']['total']['value'];
                }

                return $data;
            }
        } catch (\Exception $e) {
            $msg = $e->getMessage();
            //$msg = '服务器开小差了~';
            throw new Exception($msg);
        }
    }

    /**
     * 添加文档
     * @param $id
     * @param $doc ['id'=>100, 'title'=>'phone']
     * @param string $index_name
     * @param string $type_name
     */
    public function addDoc($id, $doc, string $type_name = '_doc')
    {
        $params = [
            'index' => $this->index,
            'type' => $type_name,
            'id' => $id,
            'body' => $doc
        ];

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


    /**
     * 添加子文档
     * @param $id
     * @param $doc
     * @param string $type_name
     */
    public function addChildDoc($id, $parent_id, $doc, string $type_name = '_doc')
    {
        $params = [
            'index' => $this->index,
            'type' => $type_name,
            'id' => $id,
            'routing' => $parent_id,//与父文档在同一个分区
            'body' => $doc
        ];
        // var_dump($params);
        // exit;
        return $this->client->index($params);
    }

    public function getIndex()
    {
        return $this->index;
    }

    /**
     * 返回ES实例
     */

    public static function setEs($index = '')
    {
        $class = get_called_class();
        if (!self::$instance || !self::$instance instanceof $class) {
            self::$instance = new static();
        }
        if ($index) {
            self::$instance->index = $index;
        }
        return self::$instance;

    }

    protected function settings()
    {
        return [
            'number_of_shards' => 1,
            'number_of_replicas' => 0,
            'analysis' => [
                'analyzer' => [
                    'ik' => [
                        'tokenizer' => 'ik_max_word'
                    ]
                ]
            ],
        ];
        // return [
        //     'number_of_shards' => 1,
        //     'number_of_replicas' => 0,
        //     'analysis' => [
        //         'analyzer' => [
        //             'default' => [
        //                 'type' => 'ik_smart'
        //             ]
        //         ]
        //     ],
        // ];
    }

    /**
     * 判断文档存在
     * @param int $id
     * @param string $type_name
     * @return bool
     */
    public function existsDoc(int $id = 1, string $type_name = '_doc'): bool
    {
        $params = [
            'index' => $this->index,
            'type' => $type_name,
            'id' => $id
        ];

        return $this->client->exists($params);

    }

    /**
     * 获取文档
     * @param int $id
     * @param string $index_name
     * @param string $type_name
     * @return array
     */
    public function getDoc(int $id = 1, string $type_name = '_doc'): array
    {
        $params = [
            'index' => $this->index,
            'type' => $type_name,
            'id' => $id
        ];

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

    /**
     * 更新文档
     * @param int $id
     * @param string $index_name
     * @param string $type_name
     * @param array $body
     * @return array
     */
    public function updateDoc(int $id = 1, array $body = [], string $type_name = '_doc'): array
    {
        //  可以灵活添加新字段,最好不要乱添加
        $params = [
            'index' => $this->index,
            'type' => $type_name,
            'id' => $id,
            'body' => $body
        ];

        return $this->client->update($params);
    }


    /**
     * 搜索文档:分页,排序,权重,过滤
     * @param string $index_name
     * @param string $type_name
     * @param array $body
     * @return array
     */
    public function searchDoc(array $body = [],  string $type_name = "_doc"): array
    {
        $params = [
            'index' => $this->index,
            'type' => $type_name,
            'body' => $body
        ];

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

    /**
     * @return bool
     * @throws Exception
     */

    public function createIndexNew($mapping)
    {
        try {
            $params = [
                'index' => $this->index
            ];

            $check = $this->indices()->exists($params);

            if ($check->getStatusCode() == 200) {
                throw new Exception('index: ' . $this->index . ' already exists');
            }

            $params = [
                'index' => $this->index,
                'body' => [
                    'settings' => $this->settings(),
                    'mappings' => [
                        '_source' => [
                            'enabled' => true,
                        ],
                        
                        'properties' => $mapping['properties']
                    ]
                ]
            ];

            //dd($params);

            $result = $this->indices()->create($params);

            return $result['acknowledged'] === true;

        } catch (\Exception $e) {

            throw new Exception($e->getMessage());

        }

    }

    /**

     * 删除索引

     *

     * @return bool

     * @throws Missing404Exception

     */

    public function deleteIndexNew()
    {

        try {

            $params = [

                'index' => $this->index

            ];

            $check = $this->indices()->exists($params);

            if (!$check) {

                return true;

            }

            $result = $this->indices()->delete([

                'index' => $this->index

            ]);

            return $result['acknowledged'] === true;

        } catch (Missing404Exception $e) {

            throw new Missing404Exception('no such index ' . $this->index);

        }

    }

    /**

     * 批量写入

     * type 1.增加/修改 2.删除

     * $data = [

     *      ['id' => 1, 'name' => 'llf', 'age' => 30],

     *      ['id' => 1, 'name' => 'llf', 'age' => 30],

     *      ['id' => 1, 'name' => 'llf', 'age' => 30],

     *      ['id' => 1, 'name' => 'llf', 'age' => 30],

     * ];

     * @param array $data

     */

    public function doBulkDocument($type = 1,$data = [])
    {
        try {

            $params = ['body' => []];

            if($type == 1){

                foreach ($data as $key => $row) {

                    $params['body'][] = [

                        'index' => [

                            '_index' => $this->index,

                            '_id' => $row['info_id'].'-'.$row['info_type']

                        ]

                    ];

                    $params['body'][] = $row;

                    if (($key+1)%10000 == 0) {

                        $this->client->bulk($params);

                        $params = ['body' => []];

                    }

                }

            }else{

                foreach ($data as $key => $row) {

                    $params['body'][] = [

                        'delete' => [

                            '_index' => $this->index,

                            '_id' => $row['info_id'].'-'.$row['info_type']

                        ]

                    ];

                }

            }

            if (!empty($params['body'])) {

                $this->client->bulk($params);

                return true;

            }

        } catch (\Exception $e) {

            throw new Exception($e->getMessage());

        }

    }

    /**
     * 更新索引
     * @return array @todo
     */

    public function updateIndex($mappings)
    {

        $putParams = [

            'index' => $this->index,

            //'type' => '_doc',

            'body' => [

                'properties' => $mappings

            ]

        ];

        return $this->indices()->putMapping($putParams);

    }

    /**
     * 方便调试, 直接返回拼接的query
     * @return $this
     */
    public function setDebug()
    {
        $this->debug = true;
        return $this;
    }

    private function indices()
    {
        return $this->client->indices();
    }

    public function analyze($text = '')
    {
        $params = [

            'index' => $this->index,

            'body' => [

                'analyzer' => 'ik_smart',

                'text' => $text

            ]

        ];

        return $this->indices()->analyze($params);
    }

    public function update($id = 0, $updateParams = [])
    {
        $params = [

            'index' => $this->index,

            'id' => $id,

            'body' => [

                'doc' => $updateParams

            ]

        ];

        return $this->client->update($params);
    }
}