ES的索引(倒排索引),文档,查询
es的結構(索引,類型,文檔,屬性)
倒排索引
 將存放的數據,以一定的方式進行分詞,并且將分詞的內容存放到一個單獨的分詞庫中。
當用戶去查詢數據時,會將用戶的查詢關鍵字進行分詞。
然后去分詞庫中匹配內容,最終得到數據的id標識。
根據id標識去存放數據的位置拉取到指定的數據。
 索引
- ES的服務中,可以創建多個索引。
 - 每一個索引默認被分成5片存儲。
 - 每一個分片都會存在至少一個備份分片。
 - 備份分片默認不會幫助檢索數據,當ES檢索壓力特別大的時候,備份分片才會幫助檢索數據。
 - 備份的分片必須放在不同的服務器中。
**類型 Type ** 
一個索引下,可以創建多個類型。
 根據版本不同,類型的創建也不同
 文檔 Doc—行
一個類型下,可以有多個文檔。這個文檔就類似于MySQL表中的多行數據。
  屬性 Field–列
一個文檔中,可以包含多個屬性。類似于MySQL表中的一行數據存在多個列
操作ES的RESTful語法(javaREST風格)
注意!要一級一級的操作(要操作文檔就要把前邊的索引和類型加上)
- GET請求:查索引 
- http://ip:port/index:查詢索引信息—+++查索引
 - http://ip:port/index/type/doc_id:查詢指定的文檔信息—+++查id
 
 - POST請求:查某一條,(當做添加文檔,自動生成id時也用這個手動生成id的時候用 put) 
- http://ip:port/index/type/_search:查詢文檔,可以在請求體中添加json字符串來代表查詢條件—+++查(加條件查詢加query)
 - http://ip:port/index/type/doc_id/_update:修改文檔,在請求體中指定json字符串代表修改的具體信息—+++改(根據id改指定的doc文檔)
 - http://ip:port/index/type:增加操作,可以自動生成id
 
 - PUT請求:增(創建索引) 
- http://ip:port/index:創建一個索引,需要在請求體中指定索引的信息,類型,結構—+++增索引
 - http://ip:port/index/type/_mappings:代表創建索引時,指定索引文檔存儲的屬性的信息—+++增(為已有索引增mapping,fired)
 
 - DELETE請求:刪 
- http://ip:port/index:刪除索引
 - http://ip:port/index/type/doc_id:刪除指定的文檔
 
 
ES中Field可以指定的類型
-  
字符串類型:
- text:一把被用于全文檢索。 將當前Field進行分詞。
 - keyword:當前Field不會被分詞。
 
 -  
數值類型:
- long:取值范圍為-9223372036854774808~922337203685477480(-2的63次方到2的63次方-1),占用8個字節
 - integer:取值范圍為-2147483648~2147483647(-2的31次方到2的31次方-1),占用4個字節
 - short:取值范圍為-32768~32767(-2的15次方到2的15次方-1),占用2個字節
 - byte:取值范圍為-128~127(-2的7次方到2的7次方-1),占用1個字節
 - double:1.797693e+308~ 4.9000000e-324 (e+308表示是乘以10的308次方,e-324表示乘以10的負324次方)占用8個字節
 - float:3.402823e+38 ~ 1.401298e-45(e+38表示是乘以10的38次方,e-45表示乘以10的負45次方),占用4個字節
 - half_float:精度比float小一半。
 - scaled_float:根據一個long和scaled來表達一個浮點型,long-345,scaled-100 -> 3.45
 
 -  
時間類型:
- date類型,針對時間類型指定具體的格式
 
 -  
布爾類型:
- boolean類型,表達true和false
 
 -  
二進制類型:
- binary類型暫時支持Base64 encode string
 
 -  
范圍類型:
- long_range:賦值時,無需指定具體的內容,只需要存儲一個范圍即可,指定gt,lt,gte,lte
 - integer_range:同上
 - double_range:同上
 - float_range:同上
 - date_range:同上
 - ip_range:同上
 
 -  
經緯度類型:
- geo_point:用來存儲經緯度的
 
 -  
ip類型:
- ip:可以存儲IPV4或者IPV6
其他的數據類型參考官網:https://www.elastic.co/guide/en/elasticsearch/reference/6.5/mapping-types.html 
 - ip:可以存儲IPV4或者IPV6
 
實例
1,創建索引并指定數據結構(必須包含:分片備份,類型,屬性)
# 創建索引,指定數據結構 PUT /book {"settings": {# 分片數"number_of_shards": 5,# 備份數"number_of_replicas": 1},# 指定數據結構"mappings": {# 類型 Type"novel": {# 文檔存儲的Field"properties": {# Field屬性名"name": {# 類型"type": "text",# 指定分詞器"analyzer": "ik_max_word",# 指定當前Field可以被作為查詢的條件"index": true ,# 是否需要額外存儲"store": false },"author": {"type": "keyword"},"count": {"type": "long"},"on-sale": {"type": "date",# 時間類型的格式化方式 "format": "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd||epoch_millis"},"descr": {"type": "text","analyzer": "ik_max_word"}}}} }2,文檔的操作
新建文檔
# 添加文檔,自動生成id #自動生成的id:P59BbXQBQN3Zxx-BQCXm POST /book/novel {"id":1,"name": "盤龍","author": "我吃西紅柿","count": 100000,"on-sale": "2000-01-01","descr": "山重水復疑無路,柳暗花明又一村" }# 添加文檔,手動指定id PUT /book/novel/1 {"name": "紅樓夢","author": "曹雪芹","count": 10000000,"on-sale": "1985-01-01","descr": "一個是閬苑仙葩,一個是美玉無瑕" }修改文檔
1,覆蓋式修改 # 添加文檔,手動指定id PUT /book/novel/1 {"name": "紅樓夢","author": "曹雪芹","count": 4353453,"on-sale": "1985-01-01","descr": "一個是閬苑仙葩,一個是美玉無瑕" }2,doc的修改方式 # 修改文檔,基于doc方式 POST /book/novel/1/_update {"doc": {# 指定上需要修改的field和對應的值"count": "1234565"} }刪除文檔
# 根據id刪除文檔 DELETE /book/novel/_id5查詢文檔(重點)
1,創建連接
public class EsClient {public static RestHighLevelClient getEsClient() { //創建連接 // 創建HttpHost對象HttpHost httpHost = new HttpHost("192.168.32.137", 9200);// 創建RestClientBuilderRestClientBuilder builder = RestClient.builder(httpHost);// 創建RestHighLevelClientRestHighLevelClient client = new RestHighLevelClient(builder);return client;} }文檔操作的代碼實現
public class JavaEsTest {// 先定義連接對選哪個,并設置索引和類型后邊會多次用到private RestHighLevelClient client = EsClient.getEsClient();private String index = "book";private String type = "fantasy";// 創建索引+++++++索引的操作@Testpublic void createIndex() throws IOException {// 1需要先setting設置分片,備份Settings.Builder settings = Settings.builder().put("number_of_shards", 5).put("number_of_replicas", 1); // 2 需要書寫類型映射mappingXContentBuilder mappings = JsonXContent.contentBuilder();mappings.startObject().startObject("properties").startObject("bookName").field("type", "text").field("analyzer", "ik_max_word").field("index", "true").endObject().startObject("id").field("type", "long").endObject().startObject("bookAuthor").field("type", "keyword").endObject().startObject("bookCreateTime").field("type", "date").field("format", "yyyy-MM-dd HH:mm:ss||yyyy-MM-dd").endObject().startObject("bookDesc").field("type", "text").field("analyzer", "ik_max_word").endObject().startObject("bookSize").field("type", "long").endObject().endObject().endObject();// 3將設置好的setting和mapping設置到請求對象中// 創建一個創建索引的請求對象,CreateIndexRequest request = new CreateIndexRequest(index);request.settings(settings);request.mapping(type, mappings);// 4執行創建索引操作==響應CreateIndexResponse response = client.indices().create(request, RequestOptions.DEFAULT);boolean acknowledged = response.isAcknowledged();System.out.println(acknowledged);}@Test //查看某一個索引存在不存在public void indexExsist() throws IOException {GetIndexRequest request = new GetIndexRequest();request.indices(index);boolean b = client.indices().exists(request, RequestOptions.DEFAULT);System.out.println(b);}@Test //索引的刪除?public void indexDelete() throws IOException {DeleteIndexRequest request = new DeleteIndexRequest(index);AcknowledgedResponse response = client.indices().delete(request, RequestOptions.DEFAULT);System.out.println(response.isAcknowledged());}//文檔的操作(文檔操作就要指明索引和類型)@Testpublic void addDoc() throws IOException {IndexRequest request = new IndexRequest(index, type);String desc = "這里是倒流澳大利亞那才叫還打";Book book = new Book("斗破蒼穹", "天蠶土豆", 1L, desc, "2020-09-09", 20000000L); // 設置添加的值String jsonStr = JSON.toJSONString(book);System.out.println(jsonStr);request.source(jsonStr, XContentType.JSON);IndexResponse index = client.index(request, RequestOptions.DEFAULT);System.out.println(index);}//文檔修改@Testpublic void updateDoc() throws IOException {UpdateRequest request = new UpdateRequest(index, type, "Rp8FbnQBQN3Zxx-BcCUz");String desc = "這里是倒流澳大利亞那才叫還打";Book book = new Book("斗破蒼穹", "di蠶土豆", 1L, desc, "2020-09-09", 20000000L); // 設置添加的值String jsonStr = JSON.toJSONString(book);System.out.println(jsonStr);request.doc(jsonStr, XContentType.JSON);UpdateResponse update = client.update(request, RequestOptions.DEFAULT);}//文檔刪除@Testpublic void deleteDoc() throws IOException {DeleteRequest request = new DeleteRequest(index, type, "Rp8FbnQBQN3Zxx-BcCUz");DeleteResponse delete = client.delete(request, RequestOptions.DEFAULT);System.out.println(delete);}// 批量操作添加@Testpublic void bulkCreateDoc() throws IOException {//準備多個jsonString desc = "這里是倒流澳大利亞那才叫還打";Book book1 = new Book("少年1", "di蠶土豆", 1L, desc, "2020-09-09", 20000000L);Book book2 = new Book("少年2", "di蠶土豆", 2L, desc, "2020-09-09", 20000000L);Book book3 = new Book("少年3", "di蠶土豆", 3L, desc, "2020-09-09", 20000000L);String jsonStr1 = JSON.toJSONString(book1);String jsonStr2 = JSON.toJSONString(book2);String jsonStr3 = JSON.toJSONString(book3);//創建request封裝jsonBulkRequest request = new BulkRequest();request.add(new IndexRequest(index, type, book1.getId().toString()).source(jsonStr1, XContentType.JSON));request.add(new IndexRequest(index, type, book2.getId().toString()).source(jsonStr2, XContentType.JSON));request.add(new IndexRequest(index, type, book3.getId().toString()).source(jsonStr3, XContentType.JSON)); // 用client執行BulkResponse resp = client.bulk(request, RequestOptions.DEFAULT);//4. 輸出結果System.out.println(resp.toString());}//批量刪除@Testpublic void bulkDeleteDoc() throws IOException { // 1封裝request 對象BulkRequest request = new BulkRequest();request.add(new DeleteRequest(index, type, "1"));request.add(new DeleteRequest(index, type, "2"));request.add(new DeleteRequest(index, type, "3"));//cliient執行BulkResponse resp = client.bulk(request, RequestOptions.DEFAULT); //3輸出System.out.println(resp);}}六、 ElasticSearch的各種查詢
6.1 term&terms查詢【重點】
6.1.1 term查詢
term的查詢是代表完全匹配,搜索之前不會對你搜索的關鍵字進行分詞,對你的關鍵字去文檔分詞庫中去匹配內容。
# term查詢 POST /sms-logs-index/sms-logs-type/_search {"from": 0, # limit ?"size": 5, # limit x,?"query": {"term": {"province": {"value": "北京"}}} }代碼實現方式
// Java代碼實現方式 @Test public void termQuery() throws IOException {//1. 創建Request對象SearchRequest request = new SearchRequest(index);request.types(type);//2. 指定查詢條件SearchSourceBuilder builder = new SearchSourceBuilder();builder.from(0);builder.size(5);builder.query(QueryBuilders.termQuery("province","北京"));request.source(builder);//3. 執行查詢SearchResponse resp = client.search(request, RequestOptions.DEFAULT);//4. 獲取到_source中的數據,并展示for (SearchHit hit : resp.getHits().getHits()) {Map<String, Object> result = hit.getSourceAsMap();System.out.println(result);} }6.1.2 terms查詢
terms和term的查詢機制是一樣,都不會將指定的查詢關鍵字進行分詞,直接去分詞庫中匹配,找到相應文檔內容。
terms是在針對一個字段包含多個值的時候使用。
term:where province = 北京;
terms:where province = 北京 or province = ?or province = ?
# terms查詢 POST /sms-logs-index/sms-logs-type/_search {"query": {"terms": {"province": ["北京","山西","武漢"]}} }代碼實現方式
// Java實現 @Test public void termsQuery() throws IOException {//1. 創建requestSearchRequest request = new SearchRequest(index);request.types(type);//2. 封裝查詢條件SearchSourceBuilder builder = new SearchSourceBuilder();builder.query(QueryBuilders.termsQuery("province","北京","山西"));request.source(builder);//3. 執行查詢SearchResponse resp = client.search(request, RequestOptions.DEFAULT);//4. 輸出_sourcefor (SearchHit hit : resp.getHits().getHits()) {System.out.println(hit.getSourceAsMap());} }6.2 match查詢【重點】
match查詢屬于高層查詢,他會根據你查詢的字段類型不一樣,采用不同的查詢方式。
- 查詢的是日期或者是數值的話,他會將你基于的字符串查詢內容轉換為日期或者數值對待。
 - 如果查詢的內容是一個不能被分詞的內容(keyword),match查詢不會對你指定的查詢關鍵字進行分詞。
 - 如果查詢的內容時一個可以被分詞的內容(text),match會將你指定的查詢內容根據一定的方式去分詞,去分詞庫中匹配指定的內容。
 
match查詢,實際底層就是多個term查詢,將多個term查詢的結果給你封裝到了一起。
6.2.1 match_all查詢
查詢全部內容,不指定任何查詢條件。
# match_all查詢 POST /sms-logs-index/sms-logs-type/_search {"query": {"match_all": {}} }代碼實現方式
// java代碼實現 @Test public void matchAllQuery() throws IOException {//1. 創建RequestSearchRequest request = new SearchRequest(index);request.types(type);//2. 指定查詢條件SearchSourceBuilder builder = new SearchSourceBuilder();builder.query(QueryBuilders.matchAllQuery());builder.size(20); // ES默認只查詢10條數據,如果想查詢更多,添加sizerequest.source(builder);//3. 執行查詢SearchResponse resp = client.search(request, RequestOptions.DEFAULT);//4. 輸出結果for (SearchHit hit : resp.getHits().getHits()) {System.out.println(hit.getSourceAsMap());}System.out.println(resp.getHits().getHits().length);}6.2.2 match查詢
指定一個Field作為篩選的條件
# match查詢 POST /sms-logs-index/sms-logs-type/_search {"query": {"match": {"smsContent": "收貨安裝"}} }代碼實現方式
@Test public void matchQuery() throws IOException {//1. 創建RequestSearchRequest request = new SearchRequest(index);request.types(type);//2. 指定查詢條件SearchSourceBuilder builder = new SearchSourceBuilder();//-----------------------------------------------builder.query(QueryBuilders.matchQuery("smsContent","收貨安裝"));//-----------------------------------------------request.source(builder);//3. 執行查詢SearchResponse resp = client.search(request, RequestOptions.DEFAULT);//4. 輸出結果for (SearchHit hit : resp.getHits().getHits()) {System.out.println(hit.getSourceAsMap());} }6.2.3 布爾match查詢
基于一個Field匹配的內容,采用and或者or的方式連接
# 布爾match查詢 POST /sms-logs-index/sms-logs-type/_search {"query": {"match": {"smsContent": {"query": "中國 健康","operator": "and" # 內容既包含中國也包含健康}}} }# 布爾match查詢 POST /sms-logs-index/sms-logs-type/_search {"query": {"match": {"smsContent": {"query": "中國 健康","operator": "or" # 內容包括健康或者包括中國}}} }代碼實現方式
// Java代碼實現 @Test public void booleanMatchQuery() throws IOException {//1. 創建RequestSearchRequest request = new SearchRequest(index);request.types(type);//2. 指定查詢條件SearchSourceBuilder builder = new SearchSourceBuilder();//----------------------------------------------- 選擇AND或者ORbuilder.query(QueryBuilders.matchQuery("smsContent","中國 健康").operator(Operator.OR));//-----------------------------------------------request.source(builder);//3. 執行查詢SearchResponse resp = client.search(request, RequestOptions.DEFAULT);//4. 輸出結果for (SearchHit hit : resp.getHits().getHits()) {System.out.println(hit.getSourceAsMap());} }6.2.4 multi_match查詢
match針對一個field做檢索,multi_match針對多個field進行檢索,多個field對應一個text。
# multi_match 查詢 POST /sms-logs-index/sms-logs-type/_search {"query": {"multi_match": {"query": "北京", # 指定text"fields": ["province","smsContent"] # 指定field們}} }代碼實現方式
// java代碼實現 @Test public void multiMatchQuery() throws IOException {//1. 創建RequestSearchRequest request = new SearchRequest(index);request.types(type);//2. 指定查詢條件SearchSourceBuilder builder = new SearchSourceBuilder();//-----------------------------------------------builder.query(QueryBuilders.multiMatchQuery("北京","province","smsContent"));//-----------------------------------------------request.source(builder);//3. 執行查詢SearchResponse resp = client.search(request, RequestOptions.DEFAULT);//4. 輸出結果for (SearchHit hit : resp.getHits().getHits()) {System.out.println(hit.getSourceAsMap());} }6.3 其他查詢
6.3.1 id查詢
根據id查詢 where id = ?
# id查詢 GET /sms-logs-index/sms-logs-type/1代碼實現方式
// Java代碼實現 @Test public void findById() throws IOException {//1. 創建GetRequestGetRequest request = new GetRequest(index,type,"1");//2. 執行查詢GetResponse resp = client.get(request, RequestOptions.DEFAULT);//3. 輸出結果System.out.println(resp.getSourceAsMap()); }6.3.2 ids查詢
根據多個id查詢,類似MySQL中的where id in(id1,id2,id2…)
# ids查詢 POST /sms-logs-index/sms-logs-type/_search {"query": {"ids": {"values": ["1","2","3"]}} }代碼實現方式
// Java代碼實現 @Test public void findByIds() throws IOException {//1. 創建SearchRequestSearchRequest request = new SearchRequest(index);request.types(type);//2. 指定查詢條件SearchSourceBuilder builder = new SearchSourceBuilder();//----------------------------------------------------------builder.query(QueryBuilders.idsQuery().addIds("1","2","3"));//----------------------------------------------------------request.source(builder);//3. 執行SearchResponse resp = client.search(request, RequestOptions.DEFAULT);//4. 輸出結果for (SearchHit hit : resp.getHits().getHits()) {System.out.println(hit.getSourceAsMap());} }6.3.3 prefix查詢
前綴查詢,可以通過一個關鍵字去指定一個Field的前綴,從而查詢到指定的文檔。
#prefix 查詢 POST /sms-logs-index/sms-logs-type/_search {"query": {"prefix": {"corpName": {"value": "途虎"}}} }代碼實現方式
// Java實現前綴查詢 @Test public void findByPrefix() throws IOException {//1. 創建SearchRequestSearchRequest request = new SearchRequest(index);request.types(type);//2. 指定查詢條件SearchSourceBuilder builder = new SearchSourceBuilder();//----------------------------------------------------------builder.query(QueryBuilders.prefixQuery("corpName","盒馬"));//----------------------------------------------------------request.source(builder);//3. 執行SearchResponse resp = client.search(request, RequestOptions.DEFAULT);//4. 輸出結果for (SearchHit hit : resp.getHits().getHits()) {System.out.println(hit.getSourceAsMap());} }6.3.4 fuzzy查詢
模糊查詢,我們輸入字符的大概,ES就可以去根據輸入的內容大概去匹配一下結果。
# fuzzy查詢 POST /sms-logs-index/sms-logs-type/_search {"query": {"fuzzy": {"corpName": {"value": "盒馬先生","prefix_length": 2 # 指定前面幾個字符是不允許出現錯誤的}}} }代碼實現方式
// Java代碼實現Fuzzy查詢 @Test public void findByFuzzy() throws IOException {//1. 創建SearchRequestSearchRequest request = new SearchRequest(index);request.types(type);//2. 指定查詢條件SearchSourceBuilder builder = new SearchSourceBuilder();//----------------------------------------------------------builder.query(QueryBuilders.fuzzyQuery("corpName","盒馬先生").prefixLength(2));//----------------------------------------------------------request.source(builder);//3. 執行SearchResponse resp = client.search(request, RequestOptions.DEFAULT);//4. 輸出結果for (SearchHit hit : resp.getHits().getHits()) {System.out.println(hit.getSourceAsMap());} }6.3.5 wildcard查詢
通配查詢,和MySQL中的like是一個套路,可以在查詢時,在字符串中指定通配符*和占位符?
# wildcard 查詢 POST /sms-logs-index/sms-logs-type/_search {"query": {"wildcard": {"corpName": {"value": "中國*" # 可以使用*和?指定通配符和占位符}}} }代碼實現方式
// Java代碼實現Wildcard查詢 @Test public void findByWildCard() throws IOException {//1. 創建SearchRequestSearchRequest request = new SearchRequest(index);request.types(type);//2. 指定查詢條件SearchSourceBuilder builder = new SearchSourceBuilder();//----------------------------------------------------------builder.query(QueryBuilders.wildcardQuery("corpName","中國*"));//----------------------------------------------------------request.source(builder);//3. 執行SearchResponse resp = client.search(request, RequestOptions.DEFAULT);//4. 輸出結果for (SearchHit hit : resp.getHits().getHits()) {System.out.println(hit.getSourceAsMap());} }6.3.6 range查詢
范圍查詢,只針對數值類型,對某一個Field進行大于或者小于的范圍指定
# range 查詢 POST /sms-logs-index/sms-logs-type/_search {"query": {"range": {"fee": {"gt": 5,"lte": 10# 可以使用 gt:> gte:>= lt:< lte:<=}}} }代碼實現方式
// Java實現range范圍查詢 @Test public void findByRange() throws IOException {//1. 創建SearchRequestSearchRequest request = new SearchRequest(index);request.types(type);//2. 指定查詢條件SearchSourceBuilder builder = new SearchSourceBuilder();//----------------------------------------------------------builder.query(QueryBuilders.rangeQuery("fee").lte(10).gte(5));//----------------------------------------------------------request.source(builder);//3. 執行SearchResponse resp = client.search(request, RequestOptions.DEFAULT);//4. 輸出結果for (SearchHit hit : resp.getHits().getHits()) {System.out.println(hit.getSourceAsMap());} }6.3.7 regexp查詢
正則查詢,通過你編寫的正則表達式去匹配內容。
Ps:prefix,fuzzy,wildcard和regexp查詢效率相對比較低,要求效率比較高時,避免去使用
# regexp 查詢 POST /sms-logs-index/sms-logs-type/_search {"query": {"regexp": {"mobile": "180[0-9]{8}" # 編寫正則}} }代碼實現方式
// Java代碼實現正則查詢 @Test public void findByRegexp() throws IOException {//1. 創建SearchRequestSearchRequest request = new SearchRequest(index);request.types(type);//2. 指定查詢條件SearchSourceBuilder builder = new SearchSourceBuilder();//----------------------------------------------------------builder.query(QueryBuilders.regexpQuery("mobile","139[0-9]{8}"));//----------------------------------------------------------request.source(builder);//3. 執行SearchResponse resp = client.search(request, RequestOptions.DEFAULT);//4. 輸出結果for (SearchHit hit : resp.getHits().getHits()) {System.out.println(hit.getSourceAsMap());} }6.4 深分頁Scroll
ES對from + size是有限制的,from和size二者之和不能超過1W
原理:
-  
from+size在ES查詢數據的方式:
- 第一步現將用戶指定的關鍵進行分詞。
 - 第二步將詞匯去分詞庫中進行檢索,得到多個文檔的id。
 - 第三步去各個分片中去拉取指定的數據。耗時較長。
 - 第四步將數據根據score進行排序。耗時較長。
 - 第五步根據from的值,將查詢到的數據舍棄一部分。
 - 第六步返回結果。
 
 -  
scroll+size在ES查詢數據的方式:
- 第一步現將用戶指定的關鍵進行分詞。
 - 第二步將詞匯去分詞庫中進行檢索,得到多個文檔的id。
 - 第三步將文檔的id存放在一個ES的上下文中。
 - 第四步根據你指定的size的個數去ES中檢索指定個數的數據,拿完數據的文檔id,會從上下文中移除。
 - 第五步如果需要下一頁數據,直接去ES的上下文中,找后續內容。
 - 第六步循環第四步和第五步
 
 
Scroll查詢方式,不適合做實時的查詢
# 執行scroll查詢,返回第一頁數據,并且將文檔id信息存放在ES上下文中,指定生存時間1m POST /sms-logs-index/sms-logs-type/_search?scroll=1m {"query": {"match_all": {}},"size": 2,"sort": [ # 排序{"fee": {"order": "desc"}}] }# 根據scroll查詢下一頁數據 POST /_search/scroll {"scroll_id": "<根據第一步得到的scorll_id去指定>","scroll": "<scorll信息的生存時間>" }# 刪除scroll在ES上下文中的數據 DELETE /_search/scroll/scroll_id代碼實現方式
// Java實現scroll分頁 @Test public void scrollQuery() throws IOException {//1. 創建SearchRequestSearchRequest request = new SearchRequest(index);request.types(type);//2. 指定scroll信息request.scroll(TimeValue.timeValueMinutes(1L));//3. 指定查詢條件SearchSourceBuilder builder = new SearchSourceBuilder();builder.size(4);builder.sort("fee", SortOrder.DESC);builder.query(QueryBuilders.matchAllQuery());request.source(builder);//4. 獲取返回結果scrollId,sourceSearchResponse resp = client.search(request, RequestOptions.DEFAULT);String scrollId = resp.getScrollId();System.out.println("----------首頁---------");for (SearchHit hit : resp.getHits().getHits()) {System.out.println(hit.getSourceAsMap());}while(true) {//5. 循環 - 創建SearchScrollRequestSearchScrollRequest scrollRequest = new SearchScrollRequest(scrollId);//6. 指定scrollId的生存時間scrollRequest.scroll(TimeValue.timeValueMinutes(1L));//7. 執行查詢獲取返回結果SearchResponse scrollResp = client.scroll(scrollRequest, RequestOptions.DEFAULT);//8. 判斷是否查詢到了數據,輸出SearchHit[] hits = scrollResp.getHits().getHits();if(hits != null && hits.length > 0) {System.out.println("----------下一頁---------");for (SearchHit hit : hits) {System.out.println(hit.getSourceAsMap());}}else{//9. 判斷沒有查詢到數據-退出循環System.out.println("----------結束---------");break;}}//10. 創建CLearScrollRequestClearScrollRequest clearScrollRequest = new ClearScrollRequest();//11. 指定ScrollIdclearScrollRequest.addScrollId(scrollId);//12. 刪除ScrollIdClearScrollResponse clearScrollResponse = client.clearScroll(clearScrollRequest, RequestOptions.DEFAULT);//13. 輸出結果System.out.println("刪除scroll:" + clearScrollResponse.isSucceeded());}6.5 delete-by-query
根據term,match等查詢方式去刪除大量的文檔
Ps:如果你需要刪除的內容,是index下的大部分數據,推薦創建一個全新的index,將保留的文檔內容,添加到全新的索引
# delete-by-query POST /sms-logs-index/sms-logs-type/_delete_by_query {"query": {"range": {"fee": {"lt": 4}}} }代碼實現方式
// Java代碼實現 @Test public void deleteByQuery() throws IOException {//1. 創建DeleteByQueryRequestDeleteByQueryRequest request = new DeleteByQueryRequest(index);request.types(type);//2. 指定檢索的條件 和SearchRequest指定Query的方式不一樣request.setQuery(QueryBuilders.rangeQuery("fee").lt(4));//3. 執行刪除BulkByScrollResponse resp = client.deleteByQuery(request, RequestOptions.DEFAULT);//4. 輸出返回結果System.out.println(resp.toString());}6.6 復合查詢
6.6.1 bool查詢
復合過濾器,將你的多個查詢條件,以一定的邏輯組合在一起。
- must: 所有的條件,用must組合在一起,表示And的意思
 - must_not:將must_not中的條件,全部都不能匹配,標識Not的意思
 - should:所有的條件,用should組合在一起,表示Or的意思
 
代碼實現方式
// Java代碼實現Bool查詢 @Test public void BoolQuery() throws IOException {//1. 創建SearchRequestSearchRequest request = new SearchRequest(index);request.types(type);//2. 指定查詢條件SearchSourceBuilder builder = new SearchSourceBuilder();BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();// # 查詢省份為武漢或者北京boolQuery.should(QueryBuilders.termQuery("province","武漢"));boolQuery.should(QueryBuilders.termQuery("province","北京"));// # 運營商不是聯通boolQuery.mustNot(QueryBuilders.termQuery("operatorId",2));// # smsContent中包含中國和平安boolQuery.must(QueryBuilders.matchQuery("smsContent","中國"));boolQuery.must(QueryBuilders.matchQuery("smsContent","平安"));builder.query(boolQuery);request.source(builder);//3. 執行查詢SearchResponse resp = client.search(request, RequestOptions.DEFAULT);//4. 輸出結果for (SearchHit hit : resp.getHits().getHits()) {System.out.println(hit.getSourceAsMap());} }6.6.2 boosting查詢
boosting查詢可以幫助我們去影響查詢后的score。
- positive:只有匹配上positive的查詢的內容,才會被放到返回的結果集中。
 - negative:如果匹配上和positive并且也匹配上了negative,就可以降低這樣的文檔score。
 - negative_boost:指定系數,必須小于1.0
 
關于查詢時,分數是如何計算的:
- 搜索的關鍵字在文檔中出現的頻次越高,分數就越高
 - 指定的文檔內容越短,分數就越高
 - 我們在搜索時,指定的關鍵字也會被分詞,這個被分詞的內容,被分詞庫匹配的個數越多,分數越高
 
代碼實現方式
// Java實現Boosting查詢 @Test public void BoostingQuery() throws IOException {//1. 創建SearchRequestSearchRequest request = new SearchRequest(index);request.types(type);//2. 指定查詢條件SearchSourceBuilder builder = new SearchSourceBuilder();BoostingQueryBuilder boostingQuery = QueryBuilders.boostingQuery(QueryBuilders.matchQuery("smsContent", "收貨安裝"),QueryBuilders.matchQuery("smsContent", "王五")).negativeBoost(0.5f);builder.query(boostingQuery);request.source(builder);//3. 執行查詢SearchResponse resp = client.search(request, RequestOptions.DEFAULT);//4. 輸出結果for (SearchHit hit : resp.getHits().getHits()) {System.out.println(hit.getSourceAsMap());} }6.7 filter查詢
query,根據你的查詢條件,去計算文檔的匹配度得到一個分數,并且根據分數進行排序,不會做緩存的。
filter,根據你的查詢條件去查詢文檔,不去計算分數,而且filter會對經常被過濾的數據進行緩存。
# filter查詢 POST /sms-logs-index/sms-logs-type/_search {"query": {"bool": {"filter": [{"term": {"corpName": "盒馬鮮生"}},{"range": {"fee": {"lte": 4}}}]}} }代碼實現方式
// Java實現filter操作 @Test public void filter() throws IOException {//1. SearchRequestSearchRequest request = new SearchRequest(index);request.types(type);//2. 查詢條件SearchSourceBuilder builder = new SearchSourceBuilder();BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();boolQuery.filter(QueryBuilders.termQuery("corpName","盒馬鮮生"));boolQuery.filter(QueryBuilders.rangeQuery("fee").lte(5));builder.query(boolQuery);request.source(builder);//3. 執行查詢SearchResponse resp = client.search(request, RequestOptions.DEFAULT);//4. 輸出結果for (SearchHit hit : resp.getHits().getHits()) {System.out.println(hit.getSourceAsMap());}}6.8 高亮查詢【重點】
高亮查詢就是你用戶輸入的關鍵字,以一定的特殊樣式展示給用戶,讓用戶知道為什么這個結果被檢索出來。
高亮展示的數據,本身就是文檔中的一個Field,單獨將Field以highlight的形式返回給你。
ES提供了一個highlight屬性,和query同級別的。
- fragment_size:指定高亮數據展示多少個字符回來。
 - pre_tags:指定前綴標簽,舉個栗子< font color=“red” >
 - post_tags:指定后綴標簽,舉個栗子< /font >
 - fields:指定哪幾個Field以高亮形式返回
 
| [外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-2Fn420ZK-1599738862960)(Pictures/1587296912692.png)] | 
RESTful實現
# highlight查詢 POST /sms-logs-index/sms-logs-type/_search {"query": {"match": {"smsContent": "盒馬"}},"highlight": {"fields": {"smsContent": {}},"pre_tags": "<font color='red'>","post_tags": "</font>","fragment_size": 10} }代碼實現方式
// Java實現高亮查詢 @Test public void highLightQuery() throws IOException {//1. SearchRequestSearchRequest request = new SearchRequest(index);request.types(type);//2. 指定查詢條件(高亮)SearchSourceBuilder builder = new SearchSourceBuilder();//2.1 指定查詢條件builder.query(QueryBuilders.matchQuery("smsContent","盒馬"));//2.2 指定高亮HighlightBuilder highlightBuilder = new HighlightBuilder();highlightBuilder.field("smsContent",10).preTags("<font color='red'>").postTags("</font>");builder.highlighter(highlightBuilder);request.source(builder);//3. 執行查詢SearchResponse resp = client.search(request, RequestOptions.DEFAULT);//4. 獲取高亮數據,輸出for (SearchHit hit : resp.getHits().getHits()) {System.out.println(hit.getHighlightFields().get("smsContent"));} }6.9 聚合查詢【重點】
ES的聚合查詢和MySQL的聚合查詢類似,ES的聚合查詢相比MySQL要強大的多,ES提供的統計數據的方式多種多樣。
# ES聚合查詢的RESTful語法 POST /index/type/_search {"aggs": {"名字(agg)": {"agg_type": {"屬性": "值"}}} }6.9.1 去重計數查詢
去重計數,即Cardinality,第一步先將返回的文檔中的一個指定的field進行去重,統計一共有多少條
# 去重計數查詢 北京 上海 武漢 山西 POST /sms-logs-index/sms-logs-type/_search {"aggs": {"agg": {"cardinality": {"field": "province"}}} }代碼實現方式
// Java代碼實現去重計數查詢 @Test public void cardinality() throws IOException {//1. 創建SearchRequestSearchRequest request = new SearchRequest(index);request.types(type);//2. 指定使用的聚合查詢方式SearchSourceBuilder builder = new SearchSourceBuilder();builder.aggregation(AggregationBuilders.cardinality("agg").field("province"));request.source(builder);//3. 執行查詢SearchResponse resp = client.search(request, RequestOptions.DEFAULT);//4. 獲取返回結果Cardinality agg = resp.getAggregations().get("agg");long value = agg.getValue();System.out.println(value); }6.9.2 范圍統計
統計一定范圍內出現的文檔個數,比如,針對某一個Field的值在 0100,100200,200~300之間文檔出現的個數分別是多少。
范圍統計可以針對普通的數值,針對時間類型,針對ip類型都可以做相應的統計。
range,date_range,ip_range
數值統計
# 數值方式范圍統計 POST /sms-logs-index/sms-logs-type/_search {"aggs": {"agg": {"range": {"field": "fee","ranges": [{"to": 5},{"from": 5, # from有包含當前值的意思 "to": 10},{"from": 10}]}}} }時間范圍統計
# 時間方式范圍統計 POST /sms-logs-index/sms-logs-type/_search {"aggs": {"agg": {"date_range": {"field": "createDate","format": "yyyy", "ranges": [{"to": 2000},{"from": 2000}]}}} }ip統計方式
# ip方式 范圍統計 POST /sms-logs-index/sms-logs-type/_search {"aggs": {"agg": {"ip_range": {"field": "ipAddr","ranges": [{"to": "10.126.2.9"},{"from": "10.126.2.9"}]}}} }代碼實現方式
// Java實現數值 范圍統計 @Test public void range() throws IOException {//1. 創建SearchRequestSearchRequest request = new SearchRequest(index);request.types(type);//2. 指定使用的聚合查詢方式SearchSourceBuilder builder = new SearchSourceBuilder();//---------------------------------------------builder.aggregation(AggregationBuilders.range("agg").field("fee").addUnboundedTo(5).addRange(5,10).addUnboundedFrom(10));//---------------------------------------------request.source(builder);//3. 執行查詢SearchResponse resp = client.search(request, RequestOptions.DEFAULT);//4. 獲取返回結果Range agg = resp.getAggregations().get("agg");for (Range.Bucket bucket : agg.getBuckets()) {String key = bucket.getKeyAsString();Object from = bucket.getFrom();Object to = bucket.getTo();long docCount = bucket.getDocCount();System.out.println(String.format("key:%s,from:%s,to:%s,docCount:%s",key,from,to,docCount));} }6.9.3 統計聚合查詢
他可以幫你查詢指定Field的最大值,最小值,平均值,平方和等
使用:extended_stats
# 統計聚合查詢 POST /sms-logs-index/sms-logs-type/_search {"aggs": {"agg": {"extended_stats": {"field": "fee"}}} }代碼實現方式
// Java實現統計聚合查詢 @Test public void extendedStats() throws IOException {//1. 創建SearchRequestSearchRequest request = new SearchRequest(index);request.types(type);//2. 指定使用的聚合查詢方式SearchSourceBuilder builder = new SearchSourceBuilder();//---------------------------------------------builder.aggregation(AggregationBuilders.extendedStats("agg").field("fee"));//---------------------------------------------request.source(builder);//3. 執行查詢SearchResponse resp = client.search(request, RequestOptions.DEFAULT);//4. 獲取返回結果ExtendedStats agg = resp.getAggregations().get("agg");double max = agg.getMax();double min = agg.getMin();System.out.println("fee的最大值為:" + max + ",最小值為:" + min); }其他的聚合查詢方式查看官方文檔:https://www.elastic.co/guide/en/elasticsearch/reference/6.5/index.html
6.10 地圖經緯度搜索
ES中提供了一個數據類型 geo_point,這個類型就是用來存儲經緯度的。
創建一個帶geo_point類型的索引,并添加測試數據
# 創建一個索引,指定一個name,locaiton PUT /map {"settings": {"number_of_shards": 5,"number_of_replicas": 1},"mappings": {"map": {"properties": {"name": {"type": "text"},"location": {"type": "geo_point"}}}} }# 添加測試數據 PUT /map/map/1 {"name": "天安門","location": {"lon": 116.403981,"lat": 39.914492 } }PUT /map/map/2 {"name": "海淀公園","location": {"lon": 116.302509,"lat": 39.991152 } }PUT /map/map/3 {"name": "北京動物園","location": {"lon": 116.343184,"lat": 39.947468 } }6.10.1 ES的地圖檢索方式
| geo_distance | 直線距離檢索方式 | 
| geo_bounding_box | 以兩個點確定一個矩形,獲取在矩形內的全部數據 | 
| geo_polygon | 以多個點,確定一個多邊形,獲取多邊形內的全部數據 | 
6.10.2 基于RESTful實現地圖檢索
geo_distance
# geo_distance POST /map/map/_search {"query": {"geo_distance": {"location": { # 確定一個點"lon": 116.433733,"lat": 39.908404},"distance": 3000, # 確定半徑"distance_type": "arc" # 指定形狀為圓形}} }geo_bounding_box
# geo_bounding_box POST /map/map/_search {"query": {"geo_bounding_box": {"location": {"top_left": { # 左上角的坐標點"lon": 116.326943,"lat": 39.95499},"bottom_right": { # 右下角的坐標點"lon": 116.433446,"lat": 39.908737}}}} }geo_polygon
# geo_polygon POST /map/map/_search {"query": {"geo_polygon": {"location": {"points": [ # 指定多個點確定一個多邊形{"lon": 116.298916,"lat": 39.99878},{"lon": 116.29561,"lat": 39.972576},{"lon": 116.327661,"lat": 39.984739}]}}} }6.10.3 Java實現geo_polygon
// 基于Java實現geo_polygon查詢 @Test public void geoPolygon() throws IOException {//1. SearchRequestSearchRequest request = new SearchRequest(index);request.types(type);//2. 指定檢索方式SearchSourceBuilder builder = new SearchSourceBuilder();List<GeoPoint> points = new ArrayList<>();points.add(new GeoPoint(39.99878,116.298916));points.add(new GeoPoint(39.972576,116.29561));points.add(new GeoPoint(39.984739,116.327661));builder.query(QueryBuilders.geoPolygonQuery("location",points));request.source(builder);//3. 執行查詢SearchResponse resp = client.search(request, RequestOptions.DEFAULT);//4. 輸出結果for (SearchHit hit : resp.getHits().getHits()) {System.out.println(hit.getSourceAsMap());} }總結
以上是生活随笔為你收集整理的ES的索引(倒排索引),文档,查询的全部內容,希望文章能夠幫你解決所遇到的問題。
                            
                        - 上一篇: 软件测试中的“银行企业银行对公渠道转账和
 - 下一篇: Layer btnAlign 失效问题解