前面分享了Xunserch 和 ElasticSearch的安装,本来是要用Xunserch 的因为官网项目部需要太强大的插件,但是安装不给力,各种错误,最后用ElasticSearch。。。其实ElasticSearch并不太难,,, 不说了 直接上代码
先说下我的设计模式:
后台添加数据的同时王es里面插入索引, es6.0版本后一个索引只能插入一个type,所以es每个索引对应数据表, es存放前端要显示的字段即可,没必要全部字段都存在里面。 插入或者更新是调用一个公共方法。。。 搜索调用搜索接口
优化点 如果你的集群只有一个节点,这不要设置副本数,不然健康值会变成yellow,设置为0即可
阿里云的dts可以自动把mysql数据库的数据添加和更新索引到ES 但是需要付费。。。这个不做过多的说明
1.在 项目配置文件 web.php (高级模板main.php) 中的components中添加配置:
//现使用 es
'elasticsearch' => [
'class' => 'yii\elasticsearch\Connection',
//默认为true 如果单节点 设为false 不然会连接错误
'autodetectCluster' => false,
'nodes' => [
['http_address' => '127.0.0.1:9200'],
],
],
2.搜索接口
/**
* auth - rk
* 获取全站搜索数据
* 支持分词搜索和 多条件+多字段搜索
*/
public function actionSearch(){
if(Yii::$app->request->isPost){
$searchData = Yii::$app->request->post('name','');
$pageSize = Yii::$app->request->post('pageSize',5);
$page = Yii::$app->request->post('page',1);
$cate = Yii::$app->request->post('cate','');
$offset = ($page-1)*$pageSize;
$searchData =trim($searchData);
// $searchData = str_replace(" ","",$searchData);
if(empty($searchData)){
$data['res_code']='200';
$data['res_msg']='success';
$data['res_data']=[];
return $data;
}
//ElasticSearch
$es = new ElasticSearch();
//高亮数据
$highlight = [
#左标签,配合前端.highlight这个类来实现高亮
"pre_tags"=>['<span class="highlight">'],
"post_tags"=>['</span>'],
#在原生api中写的是{}表示空对象,因此使用php的stdClass来表示空对象
"fields"=>[
"title"=>new \stdClass(),
"subtitle"=>new \stdClass(),
]
];
$cat = ['product','cases','news','about_us'];
if(!empty($cate)){
if(in_array($cate,$cat)){
$cat = [$cate];
}
}
$resArr = [];
foreach ($cat as $val){
$es::$index_db_name = $val;
$es::$type_tb_name = $val;
$where = ["is_delete"=>0,"status"=>1];
if($val == 'about_us'){
$where = ["is_delete"=>0];
}
$must[] = [
"multi_match" => [//分词多字段搜索
'query' => $searchData,//['fuzzy'=>$searchData]
'fields' => ['title','subtitle'],//搜索的字段
],
];
$query = [
'bool'=>[
'must' => $must,
'should' => [
['match'=> ['is_delete' => 0]]
],
'minimum_should_match' => 1,
]
];
$sort = ['sort' => ['order' => 'desc']];
//->query($query)->orderBy($sort)->asArray()->all() 'terms'=> $where
$esQuery = $es::find()->query($query);//->offset($offset)->limit($pageSize)->orderBy($sort)->highlight($highlight)->asArray()->all()
$count = $es::find()->query($query)->search();
$res = $esQuery->offset($offset)->limit($pageSize)->orderBy($sort)->highlight($highlight)->asArray()->all();
$resArr[$val]['count'] = $count['hits']['total']['value'] ??0;
$resArr[$val]['pageSize'] = $pageSize;
$resArr[$val]['data'] = $res??[];
}
//搜索数据存储到后台
$searchModel = new Search();
//先判断是否存在搜索数据 存在更新次数
$querySql = $searchModel::find()->where(['like','content',$searchData]);
$count = $querySql->count();
if($count !==0){
$queryData = $querySql->asArray()->all();
foreach ($queryData as $key => &$val){
$num = $val['num'] +=1;
$searchModel->updateAll(['num'=>$num],['id'=>$val['id']]);
}
}else{
$searchModel->ip = Yii::$app->request->getUserIP();
$searchModel->content = $searchData;
$searchModel->save();
}
//xunsearch
// $db = \Yii::$app->xunsearch->getDatabase('mmc_uav',true);
// $xs = $db->xs;
// $search = $db->getSearch();
// $index = $db->getIndex();
// var_dump($search);die;
$data['res_code']='200';
$data['res_msg']='success';
$data['host'] = Yii::$app->request->hostInfo;
$data['res_data']=$resArr;
return $data;
}
$data['res_code']='400';
$data['res_msg']='请求方式错误';
return $data;
}
3.ElasticSearch模型
<?php
/**
* Created by PhpStorm.
* User: renkun
* Date: 2020/4/9
* Time: 10:09
*/
namespace app\models;
use yii\elasticsearch\ActiveRecord;
class ElasticSearch extends ActiveRecord
{
public static $index_db_name = '';
public static $type_tb_name = '';
// # 定义db链接
public static function getDb()
{
return \Yii::$app->get('elasticsearch');
}
// 索引名相当于库名
public static function index(){
return self::$index_db_name;
}
// 类别名相当于表名
public static function type(){
return self::$type_tb_name;
}
# 属性
public function attributes()
{
$mapConfig = self::mapConfig();
return array_keys($mapConfig[self::$type_tb_name]);
}
# mapping配置
public static function mapConfig(){
return [
'news' => [
'id' => ['type' => 'int'],
'userId' => ['type' => 'int'],
'categoryId' => ['type' => 'int'],
'title' => ['type' => 'string'],//默认分词
'subtitle' => ['type' => 'string'],
'content' => ['type' => 'string'],
'recommend' => ['type' => 'int'],
'publishTime' => ['type' => 'int'],
'auth' => ['type' => 'string'],
'cover_img' => ['type' => 'string'],
'origin' => ['type' => 'string'],
'origin_url' => ['type' => 'string'],
'status' => ['type' => 'int'],
'sort' => ['type' => 'int'],
'is_delete' => ['type' => 'int'],
],
'about_us' => [
'id' => ['type' => 'int'],
'content' => ['type' => 'string'],
'addtime' => ['type' => 'int'],
'title' => ['type' => 'string'],//默认分词
'description' => ['type' => 'string'],
'type' => ['type' => 'int'],
'sort' => ['type' => 'int'],
'is_delete' => ['type' => 'int'],
],
'cases' => [
'id' => ['type' => 'int'],
'userId' => ['type' => 'int'],
'categoryId' => ['type' => 'int'],
'title' => ['type' => 'string'],//默认分词
'subtitle' => ['type' => 'string'],
'auth' => ['type' => 'string'],
'device' => ['type' => 'string'],
'content' => ['type' => 'string'],
'recommend' => ['type' => 'int'],
'status' => ['type' => 'int'],
'cover_img' => ['type' => 'string'],
'create_at' => ['type' => 'int'],
'sort' => ['type' => 'int'],
'is_delete' => ['type' => 'int'],
],
'product' => [
'id' => ['type' => 'int'],
'product_type_id' => ['type' => 'int'],
'title' => ['type' => 'string'],//默认分词
'description' => ['type' => 'string'],
'cover_img_url' => ['type' => 'string'],
'product_introduction' => ['type' => 'string'],
'extension' => ['type' => 'string'],
'images' => ['type' => 'long'],
'recommend' => ['type' => 'string'],
'addtime' => ['type' => 'int'],
'is_recommend' => ['type' => 'int'],
'user_id' => ['type' => 'int'],
'status' => ['type' => 'int'],
'sort' => ['type' => 'int'],
'is_delete' => ['type' => 'int'],
]
];
}
// 映射
public static function mapping(){
$mapConfig = self::mapConfig();
return [
static::type() => $mapConfig[self::$type_tb_name]
];
}
// 更新映射
public static function updateMapping(){
$db = self::getDb();
$command = $db->createCommand();
if(!$command->indexExists(self::index())){
$command->createIndex(self::index());
}
$res = $command->setMapping(self::index(), self::type(), self::mapping());
return $res;
}
// 创建索引
public static function createIndex(){
$db = static::getDb();
$command = $db->createCommand();
if(!$command->indexExists(self::index())){
$command->createIndex(static::index(), [
'settings' => [
'index' => [
// 指定新索引的时候不会立即生效,1s之后刷新生效 默认是1s
'refresh_interval' => '1s',
// 索引分片个数 默认5片
'number_of_shards' => 5,
// 每个分片副本个数 默认1个 单节点集群如果副本不设置为0 集群健康值会变成黄色 因为单点集群无法存放副本
'number_of_replicas' => 0,
]
],
// 'mappings' => static::mapping(),
//'warmers' => [ /* ... */ ],
//'aliases' => [ /* ... */ ],
//'creation_date' => '...'
]);
}
}
// 删除索引 (谨慎使用)
public static function deleteIndex(){
$db = static::getDb();
$command = $db->createCommand();
$res = $command->deleteIndex(static::index(), static::type());
return $res;
}
//创建es索引 并赋值
public static function es($index,$data=[],$action = 0){ //0 添加 1 创建
$es = new self();
$es::$index_db_name = $index;
$es::$type_tb_name = $index;
$attr = $es::mapping();
$indexKey = array_keys($attr[$index]);
// var_dump($data);die;
$tmpData = $data;
//只获取主要字段
if(!empty($data)){
if($action == 1){
$es = $es::findOne($data['id']); //更新
if(empty($es)){
self::es($index,$tmpData,0);
}
}else{
$es::createIndex();
$es->primaryKey = $data['id'];
}
foreach ($data as $key => $val){
// var_dump($indexKey);
// var_dump($data);die;
if(in_array($key,$indexKey)){
$es->$key = $val;
}
}
if ($es->save() == false) {
die("es数据出现错误,请联系管理员");
}
}
}
}
- 手动添加索引和更新
if( $model->save()){
//同步到es
ElasticSearch::es('product',$model->attributes);
return $this->redirect(['view', 'id' => $model->id]);
}
//最终效果