一、全文檢索與ES
(一)全文檢索
數據可以分為結構化數據和非結構化數據,比如說我們常用的sql語句就都是操作結構化數據,郵件等信息都是非結構化數據;
對於結構化數據的查詢可以使用sql語句進行查詢,速度較快;
對於非結構化數據的查詢可以把非結構化數據變成結構化數據:先根據空格進行字符串拆分,得到一個基礎的單詞列表。需要去掉標點符號、轉換大小寫、去除停用詞、重復的單詞只記錄一次,就得到了一個最終的單詞列表。然后基於單詞列表創建一個索引。查詢時直接查詢索引,找到關鍵詞后根據關鍵詞和文檔的對應關系,找到文檔列表。這個過程就是全文檢索的過程。
全文檢索的場景有:搜索引擎、站內搜索、系統文件搜索
全文檢索技術有:
Lucene:使用該技術需要對Lucene的API和底層架構非常了解,而且需要編寫大量的JAVA代碼
Solr:使用JAVA實現一個web應用,可以使用rest方式的http請求,進程遠程API調用
ElasticSearch(ES):可以使用rest方式的http請求,進行遠程API調用
全文檢索可以分為兩大步驟:創建索引和搜索索引
1、創建索引:
1)獲得原始文檔
原始文檔就是待搜索的文檔。
搜索引擎:原始文檔就是整個互聯網的網頁
電商搜索:數據庫中的商品數據
站內搜索:微博搜索,微博的數據。
如何獲得原始文檔:
搜索引擎:使用爬蟲程序
電商搜索:使用jdbc查詢數據庫
站內搜索:數據庫中的數據
2)需要對應每個原始文檔創建一個Document對象
Document對象是對原始文檔的封裝,需要使用Field對象保存原始文檔的屬性。例如:描述一個文件,需要有文件名稱、文件的內容、文件的路徑、文件的大小……
Field:叫做字段或者域。field由兩部分組成:名稱和內容
一個Document中可以有多個Field,不同的Document中可以有不同的Field。
3)分析文檔
field中包含文件的相關屬性,其中有些field是需要分詞的。
對需要分詞的field的內容進行分詞處理:
1)字符串拆分
2)去除標點符號
3)轉換大小寫
4)去除停用詞
最終得到一個單詞列表。單詞列表中每個單詞需要封裝成一個Term對象。
term中包含兩部分內容:term所在的field的名稱關鍵詞本身
基於Term創建索引。
注意:不是所有的field都需要分詞
4)創建索引
基於上一步得到的單詞列表創建索引,然后把索引和文檔以及關鍵詞和文檔的對應關系保存到磁盤。
索引庫中包含的內容:索引、文檔和索引與文檔的對應關系
2、搜索索引:
1)用戶交互接口:接收到用戶輸入的查詢內容。
2)把查詢內容封裝:封裝成一個Query對象,其中包含要查詢的內容以及要查詢的field。
3)根據關鍵詞到索引上查詢:迅速的定位到一個關鍵詞,根據關鍵詞和文檔對應關系找到文檔列表。
4)根據文檔的id列表取文檔對象:得到一個文檔列表,把文檔列表展示給用戶。
索引庫結構:索引庫分為索引域和文檔域,索引域里面存儲的是term字典和文檔的倒排索引,文檔域存儲的就是document。
(二)ElasticSearch
ElasticSearch是一款開源的高擴展分布式全文檢索引擎。其是使用JAVA開發並使用Lucene作為核心來實現所有索引和搜索功能的。
ES與Lucene對比:ES通過簡單的Restful API來隱藏Lucene的復雜性,從而讓全文檢索變得簡單。
ES與Solr對比:Solr是通過ZK進行分布式管理的,而ES自身帶的有分布式協調管理功能;Solr支持更多格式的數據,ES只支持Json格式的數據;Solr在傳統搜索應用中表現好於ES,但是在處理實時搜索應用時效率明顯低於ES,因為Solr創建索引時,會產生IO阻塞。
二、ES概念與架構
(一)概念
ES不僅僅是存儲,還會索引整個文檔的內容讓其可以進行搜索,在ES中,可以對文檔進行索引、搜索、排序、過濾等操作。
ES的概念有索引index、類型type、文檔document、字段field和映射mapping
1、索引index
索引可以看作是MySQL中的數據庫,一套數據可以使用一套索引。
一個索引由一個名字來標識,名字必須全部是小寫字母,當我們對這個索引中文檔進行索引、搜索、更新和刪除的時候,都需要使用這個名字。
2、類型type
類型類似與MySQL中的表,在一個索引中可以創建多個type。這個在es7及以后已經被廢棄,只有_doc。
3、文檔document
文檔類似MySQL中的一條數據,存儲的是具體的數據,存儲格式為Json
4、字段field
相當於MySQL中的字段。
5、映射mapping
mapping對處理數據方式和規則做的一些限制,如某個字段的數據類型、默認值、分析器、是否被索引等
(二)架構
ES的架構總體如上圖所示,從下到上分為網關、搜索引擎、四大組件、自動發現、通信和Restful API
1、Gateway網關
其作用是用來對數據進行持久化以及ES重啟后重新恢復數據。
es支持多種類型的gateway,有本地文件系統、分布式文件系統、Hadoop的HDFS等。
其存儲的信息包括索引信息、集群信息、mapping等
2、districted lucene directory搜索引擎
Gateway上層就是Lucene的分布式檢索框架。
ES是分布式的搜索引擎,雖然底層用的是Lucene,但是需要在每個節點上都運行Lucene進行相應的索引、查詢、更新等操作,所以需要做成一個分布式的運行框架來滿足業務需要。
3、四大組件模塊
districted lucene directory之上就是ES的四大模塊。
Index Model:索引模塊,對數據建立索引(通常是建立倒排索引)
Seacher Model:搜索模塊,就是對數據進行查詢搜索
Mapping Model:是數據映射與解析模塊,數據的每個字段可以根據建立的表結構通過mapping進行映射解析;如果沒有建立表結構,那么ES會根據數據類型來推測數據結構,並自動生成一個mapping,然后根據mapping進行解析
River Model:在es2.0之后被取消了,表示可以使用插件處理。例如可以通過一些自定義腳本將傳統數據庫的數據實時同步到es中。
4、自動發現Discovery Script
es集群中各個節點通過discovery相互發現的,默認使用的是Zen。es是一個基於p2p的系統,他先通過廣播尋找存在的節點,再通過多播協議來進行節點之間的通信,同時也支持點對點的交互。
es還可以支持多種script腳本語言,例如mvel、js、python等。
5、通信(Transport)
代表es內部節點或集群與客戶的交互方式,默認內部使用tcp協議進行交互,同時其還支持http協議,thrift、servlet、memcached、zeroMQ等通信協議。
節點間通信端口默認9300-9400
6、Restful接口
最上層就是ES暴漏給我們的訪問接口。
三、Restful訪問ES
使用Restful訪問ES的方式:curl X<VERB> <PROTOCOL>://<HOST>:<PORT>/<PATH>?<QUERY_STRING>d <BOD
其中VERB是請求方式(post、get....),PROTOCOL是協議(http、https......),HOST和PORT是es的ip和端口,PATH是API的終端路徑,QUERY_STRING表示任何可選的查詢字符串參數(例如@pertty將格式化的輸出JSON格式),BODY是一個Json格式的請求體。
1、創建索引和mapping映射(PUT ip:9200/test)
{ "settings":{ "index":{ "number_of_shards":"5", "number_of_replicas":"1" } }, "mappings":{ "_doc":{ "properties":{ "orderId":{ "type":"long", "store":true, "index":true }, "orderName":{ "type":"text", "store":true, "index":true, "analyzer":"standard" }, "content":{ "type":"text", "store":true, "index":true, "analyzer":"standard" } } } } }
也可以先設置索引,然后再設置mapping,設置mapping的路徑:post ip:9200/test/_doc/_mapping,參數與上面設置mapping的參數一樣
{ "_doc":{ "properties":{ "orderId":{ "type":"long", "store":true }, "orderName":{ "type":"text", "store":true, "index":true, "analyzer":"standard" }, "content":{ "type":"text", "store":true, "index":true, "analyzer":"standard" } } } }
這里需要特殊說明一下,如果是es5及以前的版本,type可以隨意定義,如果是es6,一個索引庫中只能有一個type,如果是es7,一個索引庫只能有一個type,且必須為“_doc”,其實Lucene是沒有type這個概念的。
對field屬性:
type:數據類型,如果是text,就一定會分詞,如果是keyword,就不會分詞
store:是否存儲原始數據,取決於是否要展示給用戶看,但是不影響分詞和索引
index:是否要把當前field的內容添加到索引中,如果分詞,就一定要索引,不分詞也可以索引(例如身份證號或訂單號)
analyzer:具體的分詞器,如果不寫,則使用默認分詞器
2、刪除索引
直接使用:delete ip:9200/test
3、document處理
對於文檔的操作,都是:ip:9200/test/_doc/123(其中test為index,_doc為type,123為document的id),post請求表示增改操作,delete請求表示刪除操作,get請求表示查詢操作。其中只有post請求需要傳遞參數,也就是設置document的參數。
{"orderId":123,"orderName":"123的測試訂單號","content":"修改隨便寫一個進行測試"}
四、查詢表達式(Query DSL)
1、查詢全部
請求地址:post ip:9200/_search 查詢可以添加索引或type進行查詢:ip:9200/test/_doc/123
請求參數:{"query":{"match_all":{}}}
2、term查詢
請求地址:post ip:9200/test/_doc/_search
請求參數:{"query":{"term":{"orderName":"123"}}}
3、queryString查詢
請求地址:post ip:9200/test/article/_search
請求參數:{"query":{"query_string":{"default_field":"orderName","query":"123"}}}
4、multi_match查詢
請求地址與上面一樣,參數可以設置多個field進行查詢:{"query":{"multi_match":{"query":"123","fields":["orderId", "orderName"]}}}
5、bool查詢
布爾過濾器,其是一個符合過濾器,可以結果多個其他過濾器作為參數,並將這些過濾器組合成各式各樣的布爾組合。
{ "bool":{ "must":[], "should":[], "must_not":[], "filter":[] } }
如上所示,一個布爾查詢由must、should、must_not、filter四部分組成。
must:必須為真,才會被匹配到
should:查詢列表中,只要有一個為真,就會被匹配到
must_not:所有必須都不為真,才會被匹配到
filter:對數據進行過濾,filter可以是多個
以下是示例:
{ "query":{ "bool":{ "must":{ "query_string":{ "query":"李四", "default_field":"name" } }, "should":{ "term":{ "address":"上海" } }, "filter":{ "query_string":{ "query":"男", "default_field":"sex" } } } } }
五、IK分詞器和ElasticScher集成使用
(一)使用標准分詞器存在的問題
在查詢數據時,在orderName中使用123就可以查詢成功,但是使用”測試“去查詢,就查詢不到數據。這是因為在創建索引的時候,使用了標准分詞器導致。
可以使用:post ip:9200/_analyze查看分詞結果,如下圖所示,可以看到,中文都被分成了一個一個漢字。
(二)使用IK分詞器
安裝IK分詞器,安裝地址:https://github.com/medcl/elasticsearch-analysis-ik/releases,這里需要注意一樣,ES的版本要和IK分詞器的版本對應,如果不對應,ES啟動不起來。
./elasticsearch-plugin install https://github.com/medcl/elasticsearch-analysis-ik/releases/download/v6.2.4/elasticsearch-analysis-ik-6.2.4.zip
版本不一致啟動出現的異常信息
訪問IK分詞器查看分詞效果:post ip:9200/_analyze,里面只需要傳入analyzer和text,其中text為測試分詞的文本,analyzer在IK分詞器中有ik_smart和ik_max_word兩種,分別是最少切分和最細力度切分。
然后使用IK分詞器創建索引:"analyzer":"ik_max_word",然后用中文詞組搜索,就可以搜索到了。