elasticsearch5.6.4 RestClient 索引操作API詳細的註釋給力篇值得擁有
最近專案有個需求,很棘手。有個統計功能,需要做到實時出結果。這個功能經過好幾個人的處理都沒有解決客戶的問題。到了我這裡,我就來填坑了。苦逼的程式猿,苦逼的命。誰叫我是這個專案現任負責人。經一番折騰大致瞭解到客戶對這個統計分析的需求之後,就進入技術選項。老套路,肯定先百度、谷歌、大神群各種諮詢、瞭解。最後決定使用elasticseaarch。至於為何選擇它,相關同學自己百度去。而我選擇的原因就是簡單、易於維護、成本低、效能也是gan gan 的。
接下來我們看看elasticsearch index api是如何使用java 來進行相關操作。強烈建議在閱讀以下所說的內容前必須先對elasticsearch有一定的瞭解
參考官網API
pom.xml
<dependency>
<groupId>org.elasticsearch.client</groupId>
<artifactId>elasticsearch-rest-high-level-client</artifactId>
<version>5.6.4</version>
</dependency>
建立連線
private static Logger log = LoggerFactory.getLogger(ESRestClient.class);
private static RestClient lowLevelRestClient = null;
private static RestHighLevelClient highLevelRestClient = null;
/**
* @Description: 初始化
* @return void
* @throws
* @author JornTang
* @date 2017年12月23日
*/
public void init(){
RestClientBuilder builder = RestClient.builder(new HttpHost("127.0.0.1", 9200));
builder.setRequestConfigCallback(new RestClientBuilder.RequestConfigCallback() {
@Override
public RequestConfig.Builder customizeRequestConfig(RequestConfig.Builder requestConfigBuilder) {
requestConfigBuilder.setConnectTimeout(10000);
requestConfigBuilder.setSocketTimeout(30000);
requestConfigBuilder.setConnectionRequestTimeout(10000);
return requestConfigBuilder;
}
});
builder.setHttpClientConfigCallback(new RestClientBuilder.HttpClientConfigCallback() {
@Override
public HttpAsyncClientBuilder customizeHttpClient(HttpAsyncClientBuilder httpClientBuilder) {
return httpClientBuilder.setDefaultIOReactorConfig(
IOReactorConfig.custom()
.setIoThreadCount(100)//執行緒數配置
.setConnectTimeout(10000)
.setSoTimeout(10000)
.build());
}
});
//設定超時
builder.setMaxRetryTimeoutMillis(10000);
//構建low level client
lowLevelRestClient = builder.build();
//構建high level client
highLevelRestClient = new RestHighLevelClient(lowLevelRestClient);
log.info("ESRestClient 初始化完成。。。。。。。。。");
}
通過init方法構造RestClient 、RestHighLevelClient
那麼如何操作index
單條處理
/**
* @Description: low level put index
* @throws IOException
* @return void
* @throws
* @author JornTang
* @date 2017年12月22日
*/
public static void lowLevelPutIndex(String idnex, String type, ArchiveMapperVo mapperVo) throws IOException{
Map<String, String> params = Collections.emptyMap();
ArchiveMapper mapper = new ArchiveMapper();
BeanUtils.copyProperties(mapperVo, mapper);
HttpEntity entity = new NStringEntity(JSON.toJSONString(mapper), ContentType.APPLICATION_JSON);
Response response = lowLevelRestClient.performRequest("PUT", "/"+idnex+"/"+type+"/" + mapperVo.getDocumentId(), params, entity);
StatusLine statusLine = response.getStatusLine();
log.info("索引執行put:【" + statusLine.getStatusCode() + "】" + statusLine.toString());
}
/**
* @Description: low level delete index
* @throws IOException
* @return void
* @throws
* @author JornTang
* @date 2017年12月22日
*/
public static void lowLevelDeleteIndex(String idnex, String type, String documentId) throws IOException{
Map<String, String> params = Collections.emptyMap();
Response response = lowLevelRestClient.performRequest("DELETE", "/"+idnex+"/"+type+"/" + documentId, params);
StatusLine statusLine = response.getStatusLine();
log.info("索引執行delete:【" + statusLine.getStatusCode() + "】" + statusLine.toString());
}
批量處理
/**
* @Description: high level bulk put index ps:need jdk1.8
* @param idnex 索引
* @param type 型別
* @param mappers 索引資料集合
* @throws Exception
* @return void
* @throws
* @author JornTang
* @date 2017年12月22日
*/
public static void bulkPutIndex(String idnex, String type, List<ArchiveMapper> mappers) throws Exception{
BulkRequest request = new BulkRequest();
if(mappers== null || mappers.size()< 1){
throw new ESIndexException("mappers can not be empty");
}
for (int i = 0; i < mappers.size(); i++) {
ArchiveMapper mapper = mappers.get(i);
request.add(new IndexRequest(idnex, type, mapper.getId())
.opType("create")
.source(JSON.toJSONString(mapper),XContentType.JSON));
}
BulkResponse bulkResponse = highLevelRestClient.bulk(request);
RestStatus stat = doSuccessful(bulkResponse);
//log.info("索引執行bulk put:【" + stat.getStatus() + "】" + stat.toString());
}
是不是覺得非常簡單呢?上面的程式碼不能直接使用要結合自己的實際情況。
比如:資料建模、model定義
資料建模參考:
PUT archives
{
"mappings": {
"archive": {
"_all": {
"enabled": false
},
"properties": {
"id": {
"type": "keyword"
},
"arch_type": {
"type": "integer"
},
"archive_id":{
"type":"integer"
},
"room_id":{
"type":"integer"
},
"if_store":{
"type":"integer"
},
"handover_state":{
"type":"integer"
},
"crt_date":{
"type":"long"
},
"if_hasfile":{
"type":"integer"
},
“doc_date”:{
"type":"long"
},
"doc_year":{
"type":"integer"
},
"fond_code":{
"type":"keyword"
},
"cate_code":{
"type":"keyword"
},
"keep_time":{
"type":"integer"
},
"file_click":{
"type":"integer"
},
"storage_date":{
"type":"long"
},
"storage_year":{
"type":"integer"
},
"into_archive_date":{
"type":"long"
},
"into_archive_year":{
"type":"integer"
},
"vol_in_num":{
"type":"integer"
},
"pro_vol":{
"type":"integer"
},
"pro_doc":{
"type":"integer"
},
"file_num":{
"type":"integer"
},
"file_size":{
"type":"long"
},
"page_nmbr":{
"type":"integer"
}
}
}
},
"settings": {
"number_of_shards":3,
"number_of_replicas":0
}
}
屬性型別定義參考:
常用的資料型別
Ø Boolean 布林型別
布林欄位接受JSON true和false值,但也可以接受被解釋為true或false的字串
示例如下:
PUT my_index
{
"mappings": {
"my_type": {
"properties": {
"is_published": {
"type": "boolean"
}
}
}
}
}
POST my_index/my_type/1
{
"is_published": "true"
}
GET my_index/_search
{
"query": {
"term": {
"is_published": true
}
}
}
以下引數被boolean欄位接受:
對映欄位級查詢時間提升。接受一個浮點數,預設為1.0。 |
|
該欄位是否應該以列步方式儲存在磁碟上,以便稍後用於排序,聚合或指令碼?接受true (預設)或false。 |
|
該領域應該搜尋?接受true(預設)和false。 |
|
接受上面列出的任何真值或假值。該值被替換為任何顯式null值。預設為null,這意味著該欄位被視為丟失。 |
|
欄位值是否應該與_source欄位分開儲存和檢索。接受true或false (預設)。 |
Ø Date 日期型別
JSON沒有日期資料型別,所以Elasticsearch中的日期可以是:
· 包含格式的日期,如字串"2015-01-01"或"2015/01/01 12:10:30"。
· 一個代表毫秒數的長數字。
· 自始至終秒的整數
在內部,日期轉換為UTC(如果指定時區)並存儲為表示毫秒以後的長數字。
日期格式可以自定義,但如果沒有format指定,則使用預設值:
"strict_date_optional_time || epoch_millis"
示例如下:
PUT my_index
{
"mappings": {
"my_type": {
"properties": {
"date": {
"type": "date"
}
}
}
}
}
PUT my_index/my_type/1
{ "date": "2015-01-01" }
PUT my_index/my_type/2
{ "date": "2015-01-01T12:10:30Z" }
PUT my_index/my_type/3
{ "date": 1420070400001 }
GET my_index/_search
{
"sort": { "date": "asc"}
}
定義多個日期格式
多個格式可以通過分隔||作為分隔符來指定。每個格式將依次嘗試,直到找到匹配的格式。第一種格式將被用於將毫秒自時代的值轉換回字串
PUT my_index
{
"mappings":{
"my_type":{
"properties":{
"date":{
"type":"date",
"format":"yyyy-MM-dd HH:mm:ss || yyyy- MM-dd || epoch_millis"
}
}
}
}
}
以下引數被date欄位接受:
對映欄位級查詢時間提升。接受一個浮點數,預設為1.0。 |
|
該欄位是否應該以列步方式儲存在磁碟上,以便稍後用於排序,聚合或指令碼?接受true (預設)或false。 |
|
可以解析的日期格式。預設為strict_date_optional_time||epoch_millis。 |
|
locale |
自從月份以來,解析日期時使用的語言環境在所有語言中都沒有相同的名稱和/或縮寫。預設是ROOT語言環境, |
如果true,格式不正確的號碼被忽略。如果false(預設),格式不正確的數字會丟擲異常並拒絕整個文件。 |
|
該領域應該搜尋?接受true(預設)和false。 |
|
接受其中一個配置的日期值format作為代替任何顯式null值的欄位。預設為null,這意味著該欄位被視為丟失。 |
|
欄位值是否應該與_source欄位分開儲存和檢索。接受true或false (預設)。 |
Ø Keyword 關鍵字型別
用於索引結構化內容的欄位,如電子郵件地址,主機名,狀態碼,郵政編碼或標籤。
它們通常用於過濾(找到我的所有部落格文章,其中 status為published),排序,和聚合。關鍵字欄位只能按其確切值進行搜尋。
如果您需要索引全文內容(如電子郵件正文或產品說明),則可能需要使用text欄位。
示例如下:
PUT my_index
{
"mappings": {
"my_type": {
"properties": {
"tags": {
"type": "keyword"
}
}
}
}
}
以下引數被keyword欄位接受:
對映欄位級查詢時間提升。接受一個浮點數,預設為1.0。 |
|
該欄位是否應該以列步方式儲存在磁碟上,以便稍後用於排序,聚合或指令碼?接受true (預設)或false。 |
|
全球序言是否應該在重新整理的時候急切地載入?接受true或false (預設)。對於經常用於術語聚合的欄位,啟用此功能是一個不錯的主意。 |
|
多欄位允許為了不同的目的以多種方式對相同的字串值進行索引,例如用於搜尋的欄位和用於排序和聚合的多欄位。 |
|
不要索引任何比這個值長的字串。預設為2147483647所有值都將被接受。 |
|
該領域應該搜尋?接受true(預設)或false。 |
|
為了評分目的,應該在索引中儲存哪些資訊。預設為,docs但也可以設定為freqs在計算分數時考慮術語頻率。 |
|
評分查詢時是否應考慮欄位長度。接受true或false(預設)。 |
|
接受一個替代任何顯式null 值的字串值。預設為null,這意味著該欄位被視為丟失。 |
|
欄位值是否應該與_source欄位分開儲存和檢索。接受true或false (預設)。 |
|
應使用哪種評分演算法或相似性。預設為BM25。 |
|
如何在索引之前預先處理關鍵字。預設為null,意味著關鍵字保持原樣。 |
Ø Number 數字型別
以下數字型別受支援:
long |
一個帶符號的64位整數,其最小值為-263,最大值為。 263-1 |
integer |
一個帶符號的32位整數,其最小值為-231,最大值為。 231-1 |
short |
一個帶符號的16位整數,其最小值為-32,768,最大值為32,767。 |
byte |
一個有符號的8位整數,其最小值為-128,最大值為127。 |
double |
雙精度64位IEEE 754浮點。 |
float |
單精度32位IEEE 754浮點。 |
half_float |
一個半精度的16位IEEE 754浮點。 |
scaled_float |
一個由long一個固定比例因子支援的浮點。 |
示例如下:
PUT my_index
{
"mappings": {
"my_type": {
"properties": {
"number_of_bytes": {
"type": "integer"
},
"time_in_seconds": {
"type": "float"
},
"price": {
"type": "scaled_float",
"scaling_factor": 100
}
}
}
}
}
對於double,float和half_float型別需要考慮-0.0和 +0.0不同的值儲存。做一個term查詢 -0.0將不匹配+0.0,反之亦然。同樣適用於範圍查詢真:如果上限是-0.0那麼+0.0將不匹配,如果下界+0.0那麼-0.0將不匹配
那麼我們應該如何使用數字型別。應遵循以下規則
l 至於整數型別(byte,short,integer和long)而言,你應該選擇這是足以讓你的用例最小的型別。這將有助於索引和搜尋更高效。但請注意,考慮到根據儲存的實際值對儲存進行優化,選擇一種型別將不會影響儲存要求
l 對於浮點型別,使用縮放因子將浮點資料儲存到整數中通常會更高效,這正是scaled_float 型別所做的。例如,一個price欄位可以儲存在一個 scaled_floatwith scaling_factor中100。所有的API都可以工作,就好像欄位被儲存為double一樣,但是在elasticsearch下面,將會使用分數price*100,這是一個整數。這對於節省磁碟空間非常有幫助,因為整數比浮點更容易壓縮。scaled_float交易磁碟空間的準確性也很好用。例如,想象一下,您正在跟蹤CPU利用率,並將其作為介於0和之間的數字1。它通常沒有多大的CPU使用率是12.7%或13%,所以你可以使用一個scaled_float 帶有scaling_factor的100,以節省空間,以全面cpu利用率到最近的百分比
如果scaled_float是不適合,那麼你應該選擇一個適合的浮點型別中的用例是足夠最小的型別:double,float和half_float。這裡是一個比較這些型別的表格,以幫助做出決定
型別 |
最小值 |
最大值 |
重要的位/數字 |
double |
2-1074 |
(2-2-52)·21023 |
53 / 15.95 |
float |
2-149 |
(2-2-23)·2127 |
24 / 7.22 |
half_float |
2-24 |
65504 |
11 / 3.31 |
以下引數被數字型別接受:
嘗試將字串轉換為數字並截斷整數的分數。接受true(預設)和false。 |
|
對映欄位級查詢時間提升。接受一個浮點數,預設為1.0。 |
|
該欄位是否應該以列步方式儲存在磁碟上,以便稍後用於排序,聚合或指令碼?接受true (預設)或false。 |
|
如果true,格式不正確的號碼被忽略。如果false(預設),格式不正確的數字會丟擲異常並拒絕整個文件。 |
|
該領域應該搜尋?接受true(預設)和false。 |
|
接受與type替換任何顯式null值的欄位相同的數字值。預設為null,這意味著該欄位被視為丟失。 |
|
欄位值是否應該與_source欄位分開儲存和檢索。接受true或false (預設)。 |
注:引數scaled_float
scaled_float 接受一個額外的引數:
scaling_factor |
編碼值時使用的縮放因子。在索引時間,數值將乘以該因子,並四捨五入到最接近的長整數值。例如,scaled_float用scaling_factor的10將內部儲存2.34的23所有搜尋時操作(查詢,彙總,排序)的行為就好像該檔案是一個值2.3。scaling_factor提高精確度的值較高,但也增加了空間要求。該引數是必需的。 |
Ø Text 文字資料型別
用於索引全文值的欄位,例如電子郵件的正文或產品的說明。這些欄位是analyzed,即它們通過 分析器來轉換字串成索引之前的單個條目列表。分析過程允許Elasticsearch 在每個全文欄位中搜索單個單詞。文字欄位不用於排序,也很少用於聚合(儘管重要的文字聚合 是一個明顯的例外)。
如果您需要為電子郵件地址,主機名,狀態碼或標籤等結構化內容編制索引,則可能需要使用keyword欄位。
示例如下:
PUT my_index
{
"mappings": {
"my_type": {
"properties": {
"full_name": {
"type": "text"
}
}
}
}
}
有時同一欄位的全文字(text)和關鍵字(keyword)版本都是有用的:一個用於全文搜尋,另一個用於聚合和排序。這可以通過多領域來實現 。
以下引數被text欄位接受:
對映欄位級查詢時間提升。接受一個浮點數,預設為1.0。 |
|
全球序言是否應該在重新整理的時候急切地載入?接受true或false (預設)。在經常用於(重要)術語聚合的欄位上啟用此功能是一個好主意。 |
|
該欄位可以使用記憶體中的欄位資料進行排序,聚合或指令碼嗎?接受true或false(預設)。 |
|
專家設定允許決定fielddata 啟用時在記憶體中載入哪些值。預設情況下所有的值都被載入。 |
|
多欄位允許為了不同的目的以多種方式對相同的字串值進行索引,例如一個欄位用於搜尋,一個多欄位用於排序和聚合,或者由不同的分析器分析相同的字串值。 |
|
該領域應該搜尋?接受true(預設)或false。 |
|
索引中應該儲存哪些資訊,用於搜尋和突出顯示目的。預設為positions。 |
|
評分查詢時是否應考慮欄位長度。接受true(預設)或false。 |
|
應該插入到一個字串陣列的每個元素之間的假詞位置的數量。預設為position_increment_gap 預設分析儀上的配置100。100被選中是因為它可以通過欄位值匹配術語來防止具有相當大的短語(小於100)的短語查詢。 |
|
欄位值是否應該與_source欄位分開儲存和檢索。接受true或false (預設)。 |
|
應使用哪種評分演算法或相似性。預設為BM25。 |
Ø Feilds 多領域引數使用
為了不同的目的,以不同的方式對相同的欄位進行索引通常是有用的。這是多領域的目的。例如,一個string 欄位可以對映為text全文搜尋的欄位,也可以對映keyword為排序或聚合的欄位:
示例如下:
PUT my_index
{
"mappings": {
"my_type": {
"properties": {
"city": {
"type": "text",
"fields": {
"raw": {
"type": "keyword"
}
}
}
}
}
}
}
PUT my_index/my_type/1
{
"city": "New York"
}
PUT my_index/my_type/2
{
"city": "York"
}
GET my_index/_search
{
"query": {
"match": {
"city": "york"
}
},
"sort": {
"city.raw": "asc"
},
"aggs": {
"Cities": {
"terms": {
"field": "city.raw"
}
}
}
}
該city.raw欄位是該欄位的一個keyword版本city。 |
該city欄位可用於全文搜尋。 |
該city.raw欄位可用於排序和聚合 |
使用多個分析器進行多欄位編輯
另一個多欄位的用例是以不同的方式分析相同的欄位以獲得更好的相關性。例如,我們可以用standard分析器對欄位進行索引,該 分析器將文字分解成文字,再用english分析器 將文字分解為詞根:
PUT my_index
{
"mappings": {
"my_type": {
"properties": {
"text": {
"type": "text",
"fields": {
"english": {
"type": "text",
"analyzer": "english"
}
}
}
}
}
}
}
PUT my_index/my_type/1
{ "text": "quick brown fox" }
PUT my_index/my_type/2
{ "text": "quick brown foxes" }
GET my_index/_search
{
"query": {
"multi_match": {
"query": "quick brown foxes",
"fields": [
"text",
"text.english"
],
"type": "most_fields"
}
}
}
該text欄位使用的standard分析儀。 |
該text.english欄位使用的english分析儀。 |
索引兩個檔案,一個與fox另一個foxes。 |
查詢text和text.english欄位並結合分數。 |
該text欄位包含fox第一個文件和foxes第二個文件中的術語。該text.english欄位包含fox這兩個文件,因為foxes被阻止fox。
查詢字串也由standard分析器分析該text 欄位,並由english分析器分析text.english欄位。詞幹欄位允許查詢foxes也匹配包含正義的文件fox。這使我們能儘可能多地匹配文件。通過查詢未定text域,我們提高了foxes準確匹配的文件的相關性得分。
Ø 元資料欄位
_all 欄位:一個把其它欄位值((("metadata, document", "_all field")))((("_all field", sortas="all field")))當作一個大字串來索引的特殊欄位。`query_string` 查詢子句(搜尋`?q=john` )在沒有指定欄位時預設使用`_all` 欄位
禁用示例
PUT /my_index/_mapping/my_type
{
"my_type": {
"_all": { "enabled": false }
}
}
如果_all欄位被禁用,則URI搜尋請求與 query_string和simple_query_string查詢將無法用它來查詢,但是你可以設定一個指定欄位。如下示例
PUT my_index
{
"mappings": {
"my_type": {
//禁用_all欄位
"_all": {
"enabled": false
},
"properties": {
"content": {
"type": "text"
}
}
}
},
"settings": {
//設定query_string 查詢指定的欄位
"index.query.default_field": "content"
}
}
_source欄位:該_source欄位包含在索引時間傳遞的原始JSON文件正文。這個 _source欄位本身沒有索引(因此是不可搜尋的),但是它被儲存以便在執行獲取請求(比如get或者search)的時候返回 。
_source段會在索引內產生儲存開銷。出於這個原因,它可以被禁用。如下:
PUT my_index
{
"mappings":{
"my_type":{
" _ source":{
"enabled":false
}
}
}
}
如果_source欄位被禁用,以下功能將不在被支援
· 高亮顯示
· 將索引從一個Elasticsearch索引重新索引到另一個索引的能力,可以更改對映或分析,也可以將索引升級到新的主要版本。
· 通過檢視索引時使用的原始文件來除錯查詢或聚合的能力。
· 在將來有可能自動修復索引損壞。
如果磁碟空間是一個問題,而是增加壓縮級別而不是禁用_source。
在Elasticsearch中,對文件的個別欄位設定儲存的做法通常不是最優的。整個文件已經被儲存為`_source` 欄位。使用`_source` 引數提取你需要的欄位總是更好的。
這個時候你可能會不想儲存所有欄位資訊,那麼你講可以通過以下方式指定你要儲存的欄位。如下
PUT my_index
{
"mappings": {
"my_type": {
"_source": {
//包含,支援萬用字元定義
"includes": [
"*.count",
"meta.*"
],
//不包含,支援萬用字元定義
"excludes": [
"meta.description",
"meta.other.*"
]
}
}
}
}
PUT my_index/my_type/1
{
"requests": {
"count": 10,
"foo": "bar"
},
"meta": {
"name": "Some metric",
"description": "Some metric description",
"other": {
"foo": "one",
"baz": "two"
}
}
}
注:以上方式只是過濾儲存的欄位,但每個欄位依舊可以被搜尋
寫在最後,針對具體相關api請參考elasticsearch官網。
建議:在實際專案中請結合線程池可以在建立索引時大大的提升效能