1. 初始ElasticSearch
1.1 ElasticSearch介紹
Elasticsearch 是一個(gè)分布式、高擴(kuò)展、高實(shí)時(shí)的搜索與數(shù)據(jù)分析引擎。它能很方便的使大量數(shù)據(jù)具有搜索、分析和探索的能力。充分利用Elasticsearch的水平伸縮性,能使數(shù)據(jù)在生產(chǎn)環(huán)境變得更有價(jià)值。Elasticsearch 的實(shí)現(xiàn)原理主要分為以下幾個(gè)步驟,首先用戶將數(shù)據(jù)提交到Elasticsearch 數(shù)據(jù)庫中,再通過分詞控制器去將對(duì)應(yīng)的語句分詞,將其權(quán)重和分詞結(jié)果一并存入數(shù)據(jù),當(dāng)用戶搜索數(shù)據(jù)時(shí)候,再根據(jù)權(quán)重將結(jié)果排名,打分,再將返回結(jié)果呈現(xiàn)給用戶。
Elasticsearch是與名為Logstash的數(shù)據(jù)收集和日志解析引擎以及名為Kibana的分析和可視化平臺(tái)一起開發(fā)的。這三個(gè)產(chǎn)品被設(shè)計(jì)成一個(gè)集成解決方案,稱為“Elastic Stack”(以前稱為“ELK stack”)。
Elasticsearch可以用于搜索各種文檔。它提供可擴(kuò)展的搜索,具有接近實(shí)時(shí)的搜索,并支持多租戶。Elasticsearch是分布式的,這意味著索引可以被分成分片,每個(gè)分片可以有0個(gè)或多個(gè)副本。每個(gè)節(jié)點(diǎn)托管一個(gè)或多個(gè)分片,并充當(dāng)協(xié)調(diào)器將操作委托給正確的分片。再平衡和路由是自動(dòng)完成的。相關(guān)數(shù)據(jù)通常存儲(chǔ)在同一個(gè)索引中,該索引由一個(gè)或多個(gè)主分片和零個(gè)或多個(gè)復(fù)制分片組成。一旦創(chuàng)建了索引,就不能更改主分片的數(shù)量。
Elasticsearch使用Lucene,并試圖通過JSON和Java API提供其所有特性。它支持facetting和percolating,如果新文檔與注冊(cè)查詢匹配,這對(duì)于通知非常有用。另一個(gè)特性稱為“網(wǎng)關(guān)”,處理索引的長期持久性;例如,在服務(wù)器崩潰的情況下,可以從網(wǎng)關(guān)恢復(fù)索引。Elasticsearch支持實(shí)時(shí)GET請(qǐng)求,適合作為NoSQL數(shù)據(jù)存儲(chǔ),但缺少分布式事務(wù)。
ElasticSearch中有一些新的概念,這里我們對(duì)應(yīng)于MySQL數(shù)據(jù)庫中的一些概念來對(duì)其進(jìn)行講解,可能會(huì)有更好的效果。
MySQL | ElasticSearch | 說明 |
---|---|---|
Table | Index | 索引,就是文檔的集合,類似于數(shù)據(jù)庫中的表 |
Row | Document | 文檔,就是一條條的數(shù)據(jù),類似數(shù)據(jù)庫中的一行,文檔都是JSON形式 |
Column | Field | 字段,就是JSON中的字段名,類似數(shù)據(jù)庫中的列 |
Schema | Mapping | Mapping是索引中文檔的約束,例如字段類型約束,類似數(shù)據(jù)庫的表結(jié)構(gòu) |
SQL | DSL | DSL是ElasticSearch提供的JSON風(fēng)格的請(qǐng)求語句,用于操作ElasticSearch |
1.2 安裝并運(yùn)行ElasticSearch
因?yàn)槲覀冞€需要部署kibana容器,因此需要讓es和kibana容器互聯(lián)。這里先創(chuàng)建一個(gè)網(wǎng)絡(luò):
docker network create es-net
然后使用elasticsearch的7.12.1版本的鏡像,直接pull。
pull elasticsearch:7.12.1
如果需要運(yùn)行es并進(jìn)行單點(diǎn)部署,那么命令如下:
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"
:前一個(gè)是設(shè)置初始堆的大小,后一個(gè)設(shè)置最大堆的大小 -
-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
:加入一個(gè)名為es-net的網(wǎng)絡(luò)中 -
-p 9200:9200
:端口映射配置,暴露的HTTP請(qǐng)求的端口 -
-p 9300:9300
:端口映射配置,暴露ElasticSearch互聯(lián)的端口
訪問虛擬機(jī)地址的9200端口,如果出現(xiàn)以下頁面,說明配置成功,
1.3 運(yùn)行kibana
kibana可以給我們提供一個(gè)ElasticSearch的可視化界面,方便我們學(xué)習(xí),運(yùn)行如下代碼表示運(yùn)行一個(gè)kibana,
docker run -d \
--name kibana \
-e ELASTICSEARCH_HOSTS=http://es:9200 \
--network=es-net \
-p 5601:5601 \
kibana:7.12.1
-
-e ELASTICSEARCH_HOSTS=http://es:9200"
:設(shè)置elasticsearch的地址,因?yàn)閗ibana已經(jīng)與elasticsearch在一個(gè)網(wǎng)絡(luò),因此可以用容器名直接訪問elasticsearch -
--network es-net
:加入一個(gè)名為es-net的網(wǎng)絡(luò)中,與elasticsearch在同一個(gè)網(wǎng)絡(luò)中 -
-p 5601:5601
:端口映射配置
之后等待其部署完畢后,訪問虛擬機(jī)的5601端口,發(fā)現(xiàn)乳腺的界面表示啟動(dòng)成功,
1.4 安裝IK分詞器
在ElasticSearch中,我們常常需要用到分詞的操作,英文還好,其自帶的就可以進(jìn)行分詞,但是中文,其只會(huì)按照逐字的方式對(duì)詞進(jìn)行劃分,這顯然是并不友好的,因此,我們需要安裝一個(gè)專門的分詞器來對(duì)中文進(jìn)行分詞。
安裝IK分詞器的步驟如下:
# 進(jìn)入容器內(nèi)部
docker exec -it es /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 es
打開kibana中的目錄,找到Dev tools,
在其中輸入DSL查詢語句進(jìn)行分詞。
# 測試分詞器
POST /_analyze
{
"text": "我們ikun不惹事,但也不怕事",
"analyzer": "ik_smart"#分詞的模式
}
分詞結(jié)果如下:
還有一種分詞模式是 ik_max_word
,能夠按照最細(xì)粒度去進(jìn)行分詞,更加占用內(nèi)存空間。
2. 操作索引庫和文檔
2.1 mapping屬性
mapping屬性相當(dāng)于就是數(shù)據(jù)庫的字段約束,主要常用的mapping屬性約束如下:
-
type:字段數(shù)據(jù)類型,常見的簡單類型如下:
- 字符串:text(客分詞的文本),keyword(精確值,如國家、品牌,ip)
- 數(shù)值: long, integer, short, byte, double, float
- 布爾值:boolean
- 日期:date
- 對(duì)象:object
- index:是否創(chuàng)建索引,默認(rèn)為true
- analyzer:使用哪種分詞器
- properties:該字段的子字段
2.2 創(chuàng)建索引庫
了解了mapping約束后,我們就可以開始創(chuàng)建索引了,創(chuàng)建索引庫的語法如下:
PUT /索引庫名
創(chuàng)建索引也就是創(chuàng)建每一個(gè)字段的約束條件,與數(shù)據(jù)庫類似,我們創(chuàng)建一個(gè)名為 ikun
的索引,索引如下:
PUT /ikun
{
"mappings": {
"properties": {
"info": {
"type": "text",
"analyzer": "ik_smart"
},
"email": {
"type": "keyword",
"index": false
},
"name": {
"type": "object",
"properties": {
"firstName": {
"type": "keyword"
},
"lastName": {
"type": "keyword"
}
}
}
}
}
}
執(zhí)行后顯示的結(jié)果如下,表明創(chuàng)建成功:
{
"acknowledged" : true,
"shards_acknowledged" : true,
"index" : "ikun"
}
2.3 對(duì)索引庫的查、刪、改
查詢索引庫的語法如下:
GET /索引庫名
刪除索引庫的語法如下:
DELETE /索引庫名
需要注意的是,索引庫一經(jīng)創(chuàng)建就不允許進(jìn)行修改,但是,我們可以對(duì)原來的索引庫進(jìn)行新增,語法如下:
PUT /索引庫名/_mapping
{ "properties":
{ "新字段名":{
"type": "integer"
}
}
}
2.4 操作文檔
在索引庫中插入文檔相當(dāng)于在數(shù)據(jù)庫的表結(jié)構(gòu)中增加一行數(shù)據(jù)。
新增文檔的DSL語法如下:
POST /索引庫名/_doc/文檔id
{
"字段1": "值1",
"字段2": "值2",
"字段3": {
"子屬性1": "值3",
"子屬性2": "值4",
}
}
文檔id如果沒有指定的話,會(huì)隨機(jī)生成。
比如我們對(duì)上面創(chuàng)建的索引庫進(jìn)行新增如下:
POST /ikun/_doc/1
{
"info": "我們ikun不惹事,但也不怕事",
"email": "snow@gmail.com",
"name": {
"firstName": "i",
"lastName": "kun"
}
}
查看文檔的語法為:
GET /ikun/_doc/1
刪除文檔的語法為:
DELETE /ikun/_doc/1
修改文檔有兩種方法。
一種是全量修改,其會(huì)首先找到舊的文檔,將舊的文檔進(jìn)行刪除,然后將修改的再添加進(jìn)去。如果舊的文檔不存在,這種方法還是會(huì)進(jìn)行新增。語法如下:
PUT /索引庫名/_doc/1
{
"字段1": "值1",
"字段2": "值2",
}
還有一種是增量修改,只會(huì)修改指定的字段,語法如下:
POST /索引庫名/_update/文檔id
{
"doc": {
"字段名": "新的值"
}
}
比如我們修改上面的文檔1的郵箱可以為:
POST /ikun/_update/1
{
"doc": {
"email": "snowsnow@gmail.com"
}
}
3. RestClient
ES官方提供了各種不同語言的客戶端,用來操作ES。這些客戶端的本質(zhì)就是組裝DSL語句,通過http請(qǐng)求發(fā)送給ES,這里我們要學(xué)習(xí)的就是java中調(diào)用RestClient。
我們的數(shù)據(jù)庫數(shù)據(jù)結(jié)構(gòu)如下所示,
故我們構(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"
},
"starName": {
"type": "keyword"
},
"bussiness": {
"type": "keyword",
"copy_to": "all"
},
"location": {
"type": "geo_point"
},
"pic": {
"type": "keyword",
"index": false
},
"all": {
"type": "text",
"analyzer": "ik_max_word"
}
}
}
}
在ElasticSearch中,對(duì)經(jīng)緯度專門指定了一個(gè)結(jié)構(gòu) geo_point
,這里面能夠存儲(chǔ)經(jīng)度和緯度的結(jié)構(gòu),除此之外,上面的 copy_to
字段是對(duì)字段進(jìn)行聯(lián)合索引的時(shí)候使用的。比如在上面我們需要對(duì) name
屬性和 brand
屬性就行搜索,一般是先搜索符合的 name
,再到結(jié)果集里面搜索符合條件的 brand
,這樣顯然非常麻煩,而加入了一個(gè) copy_to
字段后,便可以將該屬性復(fù)制一份到 all
屬性中,當(dāng)然, all
屬性并不存在與索引的 suorce
里面,此后,如果我們需要查詢符合條件的 name
和 brand
時(shí),只需要查詢 all
屬性即可。
那怎么在java中操作RestClient客戶端呢?
3.1 初始化RestClient
首先是引入依賴,需要引入如下的依賴:
<properties>
<java.version>1.8</java.version>
<elasticsearch.version>7.12.1</elasticsearch.version>
</properties>
<dependencies>
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
<version>7.12.1</version>
</dependencies>
由于SpringBoot在父maven中已經(jīng)定義了ElasticSearch的版本號(hào),所以改版本的時(shí)候需要在 properties
標(biāo)簽中覆蓋父pom定義的版本號(hào)。
之后就是初始化RestClient了。
如果我們對(duì)每一個(gè)類都要?jiǎng)?chuàng)建和銷毀RestClient客戶端的話,那就顯得太過麻煩了,我們可以將創(chuàng)建和銷毀寫作一個(gè)Ioc切面,在每一個(gè)Bean創(chuàng)建之前切入并創(chuàng)建客戶端,在每一個(gè)Bean執(zhí)行后切入并銷毀客戶端,具體代碼如下:
public class HotelIndexTest {
private RestHighLevelClient restHighLevelClient;
@BeforeEach
void setup(){
this.restHighLevelClient = new RestHighLevelClient(RestClient.builder(
HttpHost.create("http://192.168.59.233:9200")
));
}
@AfterEach
void teardown() throws IOException {
this.restHighLevelClient.close();
}
}
3.2 操作索引庫
-
創(chuàng)建索引庫
@Test void createHotelIndex() throws IOException { //1.創(chuàng)建Request對(duì)象,索引坤名稱為ikun CreateIndexRequest request = new CreateIndexRequest("ikun"); //2.準(zhǔn)備請(qǐng)求參數(shù),即DSL語句,第一個(gè)參數(shù)為DSL語句,第二個(gè)參數(shù)指定為JSON形式 request.source("{\n" + " \"mappings\": {\n" + " \"properties\": {\n" + " \"kunName\": {\n" + " \"type\": \"text\",\n" + " \"analyzer\": \"ik_smart\"\n" + " }\n" + " }\n" + " }\n" + "}", XContentType.JSON); //3.發(fā)送請(qǐng)求 restHighLevelClient.indices().create(request, RequestOptions.DEFAULT); }
-
刪除索引庫
@Test void DeleteHotelIndex() throws IOException { //1.創(chuàng)建Request對(duì)象 DeleteIndexRequest request = new DeleteIndexRequest("ikun"); restHighLevelClient.indices().delete(request, RequestOptions.DEFAULT); }
-
判斷索引庫是否存在
@Test void ExistHotelIndex() throws IOException { //1.創(chuàng)建Request對(duì)象 GetIndexRequest request = new GetIndexRequest("ikun"); boolean exist = restHighLevelClient.indices().exists(request, RequestOptions.DEFAULT); System.out.println(exist); }
3.3 操作文檔
-
增加文檔
如果我們需要用RestClient進(jìn)行文檔的增加,那么首先我們需要的就是類型轉(zhuǎn)換,我們的文檔內(nèi)容肯定是從數(shù)據(jù)庫中進(jìn)行獲取,但是,數(shù)據(jù)庫中的數(shù)據(jù)與索引庫的數(shù)據(jù)還是有一點(diǎn)不一樣的,那就是經(jīng)緯度。在數(shù)據(jù)庫中,我們定義的是經(jīng)度以及緯度,但是,在索引庫中,我們定義的是一個(gè)數(shù)據(jù)結(jié)構(gòu)
geo_point
,里面包含了經(jīng)度以及緯度,所以,我們首先定義一個(gè)與索引庫結(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(); } }
之后,便可以讀取數(shù)據(jù)庫中的信息對(duì)索引庫進(jìn)行文檔的增加了,增加的代碼如下,
@SpringBootTest public class HotelIndexTest { @Autowired private IHotelService hotelService; private RestHighLevelClient restHighLevelClient; @Test void AddHotelDocument() throws IOException { //1.根據(jù)ID查詢酒店數(shù)據(jù) Hotel hotel = hotelService.getById(61083L); //2.轉(zhuǎn)換為文檔類型 HotelDoc hotelDoc = new HotelDoc(hotel); //3.準(zhǔn)備Request對(duì)象,其參數(shù)只接受String IndexRequest request = new IndexRequest("hotel").id(hotel.getId().toString()); //4.準(zhǔn)備JSON文檔 request.source(JSON.toJSONString(hotelDoc),XContentType.JSON); //5.發(fā)送請(qǐng)求 restHighLevelClient.index(request, RequestOptions.DEFAULT); } @BeforeEach void setup(){ this.restHighLevelClient = new RestHighLevelClient(RestClient.builder( HttpHost.create("http://192.168.59.233:9200") )); } @AfterEach void teardown() throws IOException { this.restHighLevelClient.close(); } }
-
查詢文檔
@Test void FindHotelDocument() throws IOException { //1.準(zhǔn)備Request對(duì)象,其參數(shù)只接受String GetRequest request = new GetRequest("hotel", "61083"); //2.發(fā)送請(qǐng)求得到響應(yīng) GetResponse response = restHighLevelClient.get(request, RequestOptions.DEFAULT); //3.解析相應(yīng)結(jié)果,即將source字段解析為json格式的字符串 String json = response.getSourceAsString(); //4.將JSON格式的字符串解析為相應(yīng)的對(duì)象 HotelDoc hotelDoc = JSON.parseObject(json, HotelDoc.class); System.out.println(hotelDoc); }
-
更新文檔
@Test void UpdateHotelDocument() throws IOException { //1.準(zhǔn)備Request對(duì)象,其參數(shù)只接受String UpdateRequest request = new UpdateRequest("hotel", "61083"); //2.準(zhǔn)備參數(shù),特別注意,這里是逗號(hào),沒有冒號(hào)??! request.doc( "price", "1001", "startName", "四鉆" ); //3.發(fā)送請(qǐng)求 restHighLevelClient.update(request, RequestOptions.DEFAULT); }
-
刪除文檔文章來源:http://www.zghlxwxcb.cn/news/detail-688339.html
@Test void UpdateHotelDocument() throws IOException { //1.準(zhǔn)備Request對(duì)象,其參數(shù)只接受String DeleteRequest request = new DeleteRequest("hotel", "61083"); //2.發(fā)送請(qǐng)求 restHighLevelClient.delete(request, RequestOptions.DEFAULT); }
-
批量新增數(shù)據(jù)文章來源地址http://www.zghlxwxcb.cn/news/detail-688339.html
@Test void AddMoreDocument() throws IOException { //1.批量查詢數(shù)據(jù)庫中的信息 List<Hotel> hotels = hotelService.list(); //2.創(chuàng)建Request BulkRequest request = new BulkRequest(); //3.準(zhǔn)備參數(shù),添加多個(gè)新增的Request for(Hotel hotel: hotels){ HotelDoc hotelDoc = new HotelDoc(hotel); request.add(new IndexRequest("hotel") .id(hotel.getId().toString()) .source(JSON.toJSONString(hotelDoc),XContentType.JSON)); } //4.發(fā)送請(qǐng)求 restHighLevelClient.bulk(request, RequestOptions.DEFAULT); }
到了這里,關(guān)于SpringCloud(十)——ElasticSearch簡單了解(一)初識(shí)ElasticSearch和RestClient的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!