php Elasticsearch使用,Elasticsearch类,原创在用
- thinkphp5框架为例,composer下载es扩展到vender目录
- 自己随便弄台服务器,安装es
- 使用kibana来管理你的es数据库会很爽
- 创建索引是注意自己的索引数组配置,es不像数据库一样,创建好表结构是不能改的,所以每次改结构都必须以版本的形式替换(比如第一版:shop_v1.0,改一次就再创建一个shop_v1.1)
先呈上es类:
我的目录结构是
/Users/smt/www/ups-api/extend/searchEngine/EsModel.php
<?php
namespace searchEngine;
use Elasticsearch\ClientBuilder;
/**
* ES 操作类
*
* mysql 中的数据库 对应 ES 中的 index:索引
* mysql 中的数据表 对应 ES 中的 type:类型
* mysql 中的记录行 对应 ES 中的 Document:文档
* mysql 中的表结构 对应 ES 中的 Mapping :文档模板
* Class Elasticsearch
*
*/
class EsModel
{
private $client;
public $index_name;
private $es_host;
private $es_username;
private $es_password;
/**
* 构造函数
* EsModel constructor.
*/
public function __construct( $host='', $index='', $user='', $pass='' )
{
// 地址:端口
$this->es_host = empty($host) ? config('es_conf.es_host') . ':' . config('es_conf.es_port') : $host;
// 默认数据库 - darlar_test
$this->index_name = empty($index) ? config('es_conf.es_index') : $index;
// username
$this->es_username = empty($user) ? config('es_conf.es_username') : $user;
// password
$this->es_password = empty($pass) ? config('es_conf.es_password') : $pass;
$params = [ $this->es_host ];
$this->client = ClientBuilder::create()->setBasicAuthentication($this->es_username,$this->es_password)->setHosts($params)->build();
}
/**
* Explain: 测试方法
* User: smt \(^o^)/~
* Date: 2020-09-15 20:39
*/
public function index()
{
$docs = [];
$docs[] = ['fid' => '1', 'name' => '小明', 'profile' => '我做的ui界面强无敌。', 'score' => 120];
$docs[] = ['fid' => '2', 'name' => '小张', 'profile' => '我的php代码无懈可击。', 'score' => 50];
$docs[] = ['fid' => '3', 'name' => '小杨', 'profile' => '为所欲为,不行就删库跑路。', 'score' => 89];
foreach ($docs as $k => $v) {
$r = $this->addDoc($v['fid'], $v); //3.添加文档
}
$keywords = "删库 别烦我";
$body = [
'query' => [
'bool' => [
'should' => [
['match' => ['profile' => [
'query' => $keywords,
'boost' => 3, // 权重大
]]],
['match' => ['name' => [
'query' => $keywords,
'boost' => 2,
]]],
],
],
],
'sort' => ['score'=>['order'=>'desc']]
// , 'from' => $from, 'size' => $size
];
$r = $this->searchDoc($body); //4.搜索结果
dump($r);die;
}
/**
* Explain: 创建索引 - 创建数据库
* @param string $index_name
* @return array|mixed|string
* User: smt \(^o^)/~
* Date: 2020-09-15 20:41
*/
public function createIndex($index_name = '')
{
if (empty($index_name)) return false;
// 只能创建一次
$params = [
'index' => $index_name,
'body' => [
'settings'=>[
'number_of_shards' => 3,
'number_of_replicas' => 0,
'analysis' => [
'tokenizer' => ['comma' => ['type' => 'pattern', 'pattern' => ',']],
'analyzer' => ['comma' => ['type' => 'custom', 'tokenizer' => 'comma']]
]
],
'mappings'=>[
'properties' => [
'fid' => ['type' => 'integer'], // 搭配id
'fsku' => ['type' => 'keyword'], // 搭配编码
'fcate_id' => ['type' => 'text', 'analyzer' => 'comma', 'search_analyzer' => 'comma'], // 搭配分类id
'fimage' => ['type' => 'keyword'], // 搭配封面
'ftag' => ['type' => 'text', 'analyzer' => 'comma', 'search_analyzer' => 'comma'], // 标签,搜索关键词
'season' => ['type' => 'text', 'analyzer' => 'comma', 'search_analyzer' => 'comma'], // 季节
'fname' => ['type' => 'text', 'analyzer' => 'ik_max_word', 'search_analyzer' => 'ik_max_word'], // 搭配名称
'fname_standard' => ['type' => 'text', 'analyzer' => 'standard', 'search_analyzer' => 'standard'], // 搭配名称
'fproduct_prices' => ['type' => 'float'], // 搭配原价
'fprice' => ['type' => 'float'], // 折扣价
'add_time' => ['type' => 'long'], // 添加时间
'is_show' => ['type' => 'integer'], // 是否上架
'fdid' => ['type' => 'integer'], // 搭配师id
'sort' => ['type' => 'integer'], // 排序
'goods_type' => ['type' => 'integer'], // 男装女装分类
'sale_count' => ['type' => 'integer'], // 销量
'is_hot' => ['type' => 'integer'], // 热门推荐:是(1),否(0)
'top_new_id' => ['type' => 'integer'], // 上新类型id
'new_time' => ['type' => 'long'], // 上新时间
'is_del' => ['type' => 'integer'], // 是否删除 1是,0否
'height_start' => ['type' => 'integer'], // 最低身高
'height_end' => ['type' => 'integer'], // 最高身高
'weight_start' => ['type' => 'integer'], // 最小体重
'weight_end' => ['type' => 'integer'], // 最大体重
'uid' => ['type' => 'long'], // 创建人id
'up_time' => ['type' => 'long'], // 更新时间
'crowd_classify_str' => ['type' => 'text','analyzer' => 'comma', 'search_analyzer' => 'comma'], // 人群
'preference_classify_str' => ['type' => 'text','analyzer' => 'comma', 'search_analyzer' => 'comma'], // 偏好
'style_classify_str' => ['type' => 'text','analyzer' => 'comma', 'search_analyzer' => 'comma'], // 风格
'scene_classify_str' => ['type' => 'text','analyzer' => 'comma', 'search_analyzer' => 'comma'], // 场景
'grounding_time' => ['type' => 'long'], // 上架时间
'special_id_str' => ['type' => 'text', 'analyzer' => 'comma', 'search_analyzer' => 'comma'], // 专题id
'body_shape' => ['type' => 'text','analyzer' => 'comma', 'search_analyzer' => 'comma'], // 身型
'fdid_username' => ['type' => 'keyword'], // 搭配师
'uid_name' => ['type' => 'keyword'], // 创建人
'cover_width' => ['type' => 'integer'], // 封面宽度
'cover_height' => ['type' => 'integer'], // 封面高度
'kol_fprice' => ['type' => 'float'], // kol折扣价
'discount' => ['type' => 'float'], // 折扣比例
'size_str' => ['type' => 'text', 'analyzer' => 'comma', 'search_analyzer' => 'comma'], // 各个规格尺码
'colour_str' => ['type' => 'text', 'analyzer' => 'comma', 'search_analyzer' => 'comma'], // 各个规格颜色
]
]
]
];
try {
return $this->client->indices()->create($params);
} catch (Elasticsearch\Common\Exceptions\BadRequest400Exception $e) {
$msg = $e->getMessage();
$msg = json_decode($msg, true);
return $msg;
}
}
/**
* Explain: 删除索引 - 删除数据库
* @param string $index_name 索引名称(数据库)
* @return array
* User: smt \(^o^)/~
* Date: 2020-09-15 20:42
*/
public function deleteIndex($index_name = '')
{
if (empty($index_name)) return false;
$params = ['index' => $index_name];
try {
$response = $this->client->indices()->delete($params);
return $response;
} catch (Elasticsearch\Common\Exceptions\BadRequest400Exception $e) {
$msg = $e->getMessage();
$msg = json_decode($msg, true);
return $msg;
}
}
/**
* Explain: 创建文档模板 - 创建表结构
* @param array $params 参数
* @return array
* User: smt \(^o^)/~
* Date: 2020-09-15 20:43
*/
public function createMappings($params)
{
try {
$response = $this->client->indices()->putMapping($params);
return $response;
} catch (Elasticsearch\Common\Exceptions\BadRequest400Exception $e) {
$msg = $e->getMessage();
$msg = json_decode($msg, true);
return $msg;
}
}
/**
* Explain: 查看映射
* @param string $index_name
* @return array
* User: smt \(^o^)/~
* Date: 2020-09-15 20:46
*/
public function getMapping( $index_name = '')
{
if (empty($index_name)) $index_name = $this->index_name;
$params = [
'index' => $index_name,
];
$response = $this->client->indices()->getMapping($params);
return $response;
}
/**
* 添加新字段
* author: jason
* @param $fieldName 字段名 :name
* @param $fieldParam 字段参数 :['type'=>'text','analyzer'=>'comma','search_analyzer'=>'comma']
* @return array
*/
public function addField($fieldName, $fieldParam)
{
$index = $this->index_name;
$params = [
'index' => $index,
'body' => [
'properties' => [
$fieldName => $fieldParam
]
],
];
return $this->client->indices()->putMapping($params);
}
/**
* Explain: 添加文档
* @param $_id 文档id
* @param $data 要添加的数据
* @param string $index_name
* @return array
* User: smt \(^o^)/~
* Date: 2020-09-15 20:46
*/
public function addDoc($_id = 0, $data = [], $index_name = '')
{
if (empty($_id)) return false;
if (empty($data)) return false;
if (empty($index_name)) $index_name = $this->index_name;
$params = [
'index' => $index_name,
'id' => $_id,
'body' => $data
];
$response = $this->client->index($params);
return $response;
}
/**
* Explain: 判断文档存在
* @param int $id
* @param string $index_name
* @return bool
* User: smt \(^o^)/~
* Date: 2020-09-15 20:47
*/
public function existsDoc($id = 0, $index_name = '')
{
if (empty($id)) return false;
if (empty($index_name)) $index_name = $this->index_name;
$params = [
'index' => $index_name,
'id' => $id
];
$response = $this->client->exists($params);
return $response;
}
/**
* Explain: 获取文档
* @param int $id
* @param string $index_name
* @return array
* User: smt \(^o^)/~
* Date: 2020-09-15 20:48
*/
public function getDoc($id = 0, $index_name = '')
{
if (!$id) return false;
if (empty($index_name)) $index_name = $this->index_name;
$params = [
'index' => $index_name,
'id' => $id
];
$response = $this->client->get($params);
return $response;
}
/**
* Explain: 更新文档
* @param int $id
* @param array $data 要更新的数据
* @param string $index_name
* @return array
* User: smt \(^o^)/~
* Date: 2020-09-15 20:48
*/
public function updateDoc($id = 0,$data = [], $index_name = '')
{
if (empty($id)) return false;
if (empty($index_name)) $index_name = $this->index_name;
// 可以灵活添加新字段,最好不要乱添加
$params = [
'index' => $index_name,
'id' => $id,
'body' => [
'doc' => $data
]
];
$response = $this->client->update($params);
return $response;
}
/**
* Explain: 删除文档
* @param int $id
* @param string $index_name
* @return array
* User: smt \(^o^)/~
* Date: 2020-09-15 20:48
*/
public function deleteDoc($id = 0, $index_name = '')
{
if (empty($id)) return false;
if (empty($index_name)) $index_name = $this->index_name;
$params = [
'index' => $index_name,
'id' => $id
];
$response = $this->client->delete($params);
return $response;
}
/**
* Explain: 查询文档 (分页,排序,权重,过滤)
* @param string $index_name
* @param array $body
* @return array
* User: smt \(^o^)/~
* Date: 2020-09-15 20:48
*/
public function searchDoc($body = [],$index_name = '')
{
if (empty($index_name)) $index_name = $this->index_name;
$params = [
'index' => $index_name,
'body' => $body
];
$results = $this->client->search($params);
return $results;
}
/**
* Explain: 批量操作文档
* @param $params
* @return array
* User: smt \(^o^)/~
* Date: 2020-09-18 19:55
*/
public function bulkDoc($params){
$results = $this->client->bulk($params);
return $results;
}
}
基本使用:
/**
* Explain: 批量同步搭配商品到es 每天20分钟执行一次
* User: smt \(^o^)/~
* Date: 2020-09-17 20:53
* php think essync batchSyncFullToes
*/
public function batchSyncFullToes()
{
$FullModel = new StoreFull();
// 校验es连通性
$is_connect = checkElasticIsConnectionFail(2,0);
if (empty($is_connect)) exit('es_error');
// 实例化es
$esModel = new EsModel();
$index_name = $esModel->index_name; // 索引名称
// $esModel = new EsModel('xxx.xxx.xxx.xxx:9200','test_02','darlar_es','xxxxxxxx');
// $idd = $esModel->existsDoc('XJ2020083596',$index_name); // 查询文档是否存在
// dump($idd);die;
// 删除库
// $re = $esModel->deleteIndex($index_name);
// dump($re);die;
// 创建库
// $re = $esModel->createIndex($index_name);
// dump($re);die;
ignore_user_abort();//关掉浏览器,PHP脚本也可以继续执行.
set_time_limit(0);
ini_set('memory_limit', '500M');
ob_start();//打开缓冲区
// 记录时间
$start_time = date('Y-m-d H:i:s');
echo "start_time ~ $start_time \n";
$time_start = microtime(true); // 计算耗时
ob_flush();flush();// 刷新缓冲区
$page_size = 50; // 每页条数
$where = [
'is_del' => 0,
'is_show' => 1,// 已上架的搭配
'fsku' => ['neq', '']
];
$total = $FullModel->where($where)->field('fsku,count(fsku) as num ')->group('fsku desc')->count(); //总条数
$total_page = ceil($total / $page_size); // 总页数
$sum = 0;
echo "total: ~ $total \n";
ob_flush();flush();// 刷新缓冲区
// 取参数
$condition = ElasticService::getParams(1);
$condition['is_show'] = 1;
for ($i = 1; $i <= $total_page; $i++) {
$offset = ($i - 1) * $page_size;
$storeFull = $FullModel->where($where)->field('fsku,count(fsku) as num ')->group('fsku desc')->limit($offset, $page_size)->select()->toArray();
if (empty($storeFull)) {
break;
} else {
// 批量格式化搭配数据
$data = ElasticService::formatFull($storeFull, $condition);
$params['body'] = [];
foreach ($data as $key => $val) {
$params['body'][] = [
'index' => [ #创建或替换
'_index' => $index_name,
'_id' => $val['fsku'],
]
];
$params['body'][] = $val;
$sum++;
}
$res = $esModel->bulkDoc($params);
// 判断异常
if ( !isset($res['errors']) || $res['errors'] !== false ) {
add_debug_log($res, 'batchSyncFullToes', '批量同步搭配数据到ES出错');
echo 'add_error_' . $i; exit();
}
}
// 输出数量
if ($sum > 0) echo "add_total:$sum ~ " . date('Y-m-d H:i:s') . " \n";
ob_flush();flush();// 刷新缓冲区
// sleep(1); // 暂停数秒
}
$end_time = date('Y-m-d H:i:s');
echo "end_time ~ $end_time \n";
ob_flush();flush();// 刷新缓冲区
// 统计耗时
$time_end = microtime(true);
$time = $time_end - $time_start;
exit('add ok ~ add_total: ' . $sum . ', time:' . $time);
}
end