廣州建委網(wǎng)站google推廣一年的費(fèi)用
文章目錄
- 一.什么是Elasticsearch?
- 1.正向索引和倒排索引
- 2.Mysql和ES的概念對(duì)比
- 3.安裝elasticsearch、kibana
- 二.IK分詞器
- 三.索引庫(kù)操作
- 四.文檔操作
- 五.RestClient操作索引庫(kù)
- 1.初始化RestClient
- 2.創(chuàng)建索引庫(kù)
- 3.刪除索引庫(kù)
- 4.判斷索引庫(kù)是否存在
- 六.RestClient操作文檔
- 1.新增文檔
- 2.查詢數(shù)據(jù)
- 3.修改數(shù)據(jù)
- 4.刪除數(shù)據(jù)
- 5.批量插入數(shù)據(jù)
- 七.DSL查詢文檔
- 八.RestClient檢索查詢文檔
一.什么是Elasticsearch?
Elasticsearch 是一個(gè)分布式、高擴(kuò)展、高實(shí)時(shí)的搜索與數(shù)據(jù)分析引擎。它能很方便的使大量數(shù)據(jù)具有搜索、分析和探索的能力。充分利用Elasticsearch的水平伸縮性,能使數(shù)據(jù)在生產(chǎn)環(huán)境變得更有價(jià)值。
Elasticsearch是一款非常強(qiáng)大的開源搜索引擎,可以幫助我們從海量數(shù)據(jù)中快速找到需要的內(nèi)容。
Elasticsearch結(jié)合kibana、Logstash、Beats,也就是elastic stack(ELK)。被廣泛應(yīng)用在日志數(shù)據(jù)分析、實(shí)時(shí)監(jiān)控等領(lǐng)域。
Elasticsearch是elastic stack的核心(不可替換),負(fù)責(zé)存儲(chǔ)、搜索、分析數(shù)據(jù)。
1.正向索引和倒排索引
文檔(document):每條數(shù)據(jù)就是一個(gè)文檔
詞條(term):文檔按照語義分成的詞語
正向索引(Forward Index):
定義: 正向索引是根據(jù)文檔-詞項(xiàng)對(duì)的方式建立的索引。每個(gè)文檔都有一個(gè)記錄,其中包含了文檔中的所有詞項(xiàng)及其位置信息。
例如Mysql就是使用的正向索引,根據(jù)id檢索一個(gè)文檔非常快,但是根據(jù)文檔中某個(gè)字段檢索文檔只能逐條檢索
倒排索引(Inverted Index):
定義: 倒排索引是根據(jù)詞項(xiàng)-文檔對(duì)的方式建立的索引。每個(gè)詞項(xiàng)都有一個(gè)記錄,其中包含了包含該詞項(xiàng)的所有文檔及其位置信息。即對(duì)文檔內(nèi)容分詞,對(duì)詞條創(chuàng)建索引,并記錄詞條所在文檔的信息。查詢時(shí)先根據(jù)詞條查詢到文檔id,而后獲取到文檔
Elasticsearch就使用了倒排索引,將文檔按照語義分成詞條,根據(jù)詞條建立詞條表,這樣就形成了詞條-文檔的結(jié)構(gòu),導(dǎo)致檢索字段時(shí)非???/strong>
倒排索引中包含兩部分內(nèi)容:
-
詞條詞典(Term Dictionary):記錄所有詞條,以及詞條與倒排列表(Posting List)之間的關(guān)系,會(huì)給詞條創(chuàng)建索引,提高查詢和插入效率
-
倒排列表(Posting List):記錄詞條所在的文檔id、詞條出現(xiàn)頻率 、詞條在文檔中的位置等信息
-
文檔id:用于快速獲取文檔
-
詞條頻率(TF):文檔在詞條出現(xiàn)的次數(shù),用于評(píng)分
-
文檔:
elasticsearch是面向文檔存儲(chǔ)的,可以是數(shù)據(jù)庫(kù)中的一條商品數(shù)據(jù),一個(gè)訂單信息。
文檔數(shù)據(jù)會(huì)被序列化為json格式后存儲(chǔ)在elasticsearch中。
索引:
相同類型的文檔的集合
映射:
索引中文檔的字段約束信息,類似表的結(jié)構(gòu)約束
2.Mysql和ES的概念對(duì)比
Mysql:擅長(zhǎng)事務(wù)類型(ACID特性)操作,可以確保數(shù)據(jù)的安全和一致性
Elasticsearch:擅長(zhǎng)海量數(shù)據(jù)的搜索、分析、計(jì)算
總而言之:
-
正向索引適合于文檔級(jí)別的查詢,因?yàn)樗苯犹峁┝宋臋n中的詞項(xiàng)信息。
-
倒排索引適合于詞項(xiàng)級(jí)別的查詢,因?yàn)樗苯犹峁┝税硞€(gè)詞項(xiàng)的文檔信息。
3.安裝elasticsearch、kibana
通過Dokcer拉取鏡像安裝即可
二.IK分詞器
ES在創(chuàng)建倒排索引時(shí)需要對(duì)文檔分詞;在搜索時(shí),需要對(duì)用戶輸入內(nèi)容分詞。但默認(rèn)的分詞規(guī)則對(duì)中文處理并不友好。故需要更好的分詞策略:IK分詞器
安裝(安裝到es-plugins/_data即可):IK分詞器官方地址,重啟es后IK分詞器生效
ik分詞器包含兩種模式:
- ik_smart:最少切分,粗粒度
- ik_max_word:最細(xì)切分,細(xì)粒度
IK分詞器-拓展詞庫(kù)
IK分詞器雖然說按照字典查找詞語進(jìn)行組合,但是隨著網(wǎng)絡(luò)文化發(fā)展和新詞的逐漸產(chǎn)生,IK分詞器不可能馬上更新這些詞匯,這時(shí)候就需要進(jìn)行拓展詞匯庫(kù)和增加禁用詞匯
要拓展ik分詞器的詞庫(kù),只需要修改一個(gè)ik分詞器目錄中的config目錄中的IkAnalyzer.cfg.xml文件:
在ext.dic文件中添加拓展詞匯
在stopword.dic文件下添加禁用詞匯
三.索引庫(kù)操作
mapping屬性
mapping是對(duì)索引庫(kù)中文檔的約束,常見的mapping屬性包括:
-
type:字段數(shù)據(jù)類型,常見的簡(jiǎn)單類型有:
-
字符串:text(可分詞的文本)、keyword(精確值,例如:品牌、國(guó)家、ip地址)
-
數(shù)值:long、integer、short、byte、double、float
-
布爾:boolean
-
日期:date
-
對(duì)象:object
-
-
index:是否創(chuàng)建索引,默認(rèn)為true
-
analyzer:使用哪種分詞器
-
properties:該字段的子字段
創(chuàng)建索引庫(kù)
PUT /索引庫(kù)名稱
{"mappings": {"properties": {"字段名":{"type": "text","analyzer": "ik_smart"},"字段名2":{"type": "keyword","index": "false"},"字段名3":{"properties": {"子字段": {"type": "keyword"}}},// ...略}}
}
查看索引庫(kù)語法:
GET /索引庫(kù)名
刪除索引庫(kù)的語法:
DELETE /索引庫(kù)名
修改索引庫(kù)
索引庫(kù)和mapping一旦創(chuàng)建無法修改,但是可以添加新的字段,語法如下:
PUT /索引庫(kù)名/_mapping
{"properties": {"新字段名":{"type": "integer"}}
}
四.文檔操作
添加文檔
POST /索引庫(kù)名/_doc/文檔id
{"字段1": "值1","字段2": "值2","字段3": {"子屬性1": "值3","子屬性2": "值4"},// ...
}
查看文檔語法:
GET /索引庫(kù)名/_doc/文檔id
刪除文檔:
DELETE /索引庫(kù)名/_doc/文檔id
修改文檔
方式一:全量修改,會(huì)刪除舊文檔,添加新文檔
PUT /索引庫(kù)名/_doc/文檔id
{"字段1": "值1","字段2": "值2",// ... 略
}
方式二:增量修改,修改指定字段值
POST /索引庫(kù)名/_update/文檔id
{"doc": {"字段名": "新的值",}
}
文檔操作-動(dòng)態(tài)映射
當(dāng)我們向ES中插入文檔時(shí),如果文檔中字段沒有對(duì)應(yīng)的mapping,ES會(huì)幫助我們字段設(shè)置mapping,規(guī)則如下:
五.RestClient操作索引庫(kù)
什么是RestClient
ES官方提供了各種不同語言的客戶端,用來操作ES。這些客戶端的本質(zhì)就是組裝DSL語句,通過http請(qǐng)求發(fā)送給ES。
使用:
1.初始化RestClient
1.引入es的RestHighLevelClient依賴:
<dependency><groupId>org.elasticsearch.client</groupId><artifactId>elasticsearch-rest-high-level-client</artifactId></dependency>
2.可以發(fā)現(xiàn)客戶端的版本號(hào),引入的依賴版本必須和客戶端一致(不一致則需修改)
修改版本號(hào)
<properties><java.version>1.8</java.version><elasticsearch.version>客戶端版本號(hào)</elasticsearch.version></properties>
3.初始化RestHighLevelClient:
private RestHighLevelClient client;//測(cè)試類中使用@BeforeEach注解來標(biāo)記一個(gè)方法,該方法將在每個(gè)測(cè)試方法執(zhí)行之前執(zhí)行(@AfterEach同理)@BeforeEachvoid setUp(){this.client=new RestHighLevelClient(RestClient.builder(HttpHost.create("自己的Linux局域網(wǎng)ip地址:9200")));}@AfterEachvoid tearDown() throws IOException {this.client.close();}
2.創(chuàng)建索引庫(kù)
@Testvoid testCreateHotelIndex() throws IOException {CreateIndexRequest request = new CreateIndexRequest("索引庫(kù)名");request.source("DSL語句", XContentType.JSON);client.indices().create(request, RequestOptions.DEFAULT);}
其中:indices()包含了所有操作索引庫(kù)的API
3.刪除索引庫(kù)
@Testvoid testDeleteHotelIndex() throws IOException { // 1.創(chuàng)建Request對(duì)象DeleteIndexRequest request = new DeleteIndexRequest("索引庫(kù)名");// 2.發(fā)起請(qǐng)求client.indices().delete(request, RequestOptions.DEFAULT);
}
4.判斷索引庫(kù)是否存在
@Testvoid testExistsHotelIndex() throws IOException { // 1.創(chuàng)建Request對(duì)象 GetIndexRequest request = new GetIndexRequest("索引庫(kù)名"); // 2.發(fā)起請(qǐng)求 boolean exists = client.indices().exists(request, RequestOptions.DEFAULT); // 3.輸出 System.out.println(exists);
}
六.RestClient操作文檔
1.新增文檔
IndexRequest request = new IndexRequest("索引庫(kù)名").id(設(shè)置id);request.source(JSON文檔, XContentType.JSON);client.index(request, RequestOptions.DEFAULT);
2.查詢數(shù)據(jù)
GetRequest request = new GetRequest(索引庫(kù)名, id); GetResponse response = client.get(request, RequestOptions.DEFAULT); String json = response.getSourceAsString();
3.修改數(shù)據(jù)
UpdateRequest request = new UpdateRequest(索引庫(kù)名,id);request.doc("鍵1","值1","鍵2","值2"...);client.update(request, RequestOptions.DEFAULT);
4.刪除數(shù)據(jù)
DeleteRequest request = new DeleteRequest(索引庫(kù)名,id);client.delete(request, RequestOptions.DEFAULT);
5.批量插入數(shù)據(jù)
request.add(new IndexRequest(索引庫(kù)名).id(id1).source(JSON文檔1, XContentType.JSON));
request.add(new IndexRequest(索引庫(kù)名).id(id2).source(JSON文檔2, XContentType.JSON));
request.add(new IndexRequest(索引庫(kù)名).id(id3).source(JSON文檔3, XContentType.JSON));
//request可以添加多個(gè)IndexRequest
client.bulk(request, RequestOptions.DEFAULT);
七.DSL查詢文檔
DSL Query的分類
Elasticsearch提供了基于JSON的DSL(Domain Specific Language)來定義查詢。常見的查詢類型包括:
- 查詢所有:查詢出所有數(shù)據(jù),一般測(cè)試用。例如:match_all
- 全文檢索(full text)查詢:利用分詞器對(duì)用戶輸入內(nèi)容分詞,然后去倒排索引庫(kù)中匹配。例如:
- match_query
- multi_match_query
- 精確查詢:根據(jù)精確詞條值查找數(shù)據(jù),一般是查找keyword、數(shù)值、日期、boolean等類型字段。例如:
- ids
- range
- term
- 地理(geo)查詢:根據(jù)經(jīng)緯度查詢。例如:
- geo_distance
- geo_bounding_box
- 復(fù)合(compound)查詢:復(fù)合查詢可以將上述各種查詢條件組合起來,合并查詢條件。例如:
- bool
- function_score
查詢的基本語法如下:
GET /indexName/_search
{"query": {"查詢類型": {"查詢條件": "條件值"}}
}
全文檢索查詢
match查詢:全文檢索查詢的一種,會(huì)對(duì)用戶輸入內(nèi)容分詞,然后去倒排索引庫(kù)檢索,語法:
GET /indexName/_search
{"query": {"match": {"FIELD": "TEXT"}}
}
multi_match:與match查詢類似,只不過允許同時(shí)查詢多個(gè)字段,語法:
GET /indexName/_search
{"query": {"multi_match": {"query": "TEXT","fields": ["FIELD1", " FIELD12"]}}
}
需要注意的是:根據(jù)多個(gè)字段查詢,參與查詢字段越多,查詢性能越差,建議使用多個(gè)字段拷貝到一個(gè)字段進(jìn)行多字段的查詢
精確查詢
精確查詢一般是查找keyword、數(shù)值、日期、boolean等類型字段。所以不會(huì)對(duì)搜索條件分詞。常見的有:
- term:根據(jù)詞條精確值查詢
- range:根據(jù)值的范圍查詢
term查詢:
// term查詢
GET /indexName/_search
{"query": {"term": {"FIELD": {"value": "VALUE"}}}
}
range查詢:
// range查詢
GET /indexName/_search
{"query": {"range": {"FIELD": {"gte": 10,"lte": 20}}}
}
地理查詢
geo_bounding_box(不常用):查詢geo_point值落在某個(gè)矩形范圍的所有文檔
// geo_bounding_box查詢
GET /indexName/_search
{"query": {"geo_bounding_box": {"FIELD": {"top_left": {"lat": 31.1,"lon": 121.5},"bottom_right": {"lat": 30.9,"lon": 121.7}}}}
}
geo_distance(常用):查詢到指定中心點(diǎn)小于某個(gè)距離值的所有文檔
// geo_distance 查詢
GET /indexName/_search
{"query": {"geo_distance": {"distance": "15km","FIELD": "31.21,121.5"}}
}
復(fù)合查詢
復(fù)合(compound)查詢:復(fù)合查詢可以將其它簡(jiǎn)單查詢組合起來,實(shí)現(xiàn)更復(fù)雜的搜索邏輯,例如:
fuction score:算分函數(shù)查詢,可以控制文檔相關(guān)性算分,控制文檔排名。例如百度競(jìng)價(jià)
當(dāng)我們利用match查詢時(shí),文檔結(jié)果會(huì)根據(jù)與搜索詞條的關(guān)聯(lián)度打分(_score),返回結(jié)果時(shí)按照分值降序排列。
常見的三個(gè)算分函數(shù):
TF-IDF:在elasticsearch5.0之前,會(huì)隨著詞頻增加而越來越大
BM25:在elasticsearch5.0之后,會(huì)隨著詞頻增加而增大,但增長(zhǎng)曲線會(huì)趨于水平
Function Score Query
使用 function score query,可以修改文檔的相關(guān)性算分(query score),根據(jù)新得到的算分排序。
function score query定義的三要素:
- 過濾條件:哪些文檔要加分
- 算分函數(shù):如何計(jì)算function score
- 加權(quán)方式:function score 與 query score如何運(yùn)算
復(fù)合查詢 Boolean Query
布爾查詢是一個(gè)或多個(gè)查詢子句的組合。子查詢的組合方式有:
- must:必須匹配每個(gè)子查詢,類似“與”
- should:選擇性匹配子查詢,類似“或”
- must_not:必須不匹配,不參與算分,類似“非”
- filter:必須匹配,不參與算分
例子:搜索名字包含“如家”,價(jià)格不高于400,在坐標(biāo)31.21,121.5周圍10km范圍內(nèi)的酒店。
GET /hotel/_search
{"query": {"bool": {"must": [{"match": {"name": "如家"}}],"must_not": [{"range": { "price": {"gt": 400}}}],"filter": [{"geo_distance": {"distance": "10km", "location": {"lat": 31.21, "lon": 121.5}}}]}}
}
搜索結(jié)果處理
1.排序
elasticsearch支持對(duì)搜索結(jié)果排序,默認(rèn)是根據(jù)相關(guān)度算分(_score)來排序。可以排序字段類型有:keyword類型、數(shù)值類型、地理坐標(biāo)類型、日期類型等。
GET /indexName/_search
{"query": {"match_all": {}},"sort": [{"FIELD": "desc" // 排序字段和排序方式ASC、DESC}]
}
GET /indexName/_search
{"query": {"match_all": {}},"sort": [{"_geo_distance" : {"FIELD" : "緯度,經(jīng)度","order" : "asc","unit" : "km"}}]
}
2.分頁
elasticsearch 默認(rèn)情況下只返回top10的數(shù)據(jù)。而如果要查詢更多數(shù)據(jù)就需要修改分頁參數(shù)了。
elasticsearch中通過修改from、size參數(shù)來控制要返回的分頁結(jié)果:
GET /hotel/_search
{"query": {"match_all": {}},"from": 990, // 分頁開始的位置,默認(rèn)為0"size": 10, // 期望獲取的文檔總數(shù)"sort": [{"price": "asc"}]
}
深度分頁問題(ES設(shè)定結(jié)果集查詢的上限是10000)
針對(duì)深度分頁,ES提供了兩種解決方案:
- search after:分頁時(shí)需要排序,原理是從上一次的排序值開始,查詢下一頁數(shù)據(jù)。官方推薦使用的方式。
- scroll:原理將排序數(shù)據(jù)形成快照,保存在內(nèi)存。官方已經(jīng)不推薦使用。
3.高亮
高亮:就是在搜索結(jié)果中把搜索關(guān)鍵字突出顯示。
原理是這樣的:
將搜索結(jié)果中的關(guān)鍵字用標(biāo)簽標(biāo)記出來
在頁面中給標(biāo)簽添加css樣式
語法:
GET /hotel/_search
{"query": {"match": {"FIELD": "TEXT"}},"highlight": {"fields": { "FIELD": {// 指定要高亮的字段"pre_tags": "<em>", // 用來標(biāo)記高亮字段的前置標(biāo)簽"post_tags": "</em>" // 用來標(biāo)記高亮字段的后置標(biāo)簽}}}
}
八.RestClient檢索查詢文檔
需要說明的是,這里的RestClient查詢文檔不同于上面使用的GetRequest查詢,GetRequest查詢是簡(jiǎn)單查詢,傳入的參數(shù)只限制以下幾個(gè):
1.match_all查詢
@Test
void testMatchAll() throws IOException {SearchRequest request = new SearchRequest("hotel");request.source().query(QueryBuilders.matchAllQuery());SearchResponse response = client.search(request, RequestOptions.DEFAULT);SearchHits searchHits = response.getHits();long total = searchHits.getTotalHits().value;System.out.println("共搜索到" + total + "條數(shù)據(jù)");SearchHit[] hits = searchHits.getHits();for (SearchHit hit : hits) {String json = hit.getSourceAsString();HotelDoc hotelDoc = JSON.parseObject(json, HotelDoc.class);System.out.println("hotelDoc=" + hotelDoc);}}
可以把request.source()理解為查詢的整體,在source()下又有sort(),highlighter(),size(),from(),query()等等,這些方法均是已經(jīng)學(xué)習(xí)過的DSL查詢的query()的平級(jí),即RestAPI中其中構(gòu)建DSL是通過HighLevelRestClient中的resource()來實(shí)現(xiàn)的,其中包含了查詢、排序、分頁、高亮等所有功能
RestAPI中其中構(gòu)建查詢條件的核心部分是由一個(gè)名為QueryBuilders的工具類提供的,其中包含了各種查詢方法:
2.全文檢索查詢
全文檢索的match和multi_match查詢與match_all的API基本一致。==差別是查詢條件,也就是query的部分。==同樣是利用QueryBuilders提供的方法:
其實(shí)就是query里面的參數(shù)不同,對(duì)應(yīng)到j(luò)ava代碼中就是QueryBuilders調(diào)用的API不同而已
// 單字段查詢
QueryBuilders.matchQuery("字段", "字符串");
// 多字段查詢
QueryBuilders.multiMatchQuery("字符串", "字段1", "字段2");
3.精確查詢
精確查詢常見的有term查詢和range查詢
// 詞條查詢
QueryBuilders.termQuery("字段", "字符串");
// 范圍查詢
QueryBuilders.rangeQuery("字段").gte(xxx).lte(xxx);
4.復(fù)合查詢-boolean query
精確查詢常見的有term查詢和range查詢,由于在DSL語句中bool包含了多個(gè)屬性,故需要先創(chuàng)建一個(gè)BoolQueryBuilder對(duì)象,依次向?qū)ο笾刑砑訔l件屬性
// 創(chuàng)建布爾查詢
BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
// 添加must條件
boolQuery.must(QueryBuilders.termQuery("字段", "字符串"));
// 添加filter條件
boolQuery.filter(QueryBuilders.rangeQuery("字段").lte(xxx));
//添加must_not條件
boolQuery.mustNot((QueryBuilders.xxx);
5.排序和分頁
搜索結(jié)果的排序和分頁是與query同級(jí)的參數(shù),故改變source()調(diào)用的API即可
request.source().from(起始頁碼).size(每頁顯示條數(shù));request.source().sort("字段", SortOrder.ASC);//升序
6.高亮
request.source().highlighter(new HighlightBuilder() .field("字段") // 是否需要與查詢字段匹配 .requireFieldMatch(false)//不填寫pre_tags和post_tags屬性默認(rèn)為<em>標(biāo)簽
);
高亮的結(jié)果處理(其實(shí)就是獲取每一個(gè)hit里面的highlight值,就可以取出高亮字段中的值了):