各位知道,搜索程序通常由索引鏈及搜索組件組成。數據庫
索引鏈功能的實現須要按照幾個獨立的步驟依次完成:檢索原始內容、根據原始內容來建立對應的文檔、對建立的文檔進行索引。編程
搜索組件用於接收用戶的查詢請求並返回相應結果,通常由用戶接口、構建可編程查詢語句的方法、查詢語句執行引擎及結果展現組件組成。瀏覽器
著名的開源程序Lucene是爲索引組件,它提供了搜索程序的核心索引和搜索模塊,例如圖中的「Index」及下面的部分;而ElasticSearch則更像一款搜索組件,它利用Lucene進行文檔索引,並向用戶提供搜索組件,例如「Index」上面的部分。兩者結合起來組成了一個完整的搜索引擎。緩存
咱們先說索引組件。服務器
索引是一種數據結構,它容許對存儲在其中的單詞進行快速隨機訪問。網絡
當須要從大量文本中快速檢索文本目標時,必須首先將文本內容轉換成可以進行快速搜索的格式,以創建針對文本的索引數據結構,此即爲索引過程。數據結構
它一般由邏輯上互不相關的幾個步驟組成app
第一步:獲取內容。dom
過網絡爬蟲或蜘蛛程序等來蒐集及界定須要索引的內容。curl
Lucene並不提供任何獲取內容的組件,所以,須要由其它應用程序負責完成這一功能,例如著名的開源爬蟲程序Solr、Nutch、Grub及Aperture等。
必要時,還能夠自行開發相關程序以高效獲取自有的特定環境中的數據。獲取到的內容須要創建爲小數據塊,即文檔(Document)。
第二步:創建文檔。
獲取的原始內容須要轉換成專用部件(文檔)才能供搜索引擎使用。
通常來講,一個網頁、一個PDF文檔、一封郵件或一條日誌信息能夠做爲一個文檔。文檔由帶「值(Value)」的「域(Field)」組成,例如標題(Title)、正文(body)、摘要(abstract)、做者(Author)和連接(url)等。
不過,二進制格式的文檔處理起來要麻煩一些,例如PDF文件。
對於創建文檔的過程來講有一個常見操做:向單個的文檔和域中插入加權值,以便在搜索結果中對其進行排序。權值可在索引操做前靜態生成,也可在搜索期間才動態生成。權值決定了其搜索相關度。
第三步:文檔分析。
搜索引擎不能直接對文本進行索引,確切地說,必須首先將文本分割成一系列被稱爲語彙單元(token)的獨立原子元素,此過程即爲文檔分析。
每一個token大體能與天然語言中的「單詞」對應起來,文檔分析就是用於肯定文檔中的文本域如何分割成token序列。
文檔分析中要解決的問題包括如何處理鏈接一體的各個單詞、是否須要語法修正(例如原始內容存在錯別字)、是否須要向原始token中插入同義詞(例如laptop和notebook)、是否須要將大寫字符統一轉換爲小寫字符,以及是否將單數和複數格式的單詞合併成同一個token等。
這一般須要詞幹分析器等來完成此類工做,Lucene提供了大量內嵌的分析器,也支持用戶自定義分析器,甚至聯合Lucene的token工具和過濾器建立自定義的分析鏈。
第四步、文檔索引
在索引步驟中,文檔將被加入到索引列表。事實上,Lucene爲此僅提供了一個很是簡單的API,然後自行內生地完成了此步驟的全部功能。
接下來,咱們說搜索組件。
索引處理就是從索引中查找單詞,從而找到包含該單詞的文檔的過程。搜索質量主要由查準率(Precision)和查全率(Recall)兩個指標進行衡量。
查準率用來衡量搜索系列過濾非相關文檔的能力,而查全率用來衡量搜索系統查找相關文檔的能力。
另外,除了快速搜索大量文本和搜索速度以後,搜索過程還涉及到了許多其它問題,例如單項查詢、多項查詢、短語查詢、通配符查詢、結果ranking和排序,以及友好的查詢輸入方式等。這些問題的解決,一般須要多個組件協做完成。
搜索組件主要由如下幾個部分組成:
一、用戶搜索界面
UI(User Interface)是搜索引擎的重要組成部分,用戶經過搜索引擎界面進行搜索交互時,他們會提交一個搜索請求,該請求須要先轉換成合適的查詢對象格式,以便搜索引擎能執行查詢。
二、創建查詢
用戶提交的搜索請求一般以HTML表單或Ajax請求的形式由瀏覽器提交到搜索引擎服務器,所以,須要事先由查詢解析器一類的組件將這個請求轉換成搜索引擎使用的查詢對象格式。
三、搜索查詢
當查詢請求創建完成後,就須要查詢檢索索引並返回與查詢語句匹配的並根據請求排好序的文檔。
搜索查詢組件有着複雜的工做機制,它們一般根據搜索理論模型執行查詢操做。
常見的搜索理論模型有純布爾模型、向量空間模型及機率模型三種。Lucene採用了向量空間模型和純布爾模型。
四、展示結果
查詢得到匹配查詢語句並排好序的文檔結果集後,須要用直觀、經濟的方式爲用戶展示結果。
UI也須要爲後續的搜索或操做提供清晰的嚮導,如完善搜索結果、尋找與匹配結果類似的文檔、進入下一頁面等。
本節的最後簡單說一下Lucene。
Lucene是一款高性能的、可擴展的信息檢索(IR)工具庫,是由Java語言開發的成熟、自由開源的搜索類庫,基於Apache協議受權。
Lucene只是一個軟件類庫,若是要發揮Lucene的功能,還須要開發一個調用Lucene類庫的應用程序。
文檔是Lucene索引和搜索的原子單位,它是包含了一個或多個域的容器,而域的值則是真正被搜索的內容。每一個域都有其標識名稱,一般爲一個文本值或二進制值。
將文檔加入索引中時,須要首先將數據轉換成Lucene能識別的文檔和域,域值是被搜索的對象。
例如,用戶輸入搜索內容「title:elasticsearch」時,則表示搜索「標題」域值中包含單詞「elasticsearch」的全部文檔。
默認狀況下,全部文檔都沒有加權值,或者說其加權因子都爲1.0。
經過改變文檔的加權因子,能夠指示Lucene在計算相關性時或多或少地考慮到該文檔針對索引中其它文檔的重要程度,從而可以將有着較大加權因子的文檔排列在較前的位置。
除了對文檔進行加權,Lucene還支持對域進行加權操做。
ElasticSearch(簡稱ES)是一個基於Lucene構建的開源、分佈式、RESTful的全文本搜索引擎。
不過,ElasticSearch卻也不只只是一個全文本搜索引擎,它仍是一個分佈式實時文檔存儲,其中每一個field均是被索引的數據且可被搜索;也是一個帶實時分析功能的分佈式搜索引擎,而且可以擴展至數以百計的服務器存儲及處理PB級的數據。
如前所述,ElasticSearch在底層利用Lucene完成其索引功能,所以其許多基本概念源於Lucene。
咱們先說說ES的基本概念。
索引(Index)
ES將數據存儲於一個或多個索引中,索引是具備相似特性的文檔的集合。類比傳統的關係型數據庫領域來講,索引至關於SQL中的一個數據庫,或者一個數據存儲方案(schema)。
索引由其名稱(必須爲全小寫字符)進行標識,並經過引用此名稱完成文檔的建立、搜索、更新及刪除操做。一個ES集羣中能夠按需建立任意數目的索引。
類型(Type)
類型是索引內部的邏輯分區(category/partition),然而其意義徹底取決於用戶需求。所以,一個索引內部可定義一個或多個類型(type)。通常來講,類型就是爲那些擁有相同的域的文檔作的預約義。
例如,在索引中,能夠定義一個用於存儲用戶數據的類型,一個存儲日誌數據的類型,以及一個存儲評論數據的類型。類比傳統的關係型數據庫領域來講,類型至關於「表」。
文檔(Document)
文檔是Lucene索引和搜索的原子單位,它是包含了一個或多個域的容器,基於JSON格式進行表示。
文檔由一個或多個域組成,每一個域擁有一個名字及一個或多個值,有多個值的域一般稱爲「多值域」。每一個文檔能夠存儲不一樣的域集,但同一類型下的文檔至應該有某種程度上的類似之處。
映射(Mapping)
ES中,全部的文檔在存儲以前都要首先進行分析。用戶可根據須要定義如何將文本分割成token、哪些token應該被過濾掉,以及哪些文本須要進行額外處理等等。
另外,ES還提供了額外功能,例如將域中的內容按需排序。事實上,ES也能自動根據其值肯定域的類型。
接下去再說說ES Cluster相關的一些概念。
集羣(Cluster)
ES集羣是一個或多個節點的集合,它們共同存儲了整個數據集,並提供了聯合索引以及可跨全部節點的搜索能力。
多節點組成的集羣擁有冗餘能力,它能夠在一個或幾個節點出現故障時保證服務的總體可用性。
集羣靠其獨有的名稱進行標識,默認名稱爲「elasticsearch」。節點靠其集羣名稱來決定加入哪一個ES集羣,一個節點只能屬一個集羣。
若是不考慮冗餘能力等特性,僅有一個節點的ES集羣同樣能夠實現全部的存儲及搜索功能。
節點(Node)
運行了單個實例的ES主機稱爲節點,它是集羣的一個成員,能夠存儲數據、參與集羣索引及搜索操做。
相似於集羣,節點靠其名稱進行標識,默認爲啓動時自動生成的隨機Marvel字符名稱。
用戶能夠按須要自定義任何但願使用的名稱,但出於管理的目的,此名稱應該儘量有較好的識別性。
節點經過爲其配置的ES集羣名稱肯定其所要加入的集羣。
分片(Shard)和副本(Replica)
ES的「分片(shard)」機制可將一個索引內部的數據分佈地存儲於多個節點,它經過將一個索引切分爲多個底層物理的Lucene索引完成索引數據的分割存儲功能,這每個物理的Lucene索引稱爲一個分片(shard)。
每一個分片其內部都是一個全功能且獨立的索引,所以可由集羣中的任何主機存儲。建立索引時,用戶可指定其分片的數量,默認數量爲5個。
Shard有兩種類型:primary和replica,即主shard及副本shard。
Primary shard用於文檔存儲,每一個新的索引會自動建立5個Primary shard,固然此數量可在索引建立以前經過配置自行定義,不過,一旦建立完成,其Primary shard的數量將不可更改。
Replica shard是Primary Shard的副本,用於冗餘數據及提升搜索性能。
每一個Primary shard默認配置了一個Replica shard,但也能夠配置多個,且其數量可動態更改。ES會根據須要自動增長或減小這些Replica shard的數量。
ES集羣可由多個節點組成,各Shard分佈式地存儲於這些節點上。
ES可自動在節點間按須要移動shard,例如增長節點或節點故障時。簡而言之,分片實現了集羣的分佈式存儲,而副本實現了其分佈式處理及冗餘功能。
下面說說ES系統及插件。
ES依賴於JDK,使用Oracke JDK或OpenJDK都可。
JDK在不一樣平臺的安裝方式各異,具體方法這裏再也不介紹。ES的安裝也很是容易,一般只須要簡單修改其配置文件中的集羣名稱,並啓動服務便可,這裏再也不贅述。
ElasticSearch在設計上支持插件式體系結構,用戶可根據須要經過插件來加強ElasticSearch的功能。
目前,經常使用的經過插件擴展的功能包括添加自定義映射類型、自定義分析器、本地腳本、自定義發現方式等等。
安裝及移除插件
插件的安裝有兩種方式:直接將插件放置於plugins目錄中,或經過plugin腳本進行安裝。
Marvel、BigDesk及Head這三個是較爲經常使用的插件。
ElasticSearch提供了易用但功能強大的RESTful API以用於與集羣進行交互,這些API大致可分爲以下四類:
(1) 檢查集羣、節點、索引等健康與否,以及獲取其相關狀態與統計信息;
(2) 管理集羣、節點、索引數據及元數據;
(3) 執行CRUD操做及搜索操做;
(4) 執行高級搜索操做,例如paging、filtering、scripting、faceting、aggregations及其它操做;
下面簡單說一說ES中的數據查詢。
Query API是ElasticSearch的API中較大的一部分,基於Query DSL(JSON based language for building complex queries),可完成諸多類型查詢操做,例如simple term query, phrase, range, boolean, fuzzy, span, wildcard, spatial等簡單類型查詢、組合簡單查詢類型爲複雜類型查詢,以及文檔過濾等。
另外,查詢執行過程一般要分紅兩個階段,分散階段及合併階段。
分散階段是向所查詢的索引中的全部shard發起執行查詢的過程,合併階段是將各shard返回的結果合併、排序並響應給客戶端的過程。
向ElasticSearch發起查詢操做有兩種方式:一是經過RESTful request API傳遞查詢參數,也稱「query-string」;另外一個是經過發送REST request body,也稱做JSON格式。
經過發送request body的方式進行查詢,能夠經過JSON定義查詢體編寫更具表現形式的查詢請求。訪問ElasticSearch的search API須要經過_search端點進行。例如,向students索引起起一個空查詢。
~]$ curl -XGET 'localhost:9200/students/_search?pretty'
上面的查詢命令也可改寫爲帶request body的格式,其等同效果的命令以下。
~]$ curl -XGET 'localhost:9200/students/_search?pretty' -d ' { "query": { "match_all": { } } }'
此命令所示的查詢語句是ElasticSearch提供的JSON風格的域類型查詢語言,也即所謂的Query DSL。
上面的命令中,「query」參數給出了查詢定義,match_all給出了查詢類型,它表示返回給定索引的全部文檔。
除了query參數以外,還能夠額外指定其它參數來控制搜索結果,例如「size」參數可定義返回的文檔數量(默認爲10),而「from」參數可指定結果集中要顯示出的文檔的起始偏移量(默認爲0),「sort」參數可指明排序規則等。
ElasticSearch的大多數search API(除了Explain API)都支持多索引(mutli-index)和多類型(multi-type)。若是不限制查詢時使用的索引和類型,查詢請求將發給集羣中的全部文檔。
ElasticSearch會把查詢請求並行發給全部shard的主shard或某一副本shard,將返回的結果集中的前10返回給用戶。
不過,若是是想向某一或某些個索引的某一或某些類型發起查詢請求,可經過指定查詢的URL進行。
/_search:搜索全部索引的全部類型; /students/_search:搜索students索引的全部類型; /students,tutors/_search:搜索students和tutors索引的全部類型; /s*,t*/_search:搜索名稱以s和t開頭的全部索引的全部類型; /students/class1/_search:搜索students索引的class1類型; /_all/class1,class2/_search:搜索全部索引的class1和class2類型;
索引一個文檔時,Elasticsearch會取得其全部域的全部值,並將其鏈接起來合併爲一個大字符串,其被索引爲一個特殊域_all。
在某次查詢中,若是在query-string中未指定查詢的域,則使用_all域進行查詢。
下面四個查詢的功用會有所不一樣。前兩個在_all域中搜索,然後兩個將會在class域上作精確搜索。
GET /_search?q="Huashan" GET /_search?q="Huashan Pai" GET /_search?q=class:"Huashan Pai" GET /_search?q=class:"Huahan"
須要注意的是,文檔中每一個域的值可能會存儲爲特定類型,而非字符串類型,所以,_all域的索引方式與特域的索引方式未必徹底相同。
文檔中,域的數據存儲時支持「string」、「numbers」、「Booleans」和「dates」幾種類型,不一樣類型的數據在索引時是略有區別的。
在建立文檔時,Elasticsearch會經過檢查域的值來動態爲其建立mapping,可經過Mapping API來查看type的mapping,其訪問端點是_mapping。
下面,咱們聊一個麻煩一點的問題,ES的精確值、full-text及倒排索引。
ES的數據可被廣義的分爲兩種類型:「types:exect」和「full-text」。
精確值(Exact values)就是指數據不曾加工過的原始值,而Full-text則用於引用文本中的數據。
在查詢中,精確值是很容易進行搜索的,但full-text則須要判斷文檔在「多大程度上」匹配查詢請求,換句話講,即須要評估文檔與給定查詢的相關度(relevant)。
所以,所謂的full-text查詢一般是指在給定的文本域內部搜索指定的關鍵字,但搜索操做該須要真正理解查詢者的目的,例如:
(1) 搜索「UK」應該返回包含「United Kingdom」的相關文檔;
(2) 搜索「jump」應該返回包含「JUMP」、「jumped」、「jumps」、「jumping」甚至是「leap」的文檔;
(3) 搜索「johnny walker」應該匹配包含「Johnnie Walker」的文檔;
爲了完成此類full-text域的搜索,ES必須首先分析文本並將其構建成爲倒排索引(inverted index),倒排索引由各文檔中出現的單詞列表組成,列表中的各單詞不能重複且須要指向其所在的各文檔。
所以,爲了建立倒排索引,須要先將各文檔中域的值切分爲獨立的單詞(也稱爲term或token),然後將之建立爲一個無重複的有序單詞列表。這個過程稱之爲「分詞(tokenization)」。
其次,爲了完成此類full-text域的搜索,倒排索引中的數據還需進行「正規化(normalization)」爲標準格式,才能評估其與用戶搜索請求字符串的類似度。
例如,將全部大寫字符轉換爲小寫,將複數統一單數,將同義詞統一進行索引等。
另外,執行查詢以前,還須要將查詢字符串按照同與索引過程的同種格式進行「正規化(normalization)」。
這裏的「分詞」及「正規化」操做也稱爲「分析(analysis)」。
Analysis過程由兩個步驟的操做組成:首先將文本切分爲terms(詞項)以適合構建倒排索引,其次將各terms正規化爲標準形式以提高其「可搜索度」。這兩個步驟由分析器(analyzers)完成。
一個分析器一般須要由三個組件構成:字符過濾器(Character filters)、分詞器(Tokenizer)和分詞過濾器(Token filters)組成。
字符過濾器:在文本被切割以前進行清理操做,例如移除HTML標籤,將&替換爲字符等;
分詞器:將文本切分爲獨立的詞項;簡單的分詞器一般是根據空白及標點符號進行切分;
分詞過濾器:轉換字符(如將大寫轉爲小寫)、移除詞項(如移除a、an、of及the等)或者添加詞項(例如,添加同義詞);
Elasticsearch內置了許多字符過濾器、分詞器和分詞過濾器,用戶可按需將它們組合成「自定義」的分析器。
當然,建立倒排索引時須要用到分析器,但傳遞搜索字符串時也可能須要分析器,甚至還要用到與索引建立時相同的分析器才能保證單詞匹配的精確度。
執行full-text域搜索時,須要用到分析器,但執行精確值搜索時,查詢過程不會分析查詢字符串而是直接進行精確值匹配。
關於ES,咱們最後說一說Queries and Filters。
儘管統一稱之爲query DSL,事實上Elasticsearch中存在兩種DSL:查詢DSL(query DSL)和過濾DSL(filter DSL)。
查詢子句和過濾子句的天然屬性很是相近,但在使用目的上略有區別。
簡單來說,當執行full-text查詢或查詢結果依賴於相關度分值時應該使用查詢DSL,當執行精確值(extac-value)查詢或查詢結果僅有「yes」或「no」兩種結果時應該使用過濾DSL。
Filter DSL計算及過濾速度較快,且適於緩存,所以可有效提高後續查詢請求的執行速度。
而query DSL不只要查找匹配的文檔,還須要計算每一個文件的相關度分值,所以爲更重量級的查詢,其查詢結果不會被緩存。
不過,得益於倒排索引,一個僅返回少許文檔的簡單query或許比一個跨數百萬文檔的filter執行起來並得顯得更慢。
Elasticsearch支持許多的query和filter,但最經常使用的也不過幾種。
Filter DSL中常見的有term Filter、terms Filter、range Filter、exists and missing Filters和bool Filter。
而Query DSL中常見的有match_all、match 、multi_match及bool Query。鑑於時間關係,這裏再也不細述,朋友們可參考官方文檔學習。
Queries用於查詢上下文,而filters用於過濾上下文,不過,Elasticsearch的API也支持此兩者合併運行。
組合查詢可用於合併查詢子句,組合過濾用於合併過濾子句,然而,Elasticsearch的使用習慣中,也常會把filter用於query上進行過濾。不過,不多有機會須要把query用於filter上的。