Elasticsearch
為什么要有搜索引擎?
????? 在傳統(tǒng)的數據庫中,如果想實現搜索一般我們會用like方式,但遇到不是完全匹配或有差異的的就搜索不到了。
????? 而且傳統(tǒng)的數據庫實現搜索不靠譜,性能差、效率低。
什么是Elasticsearch?
????? Elasticsearch是一個基于Lucene的搜索服務器,他提供了一個分布式多用戶能力的全文搜索引擎,基于RESTful Web接口。
????? Elasticsearch是用Java開發(fā)的,隱藏了復雜性,開箱即用,并設計用于云計算,能夠達到實時搜索,穩(wěn)定、可靠、快速、安裝方便(插件可不方便),不僅能對海量規(guī)模的數據完成分布式索引和檢索,還能提供數據聚合分析。
????? Lucene就是一個jar包里面有著大量各種建立倒排索引,以及進行搜索的代碼、算法等。
什么是全文檢索?
????? 全文檢索是指計算機索引程序通過掃描文章中的每一個詞,對每一個詞建立一個索引,指明該詞在文章中出現的次數和位置,當用戶查詢時,檢索程序就根據事先建立好的索引進行查找,并將查詢結果反饋給用戶的檢索方式,這個過程類似于通過字段中的檢索字表查字的過程。
????? 也就是對全文數據進行詞、字、段落等更深層次的編輯、加工,將非結構化的數據一部分信息提取出來,重新組裝使得其有一定的結構,然后對此結構的數據進行搜索。
什么是倒排索引?
????? 根據分詞器對全文數據進行分詞,對分完后的詞進行標識,這個詞在哪里出現過,出現了幾次,再次查找的時候就根據位置去獲取完整的數據。
倒排索引包括:文檔的列表、數量,及詞條在每個文檔中出現的次數,出現的位置,每個文檔的長度,所有文檔的平均長度。
分詞器
????? 分詞器就是從一段文本切分出一個一個的詞條,并對詞條進行標準化處理。
分詞步驟
?????????? 預處理:過濾掉HTML標簽,特殊符號轉換等
?????????? 分詞:按照分詞器的算法進行分詞
?????????? 標準化:對分詞后的詞進行統(tǒng)一歸檔處理
Elasticsearch內置分詞器
????? Standard分詞器(默認):會將詞匯單元轉換為小寫形式,并去掉停用詞和標點符號,支持中文采用的方式為單字切分。
????? Simple分詞器:首先會通過非字母字符來分割文本信息,然后將詞匯單元統(tǒng)一為小寫形式,該分詞器會去掉數字類型的字符。
????? Whitespace分詞器:只是去除空格,對字符沒有l(wèi)owcase化,不支持中文,并不對生成的詞匯單元進行其他的標準化處理。
????? Language分詞器:特定語言的分詞器,不支持中文。
????? IK分詞器(不是內置的分詞器):對中文切分較好,通常會使用這個。
特點
????? 1.大型分布式集群(數百臺機器),處理PB級的數據
????? 2.開箱即用,非常簡單,操作也不復雜
????? 3.數據庫的功能面對很多領域是不夠用的(事務、聯(lián)機事務型操作、特殊的功能、全文檢索、同義詞處理、相關度排名、海量數據近實時處理等),Elasticsearch作為數據庫的一個補充,提供了數據庫所不能提供的很多功能。
???? Elasticsearch的麻煩在于插件,Elasticsearch的部分插件跟不上Elasticsearch的版本。
????? 4.近實時,從寫入數據到數據可以被搜索到有一個延遲(大約1秒)。基于Elasticsearch執(zhí)行搜索和分析可以達到秒級。
Elasticsearch核心概念
????? Cluster(集群):集群包含多個節(jié)點,每個節(jié)點屬于哪個集群是通過一個配置(集群名稱,默認是elasticsearch)來決定,只要配置文件內的集群名稱一致,內部會自動的去尋找,會自動組成集群。
????? Node(節(jié)點):集群中的單個節(jié)點,節(jié)點也有默認名稱(默認是隨機分配的),默認節(jié)點會加入一個名為elasticsearch的集群,如果直接啟動一堆節(jié)點,他們會自動組成一個elasticsearch集群,一個節(jié)點也可以組成集群。
數據庫ElasticsearchElasticsearch描述
database(庫)index (索引)一個索引通常代表一類數據
table(表)type(類型)Elasticsearch在index中建立type,通過mapping進行映射
column (列)field(字段)?
row(行)document(文檔)Elasticsearch存儲是文檔形的,相當于數據庫的行
constraint(約束)mapping(映射)創(chuàng)建index時,可以預先定義好字段的類型和相關屬性,這樣就可以自動進行類型轉換,如:字符串轉日期
SQL(數據操作語句)Query DSL(數據操作語句)GET/PUT/POST/DELETE
對應數據庫
select/update/delete
index(索引)indexed(索引)Elasticsearc默認是加上索引,觸發(fā)特殊指定不建立索引
架構
Gateway層:是Elasticsearch用來存儲索引文件的一個文件系統(tǒng),主要用來對數據進行長持久化以及整個集群重啟后可以通過Gateway重新恢復數據,且支持很多類型,例如:本地磁盤、共享存儲、hadoop的hdfs分布式存儲、亞馬遜的S3。
????? Dstributed Lucene Directory:Gateway上層就是Lucene的分布式框架,Lucene是做檢索的,但是他是一個單機的搜索引擎,像Elasticsearch這種分布式搜索引擎系統(tǒng),雖然底層使用Lucene,但是需要再每個節(jié)點上都運行Lucene進行相應的索引、查詢、以及更新等操作,就需要做成一個分布式的運行框架來滿足需求。
????? DstributedLucene Directory以上的是ES一些模塊。
????? Index Module:索引模塊,對數據建立索引,也就是倒排索引。
????? Search Module:搜索模塊,對數據進行查詢搜索。
????? Mapping:數據映射與解析模塊,就是數據的每個字段可以根據建立的表結構進行映射、解析,如果沒有建立表結構,Elasticsearch會根據數據類型推測出數據結構后,自己生成一個Mapping,然后根據這個Mapping去解析數據。
????? River:此模塊在Elasticsearch-2.0應該是被取消了,就是可以通過一些自定義的腳本將傳統(tǒng)的數據庫等數據源通過格式化轉換后同步到Elasticsearch集群。
????? Discovery:Elasticsearch是一個額集群有很多節(jié)點,很多節(jié)點需要互相發(fā)現對方,然后組成一個集群,包括選舉主節(jié)點,這些Elasticsearch都是用Discovery模塊,默認是使用Zen,也可以是使用EC2。
????? Script:Elasticsearch查詢還可以支持多種基本語言,包括mvel、js、python等。
????? Transport:協(xié)議層,支持的也比較多,Thrift、Memcached、Http等,默認是Http。
????? JMX:java的一個遠程監(jiān)控管理框架,Elasticsearch是使用java實現的。
????? RESTful:接口層,官方推薦使用RESTful接口,直接發(fā)送http情況,方便后續(xù)的nginx做代理、分發(fā),包括權限的管理是,通過http就很方便做了,如果使用java客戶端是直接調用Api,在做負載均衡及權限管理還是不太好做。
內部架構解析
Master節(jié)點
????? 主要職責是和集群操作相關的內容,如創(chuàng)建或刪除索引,跟跟蹤哪些節(jié)點是集群的衣服,決定哪些分片分配給相關的節(jié)點,穩(wěn)定的主節(jié)點對集群的監(jiān)控非常重要。
節(jié)點對等
????? 每個節(jié)點都能接收請求
????? 每個節(jié)點接收到請求后都能把該請求路由到有相關的數據的其他節(jié)點上。
????? 接收原始請求的節(jié)點負責采集數據并返回給客戶端
文檔數據路由原理
????? 1.文檔路由到分片上,一個索引由多個分片構成,當增刪改一個文檔時,Elasticsearch就需要決定存儲在哪個分片上。
????? 2.路由算法:shard=hash(routing) %number_of_pirmary_shards
???????????????? 1.每次增刪改查的時候,都有一個路由值,默認是文檔的_id值
???????????????? 2.對這個路由值使用哈希函數進行計算
???????????????? 3.計算出的值在和分片個數取余數,這個時候就確定了在哪個分片。
????? 3.算法決定了,主分片個數一旦確定就不能修改了。
分片和副本機制
1.index包含多個分片。
????? 2.每個分片都是一個最小的工作單元,承載部分數據,每個分片都是一個lucene實例,有完整的建立索引和處理請求的能力。
????? 3.增刪節(jié)點時,分片會自動的在集群中負載均衡
????? 4.主分片和副分片,每個document肯定只存在于某一個主分片及其對應的副分片,不可能存在于多個主分片中。
????? 5.副分片是主分片的副本,負責容錯,以及承擔讀請求負載
????? 6.主分片的數量在創(chuàng)建索引的時候就固定了,副分片的數量可以隨時修改。
????? 7.主分片的默認數量是5,副分片默認是1,默認有10個分片,5個主分片,5個副分片。
????? 8.主分片不能和自己的副分片在同一個節(jié)點(節(jié)點掛了,主和副都掛了,沒有意義,做不到容錯),但是可以和其他的主分片的副分片放在同一節(jié)點上。
水平擴容的過程
1.擴容后主分片和副分片都會自動負載均衡
????? 2.擴容后每個節(jié)點上的分片會減少,那么分配給每個分片的CPU、內存、IO資源會更多,性能會提高。
????? 3.擴容極限,如果有6個分片,擴容的極限就是6個節(jié)點,每個節(jié)點上一個分片,如果超出擴容極限,比如擴容到9個節(jié)點,那么可以增加副分片的個數。
Elasticsearch容錯機制
????? 如果master掛了,此時不是所有的主分片都是Active狀態(tài),所以此時的集群狀態(tài)是red。
????? 第一步:選舉出一臺服務器作為master
????? 第二步:新選舉出的master會把掛掉的主分片的某個副分片提升為主分片,此時集群的狀態(tài)是yellow,因為少了一個副分片,并不是所有的副分片都是Active狀態(tài)
????? 第三步:重啟故障機器,新的master會把所有的副本都復制一份到該節(jié)點上(同步一下掛了后發(fā)生的修改),此時集群的狀態(tài)為green,因為所有的主分片和副分片都是Active狀態(tài)。
刪除文檔
????? 標記為deleted,隨著數據量的增加,Elasticsearch會選擇合適的時間刪除。
文檔增刪改原理
????? 1.發(fā)送增刪改請求,可以選擇任意一個節(jié)點,該節(jié)點就成為協(xié)調節(jié)點。
????? 2.協(xié)調節(jié)點使用路由算法進行路由,然后將請求轉給主分片所在的節(jié)點,該節(jié)點處理請求,并把數據同步到它的副分片。
????? 3.協(xié)調節(jié)點對客戶端返回結果。
文檔查詢內部原理
????? 1.查詢請求發(fā)送任意一個節(jié)點,該節(jié)點就成了協(xié)調節(jié)點,該節(jié)點使用路由算法,算出文檔所在的主分片。
????? 2.協(xié)調節(jié)點把請求轉發(fā)給主分片,也可以轉發(fā)給副分片(使用輪詢調度算法,把請求平均的分配給主分片和副分片)。
????? 3.處理請求的節(jié)點是把結果返回給協(xié)調節(jié)點,協(xié)調節(jié)點在返回給應用程序。
特殊情況:請求的文檔還在建立索引的過程中,主分片上存在,但副分片上不存在,但請求被轉發(fā)到了副分片上,這時就提示找不到文檔了。
索引為什么不可變?
????? 不需要鎖,提升了并發(fā)的性能
????? 可以一直在緩存中
????? 節(jié)省了CPU和IO開銷
版本控制 & 鎖 & 數據一致性
????? Elasticsearch采用樂觀鎖來保證數據的一致性,也就是說,當用戶對document進行操作時,并不需要對該document做加鎖和解鎖的操作,只需要制定要操作的版本即可,當版本號一致時,Elasticsearch會允許該操作順利執(zhí)行,而當版本號存在沖突時,Elasticsearch會拋出異常提示沖突了。()
????? Elasticsearch的版本號取值范圍是1到2^63-1
????? 內部版本控制:使用_version
????? 外部版本控制:Elasticsearch在處理外部版本號時會與內部版本號處理有些不同,他不再是檢查version是否和請求中的指定數值相同,而是檢查當前的version是否比指定數值小,如果請求成功,那么外部版本號就會被存儲到文檔的_verison中。
????? 為了保證_version和外部版本控制的數據一致性使用version_type=external
數據類型
字符型text長文本,在建立索引前會將這些文本進行分詞,轉化成分詞組合,建立索引,text類型不能排序和聚合。
keyword不需要進行分詞,可以用來檢索過濾、排序和聚合。
只能用本身進行檢索
數值類型long?
integer?
short?
byte?
double?
float?
日期類型date?
布爾類型boolean?
二進制類型binary?
數組類型Array字符型數組:
[ "one", "two" ]
整型數組:[ 1, 2 ]
數組型數組:[ 1, [ 2, 3 ]] 等價于[ 1, 2, 3 ]
對象數組:[ { "name":? "Mary", "age": 12 }, { "name":? "John", "age": 10 }]
對象類型_ object _用于單個json對象
嵌套類型_ nested _用于json數組
???
地理坐標類型geo-point經度
geo-shape類似多邊形的復雜形狀
Ipv4_ ip _用于表示Ipv4地址
Completion類型_ completion _提供自動補全建議
Toekn count類型_ token_count _用于統(tǒng)計做了標記的字段的index數目,該值會一直增加,不會因為過濾條件而減少
Mapper-murmur 3_ murmur 3 _計算index的hash值
附加類型mapper-attachments可支持_ attachments _ 索引,例如Microsoft office格式,html等
Mapping字段說明
{[if !vml]
[endif]
??? "state": "open",
??? "settings": {[if !vml]
[endif]
??????? "index": {[if !vml]
[endif]
??????????? "creation_date": "1546288211675",
??????????? "number_of_shards": "5",
??????????? "number_of_replicas": "1",
??????????? "uuid": "lUe3u9JvROO4edTro7SDRA",
??????????? "version": {[if !vml]
[endif]
??????????????? "created": "5020299"
??????????? },
??????????? "provided_name": "commodity"
??????? }
??? },
??? "mappings": {[if !vml]
[endif]
??????? "vegetables": {[if !vml]
[endif]
??????????? "properties": {[if !vml]
[endif]
??????????????? "mid": {[if !vml]
[endif]
??????????????????? "type": "text",
??????????????????? "fields": {[if !vml]
[endif]
??????????????????????? "keyword": {[if !vml]
[endif]
??????????????????????????? "ignore_above": 256,
??????????????????????????? "type": "keyword"
??????????????????????? }
??????????????????? }
??????????????? },
??????????????? "title": {[if !vml]
[endif]
??????????????????? "type": "text",
??????????????????? "fields": {[if !vml]
[endif]
??????????????????????? "keyword": {[if !vml]
[endif]
??????????????????????????? "ignore_above": 256,
??????????????????????????? "type": "keyword"
??????????????????????? }
??????????????????? }
??????????????? },
??????????????? "content": {[if !vml]
[endif]
??????????????????? "type": "text",
??????????????????? "fields": {[if !vml]
[endif]
??????????????????????? "keyword": {[if !vml]
[endif]
??????????????????????????? "ignore_above": 256,
??????????????????????????? "type": "keyword"
??????????????????????? }
??????????????????? }
??????????????? }
??????????? }
??????? },
??????? "phone": {[if !vml]
[endif]
??????????? "properties": {[if !vml]
[endif]
??????????????? "mid": {[if !vml]
[endif]
??????????????????? "type": "text",
??????????????????? "fields": {[if !vml]
[endif]
??????????????????????? "keyword": {[if !vml]
[endif]
??????????????????????????? "ignore_above": 256,
??????????????????????????? "type": "keyword"
??????????????????????? }
??????????????????? }
??????????????? },
??????????????? "title": {[if !vml]
[endif]
??????????????????? "type": "text",
??????????????????? "fields": {[if !vml]
[endif]
??????????????????????? "keyword": {[if !vml]
[endif]
??????????????????????????? "ignore_above": 256,
??????????????????????????? "type": "keyword"
??????????????????????? }
??????????????????? }
??????????????? },
??????????????? "content": {[if !vml]
[endif]
??????????????????? "type": "text",
??????????????????? "fields": {[if !vml]
[endif]
??????????????????????? "keyword": {[if !vml]
[endif]
??????????????????????????? "ignore_above": 256,
??????????????????????????? "type": "keyword"
??????????????????????? }
??????????????????? }
??????????????? }
??????????? }
??????? }
??? },
??? "aliases": [ ],
??? "primary_terms": {[if !vml]
[endif]
??????? "0": 1,
??????? "1": 1,
??????? "2": 1,
??????? "3": 1,
??????? "4": 1
??? },
??? "in_sync_allocations": {[if !vml]
[endif]
??????? "0": [[if !vml]
[endif]
??????????? "rcxcuP1kQrelWd935T_xPw"
??????? ],
??????? "1": [[if !vml]
[endif]
??????????? "7mpsfo23RHCUX9w6Rz1jrA"
??????? ],
??????? "2": [[if !vml]
[endif]
??????????? "Pj4yx7sjTnqAdwWkgPNZVQ"
??????? ],
??????? "3": [[if !vml]
[endif]
??????????? "KoHCxlZ7SDOepUjqJzT83g"
??????? ],
??????? "4": [[if !vml]
[endif]
??????????? "Tz6Xavw1Sv6zHNbbmZixCQ"
??????? ]
??? }
}
"store":"false"?????????? 是否單獨設置此字段的是否存儲而從_source字段中分離,默認是false,只能搜索不能取值
"index": "true"?????????? 分詞,不分詞為false
"analyzer":"ik"????? 指定分詞器,默認是standard analyzer
"boost":"1.23"?????????? 字段級別的分數加權,默認是1.0
"doc_values": "false"?????? 對not_analyzed字段,默認都是開啟,分詞字段不能使用,對排序和聚合能提升比較明顯的性能,節(jié)約內存
"fielddata":{ "format":"disabled"}???????? 針對分詞字段,參與排序或聚合時能提升性能,不分詞字段統(tǒng)一建議使用doc_value
"fields":{"keyword":{"ignore_above": 256, "type": "keyword"}}???????? 字段屬性
"ignore_above":256?????? 超過256個字符的文本會被忽略,不被索引
"include_in_all":true??????? 設置是否詞字段包含在_all字段中,默認是true,除非設置no
"index_options":"docs"??????? 4個可選參數,docs(索引文檔號),freqs(文檔號+詞頻),positions(文檔號+詞頻+位置,通常用在距離查詢),offsets(文檔號+詞頻+位置+偏移量,通常用在高亮字段),分詞默認是position,其他的默認為docs
"null_value":"NULL"??????? 缺失字段的初始值,只有string可以使用,分詞字段的null值也會被分詞
"position_increament_gap":"0"?????? 影響距離查詢或近似查詢,可以設置在多值字段的數據上火的分詞字段上,查詢時可用slop間隔,默認值是100
"search_analyer":"ik"????? 設置搜索時的分詞器
"similarity":"BM25"???????? 默認是TF/IDF算法,指定一個字段評分策略,只對字符串類型的分詞有效
"term_vector":"no"???????? 默認不存儲向量信息,支持參數yes(term存儲),with_positions(term+位置),with_offsets(term+偏移量),with_positions_offsets(term+位置+偏移量)對快速高亮fast
vector highlighter能提升性能,但開啟又會加大索引體積,不適合大數據量用。
"dynamic":"true" 動態(tài)映射,當Elasticsearch遇到mapping中沒有的字段,會利用動態(tài)映射來決定字段類型,并自動對該字段添加索引。(true默認,false忽略新字段,strict拋出異常)
Java Api
pom.xml
<projectxmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
?<modelVersion>4.0.0</modelVersion>
?<groupId>com.levi.elasticsearch</groupId>
?<artifactId>elasticsearch</artifactId>
?<version>0.0.1-SNAPSHOT</version>
??? <dependencies>
???????? <dependency>
????????????? <groupId>junit</groupId>
????????????? <artifactId>junit</artifactId>
????????????? <version>3.8.1</version>
????????????? <scope>test</scope>
???????? </dependency>
???????? <dependency>
????????????? <groupId>org.elasticsearch</groupId>
????????????? <artifactId>elasticsearch</artifactId>
????????????? <version>5.2.2</version>
???????? </dependency>
???????? <dependency>
????????????? <groupId>org.elasticsearch.client</groupId>
????????????? <artifactId>transport</artifactId>
????????????? <version>5.2.2</version>
???????? </dependency>
???????? <dependency>
????????????? <groupId>org.apache.logging.log4j</groupId>
????????????? <artifactId>log4j-core</artifactId>
????????????? <version>2.9.0</version>
???????? </dependency>
???? </dependencies>
</project>
App.java
import java.io.IOException;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.ExecutionException;
importorg.elasticsearch.action.admin.indices.create.CreateIndexRequestBuilder;
importorg.elasticsearch.action.admin.indices.delete.DeleteIndexRequestBuilder;
importorg.elasticsearch.action.admin.indices.mapping.put.PutMappingRequest;
importorg.elasticsearch.action.admin.indices.mapping.put.PutMappingResponse;
importorg.elasticsearch.action.bulk.BulkRequestBuilder;
import org.elasticsearch.action.bulk.BulkResponse;
importorg.elasticsearch.action.delete.DeleteRequestBuilder;
importorg.elasticsearch.action.get.GetRequestBuilder;
importorg.elasticsearch.action.get.MultiGetItemResponse;
importorg.elasticsearch.action.get.MultiGetRequestBuilder;
import org.elasticsearch.action.index.IndexRequest;
importorg.elasticsearch.action.index.IndexResponse;
importorg.elasticsearch.action.search.SearchRequestBuilder;
importorg.elasticsearch.action.search.SearchResponse;
importorg.elasticsearch.action.update.UpdateRequest;
importorg.elasticsearch.action.update.UpdateResponse;
import org.elasticsearch.client.Requests;
importorg.elasticsearch.client.transport.TransportClient;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.transport.InetSocketTransportAddress;
importorg.elasticsearch.common.xcontent.XContentBuilder;
importorg.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
importorg.elasticsearch.transport.client.PreBuiltTransportClient;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
/**
?*集群健康值
?* ES存儲數據也是按照切片的方式,默認是5個切片,而且切片都是有副本的
?* ES服務默認端口是9300
?* ES-Web管理端口9200
?* ES的2.x系列的版本和5.x版本或以上的Api區(qū)別在于內部數個組件的整合,
?*使得版本兼容不在痛苦,所以一下子2.X版本直接到5.X版本
?*@author AimSpeed
?*@Projectelasticsearch
?*@Package com.levi.elasticsearch
?*@FileName App.java
?*@ClassNameApp
?*@date 2018年12月31日 下午7:33:59
?*/
public class App {
???? private TransportClient transportClient;
/**
???? ?*獲取客戶端對象
???? ?*@author AimSpeed
???? ?*@throwsUnknownHostException??
???? ?*@TitlegetClient? void?
???? ?*@date 2018年12月31日 下午7:44:35
???? ?*/
???? @Before
???? public void getClient() throwsUnknownHostException? {
???????? //設置集群名稱
???????? Settingssettings= Settings.builder().put("cluster.name","my-application").build();
???????? //獲取客戶端對象
???????? transportClient = new PreBuiltTransportClient(settings);
???????? //具體要連接的集群地址,是一個可變參數,多臺服務器就填寫多個地址
???????? transportClient.addTransportAddress(newInetSocketTransportAddress(InetAddress.getByName("192.168.1.110"),
???????????????????????????????????????????????????????????????????????????????????????? 9300));
???????? System.out.println(transportClient);
???? }
???? /**
???? ?*執(zhí)行完之后關閉資源
???? ?*@author AimSpeed
???? ?*@TitlecloseClient? void?
???? ?*@date 2018年12月31日 下午8:28:47
???? ?*/
???? @After
???? public void closeClient() {
???????? //關閉資源
???????? transportClient.close();
???????? System.out.println("成功,關閉資源!");
???? }
???? /**
???? ?*創(chuàng)建索引 = 數據庫
???? ?*可在圖形化界面查看
???? ?*@author AimSpeed
???? ?*@TitlecreateIndex? void?
???? ?*@date 2018年12月31日 下午7:55:38
???? ?*/
???? @Test
???? public void createIndex() {
???????? //創(chuàng)建一個商品索引(商品數據庫)
???????? CreateIndexRequestBuilderrequest = transportClient.admin().indices().prepareCreate("commodity");
???????? System.out.println(request.get().isAcknowledged());
???? }
???? /**
???? ?*刪除索引 = 數據庫
???? ?*可在圖形化界面查看
???? ?*@author AimSpeed
???? ?*@TitledelIndex? void?
???? ?*@date 2018年12月31日 下午8:26:01
???? ?*/
???? @Test
???? public void delIndex() {
???????? DeleteIndexRequestBuilderrequest = transportClient.admin().indices().prepareDelete("commodity");
???????? System.out.println(request.get().isAcknowledged());
???? }
???? //=========================================================
?
???? /**
???? ?*以json的方式插入文檔
???? ?*@author AimSpeed
???? ?*@TitlecreateDocumentByJson? void?
???? ?*@date 2018年12月31日 下午8:41:27
???? ?*/
???? @Test
???? public void createDocumentByJson() {
???????? //數據
???????? Stringjson = "{" + "\"mid\":\"A\"," + "\"title\":\"00年諾基亞\","
?????????????????? +"\"content\":\"耐砸\"" + "}";
???????? //插入數據
???????? //index(索引 = 庫)????? type(類型 = 表)
???????? //手動插入Id
???????? //IndexResponse response =
transportClient.prepareIndex("commodity", "phone",
"1")
???????????????????????????????????????????????????????????? //.setSource(json).execute().actionGet();
???????? //自動生成內部Id
???????? IndexResponseresponse = transportClient.prepareIndex("commodity", "phone")
??????????????????????????????????????????????????????? .setSource(json).execute().actionGet();
???????? System.out.println("位于的索引(庫):" + response.getIndex());
???????? System.out.println("位于的類型(表):" + response.getType());
???????? System.out.println("版本:" + response.getVersion());
???????? System.out.println("Id:" + response.getId());
???????? System.out.println("結果:" + response.getResult());
???? }
???? /**
???? ?*以Map的方式插入數據
???? ?*@author AimSpeed
???? ?*@TitlecreateDocumentByMap? void?
???? ?*@date 2018年12月31日 下午9:06:19
???? ?*/
???? @Test
???? public void createDocumentByMap() {
???????? //數據
???????? Mapmap = new HashMap<String, Object>();
???????? map.put("mid", "A");
???????? map.put("title", "花菜");
???????? map.put("content", "送給女的,會給打么?");
???????? //插入數據
???????? //index(索引 = 庫)????? type(類型 = 表)
???????? IndexResponseresponse = transportClient.prepareIndex("commodity", "vegetables")
??????????????????????????????????????????????????????? .setSource(map).execute().actionGet();
???????? System.out.println("位于的索引(庫):" + response.getIndex());
???????? System.out.println("位于的類型(表):" + response.getType());
???????? System.out.println("版本:" + response.getVersion());
???????? System.out.println("Id:" + response.getId());
???????? System.out.println("結果:" + response.getResult());
???? }
?
???? /**
???? ?*以Builder方式插入數據
???? ?*@author AimSpeed
???? ?*@Title createDocumentByBuilder
???? ?*@throwsIOException void?
???? ?*@date 2018年12月31日 下午9:15:59
???? ?*/
???? @Test
???? public void createDocumentByBuilder() throws IOException {
???????? //數據
???????? XContentBuilderbuilder= XContentFactory.jsonBuilder()
???????????????????????????????????????????????????????????????? .startObject()
????????????????????????????????????????????????????????????????????? .field("mid","B")
????????????????????????????????????????????????????????????????????? .field("title", "三防手機")
????????????????????????????????????????????????????????????????????? .field("content","能抵擋洪水?")
???????????????????????????????????????????????????????????????? .endObject();
???????? //插入數據,自定義ES內部ID。
???????? IndexResponseresponse = transportClient.prepareIndex("commodity", "phone","2")
??????????????????????????????????????????????????????? .setSource(builder).execute().actionGet();
???????? System.out.println("位于的索引(庫):" + response.getIndex());
???????? System.out.println("位于的類型(表):" + response.getType());
???????? System.out.println("版本:" + response.getVersion());
???????? System.out.println("Id:" + response.getId());
???????? System.out.println("結果:" + response.getResult());
???? }
???? /**
???? ?*批量插入
???? ?*@author AimSpeed
???? ?*@Title createBulkDocument
???? ?*@throwsException void?
???? ?*@date 2018年12月31日 下午10:54:11
???? ?*/
???? @Test
???? public void createBulkDocument() throws Exception {
???????? //批量操作對象
???????? BulkRequestBuilderprepareBulk = transportClient.prepareBulk();
???????? //第1條數據
???????? prepareBulk.add(transportClient.prepareIndex("commodity", "phone")
?????????????????????????????????????????????? .setSource(XContentFactory.jsonBuilder()
???????????????????????????????????????????????????????????????? .startObject()
???????????????????????????????????????????????????????????????? .field("mid","E")
???????????????????????????????????????????????????????????????? .field("title", "6防手機")
???????????????????????????????????????????????????????????????? .field("content","能抵擋洪水?")
???????????????????????????????????????????????????????????? .endObject()));
???????? //第2條數據
???????? prepareBulk.add(transportClient.prepareIndex("commodity", "phone")
?????????????????????????????????????????????? .setSource(XContentFactory.jsonBuilder()
???????????????????????????????????????????????????????????????? .startObject()
???????????????????????????????????????????????????????????????? .field("mid","F")
???????????????????????????????????????????????????????????????? .field("title", "99防手機")
???????????????????????????????????????????????????????????????? .field("content","能抵擋洪水?")
??????????????????????????????????????????????????????? ???? .endObject()));
???????? BulkResponseresponse = prepareBulk.get();
???????? System.out.println("是否有失敗數據:" + response.hasFailures());
???? }
???? //=========================================================
????
???? /**
???? ?*搜索文檔(單個)
???? ?*@author AimSpeed
???? ?*@TitlequeryById? void?
???? ?*@date 2018年12月31日 下午9:18:55
???? ?*/
???? @Test
???? public void queryById () {
???????? //內部ID,作為搜索索引
???????? //GetRequestBuilder request =
transportClient.prepareGet("commodity", "phone",
"AWgGAaE6zc8qunjZLZcr");
???????? //自定義插入的ID,作為搜索索引
???????? GetRequestBuilderrequest = transportClient.prepareGet("commodity", "phone", "1");
???????? System.out.println(request.get().getSourceAsString());
???? }
???? /**
???? ?*多個ID查詢
???? ?*@author AimSpeed
???? ?*@TitlequeryByMultiId? void?
???? ?*@date 2018年12月31日 下午9:25:41
???? ?*/
???? @Test
???? public void queryByMultiId() {
???????? MultiGetRequestBuildermultiGetRequestBuilder = transportClient.prepareMultiGet()
????????????????????????????????????????????????????????????????????? .add("commodity", "phone", "1")//自定義的id
????????????????????????????????????????????????????????????????????? .add("commodity", "phone", "AWgGAaE6zc8qunjZLZcr","2");//多個id,可變參數
???????? for (MultiGetItemResponse response : multiGetRequestBuilder.get()) {
????????????? if(response.getResponse().isExists()) {
?????????????????? System.out.println(response.getResponse().getSourceAsString());
????????????? }
???????? }???????????????????????????????????????????????????????????
???? }
???? /**
???? ?*查找所有
???? ?*@author AimSpeed
???? ?*@TitlequeryMatch? void?
???? ?*@date 2018年12月31日 下午9:27:30
???? ?*/
???? @Test
???? public void queryMatch() {
???????? //任何的Api,在使用的時候注意下傳入的參數,可能是可變形參
???????? SearchRequestBuilderrequest = transportClient.prepareSearch("commodity").setTypes("phone","vegetables")
???????????????????????????????????????????????????????????? .setQuery(QueryBuilders.matchAllQuery());
???????? //查詢結果
???????? SearchHitshits = request.get().getHits();
???????? System.out.println("查詢結果數量: " + hits.getTotalHits());
???????? //打印結果
???????? Iteratoriterator = hits.iterator();
???????? while(iterator.hasNext()) {
????????????? SearchHitsearchHit = iterator.next();
????????????? System.out.println(searchHit.getSourceAsString());
???????? }
???? }
????
???? /**
???? ?*分詞查詢
???? ?*@author AimSpeed
???? ?*@TitlequeryByParticiple? void?
???? ?*@date 2018年12月31日 下午9:36:11
???? ?*/
???? @Test
???? public void queryByParticiple() {
???????? //查詢語句
???????? SearchResponseresponse = transportClient.prepareSearch("commodity").setTypes("phone","vegetables")
???????????????????????????????????????????????????????????? .setQuery(QueryBuilders.queryStringQuery("拍照")).get();
???????? //查詢結果
???????? SearchHitshits = response.getHits();
???????? System.out.println("查詢結果數量:" + hits.getTotalHits());
???????? //打印結果
???????? Iteratoriterator = hits.iterator();
???????? while(iterator.hasNext()) {
????????????? SearchHithit = iterator.next();
????????????? System.out.println(hit.getSourceAsString());
???????? }
???? }
???? /**
???? ?*詞條搜索
???? ?*@author AimSpeed
???? ?*@TitlequeryByTerm? void?
???? ?*@date 2018年12月31日 下午9:42:17
???? ?*/
???? @Test
???? public void queryByTerm() {
???????? //查詢語句
???????? SearchResponseresponse = transportClient.prepareSearch("commodity").setTypes("phone","vegetables")
????????????????????????????????????????? //會發(fā)現查詢不到,詞條查詢就是詞查詢,每個字作為索引的(切分成一個個的詞),除非引入分詞器(ik分詞器等)?????????????????
????????????????????????????????????????? //.setQuery(QueryBuilders.termQuery("content",
"拍照")).get();
????????????????????????????????????????? .setQuery(QueryBuilders.termQuery("content", "拍")).get();
???????? //查詢結果對象
???????? SearchHitssearchHits = response.getHits();
???????? System.out.println("查詢結果數量:" + searchHits.getTotalHits());
???????? //遍歷打印
???????? Iteratoriterator = searchHits.iterator();
???????? while(iterator.hasNext()) {
????????????? System.out.println(iterator.next().getSourceAsString());
???????? }
???? }
???? /**
???? ?*通配符查詢
???? ?* *多個字符
???? ?* ?單個字符
???? ?*@author AimSpeed
???? ?*@TitlequeryByWildcard? void?
???? ?*@date 2018年12月31日 下午9:49:24
???? ?*/
???? @Test
???? public void queryByWildcard() {
???????? //查詢語句
???????? SearchResponseresponse = transportClient.prepareSearch("commodity").setTypes("phone","vegetables")
????????????????????????????????????????? .setQuery(QueryBuilders.wildcardQuery("content", "玩*")).get();
???????? //查詢結果對象
???????? SearchHitssearchHits = response.getHits();
???????? System.out.println("查詢結果數量:" + searchHits.getTotalHits());
???????? //遍歷打印
???????? Iteratoriterator = searchHits.iterator();
???????? while(iterator.hasNext()) {
????????????? System.out.println(iterator.next().getSourceAsString());
???????? }
???? }
????
???? /**
???? ?*模糊查詢
???? ?*@author AimSpeed
???? ?*@TitlequeryByLike? void?
???? ?*@date 2018年12月31日 下午9:53:17
???? ?*/
???? @Test
???? public void queryByLike() {
???????? //查詢語句
???????? SearchResponseresponse = transportClient.prepareSearch("commodity").setTypes("phone","vegetables")
????????????????????????????????????????? .setQuery(QueryBuilders.fuzzyQuery("content", "游")).get();
???????? //查詢結果對象
???????? SearchHitssearchHits = response.getHits();
???????? System.out.println("查詢結果數量:" + searchHits.getTotalHits());
???????? //遍歷打印
???????? Iteratoriterator = searchHits.iterator();
???????? while(iterator.hasNext()) {
????????????? System.out.println(iterator.next().getSourceAsString());
???????? }
???? }
???? //=========================================================
???? /**
???? ?*更新數據
???? ?*@author AimSpeed
???? ?*@Title update
???? ?*@throws IOException
???? ?*@throws InterruptedException
???? ?*@throwsExecutionException void?
???? ?*@date 2018年12月31日 下午10:38:21
???? ?*/
???? @Test
???? public void update() throws IOException, InterruptedException,
ExecutionException {
???????? //創(chuàng)建更新數據的請求對象
???????? UpdateRequestupdateRequest = new UpdateRequest("commodity", "phone", "1");
???????? //更新
???????? updateRequest.doc(XContentFactory.jsonBuilder()
?????????????????????????????????????????????????? .startObject()
??????????????????????????????????????????????????????? .field("content","更新...")
?????????????????????????????????????????????????? .endObject());
???????? UpdateResponseresponse = transportClient.update(updateRequest).get();
???????? System.out.println("位于的索引(庫):" + response.getIndex());
???????? System.out.println("位于的類型(表):" + response.getType());
???????? System.out.println("版本:" + response.getVersion());
???????? System.out.println("Id:" + response.getId());
???????? System.out.println("結果:" + response.getResult());
???? }
?
???? /**
???? ?*更新數據,如果不存在就添加數據
???? ?*@author AimSpeed
???? ?*@Titleupsert? void?
???? ?*@date 2018年12月31日 下午10:38:56
???? ?*/
???? @Test
???? public void upsert() throws IOException, InterruptedException, ExecutionException
{
???????? //沒有這個文檔就創(chuàng)建
???????? //索引??????類型??????? 內部id(可自動生成)
//?????? IndexRequestindexRequest = new IndexRequest("commodity", "phone");
???????? IndexRequestindexRequest = new IndexRequest("commodity", "phone", "5");
???????? indexRequest.source(XContentFactory.jsonBuilder()
??????????????????????????????????????????????????????? .startObject()
???????????????????????????????????????????????????????????? .field("mid","D")
???????????????????????????????????????????????????????????? .field("title","不存在的數據,無法更新,那就添加")
??????????????????????????????????????????????????????? .endObject());
???????? UpdateRequestupdateRequest = new UpdateRequest("commodity", "phone", "6");
???????? //更新
???????? updateRequest.doc(XContentFactory.jsonBuilder()
?????????????????????????????????????????????????? .startObject()
??????????????????????????????????????????????????????? .field("content","upsert() 更新...")
?????????????????????????????????????????????????? .endObject());
???????? //更新失敗就添加
???????? updateRequest.upsert(indexRequest);
???????? //更新
???????? UpdateResponseresponse = transportClient.update(updateRequest).get();
???????? System.out.println("位于的索引(庫):" + response.getIndex());
???????? System.out.println("位于的類型(表):" + response.getType());
???????? System.out.println("版本:" + response.getVersion());
???????? System.out.println("Id:" + response.getId());
???????? System.out.println("結果:" + response.getResult());
???????? //注意看頁面的結果id列,很有趣。
???? }
???? //=========================================================
???? @Test
???? public void delDocument() {
???????? DeleteRequestBuilderrequest = transportClient.prepareDelete("commodity", "phone", "6");
???????? System.out.println(request.get().getResult());
???? }
???? //=========================================================
????
???? /**
???? ?*創(chuàng)建映射 = 約束
???? ?*@author AimSpeed
???? ?*@throws IOException
???? ?*@throws ExecutionException
???? ?*@throws InterruptedException
???? ?*@TitlecreateMapping? void?
???? ?*@date 2018年12月31日 下午10:17:53
???? ?*/
???? @Test
???? public void createMapping() throws IOException,
InterruptedException, ExecutionException {
???????? //定義映射 = 約束
???????? XContentBuilderxContentBuilder= XContentFactory.jsonBuilder()
?????????????????????????????????????????????????? .startObject()
??????????????????????????????????????????????????????? .startObject("fruits")
???????????????????????????????????????????????????????????? .startObject("properties")
???????????????????????????????????????????????????????????????? .startObject("mid")
????????????????????????????????????????????????????????????????????? .field("type","string")
???????????????????????????????????? ???????????????????????????????? .field("store","yes")
???????????????????????????????????????????????????????????????? .endObject()
???????????????????????????????????????????????????????????????? .startObject("title")
????????????????????????????????????????????????????????????????????? .field("type","string")
????????????????????????????????????????????????????????????????????? .field("store","no")
???????????????????????????????????????????????????????????????? .endObject()
???????????????????????????????????????????????????????????????? .startObject("content")
????????????????????????????????????????????????????????????????????? .field("type","string")
????????????????????????????????????????????????????????????????????? .field("store","yes")
???????????????????????????????????????????????????????????????? .endObject()
???????????????????????????????????????????????????????????? .endObject()
??????????????????????????????????????????????????????? .endObject()
?????????????????????????????????????????????????? .endObject();
???????? //添加索引
???????? PutMappingRequestmapping= Requests.putMappingRequest("commodity2")
???????????????????????????????????? ????????????? .type("fruits").source(xContentBuilder);
???????? //添加
???????? PutMappingResponseputMappingResponse = transportClient.admin().indices().putMapping(mapping).get();
???????? System.out.println(putMappingResponse.isAcknowledged());
???? }
}