安装扩展
composer require elasticsearch/elasticsearch 7.15
操作ES客户端类
<?php
namespace common;
use Elasticsearch\ClientBuilder;
use think\facade\Db;
class Elasticsearch
{
private $client;
private $indexName;
private $type;
/**
* 构造函数
*/
public function __construct()
{
$params = array('127.0.0.1:9200');
$this->client = ClientBuilder::create()->setHosts($params)->build();
}
/**
* 设置索引与类型
* @param string $indexName
* @param string $type
*/
public function setIndex($indexName = 'index', $type = 'type')
{
$this->indexName = $indexName;
$this->type = $type;
}
/**
* mysql涉及到搜索的数据插入文档
* @throws \think\db\exception\DataNotFoundException
* @throws \think\db\exception\DbException
* @throws \think\db\exception\ModelNotFoundException
*/
public function sync()
{
$list = Db::name("test")->select();
foreach ($list as $k => $v) {
$r = $this->addDoc($v['id'], $v);
}
}
/**
* 查看索引是否存在
*/
public function existsIndex(){
$params = ['index' => $this->indexName];
return $this->client->indices()->exists($params);
}
/**
* 删除索引
*
* @return void
*/
public function deleteIndex()
{
$params = ['index' => $this->indexName];
return $this->client->indices()->delete($params);
}
/**
* 创建索引
*/
public function createIndex()
{
// 只能创建一次
$params = [
'index' => $this->indexName,
'type' => $this->type,
'body' => []
];
return $this->client->index($params);
}
/**
* 获取文档
*
* @param [type] $id
* @return void
*/
public function getDoc($id)
{
$params = [
'index' => $this->indexName,
'type' => $this->type,
'id' => $id
];
return $this->client->get($params);
}
/**
* 创建文档结构
*
* @param [type] $id
* @param [type] $data
* @return void
*/
public function createMappings($body)
{
$params = [
'index' => $this->indexName,
'type' => $this->type,
'include_type_name' => true, //7.0以上版本必须有
'body' => $body
];
return $this->client->indices()->putMapping($params);
}
/**
* 查看映射
*
* @return void
*/
public function getMapping()
{
$params = [
'index' => $this->indexName,
'type' => $this->type,
'include_type_name' => true, //7.0以上版本必须有
];
return $this->client->indices()->getMapping($params);
}
/**
* 添加文档
* @param string $id
* @param array $doc 跟创建文档结构时properties的字段一致
* @return array|callable
*/
public function addDoc(string $id, array $doc)
{
$params = [
'index' => $this->indexName,
'type' => $this->type,
'id' => $id,
'body' => $doc
];
return $this->client->index($params);
}
/**
* 批量添加文档
* @param $body
* @return array
*/
public function addBulkDoc(array $body)
{
$params = [
'index' => $this->indexName,
'type' => $this->type,
'body' => $body
];
return $this->client->bulk($params);
}
/**
* 判断文档存在
*
* @param integer $id
* @return void
*/
public function existsDoc($id = 1)
{
$params = [
'index' => $this->indexName,
'type' => $this->type,
'id' => $id
];
return $this->client->exists($params);
}
/**
* 更新文档
* @param $id
* @param $key
* @param $value
* @return array|callable
*/
public function updateDoc($id, $key, $value)
{
// 可以灵活添加新字段,最好不要乱添加
$params = [
'index' => $this->indexName,
'type' => $this->type,
'id' => $id,
'body' => [
'doc' => [
$key => $value
]
]
];
return $this->client->update($params);
}
/**
* 删除文档
* @param int $id
* @return array|callable
*/
public function deleteDoc($id = 1)
{
$params = [
'index' => $this->indexName,
'type' => $this->type,
'id' => $id
];
return $this->client->delete($params);
}
/**
* 查询表达式搜索
* @param $keywords
* @param $from
* @param $size
* @return array
*/
public function searchDoc1($keywords, $from, $size)
{
return [
'query' => [
"match" => [
//"name"=>$keywords, 或者
"name" => [
'query' => $keywords,
'boost' => 3, // 权重
],
],
],
'from' => $from,
'size' => $size
];
}
/**
* 短语搜索
* @param $keywords
* @param $from
* @param $size
* @return array
*/
public function searchDoc3($keywords, $from, $size)
{
return [
'query' => [
"match_phrase" => [
//"name"=>$keywords, 或者
"name" => [
'query' => $keywords,
'boost' => 3, // 权重
],
],
],
'from' => $from,
'size' => $size
];
}
/**
* 高亮搜索
* @param string $keywords
* @param $from
* @param $size
* @return array
*/
public function searchDoc4(string $keywords, $from, $size): array
{
return [
'query' => [
"match_phrase" => [
//"name"=>$keywords, 或者
"name" => [
'query' => $keywords,
'boost' => 3, // 权重
],
],
],
'highlight' => [
"fields" => [
//必须加object,要不然转json时,这里依然是数组,而不是对象
"name" => (object)[]
]
],
'from' => $from,
'size' => $size
];
}
/**
* 搜索结果增加分析
* @param string $keywords
* @param int $from
* @param int $size
* @return array
*/
public function searchDoc5(string $keywords = '', int $from = 0, int $size = 12)
{
return [
'query' => [
'bool' => [
//必须匹配
"must" => [
"match" => [
"profile" => $keywords,
]
],
//应该匹配
'should' => [
['match' => [
'profile' => [
'query' => $keywords,
'boost' => 3, // 权重
]
]],
['match' => ['name' => [
'query' => $keywords,
'boost' => 2,
]]],
],
//复杂的搜索 限制年龄大于25岁
'filter' => [
"range" => [
"age" => ["gt" => 25]
]
]
],
],
'highlight' => [
"fields" => [
//必须加object,要不然转json时,这里依然是数组,而不是对象
"name" => (object)[]
]
],
'aggs' => [
"result" => [
//terms 桶 统计文档数量
"terms" => [
"field" => "age"
]
],
"avg" => [
//avg 平均值
"avg" => [
"field" => "age"
]
],
"max" => [
//max 最大值
"max" => [
"field" => "age"
]
],
"min" => [
//avg 最小值
"min" => [
"field" => "age"
]
],
],
'from' => $from,
'size' => $size,
];
}
/**
* 使用过滤器 filter
* @param $keywords
* @param $from
* @param $size
* @return array
*/
public function searchDoc2($keywords, $from, $size)
{
return [
'query' => [
'bool' => [
//必须匹配
"must" => [
"match" => [
"name" => $keywords,
]
],
//应该匹配
'should' => [
['match' => [
'profile' => [
'query' => $keywords,
'boost' => 3, // 权重
]
]],
['match' => ['name' => [
'query' => $keywords,
'boost' => 2,
]]],
],
//复杂的搜索 限制年龄大于25岁
'filter' => [
"range" => [
"age" => ["gt" => 25]
]
]
],
],
// 'sort' => ['age'=>['order'=>'desc']],
'from' => $from,
'size' => $size
];
}
/**
* 查询文档 (分页,排序,权重,过滤)
* @param $keywords
* @param int $from
* @param int $size
* @return array|callable
*/
public function searchDoc($keywords, $from = 0, $size = 12)
{
$query = $this->searchDoc5($keywords, $from, $size);
$params = [
'index' => $this->indexName,
'type' => $this->type,
'body' => $query
];
return $this->client->search($params);
}
}
具体使用案例
<?php
namespace app\controller;
use app\BaseController;
use common\Elasticsearch;
class Index extends BaseController
{
public $elasticsearch;
/**
* 构造方法
*/
public function __construct()
{
$this->elasticsearch = new Elasticsearch();
}
public function index()
{
$index = 'user';
$type = 'user_';
//设置索引
$this->elasticsearch->setIndex($index,$type);
//判断索引是否存在,不存在则创建
if(!$this->elasticsearch->existsIndex())
{
$this->elasticsearch->createIndex();
}
//获取映射 如果为空则创建
$mapping = $this->elasticsearch->getMapping();
if(empty($mapping[$index]['mappings'][$type])){
$params = [
'properties' => [
'id' => [
'type' => 'long', // 整型
],
'name' => [
//5.x以上已经没有string类型。如果需要分词的话使用text,不需要分词使用keyword。
'type' => 'text', // 字符串型
],
'profile' => [
'type' => 'text',
],
'age' => [
'type' => 'long',
],
'job' => [
'type' => 'text',
],
]
];
$this->elasticsearch->createMappings($params);
}
//添加文档
// $data = [
// 'id' => 5,
// 'name' => '张三',
// 'profile' => '我是张三',
// 'age' => 30,
// 'job' => '程序员',
// ];
// $this->elasticsearch->addDoc(4,$data);
$arr=[];
for ($i=6;$i< 20;$i++){
$arr[]=[
'id' => $i,
'name' => '张三_'. $i,
'profile' => '我是张三_'. $i,
'age' => 30 + $i,
'job' => '程序员_'. $i,
];
}
$body=[];
foreach ($arr as $k=>$v){
$body[($k*2)] = [
'index' => [
'_id' => $v['id'].'_'.$v['age'],
'_type'=> $type,
'_index'=> $index
]
];
$body[(($k*2)+1)] = $v;
}
//$this->elasticsearch->addBulkDoc($body);
//获取文档
$data = $this->elasticsearch->searchDoc('张',0,30);
var_dump($data['hits']['hits']);
//var_dump($data['aggregations']['min']);
}
}