天天看點

《深入了解Elasticsearch(原書第2版)》——第1章 Elasticsearch簡介 1.1 Apache Lucene簡介

本節書摘來自華章計算機《深入了解elasticsearch(原書第2版)》一書中的第1章,第1.1節,作者 [美]拉斐爾·酷奇(rafal ku)馬雷克·羅戈任斯基(marek rogoziski),張世武 餘洪淼 商旦 譯,更多章節内容可以通路雲栖社群“華章計算機”公衆号檢視。

我們希望讀者通過閱讀本書來擷取和拓展關于elasticsearch的基本知識。假設讀者已經知道如何使用elasticsearch進行單次或批量索引建立,如何發送請求檢索感興趣的文檔,如何使用過濾器縮減檢索傳回文檔的數量,以及使用切面/聚合(faceting/aggregation)機制來計算資料的一些統計量。不過,在接觸elasticsearch提供的各種令人激動的功能之前,希望讀者能對apache lucene有一個快速的了解,因為elasticsearch使用開源全文檢索庫lucene進行索引和搜尋。此外,我們還希望讀者能了解elasticsearch的一些基礎概念。為了加速我們的學習,需要牢記這些基礎知識,當然,這并不難掌握。同時,我們也需要確定讀者能按elasticsearch所需要的那樣正确地了解lucene。到本章結束為止,将涵蓋以下内容:

apache lucene是什麼

lucene的整體架構

文本分析過程是如何實作的

apache lucene的查詢語言及其使用

elasticsearch的基本概念

elasticsearch内部是如何通信的

為了全面了解elasticsearch的工作原理,尤其是索引和查詢處理環節,對apache lucene的了解顯得至關重要。揭開elasticsearch神秘的面紗,你會發現它在内部使用apache lucene建立索引,同時也使用apache lucene進行搜尋。在接下來的幾頁裡,将向讀者展示apache lucene的基本概念,特别是那些從來沒有使用過lucene的讀者們。

讀者也許會好奇,為什麼elasticsearch的創始人決定使用apache lucene而不是開發一個自己的全文檢索庫。對于這個問題,筆者并不是很确定,畢竟我們不是這個項目的創始人,我們猜想是因為lucene的以下特點而得到了創始人的青睐:成熟,高性能,可擴充,輕量級以及強大的功能。lucene核心可以建立為單個java庫檔案,并且不依賴第三方代碼,使用者可以使用它提供的各種所見即所得的全文檢索功能進行索引和搜尋操作。當然,lucene還有很多擴充,它們提供了各種各樣的功能,例如多語言處理、拼寫檢查、高亮顯示等。如果不需要這些額外的特性,可以下載下傳單個的lucene core庫檔案,直接在應用程式中使用它。

盡管我們可以直接探讨apache lucene架構的細節,但是有些概念還是需要提前了解的,以便于更好地了解lucene的架構,它們包括:

文檔(document):索引與搜尋的主要資料載體,它包含一個或多個字段,存放将要寫入索引的或将從索引搜尋出來的資料。

字段(field):文檔的一個片段,它包括字段的名稱和字段的内容兩個部分。

詞項(term):搜尋時的一個機關,代表了文本中的一個詞。

詞條(token):詞項在字段文本中的一次出現,包括詞項的文本、開始和結束的偏移以及詞條類型。

apache lucene将寫入索引的所有資訊組織為反向索引(inverted index)的結構形式。反向索引是一種将詞項映射到文檔的資料結構,它與傳統的關系資料庫的工作方式不同。你可以認為反向索引是面向詞項的而不是面向文檔的。我們來看看簡單的反向索引是什麼樣的。例如,假設我們有一些隻包含title字段的文檔,如下所示:

elasticsearch server(文檔1)

mastering elasticsearch(文檔2)

apache solr 4 cookbook(文檔3)

這些文檔索引好以後,可簡略地顯示如下圖:

《深入了解Elasticsearch(原書第2版)》——第1章 Elasticsearch簡介 1.1 Apache Lucene簡介

正如你所見,每個詞項指向該詞項所出現過的文檔數。這種索引組織方式允許快速有效的搜尋操作,例如基于詞項的查詢。除了詞項本身以外,每個詞項有一個與之關聯的計數(即文檔頻率),該計數可以告訴lucene這個詞項在多少個文檔中出現過。

每個索引由多個段(segment)組成,每個段寫入一次但是查詢多次。索引期間,一個段建立以後不再修改。例如,文檔被删除以後,删除資訊單獨儲存在一個檔案中,而段本身并沒有被修改。

多個段将會在段合并(segments merge)階段被合并在一起。或者強制執行段合并,或者由lucene的内在機制決定在某個時刻執行段合并,合并後段的數量更少,但是更大。段合并非常耗費i/o,合并期間有些不再使用的資訊将被清理掉,例如,被删除的文檔。對于容納相同資料的索引,段的數量更少的時候搜尋速度更快。盡管如此,還是需要強調一下:因為段合并非常耗費i/o,請不要強制進行段合并,你隻需要仔細配置段合并政策,剩餘的事情lucene會自行完成。

當然,實際的lucene索引比前面提到的更複雜、更高深,除了詞項的文檔頻率和出現該詞項的文檔清單外,還包含其他附加資訊。在這裡我們會介紹一些索引中的附加資訊。了解這些資訊對我們很有幫助,盡管它們隻在lucene内部使用。

(1)norm

norm是一種與每個被索引文檔相關的因子,它存儲文檔的歸一化結果,被用于計算查詢的相關得分。norm基于索引時的文檔權重值(boost)計算得出,與文檔一起被索引存儲。使用norm可以讓lucene在建立索引時考慮不同文檔的權重,不過需要一些額外的磁盤空間和記憶體來索引和存儲norm資訊。

(2)詞項向量

詞項向量(term vector)是一種針對每個文檔的微型反向索引。詞項向量的每個維由詞項和出現頻率結對組成,還可以包括詞項的位置資訊。lucene和elasticsearch預設都禁用詞項向量索引,不過要實作某些功能,如關鍵詞高亮等需要啟用這個選項。

(3)倒排項格式

随着lucene 4.0的釋出,lucene引入了解碼器架構,允許開發者控制索引檔案寫入磁盤的格式,倒排項就是索引中可定制的部分之一。倒排項中可以存儲字段、詞項、文檔、詞項位置和偏移以及載荷(payload,一個在lucene索引中随意存放的位元組數組,可以包含任何我們需要的資訊)。針對不同的使用目的,lucene提供了不同的倒排項格式。比如,有一種優化後的格式是專門為高散列範圍字段如唯一辨別提供的。

(4)doc values

我們前面提到過,lucene索引是一種反向索引。不過,針對某些功能,如切面(faceting)或聚合(aggregation),這種反向索引架構就不是最佳選擇。這類功能通常需要操作文檔而不是詞項,lucene需要把索引翻轉過來構成正排索引才能完成這些功能所需要的計算。基于這些考慮,lucene引入了doc values和額外的資料結構來進行分組、排序和聚合。doc values存儲字段的正排索引。lucene和elasticsearch都允許我們通過配置來指定doc values的存儲實作方式。可選的存儲實作包括基于記憶體的、基于硬碟的,以及二者的混合。

讀者也許會好奇,文檔中的資料是如何轉化為反向索引的?查詢串又是怎麼轉換為可以用于搜尋的詞項的?這個轉換過程被稱為分析(analysis)。

文本分析由分析器來執行,它建立在分詞器(tokenizer)、過濾器(filter)及字元映射器(character mapper)之上。

lucene的分詞器用來将文本切割成詞條,詞條是攜帶各種額外資訊的詞項,這些資訊包括:詞項在原始文本中的位置,詞項的長度。分詞器工作的結果被稱為詞條流,因為這些詞條被一個接一個地推送給過濾器處理。

除了分詞器,過濾器也是lucene分析器的組成部分。過濾器數額可選,可以為零個、一個或多個,用于處理詞條流中的詞條。例如,它可以移除、修改詞條流中的詞條,甚至可以創造新的詞條。lucene中有很多現成的過濾器,你也可以根據需要實作新的過濾器。以下是一些過濾器的例子。

小寫過濾器:将所有詞條轉化為小寫。

ascii過濾器:移除詞條中所有非ascii字元。

同義詞過濾器:根據同義詞規則,将一個詞條轉化為另一個詞條。

多語言詞幹還原過濾器:将詞條的文本部分歸約到它們的詞根形式,即詞幹還原。當分析器中有多個過濾器時,會逐個處理,理論上可以有無限多個過濾器。

過濾器可以一個接一個地被調用,是以我們可以通過逐個添加多個過濾器的方式來獲得近乎無限的分析能力。

最後我們介紹字元映射器,它用于調用分詞器之前的文本預處理操作。字元映射器的一個例子就是html文本的去标簽處理。

也許讀者會好奇,lucene以及所有基于lucene的軟體是如何控制索引及查詢操作的?在索引期,lucene會使用你選擇的分析器來處理文檔中的内容,可以對不同的字段使用不同的分析器,例如,文檔的title字段與description字段就可以使用不同的分析器。

在檢索時,如果你使用了某個查詢分析器(query parser),那麼你的查詢串将會被分析。當然,你也可以選擇不分析資料。有一點需要牢記,elasticsearch中有些查詢會被分析,而有些則不會被分析。例如,字首查詢(prefix query)不會被分析,而比對查詢(match query)會被分析。

你還應該記住,索引期與檢索期的文本分析要采用同樣的分析器,隻有查詢(query)分詞出來的詞項與索引中詞項能比對上,才會傳回預期的文檔集。例如,如果在索引期使用了詞幹還原與小寫轉換,那麼在查詢期,也應該對查詢串做相同的處理,否則,查詢可能不會傳回任何結果。

elasticsearch提供的一些查詢類型(query type)支援apache lucene的查詢解析文法,是以,我們應該深入了解lucene的查詢語言。

在lucene中,一個查詢(query)通常被分割為詞項與操作符。lucene中的詞項可以是單個的詞,也可以是一個短語(用雙引号括起來的一組詞)。如果查詢被設定為要被分析,那麼預先標明的分析器将會對查詢中的所有詞項進行處理。

一個查詢也可以包含布爾操作符。布爾操作符連接配接多個詞項,使之構成從句(clause)。有以下這些布爾操作符。

and:它的含義是,文檔比對目前從句當且僅當and操作符左右兩邊的詞項都在文檔中出現。例如,我們想執行“apache and lucene”這樣的查詢,隻有同時包含“apache”和“lucene”這兩個詞項的文檔才會被傳回給使用者。

or:它的含義是,包含目前從句中任意詞項的文檔都被視為與該從句比對。例如,我們執行“apache or lucene”這樣的查詢,任意包含詞項“apache”或詞項“lucene”的文檔都會傳回給使用者。

not:它的含義是,與目前從句比對的文檔必須不包含not操作符後面的詞項。例如,我們執行“lucene not elasticsearch”這樣的查詢,隻有包含詞項“lucene”且不包含詞項“elasticsearch”的文檔才會被傳回給使用者。

除了前面介紹的那些操作符以外,我們還可以使用以下這些操作符。

+:它的含義是,隻有包含了“+”操作符後面詞項的文檔才會被認為與從句比對。例如,我們想查找那些必須包含“lucene”但是“apache”可出現可不出現的文檔,可執行如下查詢:“+lucene apache”。

–:它的含義是,與從句比對的文檔,不能出現“-”操作符後的詞項。例如,我們想查找那些包含了“lucene”但是不包含“elasticsearch”的文檔,可以執行如下查詢:“+lucene -elasticsearch”。

如果查詢中沒有出現前面提到過的任意操作符,那麼預設使用or操作符。

除了前面介紹的内容之外,有一件事情值得一提:可以使用圓括号對從句進行分組,以構造更複雜的從句,例如:

《深入了解Elasticsearch(原書第2版)》——第1章 Elasticsearch簡介 1.1 Apache Lucene簡介

就像elasticsearch的處理方式那樣,lucene中所有資料都存儲在字段(field)中,而字段又是文檔的組成機關。為了實作針對某個字段的查詢,使用者需要提供字段名稱,再加上冒号以及将要在該字段中執行查詢的從句。如果你想查詢所有在“title”字段中包含詞項“elasticsearch”的文檔,可執行以下查詢:

《深入了解Elasticsearch(原書第2版)》——第1章 Elasticsearch簡介 1.1 Apache Lucene簡介

也可以在一個字段中同時使用多個從句,例如,如果你想查找所有在“title”字段中同時包含詞項“elasticsearch”和短語“mastering book”的文檔,可執行如下查詢:

《深入了解Elasticsearch(原書第2版)》——第1章 Elasticsearch簡介 1.1 Apache Lucene簡介

當然,上面的查詢也可以寫成下面這種形式:

《深入了解Elasticsearch(原書第2版)》——第1章 Elasticsearch簡介 1.1 Apache Lucene簡介

除了使用簡單詞項和從句的正常字段查詢以外,lucene允許使用者使用修飾符(modifier)修改傳入查詢對象的詞項。毫無疑問,最常見的修飾符就是通配符(wildcard)。lucene支援兩種通配符:?和*。前者比對任意一個字元,而後者比對多個字元。

 請記住,出于對性能的考慮,通配符不能作為詞項的第一個字元出現。

除通配符之外,lucene還支援模糊(fuzzy and proximity)查詢,辦法是使用“~”字元以及一個緊随其後的整數值。當使用該修飾符修飾一個詞項時,意味着我們想搜尋那些包含該詞項近似詞項的文檔(是以這種查詢稱為模糊查詢)。~字元後的整數值确定了近似詞項與原始詞項的最大編輯距離。例如,當我們執行查詢writer~2,意味着包含詞項writer和writers的文檔都可以被視為與查詢比對。

當修飾符~用于短語時,其後的整數值用于告訴lucene詞項之間多大距離是可以接受的。例如,我們執行如下查詢:

《深入了解Elasticsearch(原書第2版)》——第1章 Elasticsearch簡介 1.1 Apache Lucene簡介

在title字段中包含mastering elasticsearch的文檔被視為與查詢比對,而包含mastering book elasticsearch的文檔則被認為不比對。而如果我們執行下面這個查詢:

《深入了解Elasticsearch(原書第2版)》——第1章 Elasticsearch簡介 1.1 Apache Lucene簡介

則這兩個文檔都被認為與查詢比對。

此外,還可以使用^字元并賦以一個浮點數對詞項權重(boosting),進而提高該詞項的重要程度。如果都被權重,則權重值較大的詞項更重要。預設情況下詞項權重為1。可以參考2.1節進一步了解什麼是權重值(boost value),以及其在文檔評分中的作用。

我們也可以使用方括号和花括号來建構範圍查詢。例如,我們想在一個數值類型的字段上執行一個範圍查詢,執行如下查詢即可:

《深入了解Elasticsearch(原書第2版)》——第1章 Elasticsearch簡介 1.1 Apache Lucene簡介

上面查詢的傳回文檔的price字段的值大于等于10.00并小于等于15.00。

當然,我們也可以在字元串類型的字段上執行範圍查詢(range query),例如:name:[adam to adria]

上面查詢的傳回文檔的name字段中,包含了按字典順序介于adam 和adria之間(包括adam和adria)的詞項。

如果想執行範圍查詢同時又想排除邊界值,則可使用花括号作為修飾符。例如,我們想查找price字段值大于等于10.00但小于15.00的文檔,可使用如下查詢:

《深入了解Elasticsearch(原書第2版)》——第1章 Elasticsearch簡介 1.1 Apache Lucene簡介

如果想執行一邊受限而另一邊不做限制的範圍查詢,例如,查找price字段值大于等于10.00的文檔,可使用如下查詢:

《深入了解Elasticsearch(原書第2版)》——第1章 Elasticsearch簡介 1.1 Apache Lucene簡介

很多應用場景中,也許你想搜尋某個特殊字元(這些特殊字元包括+、–、&&、||、!、(,)、{}、[]、^、"、~、*、?、:、、/),需要先使用反斜杠對這些特殊字元進行轉義。例如,你可能想搜尋abc"efg這個詞項,需要按如下方式處理:abc"efg