
ElasticSearch是一个基于RESTful web接口的分布式全文搜索引擎。主要包括数据接入、数据索引和全文搜索3个模块。适用于各种项目的各种搜索场景。
安装JDK
因为ElasticSearch是用java语言开发的,运行需要安装JDK。
JDK (Java Development Kit) ,是整个Java的核心,包括了Java运行环境(Java Runtime Envirnment),一堆Java工具和Java基础的类库
下载JDK地址https://www.oracle.com/technetwork/java/javase/downloads/index.html

下载安装完成后,需要配置环境变量


打开命令行窗口,输入java -version查看JDK版本

这样就JDK,就这样了
安装Elasticsearch
下载Elasticsearch地址 https://www.elastic.co/downloads


下载完成后,就解压,解压完成后,也需要给解压出来的文件配置环境变量

配置完成后,打开命令行窗口 执行命令 elasticsearch -d 启动elasticsearch


安装Elasticsearch-Head
elasticsearch-head是一个用于浏览ElasticSearch集群并与其进行交互的Web项目
GitHub托管地址:https://github.com/mobz/elasticsearch-head


下载解压:
打开命令行,切换到Elasticsearch-Head目录,执行以下命令 npm install
打开命令行,切换到Elasticsearch-Head目录,执行以下命令 npm run start

启动成功后,可通过http://localhost:9100进行访问
由于跨域(Elasticsearch位于9200端口),需要添加配置: E:\elasticsearch-7.1.0\config\elasticsearch.yml中
http.cors.enabled: true
http.cors.allow-origin: "*" //重新启动访问效果

该安装的或配置的东西,都配置好了, 介绍介绍ElasticSearch
安装Elasticsearch-php
使用composer安装: 项目目录下,执行以下命令
项目目录 > composer require elasticsearch/elasticsearch
配置php.ini ::配置php.ini的sys_temp_dir

否则,使用过程中可能会出现以下报错

ElasticSearch基本概念
lastic 本质上是一个分布式数据库,允许多台服务器协同工作,每台服务器可以运行多个 Elastic 实例。
单个 Elastic 实例称为一个节点(node)。一组节点构成一个集群(cluster)。
在Elasticsearch中存储数据的行为就叫做索引(indexing)
在Elasticsearch中,文档归属于一种类型(type),而这些类型存在于索引(index)中
类比传统关系型数据库:
Relational DB -> Databases数据库 -> Tables表 -> Rows行 -> Columns列
Elasticsearch -> Indices索引 -> Types类型 -> Documents文档 -> Fields字段Elasticsearch集群可以包含多个索引(indices)(数据库)
每一个索引可以包含多个类型(types)(表)
每一个类型包含多个文档(documents)(行)
然后每个文档包含多个字段(Fields)(列)
基本使用(根据自己的实际情况操作) 增 修 删
现在我们已经有了,可以使用的ElasticSearch,但里面并没有索引,和索引项目的类型
创建索引
$es = \Elasticsearch\ClientBuilder::create()->setHosts(['127.0.0.1:9200'])->build();
$params = [ //上面是连接的是哪一个域名
'index' => 'test_index' //创建的索引test_index
];
$r = $es->indices()->create($params); //创建
dump($r);die;
添加文档(索引文档)
$es = \Elasticsearch\ClientBuilder::create()->setHosts(['127.0.0.1:9200'])->build();
$params = [ //这里是需要添加 文档 的文档内容
'index' => 'test_index', //在什么索引下面
'type' => 'test_type', //文档的类型
'id' => 100, //文档的id
'body' => ['id'=>100, 'title'=>'PHP从入门到精通', 'author' => '张三'] //文档的内容
];
$r = $es->index($params);
dump($r);die;修改文档
$es = \Elasticsearch\ClientBuilder::create()->setHosts(['127.0.0.1:9200'])->build();
$params = [
'index' => 'test_index',
'type' => 'test_type',
'id' => 100,
'body' => [
'doc' => ['id'=>100, 'title'=>'ES从入门到精通', 'author' => '张三']
]
];
$r = $es->update($params);
dump($r);die;删除文档
$es = \Elasticsearch\ClientBuilder::create()->setHosts(['127.0.0.1:9200'])->build();
$params = [
'index' => 'test_index',
'type' => 'test_type',
'id' => 100,
];
$r = $es->delete($params);
dump($r);die;商品全局搜索功能
那么下面就是我们的tp框架项目里的内容了 (根据自己的实际情况操作)
封装操作es的工具类 这个类中封装了上面的基础方法,多了几个方法 基本注释在代码中
class MyElasticsearch
{
//ES客户端链接
private $client;
/**
* 构造函数
* MyElasticsearch constructor.
*/
public function __construct()
{
$params = array(
'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_ik') { // 只能创建一次
$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_ik') {
$params = ['index' => $index_name];
$response = $this->client->indices()->delete($params);
return $response;
}
/**
* 添加文档
* @param $id
* @param $doc ['id'=>100, 'title'=>'phone']
* @param string $index_name
* @param string $type_name
* @return array
*/
public function add_doc($id,$doc,$index_name = 'test_ik',$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
* @return array|bool
*/
public function exists_doc($id = 1,$index_name = 'test_ik',$type_name = 'goods') {
$params = [
'index' => $index_name,
'type' => $type_name,
'id' => $id
];
$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 = 1,$index_name = 'test_ik',$type_name = 'goods') {
$params = [
'index' => $index_name,
'type' => $type_name,
'id' => $id
];
$response = $this->client->get($params);
return $response;
}
/**
* 更新文档
* @param int $id
* @param string $index_name
* @param string $type_name
* @param array $body ['doc' => ['title' => '苹果手机iPhoneX']]
* @return array
*/
public function update_doc($id = 1,$index_name = 'test_ik',$type_name = 'goods', $body=[]) {
// 可以灵活添加新字段,最好不要乱添加
$params = [
'index' => $index_name,
'type' => $type_name,
'id' => $id,
'body' => $body
];
$response = $this->client->update($params);
return $response;
}
/**
* 删除文档
* @param int $id
* @param string $index_name
* @param string $type_name
* @return array
*/
public function delete_doc($id = 1,$index_name = 'test_ik',$type_name = 'goods') {
$params = [
'index' => $index_name,
'type' => $type_name,
'id' => $id
];
$response = $this->client->delete($params);
return $response;
}
/**
* 搜索文档 (分页,排序,权重,过滤) 注意下面会有body的参数有点乱
* @param string $index_name
* @param string $type_name
* @param array $body
*/
public function search_doc($index_name = "test_ik",$type_name = "goods",$body=[]) {
$params = [
'index' => $index_name,
'type' => $type_name,
'body' => $body
];
$results = $this->client->search($params);
return $results;
}创建添加商品全量索引 (注意代码中的字段,这些字段是本人的) 这样Elasticsearch里就有项目中的所有的商品数据
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);
}
} 然后前端的页面,带着全局搜索的模糊字段查找,
<form action="{:url('home/goods/index')}" method="get" class="sui-form form-inline">
<div class="input-append">
<input type="text" id="autocomplete" class="input-error input-xxlarge" name="keywords" value="{$Request.param.keywords}" />
<button class="sui-btn btn-xlarge btn-danger" type="submit">搜索button>
div>
form>控制器方法
public function index()
{
//接收参数
$keywords = input('keywords')
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]);
}搜索逻辑部分 GoodsLogic 类
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, // 权重大
]]],
[ '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;
}分页 paginate类 (借鉴模型的分页查询方法)
class EsPage
{
public static function paginate($results, $listRows = null, $simple = false, $config = [])
{
if (is_int($simple)) {
$total = $simple;
$simple = false;
}else{
$total = null;
$simple = true;
}
if (is_array($listRows)) {
$config = array_merge(Config::get('paginate'), $listRows);
$listRows = $config['list_rows'];
} else {
$config = array_merge(Config::get('paginate'), $config);
$listRows = $listRows ?: $config['list_rows'];
}
/** @var Paginator $class */
$class = false !== strpos($config['type'], '\\') ? $config['type'] : '\\think\\paginator\\driver\\' . ucwords($config['type']);
$page = isset($config['page']) ? (int) $config['page'] : call_user_func([
$class,
'getCurrentPage',
], $config['var_page']);
$page = $page < 1 ? 1 : $page;
$config['path'] = isset($config['path']) ? $config['path'] : call_user_func([$class, 'getCurrentPath']);
return $class::make($results, $listRows, $page, $total, $simple, $config);
}这样的话,功能就可以实现了;
补充一个,商品文档的维护
比如,后期需要改动数据库中的商品了,但每一次改动都需要,改动ElasticSearch里的数据,也就是说我们数据库中的商品数据要和ElasticSearch中的数据同步,那么手动改动的话太麻烦 ,
解决方法:
在所有的模型中,它们都继承与基础的model模型,但model模型中有一个方法

还有在model模型中的最下面 会有很多操作的回调

那么我们就可以使用这些方法
demo:在往数据库新增一条数据后,向ElasticSearch 添加 文档 (商品)
