乾貨 |《從Lucene到Elasticsearch全文檢索實戰》拆解實踐
1、題記
2018年3月初,萌生了一個想法:對Elasticsearch相關的技術書籍做拆解閱讀,該想法源自非計算機領域紅火已久的【樊登讀書會】、得到的每天聽本書、XX拆書幫等。
目前市面上Elasticsearch的中文書籍就那麼基本,針對ES5.X以上的三本左右;國外翻譯有幾本,都是針對ES1.X,2.X版本,其中《深入理解Elasticsearch》還算比較經典。
拆書的目的:
- 1)梳理已有的Elasticsearch知識體系;
- 2)拾遺拉在角落的Elasticsearch知識點;
- 3)通過手敲動程式碼或命令列,在實踐中再次“溫故知新”,提前增加知識儲備,避免專案/產品實戰中的“臨陣抱佛腳”;
- 4)最大化的節省您的寶貴時間,讓您最快的時間吸取最精華的“乾貨”。
本次解讀是《從Lucene到Elasticsearch全文檢索實戰》。
2、本書梗概
作者是中科院碩士姚攀(90後)在讀研究生期間根據實習寫成CSDN部落格,最終成書。
- 該書1/4章節講解Lucence相關原理及實戰;
- 1/2章節講解Elasticsearch基本概念:叢集入門、搜尋分類詳解、聚合分析、Java API;
- 1/4章節講解Elasticsearch叢集管理、專案實戰、Hadoop實戰。
總體評價:
優點:
- 1)涵蓋了Elasticsearch相關的基本概念、基礎原理;
- 2)有兩個實戰專案分享;
缺點:
- 1)某些概念就只是有分類,沒有講解出不同分類的區別,不同分類的適用場景;
- 2)某些細節點涵蓋不全,偏理論,好多知識技術點,實戰中應用會有不同。
- 3)書基於Elasticsearch5.4.0講解,一些特性6.X已不適用。
3、核心知識點梳理
以下的DSL都是通過ElasticsearchV6.2.2版本試驗過的。
3.1 mget 一次獲取多個檔案。
1GET test_index/test_type/_mget
2{
3 "docs":[
4 {"_id":1},5 {"_id":3}
6 ]
7}複製程式碼
最小簡化版本:
1GET test_index/test_type/_mget
2{
3 "ids":[1,3]
4}複製程式碼
3.2 update更新
——新增、刪除、更新欄位
1POST test_index/test_type/1
2{
3 "no":1,4 "name":"賓士X100",5 "addr":"德國",6 "price":1000000,7 "tags" : ["red"]
8}複製程式碼
3.2.1 新增欄位
以下添加了新欄位tags,賦值為“red”。
1POST test_index/test_type/1/_update
2{
3 "script":"ctx._source.tags = \"red\""
4}複製程式碼
修改後結果為:
1{
2 "_index": "test_index",3 "_type": "test_type",4 "_id": "1",5 "_version": 6,6 "found": true,7 "_source": {
8 "no": 1,9 "name": "賓士X100",10 "addr": "德國",11 "price": 1000000,12 "tags": "red"
13 }
14}複製程式碼
3.2.2 刪除欄位
1POST test_index/test_type/1/_update
2{
3 "script":"ctx._source.remove(\"new_field\")"
4}複製程式碼
3.2.3 更新欄位-新增
1POST test_index/test_type/1/_update
2{
3 "script" : {
4 "source": "ctx._source.tags.add(params.tag)",5 "lang": "painless",6 "params" : {
7 "tag" : "blue"
8 }
9 }
10}複製程式碼
更新後結果如下:
1{
2 "_index": "test_index",5 "_version": 8,12 "tags": [
13 "red",14 "blue"
15 ]
16 }
17}複製程式碼
3.2.4 刪除欄位(if判定)
1POST test_index/test_type/1/_update
2{
3 "script" : {
4 "source": "if (ctx._source.tags.contains(params.tag)) { ctx.op = 'delete' } else { ctx.op = 'none' }",6 "params" : {
7 "tag" : "red"
8 }
9 }
10}複製程式碼
3.3 bulk批量請求的注意事項
- 每一行的結尾處都必須有換行符"n",最後一行也要有,換行符可以有效的分隔每行。
- 注意一次提交檔案的大小,整個批量請求需要被載入到請求節點的記憶體裡,所以請求越大,給其他請求可用的記憶體越小。
- 最佳bulk請求的大小,完全取決於伺服器的硬體、檔案的大小和複雜度以及索引和搜尋的負載。
3.4 併發修改檔案導致版本衝突的問題
以下是社群的問題,我認為更切合知識點。
線上的場景可能會對一個檔案同一秒進行併發修改,導致會出現個別的VersionConflictEngineException 異常,我猜測是併發upsert請求 可能存在先獲取到版本號的請求 比 後獲取到版本號的請求 執行慢或者執行晚導致的,
畢竟預設es不會對檔案操作加鎖。但是如在不做鎖機制的情況下處理這個問題呢。
解決方案(初步):
es版本控制有內部和外部兩種型別。預設情況下,es使用內部版本控制。
version_type=external的時候是外部值控制。在使用外部版本型別時,系統會檢查傳遞給索引請求的版本號是否大於當前儲存的檔案的版本,如果為true,則檔案將被索引並使用新的版本號。
如果提供的值小於或等於儲存檔案的版本號,則會發生版本衝突,索引操作將失敗。
1PUT /test_index/test_type/10?version=1520834740000&version_type=external
2{
3 "newadd":11,4 "test":"true"
5}複製程式碼
返回結果:
1{
2 "_index": "test_index",4 "_id": "10",5 "_version": 1520834740000,7 "_source": {
8 "newadd": 11,9 "test": "true"
10 }
11}複製程式碼
所以最簡單的實現方式就是每次更新使用當前==時間戳==作為版本號,
3.5 動態對映和靜態對映的區分
- 動態對映:檔案寫入ES中,它會根據欄位的型別自動識別,這種稱為:動態對映;
- 靜態對映:寫入資料之前對欄位的屬性進行手工設定。
3.6 text欄位的特殊性
- 不用於排序,很少用於聚合(termsAggrions除外,未來版本會徹底禁止text型別聚合操作)。
- 題外話:如果需要可以藉助 multi-fields.使用:keyword 型別。
- 官網解讀:
http://t.cn/R6jy9Z3,http://t.cn/RnKU4tG
3.7 資料型別儲存建議
對於數字型別的欄位,在滿足需求的情況下,要儘可能的選擇範圍小的數字型別。
3.8 過濾和搜尋的區別
- 過濾:只根據條件對檔案進行過濾,不計算評分;
- 搜尋:解決的是相關度的問題。
當使用者輸入一個查詢,Elasticsearch通過排序模型計算檔案和查詢關鍵詞之間的相關度,按照評分排序後返回最想關的檔案給使用者。
e
細化:Elasticsearch接受到關鍵詞以後到倒排索引中進行查詢,通過倒排索引中維護的倒排記錄表找到關鍵詞對應的檔案集合,然後做評分、排序、高亮處理,最終返回搜尋結果給使用者。
注意:ES是按照查詢和檔案的相關度進行排序的,預設按照評分降序排序。
3.9指定搜尋欄位的權重
1GET _search
2{
3 "query":{
4 "multi_match": {
5 "query": "美國",6 "fields": ["addr^5","name"]
7 }
8 }
9}複製程式碼
3.10 返回欄位中至少有一個非控制的檔案。
1GET _search
2{
3 "query":{
4 "exists":{
5 "field":"name"
6 }
7 }
8}複製程式碼
3.11 固定得分檢索
1GET /_search
2{
3 "query": {
4 "constant_score" : {
5 "filter" : {
6 "term" : { "addr.keyword" : "美國"}
7 },8 "boost" : 1.2
9 }
10 }
11}複製程式碼
返回結果:
1{
2 "took": 1,3 "timed_out": false,4 "_shards": {
5 "total": 32,6 "successful": 32,7 "skipped": 0,8 "failed": 0
9 },10 "hits": {
11 "total": 3,12 "max_score": 1.2,13 "hits": [
14 {
15 "_index": "test_index",16 "_type": "test_type",17 "_id": "5",18 "_score": 1.2,19 "_source": {
20 "no": 5,21 "name": "福特500",22 "addr": "美國",23 "price": 180000
24 }
25 },26 {
27 "_index": "test_index",28 "_type": "test_type",29 "_id": "6",30 "_score": 1.2,31 "_source": {
32 "no": 6,33 "name": null,34 "addr": "美國",35 "price": 180000
36 }
37 },38 {
39 "_index": "test_index",40 "_type": "test_type",41 "_id": "3",42 "_score": 1.2,43 "_source": {
44 "no": 3,45 "name": "福特300",46 "addr": "美國",47 "price": 300000
48 }
49 }
50 ]
51 }
52}複製程式碼
3.12 修改檔案得分檢索
藉助:function Score Query 實現。
3.13 獲取相似文章
1{
2 "query": {
3 "more_like_this": {
4 "fields": [
5 "title"
6 ],7 "like": "新時代的領路人",8 "min_term_freq": 1,9 "max_query_terms": 12
10 }
11 },12 "_source": "title",13 "from": 1000,14 "size": 5
15}複製程式碼
3.14 指令碼檢索
以下內容是6.X驗證的。5.X版本要把source改成inline。
1POST test_index/_search
2{
3 "query":{
4 "bool":{
5 "must":{
6 "script":{
7 "script":{
8 "source": "doc['price'].value > 100000",9 "lang":"painless"
10 }
11 }
12 }
13 }
14 }
15}複製程式碼
3.15 多欄位高亮
欄位高亮已經比較熟悉,有一種場景是:當我搜索title欄位的時候,我期望高亮:title、content、abstr如何做到呢?
通俗的講:不搜尋某個欄位,可以順帶高亮該欄位。
1POST test_index/test_type/_search
2{
3 "query":{
4 "match_phrase":{
5 "addr":"美國"
6 }
7 },8 "highlight": {
9 "require_field_match":false,10 "fields":{
11 "addr":{"pre_tags":["<strong>"],12 "post_tags":["</strong>"]
13 },14 "name":{"pre_tags":["<strong>"],15 "post_tags":["</strong>"]}
16 }
17 }
18}
1{
2 "took": 116,4 "_shards": {
5 "total": 5,6 "successful": 5,12 "max_score": 1.1143606,17 "_id": "6",18 "_score": 1.1143606,19 "_source": {
20 "no": 6,21 "name": "大片美國",23 "price": 180000
24 },25 "highlight": {
26 "name": [
27 "大片<strong>美</strong><strong>國</strong>"
28 ],29 "addr": [
30 "<strong>美</strong><strong>國</strong>"
31 ]
32 }
33 },34 {
35 "_index": "test_index",36 "_type": "test_type",37 "_id": "5",38 "_score": 0.5753642,39 "_source": {
40 "no": 5,41 "name": "福特500",42 "addr": "美國",43 "price": 180000
44 },45 "highlight": {
46 "addr": [
47 "<strong>美</strong><strong>國</strong>"
48 ]
49 }
50 },51 {
52 "_index": "test_index",53 "_type": "test_type",54 "_id": "3",55 "_score": 0.5753642,56 "_source": {
57 "no": 3,58 "name": "福特300",59 "addr": "美國",60 "price": 300000
61 },62 "highlight": {
63 "addr": [
64 "<strong>美</strong><strong>國</strong>"
65 ]
66 }
67 }
68 ]
69 }
70}複製程式碼
3.16 分片影響評分
Elasitcsearch 5.4 之後對於text型別的欄位,預設採用是BM25評分模型,而不是基於tf-idf的向量空間模型,評分模型的選擇可以通過similarity引數在對映中指出。
需要注意的是:ES在每個分片上單獨打分,分片的數量會影響打分的結果。
這個問題比較有趣的討論如下:https://elasticsearch.cn/question/2275
3.17 叢集統計
統計叢集的兩個方面資訊:
一:索引層面
- 分片數、儲存大小、記憶體使用情況;
二:節點層面
- 節點數量、節點角色、作業系統、JVM版本、記憶體、CPU、外掛資訊x-pack等。
1GET /_cluster/stats複製程式碼
4、核心工具推薦
工欲善其事必先利其器,好的工具能提升開發效率。
4.1 Luke 工具
1、功能介紹:
檢視Luncene、Solr、Elasitcsearch索引的GUI工具,方便開發和診斷。
2、核心功能點:
- 檢視分析欄位內容;
- 搜素索引;
- 執行索引維護;
- 從HDFS讀取索引;
- 將全部或者部分索引轉換為XML格式匯出。
- 測試自定義的Lucene分詞器。
3、工具地址:https://github.com/DmitryKey/luke
4、最新版本
- Upgrade to 7.2.0
- http://t.cn/RnKU9dz
5、注意Luke的版本要和Lucene一致。
4.2 Tika工具
1、簡介
Apache Tika是一個用於文字檢測和檔案內容提取的庫。
2、特點
Tika 可以檢測超過1000種不同型別的檔案,比如PPT、PDF、DOC、XLS,所有的檔案型別可以通過一個簡單的介面被解析。
3、應用
Tika廣泛應用於搜素引擎、內容分析、文字翻譯、數字管理等領域。
4、下載地址http://tika.apache.org/download.htm
5、擴充套件
如果有全文知識庫檢索的專案,可以考慮使用Tika對多種不同型別的檔案進行檔案解析。
5、小結
此為拆解的第一本書,印證了我之前說的,核心知識點在Elasticsearch官網檔案中都有更詳盡的英文解讀。
目前市面上沒有一本書能涵蓋全部的知識點。
書的目的多半是作者的一些學習、實踐積累,更多的知識還得靠實踐中總結、實踐、再總結。
“書寫是為了更好的思考”,與大家共勉!一起加油!
下一本書,緊張梳理中…..
推薦閱讀:
為什麼選擇 Spring 作為 Java 框架?
SpringBoot RocketMQ 整合使用和監控
上篇好文:
Elasticsearch實戰 | 必要的時候,還得空間換時間!