ElasticSearch - 全文檢索服務 - RestHightLevel版
技術標籤:分散式微服務elasticsearch大資料資料庫java
Author:Allen_Huang
Version:1.0.0
ElasticSearch - 全文檢索服務 - RestHightLevel版本
文章目錄
- ElasticSearch - 全文檢索服務 - RestHightLevel版本
一、引言
1.1 資料庫查詢為什麼還要ElasticSearch?
資料庫一般只適合儲存搜尋結構化的資料,對於非結構化的資料( 比如文章內容),只能通過like%%模糊查詢,但是在大量的資料面前,like%%有兩個弊端:
1)搜尋效率會很差,因為是做一個全表掃描(like%%會讓索引失效)
2)搜尋沒辦法通過相關度匹配排序(可能返回的是使用者不關心的結果)
ElasticSearch就可以解決這些問題
1.2 什麼是全文檢索?
全文檢索 將非結構化資料中的一部分資訊提取出來,重新組織,使其變得具有一定結構,然後對此有一定結構的資料進行搜尋,從而達到搜尋相對較快的目的。這部分從非結構化資料中提取出的然後重新組織的資訊,我們稱之索引。
例如字典的拼音表和部首檢字表就相當於字典的索引,通過查詢拼音表或者部首檢字表就可以快速的查詢到我們要查的字。
這種先建立索引,再對索引進行搜尋的過程就叫全文檢索(Full-text Search)。
1.3 全文檢索的流程
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-wGmZhV8J-1607930409012)(img/image-20200521210532891.png)]
1.4 構建索引的過程
索引過程,對要搜尋的原始內容進行索引構建一個索引庫,索引過程包括:
獲得文件→建立文件→分析文件→索引文件。
1.4.1 獲得原始文件
原始文件是指要索引和搜尋的內容。原始內容包括網際網路上的網頁、資料庫中的資料、磁碟上的檔案等。
1.4.2 建立文件物件(Document)
獲取原始文件的目的是為了索引,在索引前需要將原始內容建立成文件(Document),文件中包括一個一個的域(Field),域中儲存內容。
1.4.3 分析文件(分詞)
將原始內容建立為包含域(Field)的文件(document),需要再對域中的內容進行分析,分析的過程是經過對原始文件提取單詞、將字母轉為小寫、去除標點符號、去除停用詞等過程生成最終的語彙單元。
1.4.4 建立索引
建立索引是對語彙單元索引,通過詞語找文件,這種索引的結構叫倒排索引結構。
1.5 倒排索引
1.5.1 正向索引
簡單來說,正向索引就是根據檔案ID找到該檔案的內容,在檔案內容中匹配搜尋關鍵字,這種方法是順序掃描方法,資料量大、搜尋慢。
1.5.2 反向(倒排)索引
倒排索引和正向索引剛好相反,是根據內容(詞語)找文件
[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-UJ9tMTG0-1607930409014)(img/image-20200521224043288.png)]
二、ElasticSearch
2.1 ElasticSearch簡介
ElasticSearch基本概念
2.1.1 索引庫(index)
索引庫是ElasticSearch存放資料的地方,可以理解為關係型資料庫中的一個資料庫。事實上,我們的資料被儲存和索引在分片(shards)中,索引只是一個把一個或多個分片分組在一起的邏輯空間。
2.1.2 對映型別(type)
對映型別用於區分同一個索引下不同的資料型別,相當於關係型資料庫中的表。
注意:在 6.0 的index下是無法建立多個type,並且會在 7.0 中完全移除。
2.1.3 文件(documents)
文件是ElasticSearch中儲存的實體,類比關係型資料庫,每個文件相當於資料庫表中的一行資料。
2.1.4 欄位(fields)
文件由欄位組成,相當於關係資料庫中列的屬性。
2.1.5 分片與副本
如果一個索引具有很大的資料量,它的資料量可能會超出單個節點的容量限制(硬碟容量),而且單個節點資料量過大,執行效能也會隨之下降,每個搜尋請求的執行效率都會降低。 為了解決上述問題, Elasticsearch 提出了分片的概念,索引將劃分成多份,稱為分片。每個分片都可以建立對應的副本,以便保證服務的高可用性。
2.2 ElasticSearch的安裝
1)準備ElasticSearch的docker-compose.yml檔案
version: '3.1'
services:
elasticsearch:
image: elasticsearch:6.8.5
restart: always
container_name: elasticsearch
ports:
- 9200:9200
- 9300:9300
environment:
discovery.type: single-node
volumes:
- ./es/data:/usr/share/elasticsearch/data:rw
- ./es/logs:/usr/share/elasticsearch/logs:rw
- ./es/plugins:/usr/share/elasticsearch/plugins
- config:/usr/share/elasticsearch/config
kibana:
image: kibana:6.8.5
container_name: kibana
restart: always
environment:
SERVER_NAME: kibana
ELASTICSEARCH_URL: http://193.168.195.135:9200
ports:
- 5601:5601
volumes:
config:
注意:第23行必須寫elasticsearch所在機器的ip地址,不能寫127.0.0.1
2)執行docker-compose up -d 命令啟動容器
docker-compose up -d
注意:第一次建立容器會失敗,需要給.es資料夾賦予許可權,執行chmod 777 -R ./es命令,然後重啟容器
3)安裝中文分詞器
進入elasticsearch容器,執行中文分詞器相關安裝命令
docker exec -it elasticsearch bash
./bin/elasticsearch-plugin install https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v6.8.5/elasticsearch-analysis-ik-6.8.5.zip
注意:可以訪問
**https://github.com/medcl/elasticsearch-analysis-ik/releases **
查詢和當前es匹配的版本
4)手動安裝中文分詞器
如果第3步安裝超時失敗,可以嘗試進行手動安裝,如果第三步成功則跳過該步驟
4.1)手動下載對應的IK分詞器版本**https://github.com/medcl/elasticsearch-analysis-ik/releases **
4.2)直接將分詞器壓縮包上傳到容器的plugins路徑(上傳到宿主機的容器卷路徑即可)
4.3)解壓分詞器
unzip elasticsearch-analysis-ik-6.8.5.zip -d ./ik-analyze
5)重啟容器
docker-compose restart
6)訪問kibana服務
7)測試IK分詞器
POST _analyze
{
"analyzer":"ik_smart",
"text":"殲10系列戰鬥機"
}
POST _analyze
{
"analyzer":"ik_max_word",
"text":"殲10系列戰鬥機"
}
2.3 索引庫(Index)相關操作
2.3.1 概述
ElasticSearch採用Rest風格的API,因此其API就是一次Http請求
請求分為: PUT POST GET DELETE
GET:查詢資料
PUT:插入資料
POST:更新資料,實際上很多情況下 es 不是很清晰你到底要作什麼,有些時候POST也可用於新增或者查詢
DELETE: 刪除資料
2.3.2 新增索引庫語法
PUT /索引庫名稱
{
"settings":{
"number_of_shards": 3, #分片的數量
"number_of_replicas": 2 #副本的數量
}
}
settings:表示索引庫的設定
number_of_shards:表示分片的數量
number_of_replicas:副本數量
2.3.3 查詢索引資訊
GET /索引庫名稱
結果:
{
“qf” : {
“aliases” : { },
“mappings” : { },
“settings” : {
“index” : {
“creation_date” : “1582083142738”,
“number_of_shards” : “3”,
“number_of_replicas” : “2”,
“uuid” : “RaRHf6zISkqFh3a10FDc6A”,
“version” : {
“created” : “6050499”
},
“provided_name” : “qf”
}
}
}
}
2.3.4 判斷索引庫是否存在
HEAD /索引庫名稱
2.3.5 刪除索引庫
DELETE /索引庫名稱
注意
1)索引庫 類似於 MySQL中資料庫的概念
2)如果建立索引不指定settings,預設會有5個分片,1個副本
2.4 對映型別(type)相關操作
2.4.1 新增對映型別語法
PUT /索引庫名/_mapping/型別名稱
{
"properties": {
"欄位名1": {
"type": "型別",
"index": true,
"store": true,
"analyzer": "分詞器"
},
"欄位名2": {
"type": "型別",
"index": true,
"store": true,
"analyzer": "分詞器"
}
}
}
type:型別,可以是text、long、date、integer、object、keyword(表示關鍵字,不能被分詞)
index:是否參與索引,預設為true
store:是否參與儲存,預設為false
analyzer:分詞器,可選 “ik_max_word”或者“ik_smart”,表示使用ik分詞器
2.4.2 檢視對映型別資訊
GET /索引庫名稱/_mapping/型別名稱
2.4.3 欄位屬性詳解
type
String型別,又分兩種:
text:可分詞,不可參與聚合
keyword:不可分詞,資料會作為完整欄位進行匹配,可以參與聚合Numerical:數值型別,分兩類
基本資料型別:long、interger、short、byte、double、flfloat、half_flfloat
浮點數的高精度型別:scaled_float,需要指定一個精度因子,比如10或100,elasticsearch會把真實值乘以這個因子後儲存,取出時再還原Date:日期型別
elasticsearch可以對日期格式化為字串儲存,但是建議我們儲存為毫秒值,儲存為long,節省空間boolean: 設定欄位型別為boolean後,可以填入的值為:true、false、“true”、“false”
binary: binary型別接受base64編碼的字串
geo_point: 地理點型別用於儲存地理位置的經緯度對
更多型別參考:https://www.elastic.co/guide/en/elasticsearch/reference/6.5/mapping-types.html
index
index影響欄位的索引情況。
true:欄位會被索引,則可以用來進行搜尋。預設值就是true
false:欄位不會被索引,不能用來搜尋index的預設值就是true,也就是說你不進行任何配置,所有欄位都會被索引。但是有些欄位是我們不希望被索引的,比如商品的圖片資訊,就需要手動設定index為false。
store
**是否將資料進行額外儲存。**在學習lucene和solr時,我們知道如果一個欄位的store設定為false,那麼在文件列表中就不會有這個欄位的值,使用者的搜尋結果中不會顯示出來。
但是在Elasticsearch中,即便store設定為false,也可以搜尋到結果。
原因是Elasticsearch在建立文件索引時,會將文件中的原始資料備份,儲存到一個叫做 _source 的屬性中。而且我們可以通過過濾 _source 來選擇哪些要顯示,哪些不顯示。而如果設定store為true,就會在 _source 以外額外儲存一份資料,多餘,因此一般我們都會將store設定為false,事實上,store的預設值就是false。
analyzer
定義的是該欄位的分析器,預設的分析器是 standard 標準分析器,這個地方可定義為自定義的分析器。
比如IK分詞器為: ik_max_word 或者 ik_smart
boost
**激勵因子。**這個與lucene中一樣,我們可以通過指定一個boost值來控制每個查詢子句的相對權重。
**該值預設為1。**一個大於1的boost會增加該查詢子句的相對權重比如:
GET /_search { "query": { "bool": { "must": { "match": { "content": { "query": "full text search", "operator": "and" } } }, "should": [{ "match": { "content": { "query": "Elasticsearch", "boost": 3 } } }, { "match": { "content": { "query": "Lucene", "boost": 2 } } } ] } } }
注意
1)對映型別(type) 類似於 MySQL資料庫中表的概念
2)從ElasticSearch 6.x之後,一個Index下只能有一個type
2.5 文件相關(document)操作
2.5.1 新增文件
#指定id的新增方式
PUT /索引庫名/型別名稱/id #id需要自己指定
{
"field1":"value1",
"field2":"value2",
...
}
#自動生成id的新增方式
POST /索引庫名/型別名稱 #使用POST無需指定id
{
"field1":"value1",
"field2":"value2",
...
}
#批量新增文件
PUT /索引庫名稱/型別名稱/_bulk
{"index":{"_id":id值1}}
{"field1":"value1", "field2":"value2"...}
{"index":{"_id":id值2}}
{"field1":"value1", "field2":"value2"...}
....
2.5.2 更新文件
#全域性更新,會將所有欄位更新,沒有指定的欄位會自動刪除
PUT /索引庫名/型別名稱/id #需要更新的id,id必須存在,如果不存在就變成了新增
{
"field1":"value1",
"field2":"value2",
...
}
#區域性更新,只更新需要更新的欄位
POST /索引庫名/型別名稱/id/_update
{
"doc":{
"field1": "新的value"
}
}
2.5.3 刪除文件
DELETE /索引庫名/型別名稱/id
2.5.4 查詢文件
#查詢索引庫全部資料
GET /索引庫名稱/_search
#根據id查詢
GET /索引庫名稱/型別名稱/id
#批量查詢
GET /_mget
{
"docs": [
{
"_index": "索引庫名稱1",
"_type": "對映型別1",
"_id":"查詢文件id1"
},
{
"_index": "索引庫名稱2",
"_type": "對映型別2",
"_id":"查詢文件id2"
}
]
}
注意
1)**文件(document)**類似於 資料庫中表的一條記錄
2)當新增的文件中,設定的field,而type中沒有時,type會自動的新增該field的對映記錄,
這是elasticsearch的自動對映功能
三、SpringBoot操作ElasticSearch(elasticsearch-rest-high-level-client)
3.1 配置ElasticSearch
1)新增依賴
<dependency>
<groupId>org.elasticsearch</groupId>
<artifactId>elasticsearch</artifactId>
<version>6.8.5</version>
</dependency>
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
<version>6.8.5</version>
</dependency>
2)配置application.yml
spring:
elasticsearch:
rest:
uris: http://192.168.195.135:9200
3)在需要的地方注入RestHighLevelClient物件
@Autowired
private RestHighLevelClient restHighLevelClient;
3.2 使用SpringBoot操作索引庫(Index)
@Autowired
private RestHighLevelClient client;
/**
* 建立索引
* @param indexName
* @return
*/
@Override
public boolean createIndex(String indexName) {
CreateIndexRequest indexRequest = new CreateIndexRequest(indexName);
//設定索引庫的相關屬性
Settings settings = Settings.builder()
.put("number_of_shards", 1)//設定分片數量
.put("number_of_replicas", 0)//設定副本數量
.build();
indexRequest.settings(settings);
try {
CreateIndexResponse response = client.indices().create(indexRequest, RequestOptions.DEFAULT);
//返回結果
return response.isAcknowledged();
} catch (IOException e) {
e.printStackTrace();
}
return false;
}
/**
* 判斷索引是否存在
* @param indexName
* @return
*/
@Override
public boolean isExistsIndex(String indexName) {
GetIndexRequest getIndexRequest = new GetIndexRequest(indexName);
try {
boolean exists = client.indices().exists(getIndexRequest, RequestOptions.DEFAULT);
return exists;
} catch (IOException e) {
e.printStackTrace();
}
return false;
}
/**
* 刪除索引
* @param indexName
* @return
*/
@Override
public boolean deleteIndex(String indexName) {
DeleteIndexRequest request = new DeleteIndexRequest(indexName);
try {
AcknowledgedResponse response = client.indices().delete(request, RequestOptions.DEFAULT);
return response.isAcknowledged();
} catch (IOException e) {
e.printStackTrace();
}
return false;
}
3.3 使用SpringBoot操作對映型別(type)
/**
* 新增對映
* PUT /partform_hotal/_mapping/hotal
* {
* "properties": {
* "hotalName":{
* "type": "text",
* "analyzer": "ik_max_word"
* },
* "hotalImage":{
* "type": "keyword",
* "index": false
* },
* "type":{
* "type": "integer"
* },
* "hotalInfo":{
* "type":"text",
* "analyzer": "ik_max_word"
* },
* "keyword":{
* "type":"text",
* "analyzer": "ik_max_word"
* },
* "location":{
* "type": "geo_point"
* },
* "star":{
* "type": "integer"
* },
* "brand":{
* "type": "text",
* "analyzer": "ik_max_word"
* },
* "address":{
* "type": "keyword"
* },
* "openTime":{
* "type": "date",
* "format": "yyyy-MM-dd"
* },
* "cityname":{
* "type": "keyword"
* },
* "regid":{
* "type": "text",
* "analyzer": "ik_max_word"
* }
* }
* }
*
*
* @return
*/
@Override
public boolean createMapping(String index) {
PutMappingRequest putMappingRequest = new PutMappingRequest(index);
try {
XContentBuilder builder = JsonXContent.contentBuilder();
builder
.startObject()
.startObject("properties")
.startObject("hotalName")
.field("type", "text")
.field("analyzer", "ik_max_word")
.endObject()
.startObject("hotalImage")
.field("type", "keyword")
.field("index", "false")
.endObject()
.startObject("type")
.field("type", "integer")
.endObject()
.startObject("hotalInfo")
.field("type", "text")
.field("analyzer", "ik_max_word")
.endObject()
.startObject("keyword")
.field("type", "text")
.field("analyzer", "ik_max_word")
.endObject()
.startObject("location")
.field("type", "geo_point")
.endObject()
.startObject("star")
.field("type", "integer")
.endObject()
.startObject("brand")
.field("type", "text")
.field("analyzer", "ik_max_word")
.endObject()
.startObject("address")
.field("type", "text")
.field("analyzer", "ik_max_word")
.endObject()
.startObject("openTime")
.field("type", "date")
.field("format", "yyyy-MM-dd")
.endObject()
.startObject("cityname")
.field("type", "keyword")
.endObject()
.startObject("regid")
.field("type", "text")
.field("analyzer", "ik_max_word")
.endObject()
.endObject().endObject();
//設定到Request物件中
putMappingRequest.source(builder);
client.indices().putMapping(putMappingRequest, RequestOptions.DEFAULT);
} catch (IOException e) {
e.printStackTrace();
}
return false;
}
3.4 使用SpringBoot操作文件(document)
新增文件
/**
* 給索引庫新增文件
* @param indexName
* @param hotal
* @return
*/
@Override
public boolean insertDco(String indexName, Hotal hotal) {
String json = JSON.toJSONString(hotal);
System.out.println(json);
IndexRequest indexRequest = new IndexRequest(indexName, "_doc")
.id(hotal.getId() + "")
.source(json, XContentType.JSON);
try {
IndexResponse index = client.index(indexRequest, RequestOptions.DEFAULT);
long seqNo = index.getSeqNo();
String lowercase = index.getResult().getLowercase();
int status = index.status().getStatus();
System.out.println("狀態:" + status);
System.out.println("返回:" + lowercase);
System.out.println("序號:" + seqNo);
} catch (IOException e) {
e.printStackTrace();
}
return false;
}
刪除文件
/**
* 根據ID刪除
* @param indexName
* @param id
* @return
*/
@Override
public boolean deleteDco(String indexName, Integer id) {
DeleteRequest deleteRequest = new DeleteRequest(indexName, "_doc", id + "");
try {
DeleteResponse resp = client.delete(deleteRequest, RequestOptions.DEFAULT);
int status = resp.status().getStatus();
System.out.println("結果:" + status);
} catch (IOException e) {
e.printStackTrace();
}
return false;
}
更新文件
/**
* 根據id修改資訊
* @param indexName
* @param hotal
* @return
*/
@Override
public boolean updateDco(String indexName, Hotal hotal) {
String json = JSON.toJSONString(hotal);
System.out.println(json);
// Map map = new HashMap();
// map.put("hotalInfo", "xxxx");
UpdateRequest updateRequest = new UpdateRequest(indexName, "_doc", hotal.getId() + "");
updateRequest.doc(json, XContentType.JSON);
try {
UpdateResponse response = client.update(updateRequest, RequestOptions.DEFAULT);
int status = response.status().getStatus();
System.out.println("狀態:" + status);
} catch (IOException e) {
e.printStackTrace();
}
return false;
}
四、基本查詢
4.1 term、terms查詢
什麼是term查詢?
term是代表完全匹配,也就是精確查詢,搜尋前不會再對搜尋詞進行分詞,所以我們的搜尋詞必須是文件分詞集合中的一個。 比如文件內容為:“美的微波爐”,被分詞為"美的"和"微波爐",term搜尋的關鍵字必須為"美的"或者"微波爐"才能搜尋出這個文件,搜尋"美的微波爐"搜尋不出來
語法
#term查詢
GET /索引庫/對映型別/_search
{
"query": {
"term": {
"欄位名稱": {
"value": "搜尋關鍵字"
}
}
}
}
#terms查詢 - 可以同時查詢多個關鍵詞
GET /索引庫/對映型別/_search
{
"query":{
"terms": {
"欄位名稱": [
"關鍵字1","關鍵字2"...
]
}
}
}
注意:terms查詢 多個關鍵字之間是或者的關係,也就是說只要符合一個關鍵字的文件就會被查詢出來
4.2 match查詢
什麼是match查詢?
match 查詢是高層查詢,它們瞭解欄位對映的資訊:
1.如果查詢 日期(date) 或 整數(integer) 欄位,它們會將查詢字串分別作為日期或整數對待。
2.如果查詢一個( not_analyzed )未分詞的精確值字串欄位, 它們會將整個查詢字串作為單個詞項對待。
3.但如果要查詢一個( analyzed )已分析的全文欄位, 它們會先將查詢字串傳遞到一個合適的分析器,然後生成一個供查詢的詞項列表。 一旦組成了詞項列表,這個查詢會對每個詞項逐一執行底層的查詢,再將結果合併,然後為每個文件生成一個最終的相關度評分。 match查詢其實底層是多個term查詢,最後將term的結果合併。
語法
#match_all查詢 - 查詢指定庫的指定型別的所有文件
GET /索引庫/對映型別/_search
{
"query": {
"match_all": {}
}
}
#match查詢 - 根據關鍵字查詢
GET /索引庫/對映型別/_search
{
"query": {
"match": {
"欄位名稱": "搜尋關鍵字"
}
}
}
#布林match查詢
GET /索引庫/對映型別/_search
{
"query": {
"match": {
"欄位名稱": {
"query": "搜尋關鍵字",
"operator": "OR或者AND"
}
}
}
}
**注意:**operator值為
and表示關鍵詞分詞後的結果,必須全部匹配上
or表示需要一個分詞匹配上即可,預設為or
#mulit_match查詢 - 可以查詢多個欄位
GET /索引庫/對映型別/_search
{
"query": {
"multi_match": {
"query": "搜尋關鍵字",
"fields": ["欄位名稱1^2.0", "欄位名稱2^0.5"],
"operator": "or"
}
}
}
注意:
^2.0表示這個欄位在搜尋中的權重,值越高權重越大,可以不設定。
#match_phrase查詢 - 短語查詢
GET /索引庫/對映型別/_search
{
"query": {
"match_phrase": {
"欄位名稱": "關鍵詞1 關鍵詞2"
}
}
}
注意:
match_phrase查詢,只會匹配關鍵詞1 和關鍵詞2 挨在一起的文件,如果兩個關鍵詞分開太遠的文件是不會匹配上的
4.3 Ids查詢
什麼Ids查詢?
ids查詢是一類簡單的查詢,它過濾返回的文件只包含其中指定識別符號的文件,
該查詢預設指定作用在“_id”上面。
語法
GET /索引庫/對映型別/_search
{
"query": {
"ids": {
"values": ["1","3","6"...]
}
}
}
4.4 prefix字首查詢
什麼是prefix查詢?
字首查詢,可以使我們找到某個欄位以給定字首開頭的文件。最典型的使用場景,一般是在文字框錄入的時候的聯想功能
語法
GET /索引庫/對映型別/_search
{
"query": {
"prefix": {
"欄位名稱": {
"value": "字首"
}
}
}
}
注意:字首查詢並不是和搜尋欄位的內容字首匹配,而是和搜尋欄位的所有分詞的字首匹配,匹配上一個分詞後,就會查詢出該文件,建議和keyword型別的欄位結合使用
4.5 fuzzy查詢
什麼是fuzzy查詢?
fuzzy(模糊)查詢是一種模糊查詢,
term
查詢的模糊等價。
語法
GET /索引庫/對映型別/_search
{
"query": {
"fuzzy": {
"欄位名稱": {
"value": "關鍵詞",
"fuzziness": "2"
}
}
}
}
注意:
1、fuzzy搜尋以後,會自動嘗試將你的搜尋文字進行糾錯,然後去跟文字進行匹配
2、fuzziness屬性表示關鍵詞最多糾正的次數, 比如空條 -> 空調,需要糾正一次,fuzziness需要設定為1
3、prefix_length屬性表示不能被 “模糊化” 的初始字元數。 大部分的拼寫錯誤發生在詞的結尾,而不是詞的開始。 例如通過將prefix_length 設定為 3 ,你可能夠顯著降低匹配的詞項數量。
4.6 wildcard查詢
什麼是wildcard查詢?
wildcard(萬用字元)查詢意為萬用字元查詢
GET /索引庫/對映型別/_search
{
"query": {
"wildcard": {
"欄位名稱": {
"value": "關鍵詞? *"
}
}
}
}
注意:
*表示匹配0或者多個字元
?表示匹配一個字元wildcard查詢不注意查詢效能,應儘可能避免使用。
4.7 range查詢
什麼range查詢?
range查詢既範圍查詢,可以對某個欄位進行範圍匹配
GET /索引庫/對映型別/_search
{
"query": {
"range": {
"欄位名稱": {
"gte": 0,
"lte": 2000,
}
}
}
}
注意:gte表示>=,lte表示<=,gt表示>,lt表示<
4.8 regexp查詢
什麼是regexp查詢?
正則表示式查詢,wildcard和regexp查詢的工作方式和prefix查詢完全一樣。它們也需要遍歷倒排索引中的詞條列表來找到所有的匹配詞條,然後逐個詞條地收集對應的文件ID。它們和prefix查詢的唯一區別在於它們能夠支援更加複雜的模式。
語法
GET /索引庫/對映型別/_search
{
"query": {
"regexp": {
"欄位名稱": "正則表示式"
}
}
}
注意:
1、prefix(字首),wildcard(萬用字元)以及regexp(正則)查詢基於詞條進行操作。如果你在一個analyzed欄位上使用了它們,它們會檢查欄位中的每個詞條,而不是整個欄位。
2、對一個含有很多不同詞條的欄位執行這類查詢是非常消耗資源的。應該避免使用一個以萬用字元開頭的模式(比如,*foo)
4.9 使用JavaAPI實現以上查詢
查詢的基礎結構,通過不同的QueryBuilder物件,可以實現不同的查詢
@Override
public List<Hotal> queryHotals(String indexName, QueryBuilder queryBuilder) {
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder.query(queryBuilder);
SearchRequest searchRequest = new SearchRequest(indexName);
searchRequest.source(searchSourceBuilder);
try {
SearchResponse search = client.search(searchRequest, RequestOptions.DEFAULT);
SearchHits hits = search.getHits();
//迴圈結果
for (SearchHit hit : hits) {
System.out.println("------------------------------------------");
Map<String, DocumentField> fields = hit.getFields();
for (Map.Entry<String, DocumentField> entry : fields.entrySet()) {
System.out.println(entry.getKey() + ":" + entry.getValue().getValue());
}
}
} catch (IOException e) {
e.printStackTrace();
}
return null;
}
//term
TermQueryBuilder termQueryBuilder =
QueryBuilders.termQuery("hotalName", "連鎖");
//terms
TermsQueryBuilder termsQueryBuilder =
QueryBuilders.termsQuery("hotalName", "7天", "連鎖");
//match
MatchQueryBuilder matchQueryBuilder =
QueryBuilders.matchQuery("hotalName", "愛麗絲");
//matchall
MatchAllQueryBuilder matchAllQueryBuilder =
QueryBuilders.matchAllQuery();
//Ids
IdsQueryBuilder idsQueryBuilder =
QueryBuilders.idsQuery().addIds("2","3");
//prefix
PrefixQueryBuilder prefixQueryBuilder =
QueryBuilders.prefixQuery("hotalName", "連");
//fuzzy
FuzzyQueryBuilder fuzzyQueryBuilder = QueryBuilders.fuzzyQuery("regid", "平三區")
.fuzziness(Fuzziness.TWO)
.prefixLength(0);
//wildcard
WildcardQueryBuilder wildcardQueryBuilder =
QueryBuilders.wildcardQuery("hotalName", "愛麗*");
//range
RangeQueryBuilder rangeQuery =
QueryBuilders.rangeQuery("star").gte(0).lt(3);
//regexp
RegexpQueryBuilder regexQuery =
QueryBuilders.regexpQuery("hotalName", "\\S{0,}[0-9]{1}.*");
五、複合查詢
5.1 bool查詢
bool 過濾器。 這是個 複合過濾器(compound fifilter) ,它可以接受多個其他過濾器作為引數,並將這些過濾器結合成各式各樣的布林(邏輯)組合。
語法
GET /索引庫/對映型別/_search
{
"query": {
"bool": {
"should": [
{
"term": {
"content": {
"value": "價效比"
}
}
},{
"match": {
"title": "微波爐"
}
}
],
"must": [
{
"match": {
"content": "格力造"
}
}
],
"must_not": [
{
"range": {
"price": {
"gte": 300,
"lte": 3000
}
}
}
],
"filter": {
"match": {
"title": "美的"
}
}
}
}
}
屬性含義
must: 返回的文件必須滿足must子句的條件,並且參與計算分值,與 AND 等價
**must_not:**所有的語句都 不能(must not) 匹配,與 NOT 等價
should: 返回的文件可能滿足should子句的條件。在一個Bool查詢中,如果沒有must或者filter,有一個或 者多個should子句,那麼只要滿足一個就可以返回,與 OR 等價
minimum_should_match:用來指定should至少需要匹配幾個語句
**filter:**返回的文件必須滿足filter子句的條件。但是不會像Must一樣,參與計算分值
注意
如果查詢中沒有must語句,那麼至少要匹配一個should語句
5.1.1 什麼是filter?
filter vs query
filter ,僅僅只是按照搜尋條件過濾出需要的資料而已,不計算任何相關度分數,對相關度沒有任何影響;
query ,會去計算每個document相對於搜尋條件的相關度,並按照相關度進行排序;
一般來說,如果你是在進行搜尋,需要將最匹配搜尋條件的資料先返回,那麼用query;如果你只是要根據一些條件篩選出一部分資料,不關注其排序,那麼用filter; 除非是你的這些搜尋條件,你希望越符合這些搜尋條件的document越排在前面返回,那麼這些搜尋條件要放在query中;如果你不希望一些搜尋條件來影響你的document排序,那麼就放在filter中即可;
filter和query的效能對比
filter ,不需要計算相關度分數,不需要按照相關度分數進行排序,同時還有內建的自動cache最常使用filter的資料
query ,相反,要計算相關度分數,按照分數進行排序,而且無法cache結果
所以filter查詢效能會高於query
5.3 boosting查詢
什麼是boosting查詢?
該查詢用於將兩個查詢封裝在一起,並降低其中一個查詢所返回文件的分值。它接受一個positive查詢和一個negative查詢。只有匹配了positive查詢的文件才會被包含到結果集中,但是同時匹配了negative查詢的文件會被降低其相關度,通過將文件原本的score和negative_boost引數進行相乘來得到新的score。因此,negative_boost引數必須小於1.0
運用場景
例如,在網際網路上搜索"蘋果"也許會返回,水果或者各種食譜的結果。但是使用者可能只想搜尋到蘋果手機等電子產品,當然我們可以通過排除“水果 喬木 維生素”和這類單詞,結合bool查詢中的must_not子句,將結果範圍縮小到只剩蘋果手機,但是這種做法難免會排除掉那些真的想搜尋水果的使用者,這時可以通過boosting查詢,通過降低“水果 喬木 維生素”等關鍵詞的評分,讓蘋果等電子產品的排名靠前
語法
GET /索引庫/對映型別/_search
{
"query": {
"boosting": {
"positive": {
"match": {
"title": "價效比"
}
},
"negative": {
"match": {
"content": "價效比"
}
},
"negative_boost": 0.1
}
}
}
5.4 使用JavaAPI實現以上查詢
//bool
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery()
.must(....)
.mustNot(...)
.should(...)
.filter(...)
.minimumShouldMatch(1);
//boosting
BoostingQueryBuilder boostingQueryBuilder = QueryBuilders
.boostingQuery(..., ...)
.negativeBoost(0.2f);
六、排序
ElasticSearch預設會有一套相關性分數計算,分數越高,說明文件相關性越大,也就越會排在前面。除了相關性排序之外,開發者也可以通過自己的需要,通過某些規則設定查詢文件的排序
語法
GET /索引庫/對映型別/_search
{
"query": {
"match_all": {}
},
"sort": [
{
"排序欄位1": {
"order": "asc"
}
},
{
"排序欄位2":{
"order": "desc"
}
}
]
}
//建立查詢構建器
QueryBuilder queryBuilder = .........
..................
//執行查詢
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder
.query(queryBuilder)
//設定排序
.sort("star", SortOrder.DESC)
.sort("type", SortOrder.ASC);
七、高亮
什麼是高亮?
許多應用都傾向於在每個搜尋結果中 高亮 顯示搜尋的關鍵詞,比如字型的加粗,改變字型的顏色等.以便讓使用者知道為何該文件符合查詢條件。在 Elasticsearch 中檢索出高亮片段也很容易。高亮顯示需要一個欄位的實際內容。 如果該欄位沒有被儲存(對映mapping沒有將儲存設定為 true),則載入實際的source,並從source中提取相關的欄位。
語法
GET /索引庫/對映型別/_search
{
"query": {
....
},
"highlight": {
"fields": {
"待高亮欄位1": {},
"待高亮欄位2": {}
},
"post_tags": ["</font>"],
"pre_tags": ["<font color='red'>"],
"number_of_fragments": 5,
"fragment_size": 3
}
}
引數含義
number_of_fragments: fragment 是指一段連續的文字。返回結果最多可以包含幾段不連續的文字。
預設是5。fragment_size: 某欄位的值,長度是1萬,但是我們一般不會在頁面展示這麼長,可能只是展示一部分。設定要顯示出來的fragment文字判斷的長度,預設是100
noMatchSize: 搜尋出來的這個文件這個欄位已經顯示出高亮的情況,可是其它欄位並沒有任何顯示,設定這個屬性可以顯示出來。
pre_tags: 標記 highlight 的開始標籤。
post_tags: 標記 highlight 的結束標籤。
JavaAPI
//設定高亮資訊
HighlightBuilder highlightBuilder = new HighlightBuilder();
highlightBuilder
.field("title", 100)
.field("content", 100)
.preTags("<font color='red'>")
.postTags("</font>");
SearchSourceBuilder searchSourceBuilder = new SearchSourceBuilder();
searchSourceBuilder
.query(queryBuilder)
.highlighter(highlightBuilder)
.sort("star", SortOrder.DESC)
.sort("type", SortOrder.ASC);
........
//獲得高亮結果
Map<String, HighlightField> highlightFields
= hit.getHighlightFields();
for (Map.Entry<String, HighlightField> entry : highlightFields.entrySet()) {
System.out.println(entry.getKey() + "--"
+ entry.getValue().getFragments()[0].string());
}
八、地理位置搜尋
地理位置在ElasticSearch中的欄位型別geo-point
8.1 地理位置型別的相關操作
#建立對映型別:
PUT /soufang/_mapping/house
{
"properties": {
"name": {
"type": "text"
},
"location": {
"type": "geo_point"
}
}
}
#新增座標點資料:
PUT /soufang/house/1
{
"name": "市民中心",
"location": {
"lat": 22.54737, #lat代表緯度
"lon": 114.067531 #lon代表經度
}
}
8.2 通過geo_distance過濾器搜尋座標
**geo_distance:**地理距離過濾器( geo_distance )以給定位置為圓心畫一個圓,來找出那些地理座標落在其中的文件
GET /soufang/house/_search
{
"query": {
"geo_distance":{
"location": {
"lat": 22.551013,
"lon": 114.065432
},
"distance": "1km",
"distance_type": "arc"
}
}
}
**distance:**中心點的半徑距離
**distance_type:**兩點間的距離計算的精度演算法
arc - 最慢但是最精確是弧形(arc)計算方式,這種方式把世界當作是球體來處理
plane - 平面(plane)計算方式,把地球當成是平坦的。 這種方式快一些但是精度略遜
8.3 通過geo_bounding_box過濾器搜尋座標
**geo_bounding_box: **查詢某個長方形區域內的位置
GET /soufang/house/_search
{
"query": {
"geo_bounding_box":{
"location":{
"top_left": {
"lat": 22.628427,
"lon": 114.009234
},
"bottom_right": {
"lat": 22.521103,
"lon": 114.148939
}
}
}
}
}
**top_left:**代表矩形左上角
**bottom_right:**代表矩形右下角
8.4 通過geo_polygon過濾器搜尋座標
geo_polygon:查詢位於多邊形內的地點
GET /soufang/house/_search
{
"query": {
"geo_polygon": {
"location":{
"points": [
[113.908911, 22.613748],
[114.056952,22.634298],
[114.031368,22.575843],
[114.097196,22.500803],
[113.9,22.493591]
]
}
}
}
}
8.5 過濾結果通過距離排序
GET /soufang/house/_search
{
"query": {
"geo_distance":{
"location": {
"lat": 22.551013,
"lon": 114.065432
},
"distance": "1km",
"distance_type": "arc"
}
},
"sort": [
{
"_geo_distance": {
"order": "asc",
"location": {
"lat": 22.551013,
"lon": 114.065432
},
"unit": "km",
"distance_type": "arc"
}
}
]
}
**unit:**以 公里(km)為單位,將距離設定到每個返回結果的 sort 鍵中
8.6 JavaApi的執行方式
//geo_distance查詢方式
QueryBuilders.geoDistanceQuery("location")
.point(22.55243, 114.044335)
.distance(2.8, DistanceUnit.KILOMETERS)
//geo_bounding_box查詢方式
QueryBuilders.geoBoundingBoxQuery("location")
.setCorners(
new GeoPoint(22.628427, 114.009234),
new GeoPoint(22.521103, 114.148939))
//geo_polygon查詢方式
List<GeoPoint> points = new ArrayList<>();
points.add(new GeoPoint(22.613748, 113.908911));
points.add(new GeoPoint(22.634298, 114.056952));
points.add(new GeoPoint(22.575843,114.031368));
points.add(new GeoPoint(22.500803,114.097196));
points.add(new GeoPoint(22.493591,113.9));
QueryBuilders.geoPolygonQuery("location", points)
//根據距離排序
SortBuilders
.geoDistanceSort("location", 22.586737, 113.960829)
.order(SortOrder.DESC)
.unit(DistanceUnit.KILOMETERS)
九、function_score自定義文件相關性
9.1 什麼是function_score?
在使用ES進行全文搜尋時,搜尋結果預設會以文件的相關度進行排序,而這個 “文件的相關度”,是可以通過 function_score 自己定義的,也就是說我們可以透過使用function_score,來控制 “怎麼樣的文件相關度更高” 這件事
9.2 文件相關度評分預設大概規則
1、關鍵詞詞頻越高,評分越高
2、關鍵詞在所有文件中出現的頻率越高,評分越低
3、搜尋的關鍵詞與目標文件中分詞匹配個數越多,評分越高
4、匹配的欄位權重越高,評分越高
9.3 function_score的基本用法
9.3.1 function_score提供的加強_score的函式
**1、weight:**設定權重提升值,可以用於任何查詢
2、field_value_factor: 將某個欄位的值乘上old_score
3、random_score : 為每個使用者都使用一個不同的隨機評分對結果排序,但對某一具體使用者來說,看到的順序始終是一致的
4、衰減函式 (linear、exp、guass) : 以某個欄位的值為基準,距離某個值越近得分越高
5、script_score : 當需求超出以上範圍時,可以用自定義指令碼完全控制評分計算,不過因為還要額外維護指令碼不好維護,因此儘量使用ES提供的評分函式,需求真的無法滿足再使用script_score
9.3.2 function_score其他輔助的引數
boost_mode
決定 old_score 和 加強score 如何合併
可選值:
multiply(預設) : new_score = old_score * 加強score
sum : new_score = old_score + 加強score
min : old_score 和 加強score 取較小值,new_score = min(old_score, 加強score)
max : old_score 和 加強score 取較大值,new_score = max(old_score, 加強score)
replace : 加強score直接替換掉old_score,new_score = 加強score
score_mode
決定functions裡面的加強score們怎麼合併,會先合併加強score們成一個總加強score,再使用總加強score去和old_score做合併,換言之就是會先執行score_mode,再執行boost_mode
可選值:
multiply (預設):將所有加強score相乘
sum:求和
avg:取平均值
first : 使加強首個函式(可以有filter,也可以沒有)的結果作為最終結果
max:取最大值
min:取最小值
max_boost
限制加強函式的最大效果,就是限制加強score最大能多少,但要注意不會限制old_score
9.3.3 function_score語法
單加強函式語法
GET /索引庫/對映型別/_search
{
"query": {
"function_score": {
//主查詢,查詢完後這裡自己會有一個評分,就是old_score
"query": {.....},
//在old_score的基礎上,給他加強其他欄位的評分,這裡會產生一個加強score,如果只有一個加強function時,直接將加強函式名寫在query下面就可以了
"field_value_factor": {...},
//指定用哪種方式結合old_score和加強score成為new_score
"boost_mode": "multiply",
//限制加強score的最高分,但是不會限制old_score
"max_boost": 1.5
}
}
}
多加強函式語法
GET /索引庫/對映型別/_search
{
"query": {
"function_score": {
"query": {.....},
"functions": [
//可以有多個加強函式(或是filter+加強函式),每一個加強函式會產生一個加強score,因此functions會有多個加強score
{ "field_value_factor": ... },
{ "gauss": ... },
{ "filter": {...}, "weight": ... }
],
//決定加強score們怎麼合併
"score_mode": "sum",
//決定總加強score怎麼和old_score合併
"boost_mode": "multiply"
}
}
}
weight加強函式用法
GET /shop/goods/_search
{
"query": {
"function_score": {
"query": {
"match_all": {}
},
"functions": [
{"filter": {
"range": {
"price": {
"gte": 1000,
"lte": 3000
}
}
}, "weight": 3}
],
"boost_mode": "sum"
}
}
}
解析:查詢所有文件,如果某個文件的價格在1000~3000範圍內,文件評分就會*3,並且new_score會和old_score相加得到最終評分
random_score加強函式使用案例
GET /shop/goods/_search
{
"query": {
"function_score": {
"query": {
"match_all": {}
},
"functions": [
{"random_score": {
"seed": 2
}}
]
}
}
}
解析:不同的使用者,可以設定不同的seed值(比如使用者的id號),實現隨機排序的效果,但是對同一個使用者排序結果又是恆定的
field_value_factor加強函式使用案例
GET /shop/goods/_search
{
"query": {
"function_score": {
"query": {
"match_all": {}
},
"functions": [
{"field_value_factor": {
"field": "price"
}}
]
}
}
}
解析:查詢所有文件,並且將所有文件的old_score,乘以本身的價格,得到new_score,預設將new_score * old_score,得到最終評分
9.3.4 衰減函式評分
什麼是衰減函式?
以某一個範圍為基準,距離這個範圍越遠,評分越低。 比如以100為基準,那麼大於100,或者小於100評分都將變得越來越低。
為什麼需要衰減函式?
在一次搜尋中,某個條件並不一定是線性增長或者遞減來影響最終結果評分的。比如搜尋商品,並不是價格越低就意味著越好,使用者就會越感興趣,往往可能維繫在一個價格區間的使用者會更感興趣一些。比如原來做過一個調查,如果買車大概會買什麼價位的,最後有40%的人選擇的是10~15w之間的車型。所以我們會發現,價格這個因素,對使用者來說並不是越高越好,同時也不意味著越低越好,而衰減函式就是為了對這一類的資料進行評分的
衰減函式的分類
linear、exp 和 gauss,三種衰減函式的差別只在於衰減曲線的形狀,在DSL的語法上的用法完全一樣
linear : 線性函式是條直線,一旦直線與橫軸相交,所有其他值的評分都是0
exp : 指數函式是先劇烈衰減然後變緩
gauss(最常用) : 高斯函式則是鐘形的,他的衰減速率是先緩慢,然後變快,最後又放緩
衰減函式的支援引數
origin : 中心點,或是欄位可能的最佳值,落在原點(origin)上的文件評分_score為滿分1.0,支援數值、時間 以及 "經緯度地理座標點"等型別欄位 _
offset : 從 origin 為中心,為他設定一個偏移量offset覆蓋一個範圍,在此範圍內所有的評分_score也都是和origin一樣滿分1.0
scale : 衰減率,即是一個文件從origin下落時,_score改變的速度
衰減函式案例
GET /mytest/doc/_search
{
"query": {
"function_score": {
"functions": [
//第一個gauss加強函式,決定距離的衰減率
{
"gauss": {
"location": {
"origin": { //origin點設成酒店的經緯度座標
"lat": 51.5,
"lon": 0.12
},
"offset": "2km", //距離中心點2km以內都是滿分1.0,2km外開始衰減
"scale": "3km" //衰減率
}
}
},
//第二個gauss加強函式,決定價格的衰減率,因為使用者對價格更敏感,所以給了這個gauss 加強函式2倍的權重
{
"gauss": {
"price": {
"origin": "50",
"offset": "50",
"scale": "20"
}
},
"weight": 2
}
]
}
}
}
JavaAPI設定評分
/**
* 自定義評分
*/
@Test
public void functionScore() throws IOException {
List<FunctionScoreQueryBuilder.FilterFunctionBuilder> list
= new ArrayList<>();
list.add(new FunctionScoreQueryBuilder.
FilterFunctionBuilder(ScoreFunctionBuilders.
gaussDecayFunction("location", new GeoPoint(22.586203, 114.031687), "6km", "5km")));
SearchRequest searchRequest = new SearchRequest("soufang").types("house");
searchRequest.source().query(
QueryBuilders.functionScoreQuery(QueryBuilders.matchAllQuery(), list.toArray(new FunctionScoreQueryBuilder.FilterFunctionBuilder[0]))
.boostMode(CombineFunction.REPLACE));
SearchResponse response =
restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
SearchHits hits = response.getHits();
for (SearchHit hit : hits) {
System.out.println("查詢結果:" + hit.getSourceAsString() + " 評分:" + hit.getScore());
}
}
十、ElasticSearch叢集搭建
1、建立基本目錄/usr/local/es-cluster
2、在master/conf/elasticsearch.yml新增如下內容
bootstrap.memory_lock: false
cluster.name: "es-cluster"
node.name: es-master
node.master: true
node.data: false
network.host: 0.0.0.0
http.port: 9200
transport.tcp.port: 9300
discovery.zen.ping.unicast.hosts: ["es-master:9300"]
discovery.zen.minimum_master_nodes: 1
path.logs: /usr/share/elasticsearch/logs
http.cors.enabled: true
http.cors.allow-origin: "*"
xpack.security.audit.enabled: true
3、在node1&node2/conf/elasticsearch.yml新增如下內容
cluster.name: "es-cluster"
node.name: node1 #這裡注意替換
node.master: false
node.data: true
network.host: 0.0.0.0
http.port: 9202
transport.tcp.port: 9302
discovery.zen.ping.unicast.hosts: ["es-master:9300"]
path.logs: /usr/share/elasticsearch/logs
4、編寫docker-compose.yml
version: '3.1'
services:
es-master:
image: elasticsearch:6.8.5
container_name: es-master
restart: always
volumes:
- ./master/data:/usr/share/elasticsearch/data:rw
- ./master/conf/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml
- ./master/logs:/user/share/elasticsearch/logs:rw
ports:
- 9200:9200
- 9300:9300
networks:
- es-network
es-node1:
image: elasticsearch:6.8.5
container_name: es-node1
restart: always
networks:
- es-network
volumes:
- ./node1/data:/usr/share/elasticsearch/data:rw
- ./node1/conf/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml
- ./node1/logs:/user/share/elasticsearch/logs:rw
es-node2:
image: elasticsearch:6.8.5
container_name: es-node2
restart: always
networks:
- es-network
volumes:
- ./node2/data:/usr/share/elasticsearch/data:rw
- ./node2/conf/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml
- ./node2/logs:/user/share/elasticsearch/logs:rw
es-head:
image: mobz/elasticsearch-head:5
container_name: es-head
restart: always
ports:
- 9100:9100
networks:
- es-network
kibana:
image: kibana:6.8.5
restart: always
container_name: kibana
environment:
SERVER_NAME: kibana
ELASTICSEARCH_URL: http://192.168.195.135:9200
ports:
- 5601:5601
networks:
- es-network
networks:
es-network:
5、啟動docker-compose.yml
#授權
chmod 777 -R master node1 node2
#啟動
docker-compose up -d
安裝中遇到問題
**問題:**max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144]
**解決:**在宿主機執行sysctl -w vm.max_map_count=262144,重啟docker容器
ces:
es-master:
image: elasticsearch:6.8.5
container_name: es-master
restart: always
volumes:
- ./master/data:/usr/share/elasticsearch/data:rw
- ./master/conf/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml
- ./master/logs:/user/share/elasticsearch/logs:rw
ports:
- 9200:9200
- 9300:9300
networks:
- es-network
es-node1:
image: elasticsearch:6.8.5
container_name: es-node1
restart: always
networks:
- es-network
volumes:
- ./node1/data:/usr/share/elasticsearch/data:rw
- ./node1/conf/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml
- ./node1/logs:/user/share/elasticsearch/logs:rw
es-node2:
image: elasticsearch:6.8.5
container_name: es-node2
restart: always
networks:
- es-network
volumes:
- ./node2/data:/usr/share/elasticsearch/data:rw
- ./node2/conf/elasticsearch.yml:/usr/share/elasticsearch/config/elasticsearch.yml
- ./node2/logs:/user/share/elasticsearch/logs:rw
es-head:
image: mobz/elasticsearch-head:5
container_name: es-head
restart: always
ports:
- 9100:9100
networks:
- es-network
kibana:
image: kibana:6.8.5
restart: always
container_name: kibana
environment:
SERVER_NAME: kibana
ELASTICSEARCH_URL: http://192.168.195.135:9200
ports:
- 5601:5601
networks:
- es-network
networks:
es-network:
#### 5、啟動docker-compose.yml
```shell
#授權
chmod 777 -R master node1 node2
#啟動
docker-compose up -d
安裝中遇到問題
**問題:**max virtual memory areas vm.max_map_count [65530] is too low, increase to at least [262144]
**解決:**在宿主機執行sysctl -w vm.max_map_count=262144,重啟docker容器