<?php
/**
* Created by PhpStorm.
* Date: 2021/6/24
* Time: 14:59
*/
namespace tools\es;
use Elasticsearch\ClientBuilder;
use traits\think\Instance;
class MyElasticsearch
{
//ES客户端链接
private $client;
/*
* 构造函数
* 链接es
*/
public function __construct()
{
$params = ['127.0.0.1:9200'];
$this->client = ClientBuilder::create()->setHosts($params)->build();
}
/*
* 判断索引是否存在
* @$param string $index_name
* @return bool|mixed|string
*/
public function exists_index($index_name = 'test_ik')
{
$params = [
'index' => $index_name
];
try {
return $this->client->indices()->exists($params);
} catch (\Elasticsearch\Common\Exceptions\BadRequest400Exception $e) {
$msg = $e->getMessage();
$msg = json_decode($msg, true);
return $msg;
}
}
/*
* 创建索引
* @param string $index_name
* @return array|mixed|string
*/
public function create_index($index_name = 'test_index')
{
$params = [
'index' => $index_name,
'body' => [
'settings' => [
'number_of_shards' => 5,
'number_of_replicas' => 0
]
]
];
try {
return $this->client->indices()->create($params);
} catch (\Elasticsearch\Common\Exceptions\BadRequest400Exception $e) {
$msg = $e->getMessage();
$msg = json_decode($msg, true);
return $msg;
}
}
/*
* 删除索引
* @param string $index_name
* @return array
*/
public function delete_index($index_name = 'test_es')
{
$params = [
'index' => $index_name
];
try {
return $this->client->indices()->delete($params);
} catch (\Elasticsearch\Common\Exceptions\BadRequest400Exception $e) {
$msg = $e->getMessage();
$msg = json_decode($msg, true);
return $msg;
}
}
/*
* 添加文档
* $param $id
* $param $doc ['id' => 100,'title' => 'iphone']
* $param string $index_name
* $param string $type_name
* @return array
*/
public function add_doc($id, $doc, $index_name = 'test_es', $type_name = 'goods')
{
//参数绑定
$params = [
'index' => $index_name,
'type' => $type_name,
'id' => $id,
'body' => $doc
];
//添加文档
$response = $this->client->index($params);
//返回数据
return $response;
}
/*
* 判断文档是否存在
* @param int id
* @param string $index_name
* $param string $type_name
* @param array|bool
*/
public function exists_doc($id, $index_name, $type_name)
{
$params = [
'id' => $id,
'index_name' => $index_name,
'type_name' => $type_name,
];
$response = $this->client->exists($params);
return $response;
}
/*
* 获取文档内容
* @param int $id
* @param string $index_name
* @param string $type_name
* @return array
*/
public function get_doc($id, $index_name, $type_name)
{
$params = [
'id' => $id,
'index_name' => $index_name,
'type_name' => $type_name,
];
$response = $this->client->get($params);
return $response;
}
/*
* 更新文档
* @param int $id
* @param string $index
* @param string $type_name
* @param array body ['doc' => ['title' => '商品的信息']]
* @return array
*/
public function update_doc($id, $index_name, $type_name, $body = [])
{
$params = [
'id' => $id,
'index_name' => $index_name,
'type_name' => $type_name,
'body' => $body,
];
$response = $this->client->update($params);
}
/*
* 删除文档
* @param int $id
* @param string $index_name
* @param string type_name
* @return array
*/
public function del_doc($id, $index_name, $type_name)
{
$params = [
'id' => $id,
'index_name' => $index_name,
'type_name' => $type_name,
];
$response = $this->client->delete($params);
return $response;
}
/*
* 搜索文档(分页、权重、排序、过滤)
* @param string $index_name
* @param string $type_name
* $params array $body
* $body =[
'query' => [
'bool' => [
'should' =>[
[
'match' =>[
'cate_name' => [
'query' => $keyword,
'boost' => 4, //权重大
]
]
],
[
'match' =>[
'goods_name' => [
'query' => $keyword,
'boost' => 3, //权重大
]
]
],
[
'match' =>[
'goods_introduce' => [
'query' => $keyword,
'boost' => 2, //权重大
]
]
],
],
],
],
'sort' => ['id' => ['order' => 'desc']],
'from' => $from,
'size' => $size
];
* @return array
*/
public function search_doc($index_name, $type_name, $body = [])
{
$params = [
'index' => $index_name,
'type' => $type_name,
'body' => $body,
];
$response = $this->client->search($params);
return $response;
}
}
ElasticSearch基本使用
节点与集群
Elastic 本质上是一个分布式数据库,允许多台服务器协同工作,每台服务器可以运行多个 Elastic 实例。
单个 Elastic 实例称为一个节点(node)。一组节点构成一个集群(cluster)。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yDRjk7G1-1624800902177)(img/15356465131.png)]
索引
在Elasticsearch中存储数据的行为就叫做索引(indexing)
在Elasticsearch中,文档归属于一种类型(type),而这些类型存在于**索引(index)**中
类比传统关系型数据库:
Relational DB -> Databases -> Tables -> Rows -> Columns
Elasticsearch -> Indices -> Types -> Documents -> Fields
Elasticsearch集群可以包含多个索引(indices)(数据库)
每一个索引可以包含多个类型(types)(表)
每一个类型包含多个文档(documents)(行)
然后每个文档包含多个字段(Fields)(列)。
商品搜索功能
搜索规则
可根据关键词对商品名称、商品介绍、商品分类进行全文搜索
创建商品全量索引
<?php
namespace app\cli\controller;
use think\Controller;
use think\Request;
class Es extends Controller
{
/**
* 创建商品索引并导入全部商品文档
* cd public
* php index.php /cli/Es/createAllGoodsDocs
*/
public function createAllGoodsDocs()
{
try{
//实例化ES工具类
$es = new \tools\es\MyElasticsearch();
//创建索引
if($es->exists_index('goods_index')) $es->delete_index('goods_index');
$es->create_index('goods_index');
$i = 0;
while(true){
//查询商品数据 每次处理1000条
$goods = \app\common\model\Goods::with('category')->field('id,goods_name,goods_desc, goods_price,goods_logo,cate_id')->limit($i, 1000)->select();
if(empty($goods)){
//查询结果为空,则停止
break;
}
//添加文档
foreach($goods as $v){
unset($v['cate_id']);
//调用封装的方法进行添加文档
$es->add_doc($v['id'],$v, 'goods_index', 'goods_type');
}
$i += 1000;
}
die('success');
}catch (\Exception $e){
$msg = $e->getMessage();
die($msg);
}
}
}
在thinkphp的public目录下执行
php index.php /cli/Es/createAllGoodsDocs
将数据表中的数据存储到es中
控制器部分
public function index($id=0)
{
//接收参数
$keywords = input('keywords');
if(empty($keywords)){
//获取指定分类下商品列表
if(!preg_match('/^\d+$/', $id)){
$this->error('参数错误');
}
//查询分类下的商品
$list = \app\common\model\Goods::where('cate_id', $id)->order('id desc')->paginate(10);
//查询分类名称
$category_info = \app\common\model\Category::find($id);
$cate_name = $category_info['cate_name'];
}else{//进行全文索引查询
try{
//从ES中搜索
$list = \app\home\logic\GoodsLogic::search();
$cate_name = $keywords;
}catch (\Exception $e){
$this->error('服务器异常');
}
}
return view('index', ['list' => $list, 'cate_name' => $cate_name]);
}
搜索逻辑部分
<?php
namespace app\home\logic;
use think\Controller;
class GoodsLogic extends Controller
{
public static function search(){
//实例化ES工具类
$es = new \tools\es\MyElasticsearch();
//计算分页条件
$keywords = input('keywords');
$page = input('page', 1);
$page = $page < 1 ? 1 : $page;
$size = 10;
$from = ($page - 1) * $size;
//组装搜索参数体
$body = [
'query' => [
'bool' => [
'should' => [
[ 'match' => [ 'cate_name' => [
'query' => $keywords,
'boost' => 4, // 权重大,优先查询cate_name
]]],
[ 'match' => [ 'goods_name' => [
'query' => $keywords,
'boost' => 3,
]]],
[ 'match' => [ 'goods_desc' => [
'query' => $keywords,
'boost' => 2,
]]],
],
],
],
'sort' => ['id'=>['order'=>'desc']],
//本页的第一条数据
'from' => $from,
//一页十行数据
'size' => $size
];
//进行搜索
$results = $es->search_doc('goods_index', 'goods_type', $body);
//获取数据
$data = array_column($results['hits']['hits'], '_source');
$total = $results['hits']['total']['value'];
//分页处理
$list = \tools\es\Espage::paginate($data, $size, $total);
return $list;
}
}
商品文档维护
在Goods商品模型中编写
新增商品后,在ES中添加商品文档
更新商品后,在ES中修改商品文档
删除商品后,在ES中删除商品文档
protected static function init()
{
//实例化ES工具类
$es = new \tools\es\MyElasticsearch();
//设置新增回调
self::afterInsert(function($goods)use($es){
//添加文档
$doc = $goods->visible(['id', 'goods_name', 'goods_desc', 'goods_price'])->toArray();
$doc['cate_name'] = $goods->category->cate_name;
$es->add_doc($goods->id, $doc, 'goods_index', 'goods_type');
});
//设置更新回调
self::afterUpdate(function($goods)use($es){
//修改文档
$doc = $goods->visible(['id', 'goods_name', 'goods_desc', 'goods_price', 'cate_name'])->toArray();
$doc['cate_name'] = $goods->category->cate_name;
$body = ['doc' => $doc];
$es->update_doc($goods->id, 'goods_index', 'goods_type', $body);
});
//设置删除回调
self::afterDelete(function($goods)use($es){
//删除文档
$es->delete_doc($goods->id, 'goods_index', 'goods_type');
});
}