<?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);
}
}