5.1 初始elasticsearch
elasticsearch是一款非常強大的開源搜索引擎,可以幫助我們從海量數(shù)據(jù)中快速找到需要的內(nèi)容。
elasticsearch是elastic stack的核心,負責存儲、搜索、分析數(shù)據(jù)。
5.1.1正向索引
5.1.2elasticsearch采用倒排索引:
文檔(document):每條數(shù)據(jù)就是一個文檔
詞條(term):文檔按照語義分成的詞語
倒排索引中包含兩部分內(nèi)容:
詞條詞典(Term Dictionary):記錄所有詞條,以及詞條與倒排列表(Posting List)之間的關(guān)系,會給詞條創(chuàng)建索引,提高查詢和插入效率
倒排列表(Posting List):記錄詞條所在的文檔id、詞條出現(xiàn)頻率 、詞條在文檔中的位置等信息
文檔id:用于快速獲取文檔
詞條頻率(TF):文檔在詞條出現(xiàn)的次數(shù),用于評分
1、elasticsearch是面向文檔存儲的,可以是數(shù)據(jù)庫中的一條商品數(shù)據(jù),一個訂單信息。
文檔數(shù)據(jù)會被序列化為json格式后存儲在elasticsearch中。
2、索引(index):相同類型的文檔的集合
5.1.3mysql與Elasticsearch對比
MySQL |
Elasticsearch |
說明 |
Table |
Index |
索引(index),就是文檔的集合,類似數(shù)據(jù)庫的表(table) |
Row |
Document |
文檔(Document),就是一條條的數(shù)據(jù),類似數(shù)據(jù)庫中的行(Row),文檔都是JSON格式 |
Column |
Field |
字段(Field),就是JSON文檔中的字段,類似數(shù)據(jù)庫中的列(Column) |
Schema |
Mapping |
Mapping(映射)是索引中文檔的約束,例如字段類型約束。類似數(shù)據(jù)庫的表結(jié)構(gòu)(Schema) |
SQL |
DSL |
DSL是elasticsearch提供的JSON風格的請求語句,用來操作elasticsearch,實現(xiàn)CRUD |
Mysql:擅長事務類型操作,可以確保數(shù)據(jù)的安全和一致性
Elasticsearch:擅長海量數(shù)據(jù)的搜索、分析、計算
5.1.4安裝elasticsearch
1.部署單點es
1.1.創(chuàng)建網(wǎng)絡
因為我們還需要部署kibana容器,因此需要讓es和kibana容器互聯(lián)。這里先創(chuàng)建一個網(wǎng)絡:
docker network create es-net
1.2.加載鏡像
這里我們采用elasticsearch的7.12.1版本的鏡像,這個鏡像體積非常大,接近1G。不建議大家自己pull。
課前資料提供了鏡像的tar包:
大家將其上傳到虛擬機中,然后運行命令加載即可:
# 導入數(shù)據(jù)
docker load -i es.tar
同理還有kibana的tar包也需要這樣做。
1.3.運行
運行docker命令,部署單點es:
docker run -d \
--name es \
-e "ES_JAVA_OPTS=-Xms512m -Xmx512m" \
-e "discovery.type=single-node" \
-v es-data:/usr/share/elasticsearch/data \
-v es-plugins:/usr/share/elasticsearch/plugins \
--privileged \
--network es-net \
-p 9200:9200 \
-p 9300:9300 \
elasticsearch:7.12.1
命令解釋:
-e "cluster.name=es-docker-cluster":設(shè)置集群名稱
-e "http.host=0.0.0.0":監(jiān)聽的地址,可以外網(wǎng)訪問
-e "ES_JAVA_OPTS=-Xms512m -Xmx512m":內(nèi)存大小
-e "discovery.type=single-node":非集群模式
-v es-data:/usr/share/elasticsearch/data:掛載邏輯卷,綁定es的數(shù)據(jù)目錄
-v es-logs:/usr/share/elasticsearch/logs:掛載邏輯卷,綁定es的日志目錄
-v es-plugins:/usr/share/elasticsearch/plugins:掛載邏輯卷,綁定es的插件目錄
--privileged:授予邏輯卷訪問權(quán)
--network es-net :加入一個名為es-net的網(wǎng)絡中
-p 9200:9200:端口映射配置
在瀏覽器中輸入:http://192.168.153.131:9200/ 即可看到elasticsearch的響應結(jié)果:
2.部署kibana
kibana可以給我們提供一個elasticsearch的可視化界面,便于我們學習。
2.1.部署
運行docker命令,部署kibana
docker run -d \
--name kibana \
-e ELASTICSEARCH_HOSTS=http://es:9200 \
--network=es-net \
-p 5601:5601 \
kibana:7.12.1
-
--network es-net :加入一個名為es-net的網(wǎng)絡中,與elasticsearch在同一個網(wǎng)絡中
-
-e ELASTICSEARCH_HOSTS=http://es:9200":設(shè)置elasticsearch的地址,因為kibana已經(jīng)與elasticsearch在一個網(wǎng)絡,因此可以用容器名直接訪問elasticsearch
-
-p 5601:5601:端口映射配置
kibana啟動一般比較慢,需要多等待一會,可以通過命令:
docker logs -f kibana
查看運行日志,說明成功:
此時,在瀏覽器輸入地址訪問:http://192.168.153.131:5601,即可看到結(jié)果
2.2.DevTools
kibana中提供了一個DevTools界面:
這個界面中可以編寫DSL來操作elasticsearch。并且對DSL語句有自動補全功能。
語法說明:
- POST:請求方式
- /_analyze:請求路徑,這里省略了虛擬機IP地址:9200,有kibana幫我們補充
- 請求參數(shù),json風格:
- analyzer:分詞器類型,這里是默認的standard分詞器
- text:要分詞的內(nèi)容
3.安裝IK分詞器
3.1.在線安裝ik插件(較慢)
# 進入容器內(nèi)部
docker exec -it elasticsearch /bin/bash
# 在線下載并安裝
./bin/elasticsearch-plugin install https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v7.12.1/elasticsearch-analysis-ik-7.12.1.zip
#退出
exit
#重啟容器
docker restart elasticsearch
3.2.離線安裝ik插件(推薦)
1)查看數(shù)據(jù)卷目錄
安裝插件需要知道elasticsearch的plugins目錄位置,而我們用了數(shù)據(jù)卷掛載,因此需要查看elasticsearch的數(shù)據(jù)卷目錄,通過下面命令查看:
docker volume inspect es-plugins
顯示結(jié)果:
[
{
"CreatedAt": "2022-05-06T10:06:34+08:00",
"Driver": "local",
"Labels": null,
"Mountpoint": "/var/lib/docker/volumes/es-plugins/_data",
"Name": "es-plugins",
"Options": null,
"Scope": "local"
}
]
說明plugins目錄被掛載到了:/var/lib/docker/volumes/es-plugins/_data 這個目錄中。
2)解壓縮分詞器安裝包
下面我們需要把課前資料中的ik分詞器解壓縮,重命名為ik
3)上傳到es容器的插件數(shù)據(jù)卷中
也就是/var/lib/docker/volumes/es-plugins/_data :
4)重啟容器
# 4、重啟容器
docker restart es
# 查看es日志
docker logs -f es
5)測試:
IK分詞器包含兩種模式:
- ik_smart:最少切分
- ik_max_word:最細切分
GET /_analyze
{
"analyzer": "ik_max_word",
"text": "黑馬程序員學習java太棒了"
}
結(jié)果:
{
"tokens" : [
{
"token" : "黑馬",
"start_offset" : 0,
"end_offset" : 2,
"type" : "CN_WORD",
"position" : 0
},
{
"token" : "程序員",
"start_offset" : 2,
"end_offset" : 5,
"type" : "CN_WORD",
"position" : 1
},
{
"token" : "程序",
"start_offset" : 2,
"end_offset" : 4,
"type" : "CN_WORD",
"position" : 2
},
{
"token" : "員",
"start_offset" : 4,
"end_offset" : 5,
"type" : "CN_CHAR",
"position" : 3
},
{
"token" : "學習",
"start_offset" : 5,
"end_offset" : 7,
"type" : "CN_WORD",
"position" : 4
},
{
"token" : "java",
"start_offset" : 7,
"end_offset" : 11,
"type" : "ENGLISH",
"position" : 5
},
{
"token" : "太棒了",
"start_offset" : 11,
"end_offset" : 14,
"type" : "CN_WORD",
"position" : 6
},
{
"token" : "太棒",
"start_offset" : 11,
"end_offset" : 13,
"type" : "CN_WORD",
"position" : 7
},
{
"token" : "了",
"start_offset" : 13,
"end_offset" : 14,
"type" : "CN_CHAR",
"position" : 8
}
]
}
POST /_analyze
{
"text":"黑馬程序員學習Java太棒了",
"analyzer": "ik_smart"
}
{
"tokens" : [
{
"token" : "黑馬",
"start_offset" : 0,
"end_offset" : 2,
"type" : "CN_WORD",
"position" : 0
},
{
"token" : "程序員",
"start_offset" : 2,
"end_offset" : 5,
"type" : "CN_WORD",
"position" : 1
},
{
"token" : "學習",
"start_offset" : 5,
"end_offset" : 7,
"type" : "CN_WORD",
"position" : 2
},
{
"token" : "java",
"start_offset" : 7,
"end_offset" : 11,
"type" : "ENGLISH",
"position" : 3
},
{
"token" : "太棒了",
"start_offset" : 11,
"end_offset" : 14,
"type" : "CN_WORD",
"position" : 4
}
]
}
3.3 擴展詞詞典
隨著互聯(lián)網(wǎng)的發(fā)展,“造詞運動”也越發(fā)的頻繁。出現(xiàn)了很多新的詞語,在原有的詞匯列表中并不存在。比如:“奧力給”,“傳智播客” 等。
所以我們的詞匯也需要不斷的更新,IK分詞器提供了擴展詞匯的功能。
-
打開IK分詞器config目錄:
2)在IKAnalyzer.cfg.xml配置文件內(nèi)容添加:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
<comment>IK Analyzer 擴展配置</comment>
<!--用戶可以在這里配置自己的擴展字典 *** 添加擴展詞典-->
<entry key="ext_dict">ext.dic</entry>
</properties>
3)新建一個 ext.dic,可以參考config目錄下復制一個配置文件進行修改
傳智播客
奧力給
4)重啟elasticsearch
docker restart es
日志中已經(jīng)成功加載ext.dic配置文件
5)測試效果:
GET /_analyze
{
"analyzer": "ik_max_word",
"text": "傳智播客Java就業(yè)超過90%,奧力給!"
}
注意當前文件的編碼必須是 UTF-8 格式,嚴禁使用Windows記事本編輯
3.4 停用詞詞典
在互聯(lián)網(wǎng)項目中,在網(wǎng)絡間傳輸?shù)乃俣群芸?,所以很多語言是不允許在網(wǎng)絡上傳遞的,如:關(guān)于宗教、政治等敏感詞語,那么我們在搜索時也應該忽略當前詞匯。
IK分詞器也提供了強大的停用詞功能,讓我們在索引時就直接忽略當前的停用詞匯表中的內(nèi)容。
1)IKAnalyzer.cfg.xml配置文件內(nèi)容添加:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE properties SYSTEM "http://java.sun.com/dtd/properties.dtd">
<properties>
<comment>IK Analyzer 擴展配置</comment>
<!--用戶可以在這里配置自己的擴展字典-->
<entry key="ext_dict">ext.dic</entry>
<!--用戶可以在這里配置自己的擴展停止詞字典 *** 添加停用詞詞典-->
<entry key="ext_stopwords">stopword.dic</entry>
</properties>
3)在 stopword.dic 添加停用詞
yrh
4)重啟elasticsearch
# 重啟服務
docker restart elasticsearch
docker restart kibana
# 查看 日志
docker logs -f elasticsearch
日志中已經(jīng)成功加載stopword.dic配置文件
5)測試效果:
GET /_analyze
{
"analyzer": "ik_max_word",
"text": "傳智播客Java就業(yè)率超過95%,奧力給!"
}
注意當前文件的編碼必須是 UTF-8 格式,嚴禁使用Windows記事本編輯
4.部署es集群
部署es集群可以直接使用docker-compose來完成,不過要求你的Linux虛擬機至少有4G的內(nèi)存空間
首先編寫一個docker-compose文件,內(nèi)容如下:
version: '2.2'
services:
es01:
image: docker.elastic.co/elasticsearch/elasticsearch:7.12.1
container_name: es01
environment:
- node.name=es01
- cluster.name=es-docker-cluster
- discovery.seed_hosts=es02,es03
- cluster.initial_master_nodes=es01,es02,es03
- bootstrap.memory_lock=true
- "ES_JAVA_OPTS=-Xms512m -Xmx512m"
ulimits:
memlock:
soft: -1
hard: -1
volumes:
- data01:/usr/share/elasticsearch/data
ports:
- 9200:9200
networks:
- elastic
es02:
image: docker.elastic.co/elasticsearch/elasticsearch:7.12.1
container_name: es02
environment:
- node.name=es02
- cluster.name=es-docker-cluster
- discovery.seed_hosts=es01,es03
- cluster.initial_master_nodes=es01,es02,es03
- bootstrap.memory_lock=true
- "ES_JAVA_OPTS=-Xms512m -Xmx512m"
ulimits:
memlock:
soft: -1
hard: -1
volumes:
- data02:/usr/share/elasticsearch/data
networks:
- elastic
es03:
image: docker.elastic.co/elasticsearch/elasticsearch:7.12.1
container_name: es03
environment:
- node.name=es03
- cluster.name=es-docker-cluster
- discovery.seed_hosts=es01,es02
- cluster.initial_master_nodes=es01,es02,es03
- bootstrap.memory_lock=true
- "ES_JAVA_OPTS=-Xms512m -Xmx512m"
ulimits:
memlock:
soft: -1
hard: -1
volumes:
- data03:/usr/share/elasticsearch/data
networks:
- elastic
volumes:
data01:
driver: local
data02:
driver: local
data03:
driver: local
networks:
elastic:
driver: bridge
Run docker-compose to bring up the cluster:
docker-compose up
總結(jié):
分詞器的作用是什么?
- 創(chuàng)建倒排索引時對文檔分詞
- 用戶搜索時,對輸入的內(nèi)容分詞
IK分詞器有幾種模式?
- ik_smart:智能切分,粗粒度
- ik_max_word:最細切分,細粒度
IK分詞器如何拓展詞條?如何停用詞條?
- 利用config目錄的IkAnalyzer.cfg.xml文件添加拓展詞典和停用詞典
- 在詞典中添加拓展詞條或者停用詞條
5.2 索引庫操作
type:字段數(shù)據(jù)類型,常見的簡單類型有:
字符串:text(可分詞的文本)、keyword(精確值,例如:品牌、國家、ip地址)
數(shù)值:long、integer、short、byte、double、float、
布爾:boolean
日期:date
對象:object
index:是否創(chuàng)建索引,默認為true
analyzer:使用哪種分詞器
properties:該字段的子字段
ES中通過Restful請求操作索引庫、文檔。請求內(nèi)容用DSL語句來表示。
索引庫操作有哪些?
- 創(chuàng)建索引庫:PUT /索引庫名
- 查詢索引庫:GET /索引庫名
- 刪除索引庫:DELETE /索引庫名
- 添加字段:PUT /索引庫名/_mapping
5.3 文檔操作
新增文檔的DSL語法如下:
修改文檔
文檔操作有哪些?
創(chuàng)建文檔:POST /索引庫名/_doc/文檔id { json文檔 }
查詢文檔:GET /索引庫名/_doc/文檔id
刪除文檔:DELETE /索引庫名/_doc/文檔id
修改文檔:
- 全量修改:PUT /索引庫名/_doc/文檔id { json文檔 }
- 增量修改:POST /索引庫名/_update/文檔id { "doc": {字段}}
Dynamic Mapping
插入文檔時,es會檢查文檔中的字段是否有mapping,如果沒有則按照默認mapping規(guī)則來創(chuàng)建索引。
如果默認mapping規(guī)則不符合你的需求,一定要自己設(shè)置字段mapping
5.4 RestClient操作索引庫
ES官方提供了各種不同語言的客戶端,用來操作ES。這些客戶端的本質(zhì)就是組裝DSL語句,通過http請求發(fā)送給ES。官方文檔地址:Elasticsearch Clients | Elastic
其中的Java Rest Client又包括兩種:
-
Java Low Level Rest Client
-
Java High Level Rest Client
我們學習的是Java HighLevel Rest Client客戶端API
5.4.1.導入數(shù)據(jù)
首先導入課前資料提供的數(shù)據(jù)庫數(shù)據(jù):
數(shù)據(jù)結(jié)構(gòu)如下:
CREATE?TABLE?`tb_hotel`?( ??`id`?bigint(20)?NOT?NULL?COMMENT?'酒店id', ??`name`?varchar(255)?NOT?NULL?COMMENT?'酒店名稱;例:7天酒店', ??`address`?varchar(255)?NOT?NULL?COMMENT?'酒店地址;例:航頭路', ??`price`?int(10)?NOT?NULL?COMMENT?'酒店價格;例:329', ??`score`?int(2)?NOT?NULL?COMMENT?'酒店評分;例:45,就是4.5分', ??`brand`?varchar(32)?NOT?NULL?COMMENT?'酒店品牌;例:如家', ??`city`?varchar(32)?NOT?NULL?COMMENT?'所在城市;例:上海', ??`star_name`?varchar(16)?DEFAULT?NULL?COMMENT?'酒店星級,從低到高分別是:1星到5星,1鉆到5鉆', ??`business`?varchar(255)?DEFAULT?NULL?COMMENT?'商圈;例:虹橋', ??`latitude`?varchar(32)?NOT?NULL?COMMENT?'緯度;例:31.2497', ??`longitude`?varchar(32)?NOT?NULL?COMMENT?'經(jīng)度;例:120.3925', ??`pic`?varchar(255)?DEFAULT?NULL?COMMENT?'酒店圖片;例:/img/1.jpg', ??PRIMARY?KEY?(`id`) )?ENGINE=InnoDB?DEFAULT?CHARSET=utf8mb4;
5.4.2.導入項目
然后導入課前資料提供的項目:
項目結(jié)構(gòu)如圖:
5.4.3.mapping映射分析
創(chuàng)建索引庫,最關(guān)鍵的是mapping映射,而mapping映射要考慮的信息包括:
-
字段名
-
字段數(shù)據(jù)類型
-
是否參與搜索
-
是否需要分詞
-
如果分詞,分詞器是什么?
其中:
-
字段名、字段數(shù)據(jù)類型,可以參考數(shù)據(jù)表結(jié)構(gòu)的名稱和類型
-
是否參與搜索要分析業(yè)務來判斷,例如圖片地址,就無需參與搜索
-
是否分詞呢要看內(nèi)容,內(nèi)容如果是一個整體就無需分詞,反之則要分詞
-
分詞器,我們可以統(tǒng)一使用ik_max_word
來看下酒店數(shù)據(jù)的索引庫結(jié)構(gòu):
PUT /hotel
{
? "mappings": {
? ? "properties": {
? ? ? "id": {
? ? ? ? "type": "keyword"
? ? ? },
? ? ? "name":{
? ? ? ? "type": "text",
? ? ? ? "analyzer": "ik_max_word",
? ? ? ? "copy_to": "all"
? ? ? },
? ? ? "address":{
? ? ? ? "type": "keyword",
? ? ? ? "index": false
? ? ? },
? ? ? "price":{
? ? ? ? "type": "integer"
? ? ? },
? ? ? "score":{
? ? ? ? "type": "integer"
? ? ? },
? ? ? "brand":{
? ? ? ? "type": "keyword",
? ? ? ? "copy_to": "all"
? ? ? },
? ? ? "city":{
? ? ? ? "type": "keyword",
? ? ? ? "copy_to": "all"
? ? ? },
? ? ? "starName":{
? ? ? ? "type": "keyword"
? ? ? },
? ? ? "business":{
? ? ? ? "type": "keyword"
? ? ? },
? ? ? "location":{
? ? ? ? "type": "geo_point"
? ? ? },
? ? ? "pic":{
? ? ? ? "type": "keyword",
? ? ? ? "index": false
? ? ? },
? ? ? "all":{
? ? ? ? "type": "text",
? ? ? ? "analyzer": "ik_max_word"
? ? ? }
? ? }
? }
}
幾個特殊字段說明:
location:地理坐標,里面包含精度、緯度
all:一個組合字段,其目的是將多字段的值 利用copy_to合并,提供給用戶搜索
地理坐標說明:
copy_to說明:
5.4.4.初始化RestClient
在elasticsearch提供的API中,與elasticsearch一切交互都封裝在一個名為RestHighLevelClient的類中,必須先完成這個對象的初始化,建立與elasticsearch的連接。
分為三步:
1)引入es的RestHighLevelClient依賴:
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
<version>7.12.1</version>
</dependency>
2)初始化RestHighLevelClient:
初始化的代碼如下:
public class HotelIndex {
private RestHighLevelClient client;
@Test
void testCreateHotelIndex() throws IOException {
// 1.創(chuàng)建Request對象
CreateIndexRequest request = new CreateIndexRequest("hotel");
// 2.請求參數(shù),MAPPING_TEMPLATE是靜態(tài)常量字符串,內(nèi)容是創(chuàng)建索引庫的DSL語句
request.source(MAPPING_TEMPLATE, XContentType.JSON);
// 3.發(fā)起請求
client.indices().create(request, RequestOptions.DEFAULT);
}
@BeforeEach
void setUp() {
this.client = new RestHighLevelClient(RestClient.builder(
HttpHost.create("http://192.168.153.131:9200")
));
}
@AfterEach
void tearDown() throws IOException {
this.client.close();
}
}
4.1.1.代碼解讀
創(chuàng)建索引庫的API如下:
代碼分為三步:
-
1)創(chuàng)建Request對象。因為是創(chuàng)建索引庫的操作,因此Request是CreateIndexRequest。
-
2)添加請求參數(shù),其實就是DSL的JSON參數(shù)部分。因為json字符串很長,這里是定義了靜態(tài)字符串常量MAPPING_TEMPLATE,讓代碼看起來更加優(yōu)雅。
-
3)發(fā)送請求,client.indices()方法的返回值是IndicesClient類型,封裝了所有與索引庫操作有關(guān)的方法。
4.1.2.完整示例
在hotel-demo的cn.itcast.hotel.constants包下,創(chuàng)建一個類,定義mapping映射的JSON字符串常量:
public class HotelConstants {
public static final String MAPPING_TEMPLATE="{\n" +
" \"mappings\": {\n" +
" \"properties\": {\n" +
" \"id\": {\n" +
" \"type\": \"keyword\"\n" +
" },\n" +
" \"name\":{\n" +
" \"type\": \"text\",\n" +
" \"analyzer\": \"ik_max_word\",\n" +
" \"copy_to\": \"all\"\n" +
" },\n" +
" \"address\":{\n" +
" \"type\": \"keyword\",\n" +
" \"index\": false\n" +
" },\n" +
" \"price\":{\n" +
" \"type\": \"integer\"\n" +
" },\n" +
" \"score\":{\n" +
" \"type\": \"integer\"\n" +
" },\n" +
" \"brand\":{\n" +
" \"type\": \"keyword\",\n" +
" \"copy_to\": \"all\"\n" +
" },\n" +
" \"city\":{\n" +
" \"type\": \"keyword\",\n" +
" \"copy_to\": \"all\"\n" +
" },\n" +
" \"starName\":{\n" +
" \"type\": \"keyword\"\n" +
" },\n" +
" \"business\":{\n" +
" \"type\": \"keyword\"\n" +
" },\n" +
" \"location\":{\n" +
" \"type\": \"geo_point\"\n" +
" },\n" +
" \"pic\":{\n" +
" \"type\": \"keyword\",\n" +
" \"index\": false\n" +
" },\n" +
" \"all\":{\n" +
" \"type\": \"text\",\n" +
" \"analyzer\": \"ik_max_word\"\n" +
" }\n" +
" }\n" +
" }\n" +
"}";
}
在hotel-demo中的HotelIndexTest測試類中,編寫單元測試,實現(xiàn)創(chuàng)建索引:
@Test
void testCreateHotelIndex() throws IOException {
// 1.創(chuàng)建Request對象
CreateIndexRequest request = new CreateIndexRequest("hotel");
// 2.請求參數(shù),MAPPING_TEMPLATE是靜態(tài)常量字符串,內(nèi)容是創(chuàng)建索引庫的DSL語句
request.source(MAPPING_TEMPLATE, XContentType.JSON);
// 3.發(fā)起請求
client.indices().create(request, RequestOptions.DEFAULT);
}
4.2.刪除索引庫
刪除索引庫的DSL語句非常簡單:
DELETE /hotel
與創(chuàng)建索引庫相比:
- 請求方式從PUT變?yōu)镈ELTE
- 請求路徑不變
- 無請求參數(shù)
所以代碼的差異,注意體現(xiàn)在Request對象上。依然是三步走:
- 1)創(chuàng)建Request對象。這次是DeleteIndexRequest對象
- 2)準備參數(shù)。這里是無參
- 3)發(fā)送請求。改用delete方法
在hotel-demo中的HotelIndexTest測試類中,編寫單元測試,實現(xiàn)刪除索引:
@Test
void testCreateHotelIndex() throws IOException {
// 1.創(chuàng)建Request對象
DeleteIndexRequest request = new DeleteIndexRequest("hotel");
// 3.發(fā)起請求
client.indices().delete(request, RequestOptions.DEFAULT);
}
4.3.判斷索引庫是否存在
判斷索引庫是否存在,本質(zhì)就是查詢,對應的DSL是:
GET /hotel
因此與刪除的Java代碼流程是類似的。依然是三步走:
- 1)創(chuàng)建Request對象。這次是GetIndexRequest對象
- 2)準備參數(shù)。這里是無參
- 3)發(fā)送請求。改用exists方法
@Test
void testExistsHotelIndex() throws IOException {
// 1.創(chuàng)建Request對象
GetIndexRequest request = new GetIndexRequest("hotel");
// 2.發(fā)送請求
boolean exists = client.indices().exists(request, RequestOptions.DEFAULT);
// 3.輸出
System.err.println(exists ? "索引庫已經(jīng)存在!" : "索引庫不存在!");
}
4.4.總結(jié)
JavaRestClient操作elasticsearch的流程基本類似。核心是client.indices()方法來獲取索引庫的操作對象。
索引庫操作的基本步驟:
- 初始化RestHighLevelClient
- 創(chuàng)建XxxIndexRequest。XXX是Create、Get、Delete
- 準備DSL( Create時需要,其它是無參)
- 發(fā)送請求。調(diào)用RestHighLevelClient#indices().xxx()方法,xxx是create、exists、delete
???????
ES官方提供了各種不同語言的客戶端,用來操作ES。這些客戶端的本質(zhì)就是組裝DSL語句,通過http請求發(fā)送給ES。官方文檔地址:Elasticsearch Clients | Elastic
5?RestClient操作文檔
為了與索引庫操作分離,我們再次參加一個測試類,做兩件事情:
-
初始化RestHighLevelClient
-
我們的酒店數(shù)據(jù)在數(shù)據(jù)庫,需要利用IHotelService去查詢,所以注入這個接口
@SpringBootTest
public class HotelDocumentTest {
@Autowired
private IHotelService hotelService;
private RestHighLevelClient client;
@BeforeEach
void setUp() {
this.client = new RestHighLevelClient(RestClient.builder(
HttpHost.create("http://192.168.150.101:9200")
));
}
@AfterEach
void tearDown() throws IOException {
this.client.close();
}
}
5.1.新增文檔
我們要將數(shù)據(jù)庫的酒店數(shù)據(jù)查詢出來,寫入elasticsearch中。
5.1.1.索引庫實體類
數(shù)據(jù)庫查詢后的結(jié)果是一個Hotel類型的對象。結(jié)構(gòu)如下:
@Data
@TableName("tb_hotel")
public class Hotel {
@TableId(type = IdType.INPUT)
private Long id;
private String name;
private String address;
private Integer price;
private Integer score;
private String brand;
private String city;
private String starName;
private String business;
private String longitude;
private String latitude;
private String pic;
}
與我們的索引庫結(jié)構(gòu)存在差異:
-
longitude和latitude需要合并為location
因此,我們需要定義一個新的類型,與索引庫結(jié)構(gòu)吻合:
@Data
@NoArgsConstructor
public class HotelDoc {
private Long id;
private String name;
private String address;
private Integer price;
private Integer score;
private String brand;
private String city;
private String starName;
private String business;
private String location;
private String pic;
public HotelDoc(Hotel hotel) {
this.id = hotel.getId();
this.name = hotel.getName();
this.address = hotel.getAddress();
this.price = hotel.getPrice();
this.score = hotel.getScore();
this.brand = hotel.getBrand();
this.city = hotel.getCity();
this.starName = hotel.getStarName();
this.business = hotel.getBusiness();
this.location = hotel.getLatitude() + ", " + hotel.getLongitude();
this.pic = hotel.getPic();
}
}
5.1.2.語法說明
新增文檔的DSL語句如下:
POST /{索引庫名}/_doc/1
{
? ? "name": "Jack",
? ? "age": 21
}
對應的java代碼如圖:
可以看到與創(chuàng)建索引庫類似,同樣是三步走:
-
1)創(chuàng)建Request對象
-
2)準備請求參數(shù),也就是DSL中的JSON文檔
-
3)發(fā)送請求
變化的地方在于,這里直接使用client.xxx()的API,不再需要client.indices()了。
5.1.3.完整代碼
我們導入酒店數(shù)據(jù),基本流程一致,但是需要考慮幾點變化:
-
酒店數(shù)據(jù)來自于數(shù)據(jù)庫,我們需要先查詢出來,得到hotel對象
-
hotel對象需要轉(zhuǎn)為HotelDoc對象
-
HotelDoc需要序列化為json格式
因此,代碼整體步驟如下:
-
1)根據(jù)id查詢酒店數(shù)據(jù)Hotel
-
2)將Hotel封裝為HotelDoc
-
3)將HotelDoc序列化為JSON
-
4)創(chuàng)建IndexRequest,指定索引庫名和id
-
5)準備請求參數(shù),也就是JSON文檔
-
6)發(fā)送請求
在hotel-demo的HotelDocumentTest測試類中,編寫單元測試:文章來源:http://www.zghlxwxcb.cn/news/detail-785258.html
@Test
void testAddDocument() throws IOException {
// 1.根據(jù)id查詢酒店數(shù)據(jù)
Hotel hotel = hotelService.getById(61083L);
// 2.轉(zhuǎn)換為文檔類型
HotelDoc hotelDoc = new HotelDoc(hotel);
// 3.將HotelDoc轉(zhuǎn)json
String json = JSON.toJSONString(hotelDoc);
// 1.準備Request對象
IndexRequest request = new IndexRequest("hotel").id(hotelDoc.getId().toString());
// 2.準備Json文檔
request.source(json, XContentType.JSON);
// 3.發(fā)送請求
client.index(request, RequestOptions.DEFAULT);
}
5.2.查詢文檔
5.2.1.語法說明
查詢的DSL語句如下:
GET /hotel/_doc/{id}
非常簡單,因此代碼大概分兩步:
-
準備Request對象
-
發(fā)送請求
不過查詢的目的是得到結(jié)果,解析為HotelDoc,因此難點是結(jié)果的解析。完整代碼如下:
可以看到,結(jié)果是一個JSON,其中文檔放在一個_source
屬性中,因此解析就是拿到_source
,反序列化為Java對象即可。
與之前類似,也是三步走:
-
1)準備Request對象。這次是查詢,所以是GetRequest
-
2)發(fā)送請求,得到結(jié)果。因為是查詢,這里調(diào)用client.get()方法
-
3)解析結(jié)果,就是對JSON做反序列化
5.2.2.完整代碼
在hotel-demo的HotelDocumentTest測試類中,編寫單元測試:
@Test
void testGetDocumentById() throws IOException {
// 1.準備Request
GetRequest request = new GetRequest("hotel", "61083");
// 2.發(fā)送請求,得到響應
GetResponse response = client.get(request, RequestOptions.DEFAULT);
// 3.解析響應結(jié)果
String json = response.getSourceAsString();
HotelDoc hotelDoc = JSON.parseObject(json, HotelDoc.class);
System.out.println(hotelDoc);
}
5.3.刪除文檔
刪除的DSL為是這樣的:
DELETE /hotel/_doc/{id}
與查詢相比,僅僅是請求方式從DELETE變成GET,可以想象Java代碼應該依然是三步走:
- 1)準備Request對象,因為是刪除,這次是DeleteRequest對象。要指定索引庫名和id
- 2)準備參數(shù),無參
- 3)發(fā)送請求。因為是刪除,所以是client.delete()方法
在hotel-demo的HotelDocumentTest測試類中,編寫單元測試:
@Test
void testDeleteDocument() throws IOException {
// 1.準備Request
DeleteRequest request = new DeleteRequest("hotel", "61083");
// 2.發(fā)送請求
client.delete(request, RequestOptions.DEFAULT);
}
5.4.修改文檔
5.4.1.語法說明
修改我們講過兩種方式:
- 全量修改:本質(zhì)是先根據(jù)id刪除,再新增
- 增量修改:修改文檔中的指定字段值
在RestClient的API中,全量修改與新增的API完全一致,判斷依據(jù)是ID:
- 如果新增時,ID已經(jīng)存在,則修改
- 如果新增時,ID不存在,則新增
這里不再贅述,我們主要關(guān)注增量修改。
代碼示例如圖:
與之前類似,也是三步走:
- 1)準備Request對象。這次是修改,所以是UpdateRequest
- 2)準備參數(shù)。也就是JSON文檔,里面包含要修改的字段
- 3)更新文檔。這里調(diào)用client.update()方法
5.4.2.完整代碼
在hotel-demo的HotelDocumentTest測試類中,編寫單元測試:
@Test
void testUpdateDocument() throws IOException {
// 1.準備Request
UpdateRequest request = new UpdateRequest("hotel", "61083");
// 2.準備請求參數(shù)
request.doc(
"price", "952",
"starName", "四鉆"
);
// 3.發(fā)送請求
client.update(request, RequestOptions.DEFAULT);
}
5.5.批量導入文檔
案例需求:利用BulkRequest批量將數(shù)據(jù)庫數(shù)據(jù)導入到索引庫中。
步驟如下:
- 利用mybatis-plus查詢酒店數(shù)據(jù)
- 將查詢到的酒店數(shù)據(jù)(Hotel)轉(zhuǎn)換為文檔類型數(shù)據(jù)(HotelDoc)
- 利用JavaRestClient中的BulkRequest批處理,實現(xiàn)批量新增文檔
5.5.1.語法說明
批量處理BulkRequest,其本質(zhì)就是將多個普通的CRUD請求組合在一起發(fā)送。
其中提供了一個add方法,用來添加其他請求:
可以看到,能添加的請求包括:
- IndexRequest,也就是新增
- UpdateRequest,也就是修改
- DeleteRequest,也就是刪除
因此Bulk中添加了多個IndexRequest,就是批量新增功能了。示例:
其實還是三步走:
- 1)創(chuàng)建Request對象。這里是BulkRequest
- 2)準備參數(shù)。批處理的參數(shù),就是其它Request對象,這里就是多個IndexRequest
- 3)發(fā)起請求。這里是批處理,調(diào)用的方法為client.bulk()方法
我們在導入酒店數(shù)據(jù)時,將上述代碼改造成for循環(huán)處理即可。
5.5.2.完整代碼
在hotel-demo的HotelDocumentTest測試類中,編寫單元測試:
@Test
void testBulkRequest() throws IOException {
// 批量查詢酒店數(shù)據(jù)
List<Hotel> hotels = hotelService.list();
// 1.創(chuàng)建Request
BulkRequest request = new BulkRequest();
// 2.準備參數(shù),添加多個新增的Request
for (Hotel hotel : hotels) {
// 2.1.轉(zhuǎn)換為文檔類型HotelDoc
HotelDoc hotelDoc = new HotelDoc(hotel);
// 2.2.創(chuàng)建新增文檔的Request對象
request.add(new IndexRequest("hotel")
.id(hotelDoc.getId().toString())
.source(JSON.toJSONString(hotelDoc), XContentType.JSON));
}
// 3.發(fā)送請求
client.bulk(request, RequestOptions.DEFAULT);
}
5.6.小結(jié)
文檔操作的基本步驟:文章來源地址http://www.zghlxwxcb.cn/news/detail-785258.html
- 初始化RestHighLevelClient
- 創(chuàng)建XxxRequest。XXX是Index、Get、Update、Delete、Bulk
- 準備參數(shù)(Index、Update、Bulk時需要)
- 發(fā)送請求。調(diào)用RestHighLevelClient#.xxx()方法,xxx是index、get、update、delete、bulk
- 解析結(jié)果(Get時需要)
到了這里,關(guān)于分布式搜索引擎elasticsearch(一)的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!