前言:
在開發項目中一般都會有搜索功能。如果是面向C端的搜索功能,往往都特別考驗性能。比如普通的商城系統中的商品搜索或者一些資源的站內搜索。
可能以前的做法就是對商品表做一個按名稱或商品描述做模糊查詢。更好一點的是對搜索關鍵字進行分詞,並且專門建一個搜索詞庫表。不過前期需要對搜索詞進行拆解然後冪集組合並於商品ID關聯,搜索字與詞庫表的字以完全匹配的方式查詢並找到商品ID。
雖然建詞庫表也是不錯的解決方法,但是還要拆解存庫建索引,相對比較麻煩。所以也是在網上查詢瞭解到了elasticsearch,打算以後做站內搜索用ES,下面就簡單介紹一下他的基本使用方法。
ES介紹:
ElasticSearch是一個基於Lucene的搜索服務器。它提供了一個分佈式多用戶能力的全文搜索引擎,基於RESTful web接口。Elasticsearch是用Java開發的,並作爲Apache許可條款下的開放源碼發佈,是當前流行的企業級搜索引擎。設計用於雲計算中,能夠達到實時搜索,穩定,可靠,快速,安裝使用方便。
下載安裝:
1. 找到官網,按自己的系統下載對應版本,目前200多M。
2. 解壓後打開目錄中的bin裏的"elasticsearch.bat"就可以啓動。
3. 啓動時間相對慢一點,以下只演示單機單節點使用,分佈式可以後期配置。
編碼使用:
以下是以ThinkPHP6.0.2進行演示使用,所以在ES服務開啓後,先打開瀏覽器訪問127.0.0.1:9200,如果返回下面表示成功開啓。
1. ThinkPHP6.0中通過composer install elasticsearch/elasticsearch 下載ES依賴包,如果composer慢可以試一下下載方法後再安裝。
並且每次要本地使用ES時都要先開啓elasticsearch.bat文件,不然就會出現下面的提示。
2. 在使用前需要理解一下ES的幾個名詞:索引,Type,文檔,字段,映射。並且ES以restful風格進行查詢,所以可以將前面的名詞可以和MySQL的數據庫,表,列,SQL關聯一下。比如索引相當於MySQL的數據庫,文檔相當於表中的一條記錄restful方式請求就類似於SQL語句。比如GET 索引名/Type/ID 可以獲取文檔數據,POST,PUT,DELETE等等創建索引,創建/修改/刪除文檔等可以查詢官方文檔,然後使用POSTMAN用不同的請求127.0.0.1:9200。
3. 現在回到ThinkPHP6中,在安裝ES依賴後。可以封裝一個ES基本方法工具類。
<?php
namespace app\common\helper;
use Elasticsearch\ClientBuilder as ESClientBuilder;
class Elasticsearch
{
protected $config = [
"127.0.0.1:9200",
];
protected $es;
/*
* @Title: 構造函數
* @Author: 北橋蘇
* @Times: 2020/5/14 17:35
* */
public function __construct()
{
$this->es = $this->init();
}
/*
* @Title ElasticSearch服務初始化
* @Return object ES實例
* */
private function init()
{
// 從配置文件讀取 Elasticsearch 服務器列表
$builder = ESClientBuilder::create()->setHosts($this->config);
// 應用調試模式
// if (env('APP_DEBUG')) {
// // 配置日誌,Elasticsearch 的請求和返回數據將打印到日誌文件
// $builder->setLogger(app('log')->error());
// }
return $builder->build();
}
/*
* @Title: 獲取ES服務信息
* @Return: ES服務的基本信息
* */
public function info()
{
return $this->es->info();
}
/*
* @Titles: 創建索引
* @Param: string $name 索引名
* @Param: array $mappings 字段映射
* @Return: boolean 創建結果
* */
public function index($name, $mappings=[])
{
$params = [
'index' => $name,
'body' => [
'settings' => [
'number_of_shards' => 1, // 索引分片(一個索引可以分多個分片在不同的節點上)
'number_of_replicas' => 0, // 索引分片副本
]
]
];
// 創建索引設置字段以及字段類型分詞器等
if($mappings) {
$params['body']['mappings']['properties'] = $mappings;
}
return $this->es->indices()->create($params);
}
/*
* @Title: 獲取索引配置,可以獲取單個索引,不傳參數是節點上的所有索引
* @Param: array $name 索引名數組
* @Return:array 索引的名稱分片副本數等數據
* */
public function getIndexConfig($name=[])
{
$params = [];
if(!empty($name)) {
$params = [
"index" => $name
];
}
return $this->es->indices()->getSettings($params);
}
/*
* @Title: 創建/更改映射(創建索引時也可以指定映射-其實就是存儲數據的字段類型)
* @Param: string $name 索引名
* @Param: array $data 字段設置
* @Return boolean 修改結果
* */
public function putMapping($name, $data)
{
$params = [
'index' => $name,
'body' => [
'_source' => [
'enabled' => true
],
'properties' => $data
]
];
return $this->es->indices()->putMapping($params);
}
/*
* @Tile: 獲取索引設置的字段映射
* @Param: string $name
* @Return: array
* */
public function getMapping($name)
{
$params = ['index' => $name];
return $this->es->indices()->getMapping($params);
}
/**
* @Notes: 獲取索引中的文檔
* @param $name 索引名稱
* @param $id 編號
* @Return: 文檔數據
*/
public function getIndex($name, $id)
{
return $this->es->get(['index' => $name, 'id' => $id]);
}
/**
* @Notes: 刪除索引
* @param $name 索引名稱
* @return array
*/
public function deleteIndex($name)
{
return $this->es->indices()->delete(['index' => $name]);
}
/***********************文檔部分********************************************************/
/*
* @Tiele: 在某索引中創建一個文檔(如果索引未設置映射,添加文檔會自動創建字段類型等)
* @Param: string $name 索引名
* @Param: array $data 文檔數據
* @Return array
* */
public function createDoc($name, $data)
{
$params = [
'index' => $name,
'id' => $data['id'],
'body' => $data
];
$response = $this->es->index($params);
return $response;
}
/**
* @Notes: 更新文檔
* @param $index 索引
* @param $id 文檔 id
* @param $data 字段
* @author: 北橋蘇
* @Time: 2020/4/21 9:51
*/
public function updateDoc($index, $id, array $data)
{
$params = [
'index' => $index,
'id' => $id,
//'type' => '_doc',
'body' => [
'doc' => $data
]
];
$this->es->update($params);
}
// 刪除一個文檔
/*
* @Tile: 刪除一個文檔
* @Param: array $param 刪除索引名,ID
* @Return array
* */
public function delDoc($param)
{
$this->es->delete($param);
}
// 批量創建文檔
public function bulk($params)
{
return $this->es->bulk($params);
}
// 搜索
public function queryData($index,$kye, $from = 0, $size = 10)
{
$params = [
'index' => $index,
'body' => [
'from' => $from,
'size' => $size,
'query' => [
'match' => [
'class_name' => '版',
],
]
],
];
return $this->es->search($params);
}
/*
* @Tile: 多功能搜索
* @Param string $index 索引名稱
* @Param array $where 條件數組
* @Param array $sort 排序數組
* @Param int $from 開始查詢的位置
* @Param int $size 一共查詢的條數
* @Return array 查詢結果
* */
public function query($index,$where,$sort='',$from=0,$size=1500)
{
$params = [
"index" => $index,
"body" => [
"from" => $from,
"size" => $size,
"query" => $where
]
];
if($sort) {
$params['body']['sort'] = $sort;
}
return $this->es->search($params);
}
}
4. 業務使用代碼塊。
以下app('es')在provider.php註冊到容器中
<?php
namespace app\admin\controller;
use app\BaseController;
class Test extends BaseController
{
public function index()
{
var_dump(2323);die;
}
// 測試ElasticSearch
public function esTest()
{
$res = app('es')->info();
dump($res);die;
}
// 創建一個索引
public function createIndex()
{
$res = app('es')->index("goods");
var_dump($res);die;
}
// 創建文檔
public function createDoc()
{
$goodsList = \app\common\model\Goods::page(0,1500)->select()->toArray();
foreach ($goodsList as $val) {
$res = app('es')->createDoc('goods',$val);
}
dump($res);die;
$goods = \app\common\model\Goods::find(1);
var_dump($goods);die;
}
// 搜索
public function queryDoc()
{
// 獲取所有
$where = [
'match_all' => new \stdClass(), // 空對象,PHP5.3 或者 (object)[] //也是給出空數組的方式
];
// 單字段完全匹配
// $where = [
// 'match_phrase' => [
// 'class_name' => '班',
// ]
// ];
// 單字段模糊匹配
// $where = [
// 'match' => [
// 'goods_name' => '蘋果'
// ]
// ];
// 多字段匹配
// $where = [
// 'multi_match' => [
// 'query' => '蘋果',
// 'fields' => ['goods_name'], //這裏添加多字段
// 'type' => 'best_fields' //most_fields多字段匹配更高,best_fields完全匹配佔比更高
// ]
// ];
// 聯合搜索must聯合必須,should聯合可以,must_not聯合不必須, 聯合搜索類似SQL的多字段查詢(就使用must)
// $where = [
// 'bool' => [
// 'must' => [
// 'match' => [
// 'goods_name' => '蘋果'
// ]
// ],
// 'should' => [
// 'match' => [
// 'level' => '3'
// ]
// ]
// ]
// ];
// 排序非必填
$sort = [
"sort" => "asc"
];
$res = app('es')->query('goods',$where,$sort,0,3000);
dump($res);die;
// $res = app('es')->queryData('class','class_name',0,20);
// dump($res);die;
}
// 獲取索引(索引中的某個文檔)
public function getDoc()
{
$res = app('es')->getIndex('goods',1500);
dump($res);die;
}
// 獲取的索引配置
public function getIndexConfig()
{
$res = app('es')->getIndexConfig('class,user,goods');
dump($res);die;
}
}