zincsearch服务类封装
<?php namespace addons\zincsearchplug\service; use Exception; use GuzzleHttp\Client; use GuzzleHttp\Exception\GuzzleException; use Psr\Http\Message\ResponseInterface; use app\model\system\Config; use think\Env; class ZincsearchService { protected $client; public function __construct() { $zincsearch_hosts = Config::build()->where('id', 1)->find(); $this->client = new Client([ 'auth' => [$zincsearch_hosts['zincsearch_name'], $zincsearch_hosts['zincsearch_password']], 'base_uri' => $zincsearch_hosts['zincsearch_hosts'], 'timeout' => 3 ]); $this->projectName = 'liangzhu_'; // 索引名(项目名) } public static function build() { return new self(); } /** * 处理json数据 * @param ResponseInterface $resp * @return mixed * @throws Exception */ public function handleJson(ResponseInterface $resp) { if ($resp->getStatusCode() !== 200) { throw new Exception('请求失败,失败原因:' . $resp->getBody(), $resp->getStatusCode()); } $body = $resp->getBody(); return json_decode($body, true); } /** * 获取zincSearch版本 * @return mixed * @throws GuzzleException * @throws Exception */ public function version() { $resp = $this->client->get('/version'); return $this->handleJson($resp); } /** * 批量新增或者批量更新 * @param $index string 索引 * @param $primaryKey string 主键 * @param $data array 数据 * @return mixed * @throws GuzzleException * @throws Exception */ public function bulk($index, $primaryKey, array $data) { $params = []; $key = 0; $indexName = $this->projectName . $index; foreach ($data as $v) { $params[$key]['index'] = [ '_index' => $indexName, '_id' => strval($v[$primaryKey]), ]; $key++; $params[$key] = $v; $key++; } // 把数组转化成ndjson $ndjson = ""; foreach ($params as $key => $item) { $json = json_encode($item); if (isset($params[$key + 1])) { $ndjson .= $json . PHP_EOL; } else { $ndjson .= $json; } } $resp = $this->client->request('POST', "/api/_bulk", ['body' => $ndjson]); return $this->handleJson($resp); } /** * 批量新增(不能指定zincSearch的主键) * @param $index string 索引 * @param $data array 数据 * @return mixed * @throws GuzzleException * @throws Exception */ public function bulkV2($index, $data) { $indexName = $this->projectName . $index; $params = [ 'index' => $indexName, ]; foreach ($data as $k => $v) { $params['records'][$k] = $v; } $resp = $this->client->request('POST', "/api/_bulkv2", ['json' => $params]); return $this->handleJson($resp); } /** * 新增或者编辑 * @param $index string 索引 * @param $id string 主键的值 * @param $data array 数据 * @return mixed * @throws GuzzleException * @throws Exception */ public function createOrUpdate($index, $id, $data) { $indexName = $this->projectName . $index; $resp = $this->client->request('PUT', "/api/$indexName/_doc/$id", ['json' => $data]); return $this->handleJson($resp); } /** * 删除 * @param $index string 索引 * @param $id string 主键的值 * @return mixed * @throws GuzzleException * @throws Exception */ public function delete($index, $id) { $indexName = $this->projectName . $index; $resp = $this->client->request('DELETE', "/api/$indexName/_doc/$id"); return $this->handleJson($resp); } /** * 搜索 * @param $index string 索引 * @param $keyword string 关键词 * @param $filed array 需要显示的字段 * @param $from int 开始查询的下标,默认为0 * @param $maxResult int 查询的条数,默认为20 * @param $searchType string 搜索模式,默认为match * @return array * @throws GuzzleException * @throws Exception */ public function search($index, $keyword, array $filed = [], $from = 0, $maxResult = 20, $searchType = 'match') { $indexName = $this->projectName . $index; $json = [ 'search_type' => $searchType, 'query' => [ 'term' => $keyword ], 'from' => $from, 'max_results' => $maxResult, '_source' => $filed ]; $resp = $this->client->request('POST', "/api/$indexName/_search", ['json' => $json]); $result = $this->handleJson($resp); $result = $result['hits']['hits']; return array_column($result, '_source'); } /** * 查询删除 * @param [type] $keyName 索引名 * @param [type] $fieldName 字段 * @param [type] $keywords 匹配词 * @param [type] $form 起始位置 * @param [type] $size 条数 * @return void */ function esIndexSearchAllDel($index, $fieldName, $keywords, $form = 0, $size = 10000) { $params = [ 'query' => [ 'bool' => [ 'must' => [ 'term' => [ $fieldName => $keywords ], ], ], ], ]; $params['form'] = $form; $params['size'] = $size; $indexName = $this->projectName . $index; $resp = $this->client->request('POST', "/es/$indexName/_delete_by_query", ['json' => $params]); $result = $this->handleJson($resp); return $result; } /** * 获取统计数量 * @param [type] $keyName 索引名 * @param [type] $fieldName 字段 * @param [type] $keywords 匹配词 * @return void */ function esIndexSearchAllDataTotal($index, $fieldName, $keywords) { $params = [ 'query' => [ 'bool' => [ 'must' => [ 'term' => [ $fieldName => $keywords ], ], ], ], ]; $indexName = $this->projectName . $index; $resp = $this->client->request('POST', "/es/$indexName/_search", ['json' => $params]); $result = $this->handleJson($resp); return $result['hits']['total']['value']; } /** * 文章相似推荐(同模型或同栏目) * @param $index string 索引 * @param $field string 搜索的字段名 * @param $keyword string 关键词 * @param $id string 主键的值(相似推荐排除自己) * @param $form int 开始查询的下标,默认为0 * @param $size int 查询的条数,默认为20 * @param $searchTypeKey string 同模型或同栏目 * @param $searchTypeValue int 栏目或模型id * @param $order string 排序 * @return array * @throws GuzzleException * @throws Exception */ public function esSameRecommend($index, $field, $keyword, $id, $form = 0, $size = 20, $searchTypeKey, $searchTypeValue, $order) { $indexName = $this->projectName . $index; $searchTypeValueMin = $searchTypeValue - 1; $searchTypeValueMax = $searchTypeValue + 1; $json = [ 'query' => [ 'bool' => [ 'must' => [ 'match' => [ $field => [ 'query' => $keyword, ] ] ], // 排除id 'must_not' => [ 'match' => [ 'id' => [ 'query' => $id ] ] ], // 复杂的搜索,筛选出mid等于x的 'filter' => [ 'range' => [ $searchTypeKey => [ 'gt' => $searchTypeValueMin, 'lt' => $searchTypeValueMax ] ] ], ] ], ]; if ($size != 0) { $json['from'] = $form; $json['size'] = $size; } if (!empty($order)) { $json['sort'] = [ ['create_time' => ['order' => $order]] ]; } $resp = $this->client->request('POST', "/es/$indexName/_search", ['json' => $json]); $result = $this->handleJson($resp); $result = $result['hits']['hits']; return array_column($result, '_source'); } /** * 文章相似推荐 * @param $index string 索引 * @param $field string 搜索的字段名 * @param $keyword string 关键词 * @param $id string 主键的值(相似推荐排除自己) * @param $form int 开始查询的下标,默认为0 * @param $size int 查询的条数,默认为20 * @param $order string 排序 * @return array * @throws GuzzleException * @throws Exception */ public function esRecommend($index, $field, $keyword, $id, $form = 0, $size = 20, $order) { $indexName = $this->projectName . $index; $json = [ 'query' => [ 'bool' => [ 'must' => [ 'match' => [ $field => [ 'query' => $keyword, ] ] ], // 排除id 'must_not' => [ 'match' => [ 'id' => [ 'query' => $id ] ] ], ] ], ]; if ($size != 0) { $json['from'] = $form; $json['size'] = $size; } if (!empty($order)) { $json['sort'] = [ ['create_time' => ['order' => $order]] ]; } $resp = $this->client->request('POST', "/es/$indexName/_search", ['json' => $json]); $result = $this->handleJson($resp); $result = $result['hits']['hits']; return array_column($result, '_source'); } /** * 按条件获取数据列表 * @param $index string 索引 * @param $params array 搜索的条件 * @return array * @throws GuzzleException * @throws Exception */ public function request($index, $params, $sourceType = 1) { $indexName = $this->projectName . $index; $resp = $this->client->request('POST', "/es/$indexName/_search", ['json' => $params]); $result = $this->handleJson($resp); if ($sourceType == 1) { $result = $result['hits']['hits']; return array_column($result, '_source'); } else { return $result; } } /** * 搜索 * @param $index string 索引 * @param $field string 搜索的字段名 * @param $keyword string 关键词 * @param $form int 开始查询的下标,默认为0 * @param $size int 查询的条数,默认为20 * @param $order string 排序 * @return array * @throws GuzzleException * @throws Exception */ public function esSearch($index, $field, $keyword, $form = 0, $size = 20, $order) { $indexName = $this->projectName . $index; $json = [ 'query' => [ 'bool' => [ 'must' => [ 'match' => [ $field => [ 'query' => $keyword, ] ] ], ] ], ]; if ($size != 0) { $json['from'] = $form; $json['size'] = $size; } if (!empty($order)) { $json['sort'] = [ ['create_time' => ['order' => $order]] ]; } $resp = $this->client->request('POST', "/es/$indexName/_search", ['json' => $json]); $result = $this->handleJson($resp); $result = $result['hits']['hits']; return array_column($result, '_source'); } /** * 获取统计数量 * @param [type] $keyName 索引名 * @param [type] $fieldName 字段 * @param [type] $keywords 匹配词 * @return void */ function esSearchDataTotal($index, $fieldName, $keywords) { $params = [ 'query' => [ 'bool' => [ 'must' => [ 'match' => [ $fieldName => $keywords ], ], ], ], ]; $indexName = $this->projectName . $index; $resp = $this->client->request('POST', "/es/$indexName/_search", ['json' => $params]); $result = $this->handleJson($resp); return $result['hits']['total']['value']; } /** * 判断指定的索引名是否存在 * @param indexName 索引名 * @return 存在:1; 不存在:0; */ function isExistsIndex($index) { $indexName = $this->projectName . $index; try { $resp = $this->client->request('GET', "/api/index/$indexName"); $result = 1; } catch (\Throwable $th) { $result = 0; } return $result; } /** * 创建索引 * @param [type] $keyName 索引名 * @return void */ function addZincsearchIndex($index) { // 如果索引已存在,则直接返回true if ($this->isExistsIndex($index)) { return true; } $indexName = $this->projectName . $index; $params = [ 'name' => $indexName, 'storage_type' => 'disk', 'shard_num' => 3, // 服务器分片数,默认为3 'mappings' => [ 'properties' => [ 'title' => [ 'type' => 'text', 'index' => true, 'store' => true, 'highlightable' => false, 'sortable' => false, 'aggregatable' => false, 'analyzer' => 'gse_standard', ], 'mid' => [ "type" => "numeric", "index" => true, "store" => false, "sortable" => true, "aggregatable" => true, "highlightable" => false ], 'cid' => [ "type" => "numeric", "index" => true, "store" => false, "sortable" => true, "aggregatable" => true, "highlightable" => false ], 'id' => [ "type" => "numeric", "index" => true, "store" => false, "sortable" => true, "aggregatable" => true, "highlightable" => false ], 'create_time' => [ 'type' => 'keyword', 'index' => false, 'store' => true, 'highlightable' => false, 'sortable' => false, 'aggregatable' => false, ], 'desc' => [ 'type' => 'keyword', 'index' => false, 'store' => true, 'highlightable' => false, 'sortable' => false, 'aggregatable' => false, ], 'cover' => [ 'type' => 'keyword', 'index' => false, 'store' => true, 'highlightable' => false, 'sortable' => false, 'aggregatable' => false, ], 'click' => [ 'type' => 'keyword', 'index' => false, 'store' => true, 'highlightable' => false, 'sortable' => false, 'aggregatable' => false, ], 'uuid' => [ 'type' => 'keyword', 'index' => false, 'store' => true, 'highlightable' => false, 'sortable' => false, 'aggregatable' => false, ], 'user_id' => [ 'type' => 'keyword', 'index' => false, 'store' => true, 'highlightable' => false, 'sortable' => false, 'aggregatable' => false, ], ] ] ]; $resp = $this->client->request('POST', "/api/index", ['json' => $params]); return $this->handleJson($resp); } }