各位小伙伴們,今天是今年的最后一天,這是我今年的最后一篇博客,在這里祝大家新年快樂(lè )!本次講的是近幾年比較流行的search搜索引擎,本文寫(xiě)的比較粗略,希望大家看了會(huì )有所收獲,如若寫(xiě)錯,請在評論區指出,我感激不盡!
? 實(shí)際項目中,我們建立一個(gè)網(wǎng)站或應用程序,并要添加搜索功能,令我們受打擊的是:搜索工作是很難的。我們希望我們的搜索解決方案要快,我們希望有一個(gè)零配置和一個(gè)完全免費的搜索模式,我們希望能夠簡(jiǎn)單地使用JSON/XML通過(guò)HTTP的索引數據,我們希望我們的搜索服務(wù)器始終可用,我們希望能夠從一臺開(kāi)始并在需要擴容時(shí)方便地擴展到數百,我們要實(shí)時(shí)搜索,我們要簡(jiǎn)單的多租戶(hù),我們希望建立一個(gè)云的解決方案。
? 雖然全文搜索領(lǐng)域,Lucene可以被認為是迄今為止最先進(jìn)、性能最好的、功能最全的搜索引擎庫。但是,Lucene只是一個(gè)庫。想要使用它,你必須使用Java來(lái)作為開(kāi)發(fā)語(yǔ)言并將其直接集成到你的應用中,更糟糕的是,Lucene的配置及使用非常復雜,你需要深入了解檢索的相關(guān)知識來(lái)理解它是如何工作的。
? ES即為了解決原生Lucene使用的不足,優(yōu)化Lucene的調用方式,并實(shí)現了高可用的分布式集群的搜索方案,其第一個(gè)版本于2010年2月出現在GitHub上并迅速成為最受歡迎的項目之一。
? 首先,ES的索引庫管理支持依然是基于A(yíng)pache Lucene?的開(kāi)源搜索引擎。ES也使用Java開(kāi)發(fā)并使用Lucene作為其核心來(lái)實(shí)現所有索引和搜索的功能,但是它的目的是通過(guò)簡(jiǎn)單的 RESTful API來(lái)隱藏Lucene的復雜性,從而讓全文搜索變得簡(jiǎn)單。Lucene直接通過(guò)java API調用,而ES把這些API調用過(guò)程進(jìn)行了的封裝為簡(jiǎn)單RESTful請求,讓我們調用起來(lái)更加簡(jiǎn)單.
? 不過(guò),ES的核心不在于Lucene,其特點(diǎn)更多的體現為:分布式的實(shí)時(shí)文件存儲,每個(gè)字段都被索引并可被搜索,分布式的實(shí)時(shí)分析搜索引擎,可以擴展到上百臺服務(wù)器,處理PB級結構化或非結構化數據,高度集成化的服務(wù),你的應用可以通過(guò)簡(jiǎn)單的 RESTful API、各種語(yǔ)言的客戶(hù)端甚至命令行與之交互。上手Elasticsearch非常容易。它提供了許多合理的缺省值,并對初學(xué)者隱藏了復雜的搜索引擎理論。它擁有開(kāi)瓶即飲的效果(安裝即可使用),只需很少的學(xué)習既可在生產(chǎn)環(huán)境中使用。
? Lucene和ES聯(lián)系,區別:項目中為啥使用ES而不用Lucene.
? 聯(lián)系:ElasticSearch封裝了Lucene,讓使用變得更簡(jiǎn)單,在高可用上面做得更好。
? 區別:ElasticSearch除了擁有Lucene所有優(yōu)點(diǎn)以外,還擁有自己優(yōu)點(diǎn).
? 可用性:支持集群沒(méi)有單點(diǎn)故障
? 擴展性:支持集群擴展
? 一般lucene在中小型項目中使用(但是也能使用es),而ES在大型項目中使用.因為ES支持在集群環(huán)境使用,并且自身也支持集群.
官網(wǎng)地址=>https://www.elastic.co/cn/elasticsearch
自行搜索
ES是面向文檔(document oriented)的,這意味著(zhù)它可以存儲整個(gè)對象或文檔(document)。然而它不僅僅是存儲,還會(huì )索引(index,創(chuàng )建索引)每個(gè)文檔的內容使之可以被搜索。在ES中,你可以對文檔(而非成行成列的數據)進(jìn)行索引、搜索、排序、過(guò)濾。
ES使用Javascript對象符號(JavaScript Object Notation),也就是JSON,作為文檔序列化格式。JSON現在已經(jīng)被大多語(yǔ)言所支持,而且已經(jīng)成為NoSQL領(lǐng)域的標準格式。
ES存儲的一個(gè)員工文檔的格式示例:
{
“email”: “wb@bbcc.com”,
“name”: “文兵”,
“info”: {
“addr”: “四川省成都市”,
“age”: 30,
“interests”: [ “櫻桃”, “粉嫩” ]
},
“join_date”: “2014-06-01”
}
盡管原始的 employee對象很復雜,但它的結構和對象的含義已經(jīng)被完整的體現在JSON中了,在ES中將對象轉化為JSON并做索引要比在表結構中做相同的事情簡(jiǎn)單的多。
一個(gè)文檔不只有數據。它還包含元數據(metadata)—關(guān)于文檔的信息。三個(gè)必須的元數據節點(diǎn)是:
| 節點(diǎn) | 說(shuō)明 |
|---|---|
| _index | 索引庫,文檔存儲的地方 |
| _type | 文檔類(lèi)型(6.x之后取消掉了這個(gè)屬性) |
| _id | 文檔的唯一標識 |
_index:索引庫,類(lèi)似于關(guān)系型數據庫里的“數據庫”—它是我們存儲和索引關(guān)聯(lián)數據的地方。
_type:類(lèi)型,類(lèi)似于關(guān)系型數據庫中表.在應用中,我們使用對象表示一些“事物”,例如一個(gè)用戶(hù)、一篇博客、一個(gè)評論,或者一封郵件??梢允谴髮?xiě)或小寫(xiě),不能包含下劃線(xiàn)或逗號。我們將使用 employee 做為類(lèi)型名。
_id: 與 _index 和 _type 組合時(shí),就可以在ELasticsearch中唯一標識一個(gè)文檔。當創(chuàng )建一個(gè)文檔,你可以自定義 _id ,也可以讓Elasticsearch幫你自動(dòng)生成。
另外還包括:_uid 文檔唯一標識(_type#_id)
_source:文檔原始數據
_all:所有字段的連接字符串
? 我們以員工對象為例,我們首先要做的是存儲員工數據,每個(gè)文檔代表一個(gè)員工。在ES中存儲數據的行為就叫做索引(indexing),文檔歸屬于一種類(lèi)型(type),而這些類(lèi)型存在于索引(index)中,我們可以簡(jiǎn)單的對比傳統數據庫和ES的對應關(guān)系:
關(guān)系數據庫(MYSQL) -> 數據庫DB-> 表TABLE-> 行ROW-> 列Column
Elasticsearch -> 索引庫Indices -> 類(lèi)型Types -> 文檔Documents -> 字段Fields
ES集群可以包含多個(gè)索引(indices)(數據庫),每一個(gè)索引庫中可以包含多個(gè)類(lèi)型(types)(表),每一個(gè)類(lèi)型包含多個(gè)文檔(documents)(行),然后每個(gè)文檔包含多個(gè)字段(Fields)(列)。
創(chuàng )建索引文檔
使用自己的ID創(chuàng )建
PUT {index}/{type}/{id}{ "field": "value", ...}使用內置ID創(chuàng )建
POST {index}/{type}{ "field": "value", ...}獲取指定ID的文檔
GET itsource/employee/123更新文檔
PUT {index}/{type}/{id}{ "field": "value", ...}跟創(chuàng )建語(yǔ)法一樣,但是會(huì )改變版本號
刪除文檔
DELETE {index}/{type}/{id}注意:盡管文檔不存在,但_version依舊增加了。這是內部記錄的一部分,它確保在多節點(diǎn)間不同操作可以有正確的順序
put和post是差不多的,都可以用來(lái)修改和添加,唯一區別就是,在添加時(shí)put需要手動(dòng)給id而post不需要賦id,系統自動(dòng)給個(gè)隨機id
GET /_search
GET /woniu47/_search
GET /woniu47,woniu48/_search
GET /woniu*/_search
URLsearch:
通過(guò)ID獲取
GET /woniu47/_doc/1
只返回文檔內容,不要元數據:
GET /woniu47/_doc/1/_source
查詢(xún)字符串搜索:
返回文檔的部分字段:
GET /woniu47/_doc/1?_source=name,age
查詢(xún)年齡為25歲的學(xué)員
GET /woniu47/_search?q=age:25
組合查詢(xún):
組合查詢(xún):
+表示并且,多個(gè)條件做且運算====>MUST
空格表示或,多個(gè)條件做或運算====>SHOULD
-表示非,多個(gè)條件做非運算====>MUST_NOT
+name:john +tweet:mary
+name:(mary john) +date:>2014-09-10 +(aggregations geo)
age[20 TO 30]
查詢(xún)年齡在25到28 (兩邊都包括)或則姓名為wangwu的學(xué)員
GET /woniu47/_search?q=age:>=25 +age<=28 name:wangwu
GET /woniu47/_search?q=age[25 TO 28] name:wangwu
中括號可以換成大括號,中括號有=大括號沒(méi)有
分頁(yè):
查詢(xún)20歲以上的學(xué)員,顯示3條
GET /woniu47/_search?q=age:>=20&size=3
查詢(xún)20歲以上的學(xué)員,每頁(yè)顯示3條,顯示第2頁(yè)
GET /woniu47/_search?q=age:>=20&size=3&from=3
查詢(xún)參數說(shuō)明:
| Name | Description |
|---|---|
| q | 標識查詢(xún)字符串 |
| df | 在查詢(xún)中,沒(méi)有定義字段前綴的情況下默認字段的前綴 |
| analyzer | 在分析查詢(xún)字符串時(shí),分析器的名字 |
| default_operator | 被用到的默認操作,有AND和OR兩種,默認是OR |
| explain | 對于每一個(gè)命中,對怎樣得到命中得分的計算給出一個(gè)解釋 |
| _source | 將其設置為false,查詢(xún)就會(huì )放棄檢索_source字段。也可以通過(guò)設置檢索部分文檔 |
| fields | 標識查詢(xún)字符串 |
| sort | 排序執行,可以fieldName、fieldName:asc |
| track_scores | 當排序的時(shí)候,將其設置為true,可以返回相關(guān)度得分 |
| from | 分頁(yè)查詢(xún)起始點(diǎn),默認0 |
| size | 查詢(xún)數量,默認10 |
| search_type | 搜索操作執行的類(lèi)型 |
? 倒排索引是先將文檔進(jìn)行分詞處理,標記每個(gè)詞都出現在哪些文檔里面,這樣就可以快速查詢(xún)某個(gè)詞所出現的文檔位置。與之對應的是“正排索引”,例如我們看一本書(shū)的目錄,從前往后查找目錄,就是正排索引。
? 我們先來(lái)看3組文檔,我們將文檔編號分別列為1、2、3。
https://img-blog.csdnimg.cn/img_convert/23778d72d45fcf1d7532e55f1baba182.png
這3個(gè)文檔里,核心詞是“衛生間隔斷”,如果你在搜索引擎上,直接搜索衛生間隔斷,拋開(kāi)文章內容,單看標題,哪個(gè)排第一?沒(méi)錯,就是文檔1會(huì )出現在第一位,為什么?
因為倒排索引里,會(huì )通過(guò)單詞詞典,統計一個(gè)單詞在文檔里出現的位置。我們將上述文檔里出現的詞,都賦予一個(gè)ID。
? 這是一個(gè)最簡(jiǎn)單的倒排索引示意圖:

分詞是指將文本轉換成一系列單詞的過(guò)程,也可以叫文本分析,在es里面稱(chēng)為Analysis,如下圖所示:

分詞器是ES中專(zhuān)門(mén)處理分詞的組件,英文為Analyzer,它的組成如下:
針對原始文本進(jìn)行處理,比如去除html特殊標記
將原始文本按照一定規則切分為單詞
針對tokenizer處理的單詞進(jìn)行再加工,比如轉小寫(xiě)、刪除或新增等處理
ES提供了一個(gè)測試分詞的api接口,方便驗證分詞效果,endpoint是_analyze
直接指定analyzer進(jìn)行測試
POST _analyze{ "analyzer": "standard", "text": "hello world"}直接指定索引中的字段進(jìn)行測試
POST woniu47/_analyze{ "field": "name", "text": "hello java"}ES自帶分詞器如下:
| Name | Description |
|---|---|
| standard | 默認分詞器,按詞切分,支持多語(yǔ)言,小寫(xiě)處理 |
| simple | 按照非字母切分,小寫(xiě)處理 |
| whitespace | 按照空格切分 |
| stop | 相比simple 多了stop word處理(語(yǔ)氣2組詞等修飾性的詞語(yǔ),比如the、an、的、這等等) |
| keyword | 不分詞,直接將輸入作為一個(gè)單詞輸出 |
| pattern | 通過(guò)正則表達式自定義分割符,默認是\W+, 即非字詞的符號作為分隔符 |
中文分詞器是將一個(gè)漢字序列切分成一個(gè)一個(gè)單獨的詞。在英語(yǔ)中,單詞之間是以空格作為自然分界符,漢語(yǔ)中詞沒(méi)有一個(gè)形式上的分界符。上下文不同,分詞結果迥異,比如交叉歧義問(wèn)題,比如下面兩種分詞都合理:
1.IK
實(shí)現中英文單詞的切分,支持ik_smart、ik_maxword等模式,
可自定義詞庫,支持熱更新分詞詞典
2.jieba
python中最流行的分詞系統,支持分詞和詞性標注
支持繁體分詞、自定義詞典、并行分詞等
https://github.com/sing1ee/elasticsearch-jieba-plugin
IK的使用
1.安裝
POST _analyze{ "analyzer": "ik_smart", "text": "中華人民共和國國歌"}POST _analyze{ "analyzer": "ik_max_word", "text": "中華人民共和國國歌"}3.ik_smart與ik_max_word的區別
ik_max_word: 會(huì )將文本做最細粒度的拆分,比如會(huì )將“中華人民共和國國歌”拆分為“中華人民共和國,中華人民,中華,華人,人民共和國,人民,人,民,共和國,共和,和,國國,國歌”,會(huì )窮盡各種可能的組合,適合 Term Query;
ik_smart: 會(huì )做最粗粒度的拆分,比如會(huì )將“中華人民共和國國歌”拆分為“中華人民共和國,國歌”,適合 Phrase 查詢(xún)
注意: 在config文件夾下面有很多擴展詞的配置
ES的文檔映射(mapping)機制用于進(jìn)行字段類(lèi)型確認(字段是什么類(lèi)型,什么分詞器),將每個(gè)字段匹配為一種確定的數據類(lèi)型。類(lèi)似于數據庫的表結構定義,主要作用如下:
定義Index下的字段名(Field Name)
定義字段的類(lèi)型,比如數值型、字符串型、布爾型等
定義倒排索引相關(guān)配置,比如是否索引、記錄position等
查看某個(gè)Index的Mapping
GET woniu47/_mapping創(chuàng )建一個(gè)Index的映射
PUT woniu48{ "mappings": { "properties": { "name": { "type": "keyword" }, "age": { "type": "integer" }, "desc": { "type": "text", "analyzer": "ik_smart" } } }}Mapping中的字段類(lèi)型一旦設定后,禁止直接修改,因為L(cháng)ucene實(shí)現的倒排索引生成后不允許修改
通過(guò)dynamic參數來(lái)控制字段的新增
true(默認)允許自動(dòng)新增字段
false 不允許自動(dòng)新增字段,但是文檔可以正常寫(xiě)入,但無(wú)法對字段進(jìn)行查詢(xún)等操作
strict 文檔不能寫(xiě)入,報錯
PUT woniu48{ "mappings": { "dynamic": "false", "properties": { "name": { "type": "keyword" }, "age": { "type": "integer" }, "desc": { "type": "text", "analyzer": "ik_smart" } } }}copy_to
PUT woniu48{ "mappings": { "dynamic": "false", "properties": { "name": { "type": "keyword", "copy_to": "full_name" }, "age": { "type": "integer" }, "desc": { "type": "text", "analyzer": "ik_smart", "copy_to": "full_name" }, "full_name": { "type": "text" } } }}index
PUT woniu48{ "mappings": { "dynamic": "false", "properties": { "name": { "type": "keyword", "index": false }, "age": { "type": "integer" }, "desc": { "type": "text", "analyzer": "ik_smart", "copy_to": "full_name" } } }}name字段不能夠用來(lái)搜索
null_value
PUT woniu48{ "mappings": { "properties": { "name": { "type": "keyword" }, "age": { "type": "integer" }, "desc": { "type": "text", "analyzer": "ik_smart" }, "sex": { "type": "keyword", "null_value": "null" } } }}使用null替換了空值,下面添加一個(gè)空值
PUT woniu48/_doc/1{ "name": "zs", "age": 23, "desc": "中國人很好", "sex": null}可以使用替換的null值來(lái)進(jìn)行空值搜索
GET woniu48/_search{ "query": { "match": { "sex": "null" } }}ignore_above
PUT woniu48{ "mappings": { "properties": { "name": { "type": "text", "fields": { "pinyin": { "type": "keyword", "ignore_above": 5 } } }, "age": { "type": "integer" } } }}多字段特性 multi-fields
PUT woniu48{ "mappings": { "properties": { "name": { "type": "text", "fields": { "pinyin": { "type": "keyword" } } }, "age": { "type": "integer" } } }}index_options
docs: 只記錄doc id
freqs : 記錄doc id和term frequencies
positions: 記錄doc id、term frequencies和term position
offsets: 記錄doc id、term frequencies、term position和character offsets
text類(lèi)型默認配置為positions,其他默認為docs,記錄內容越多,占用空間越大
核心數據類(lèi)型
字符串型: text、keyword
數值型: long、integer、short、byte、double、float、half_float、scaled_float
日期類(lèi)型: date
布爾類(lèi)型: boolean
二進(jìn)制類(lèi)型: binary
范圍類(lèi)型: integer_range、float_range、long_range、double_range、date_range
復雜數據類(lèi)型
數組類(lèi)型: array
對象類(lèi)型: object
嵌套類(lèi)型: nested object
地理位置數據類(lèi)型
geo_point
geo_shape
專(zhuān)用類(lèi)型
ip: 記錄IP地址
completion: 實(shí)現自動(dòng)補全
token_count: 記錄分詞數
murmur3: 記錄字符串hash值
ES在沒(méi)有配置Mapping的情況下新增文檔,ES可以自動(dòng)識別文檔字段類(lèi)型,并動(dòng)態(tài)生成字段和類(lèi)型的映射關(guān)系。
ES是依靠JSON文檔的字段類(lèi)型來(lái)實(shí)現自動(dòng)識別字段類(lèi)型,支持的類(lèi)型如下:
JSON類(lèi)型ES類(lèi)型
null忽略
booleanboolean
浮點(diǎn)類(lèi)型float
整數long
objectobject
array由第一個(gè)非null值得類(lèi)型決定
string匹配為日期格式則設為date類(lèi)型(默認開(kāi)啟);
匹配為數字的話(huà)設為float或long類(lèi)型(默認關(guān)閉)設為text類(lèi)型,并附帶keyword子字段
由ES提供豐富且靈活的查詢(xún)語(yǔ)言叫做DSL查詢(xún)(Query DSL)。將查詢(xún)語(yǔ)句通過(guò)http request body發(fā)送到ES,主要包含如下參數:
query符合Query DSL語(yǔ)法的查詢(xún)語(yǔ)句
from、size
timeout
sort
。。。
例如:
GET woniu48/_search
{
"query": {
"term": {
"name": "lisi"
}
}
}
基于JSON定義的查詢(xún)語(yǔ)言,主要包含如下兩種類(lèi)型
字段類(lèi)查詢(xún)
如term、match、range等,只針對某一個(gè)字段進(jìn)行查詢(xún)
復合查詢(xún)
如bool查詢(xún)等,包含一個(gè)或多個(gè)字段類(lèi)查詢(xún)或則復合查詢(xún)語(yǔ)句
Match Query流程

字段類(lèi)查詢(xún)主要包括以下兩類(lèi):
全文匹配針對text類(lèi)型的字段進(jìn)行全文檢索,會(huì )對查詢(xún)語(yǔ)句先進(jìn)行分詞處理,如match、match_phrase等query類(lèi)型單詞匹配不會(huì )對查詢(xún)語(yǔ)句做分詞處理,直接匹配字段的倒排索引,如term、terms、range等query類(lèi)型全文匹配(Match Query)GET woniu48/_search{ "query": { "match": { "username": "alfred way" } }}**通過(guò)operator參數可以控制單詞間的匹配關(guān)系,可選項為or或則and**GET woniu48/_search{ "query": { "match": { "desc": { "query": "非常漂亮", "operator": "and" } } }}上面查詢(xún)如果分詞器分詞為“非?!?、“漂亮”兩個(gè)詞,查詢(xún)匹配結果就必須同時(shí)包含這兩個(gè)詞的文檔單詞匹配(Term Query)GET woniu48/_search{ "query": { "term": { "name": "lisi" } }}一次傳入多個(gè)單詞進(jìn)行查詢(xún)GET woniu48/_search{ "query": { "terms": { "name": [ "lisi", "wangmazi" ] } }}Range Query(范圍查詢(xún))GET woniu48/_search{ "query": { "range": { "age": { "gte": 20, "lte": 23 } } }}復合查詢(xún)是指包含字段類(lèi)查詢(xún)或復合查詢(xún)的類(lèi)型,主要包括以下幾類(lèi):
constant_score query
bool query *
dis_max query
function_score query
boosting query
Bool Query
布爾查詢(xún)由一個(gè)或多個(gè)布爾子句組成,主要包含如下4個(gè):
NameDescription
filter只過(guò)濾符合條件的文檔,不計算相關(guān)性得分
must文檔必須復合must中的所有條件,會(huì )影響相關(guān)性得分
must_not文檔必須不符合must_not中的所有條件
should文檔可以符合should中的條件,會(huì )影響相關(guān)性得分
Filter Filter查詢(xún)只過(guò)濾符合條件的文檔,不會(huì )進(jìn)行相關(guān)性算分
ES針對Filter會(huì )有智能緩存,因此其執行效率很高
做簡(jiǎn)單匹配查詢(xún)且不考慮算分時(shí),推薦使用filter替代query等
GET woniu48/_search{ "query": { "bool": { "filter": [ { "term": { "name": "admin" } }, { "range": { "age": { "gte": 25 } } } ] } }}GET woniu48/_search{ "query": { "bool": { "must": [ { "match": { "desc": "漂亮的人" } }, { "range": { "age": { "gte": 22 } } } ] } }}查詢(xún)描述里面經(jīng)過(guò)分詞有“漂亮”一詞以及不帶“非?!币辉~的結果
GET woniu48/_search{ "query": { "bool": { "must": [ { "match": { "desc": "漂亮的人" } } ], "must_not": [ { "match": { "desc": "非常好" } } ] } }}只包含should時(shí),文檔必須至少滿(mǎn)足一個(gè)條件(minimun_should_match可以控制滿(mǎn)足條件的個(gè)數或則百分比)
例如:下面至少滿(mǎn)足一個(gè)條件
GET woniu48/_search{ "query": { "bool": { "should": [ { "match": { "desc": "漂亮的人" } }, { "range": { "age": { "lte": 20 } } } ] } }}例如:下面需要滿(mǎn)足兩個(gè)條件
GET woniu48/_search{ "query": { "bool": { "should": [ { "match": { "desc": "漂亮的人" } }, { "range": { "age": { "lte": 20 } } } ], "minimum_should_match": 2 } }}同時(shí)包含should和must時(shí),文檔不必滿(mǎn)足should中的條件,但是如果滿(mǎn)足條件,會(huì )增加相關(guān)性得分
該查詢(xún)只會(huì )以must匹配來(lái)搜索
GET woniu48/_search{ "query": { "bool": { "should": [ { "range": { "age": { "lte": 19 } } } ], "must": [ { "match": { "desc": "漂亮的人" } } ] } }}綜合實(shí)例
查詢(xún)年齡大于20并且包含“漂亮的人”描述或則年齡小于18的學(xué)員,分頁(yè)顯示第2頁(yè),每頁(yè)顯示2條,且按照年齡由低到高排序
GET woniu48/_search{ "query": { "bool": { "should": [ { "bool": { "must": [ { "match": { "desc": "漂亮的人" } }, { "range": { "age": { "gte": 20 } } } ] } }, { "range": { "age": { "lte": 18 } } } ] } }, "from": 2, "size": 2, "sort": [ { "age": { "order": "desc" } } ]}可以通過(guò)Spring Data Elasticsearch來(lái)進(jìn)行操作,boot對其進(jìn)行了集成:
導包
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-elasticsearch</artifactId></dependency>配置連接
spring: elasticsearch: rest: uris: - http://localhost:9200注入模板對象
@Autowired
ElasticsearchOperations operations;
ES的文檔對應于Java的對象,可以通過(guò)Spring-Data-Es提供的注解來(lái)進(jìn)行關(guān)聯(lián):
@Data@Document(indexName = "woniu48")public class Student {@Id@Field(type = FieldType.Integer,name = "id")private Integer id;@Field(type = FieldType.Keyword,name="name")private String name;@Field(type = FieldType.Integer,name="age")private Integer age;@Field(type = FieldType.Text,analyzer = "ik_max_word",name = "desc")private String desc;}@Document: 在類(lèi)級別應用,以指示該類(lèi)是映射到ES的哪一個(gè)Index。最重要的屬性是:
indexName: 用于存儲此實(shí)體的索引的名稱(chēng)
@Id: 用來(lái)標識ID
@Field: 在字段級別應用并定義字段的屬性:
name: 字段名稱(chēng)
type: 字段類(lèi)型
analyzer: 分詞器
保存(新增與更新)對象:
Student stu = new Student();stu.setId(3333);stu.setName("張三豐");stu.setDesc("此人漲得不好看");stu.setAge(100);operations.save(stu);刪除對象:
//第一個(gè)參數是idoperations.delete("3333", Student.class);查詢(xún)對象:
查詢(xún)有三種主要方式:通過(guò)CriteriaQuery條件來(lái)查詢(xún),可以簡(jiǎn)單組合條件,不能分詞,適用于非常簡(jiǎn)單的查詢(xún)//查詢(xún)年齡在20-25之間或則名稱(chēng)是"admin"的學(xué)員Criteria c = new Criteria("age").between(20, 25).or("name").is("admin");CriteriaQuery cq = new CriteriaQuery(c);SearchHits<Student> shs = operations.search(cq, Student.class);System.out.println(shs.getTotalHits());shs.getSearchHits().forEach(student -> {System.out.println(student.getContent().getName()+":"+student.getContent().getAge());});通過(guò)StringQuery來(lái)查詢(xún),直接在java這邊構建查詢(xún)json字符串,不推薦使用通過(guò)NativeSearchQuery來(lái)構建復雜查詢(xún),包括bool查詢(xún)等等,推薦使用/*查詢(xún)匹配非常漂亮的人或則年齡在90以上的*///構建NativeSearchQuery建造器NativeSearchQueryBuilder nsqb = new NativeSearchQueryBuilder();//構造Bool查詢(xún)條件BoolQueryBuilder bqb = new BoolQueryBuilder();//通過(guò)該Bool查詢(xún)器構建should查詢(xún)器List<QueryBuilder> should = bqb.should();//向should里面添加兩個(gè)查詢(xún)條件should.add(new MatchQueryBuilder("desc", "非常漂亮的人"));should.add(new RangeQueryBuilder("age").gt(90));//注冊bool查詢(xún)條件nsqb.withQuery(bqb);//注冊分頁(yè)(第一頁(yè),顯示3條)nsqb.withPageable(PageRequest.of(0, 3));//注冊排序(age降序)nsqb.withSort(SortBuilders.fieldSort("age").order(SortOrder.DESC));//構建NativeSearchQueryNativeSearchQuery nsq = nsqb.build();//執行查詢(xún)方法SearchHits<Student> shs = operations.search(nsq, Student.class);System.out.println(shs.getTotalHits());shs.getSearchHits().forEach(x -> {Student stu = x.getContent();System.out.println(stu.getName()+":"+stu.getDesc()+":"+stu.getAge());});查詢(xún)結果高亮顯示
//創(chuàng )建高亮顯示器HighlightBuilder hb = new HighlightBuilder();//設置高亮字段hb.field("desc");//設置為false,匹配字段都會(huì )高亮顯示hb.requireFieldMatch(false);//設置如何高亮顯示hb.preTags("<span style=\"color:red\">");hb.postTags("</span>");//設置高亮顯示范圍以字符為單位hb.fragmentSize(800);//設置高亮顯示的開(kāi)始位置hb.numOfFragments(0);NativeSearchQueryBuilder nsqb = new NativeSearchQueryBuilder();//注冊高亮顯示器nsqb.withHighlightBuilder(hb);nsqb.withQuery(new MatchQueryBuilder("desc", "非常漂亮"));NativeSearchQuery nsq = nsqb.build();SearchHits<Student> shs = operations.search(nsq, Student.class);shs.getSearchHits().forEach(x -> {Student stu = x.getContent(); String desc = stu.getDesc(); //獲取高亮顯示字段List<String> hs = x.getHighlightField("desc"); if(hs != null && hs.size() > 0) desc = hs.get(0);System.out.println(stu.getName()+":"+desc);});
聯(lián)系客服