提到搜索,我們先講一個和搜索相類似的名詞"查詢"。當然在不同領域內,他們的定義不盡相同,在我們的業務領域他們有什麼區別呢,我們來想一下我們整個業務的發展,信息化建設一開始主要提供的是查詢的服務。在沒有信息整合的時期,我們實現的是對單類信息的查詢,而且在查詢時我們也是明確數據項的。大家最常見的是系統界面會有多個表單,比如你要查的是人口信息,那麼有姓名的表單,性別的表單、民族的表單,你提供查詢請求後,系統會按照系統界面中的對應關係去後臺數據庫中去查詢相應的結果,在這裡查詢後端的數據庫大多也是關係型數據庫。大家知道關係型數據庫存儲的是結構化數據,所以我大致理解查詢就是基於結構化數據的查詢。而且大多數查詢是知道查詢項的,比如查"張三",一般明確的就是查姓名這個數據項。
在這裡提一下"索引"這個術語,索引在一般場景裡指將書刊中的內容分類摘錄,表明頁數,按一定次序排列的一種工具。而在數據庫場景中索引是一種單獨的、物理的對數據庫中一列或多列的值進行排序的一種存儲結構。舉例來說,書刊的例子如字典裡的按漢字筆畫查字這個工具,它就是筆畫索引。我們數出"二"這個字有兩筆,然後就可以去筆畫索引那裡去找兩筆的字,然後找到"二"後就可以找到"二"所在的字典頁碼了。
同樣的在數據庫中索引實現的原理也是一樣的。比如我們有年齡這麼一個字段,我們就可以按照年齡大小進行排序,然後構造年齡這個字段的索引。當我們查詢一個年齡為45歲的人時,數據庫會先去查年齡的索引,然後找到45歲所對應的的數據位置。可以看出索引的引入會大大加快數據查詢的速度,如果沒有索引,原則上你查一個年齡為45歲的人,後臺數據庫會進行全表掃描,逐條匹配,這樣的話不僅速度慢而且浪費了大量的數據庫性能。
簡單加上索引的關係數據庫查詢在業務前期滿足了我們基本的信息查詢需求,但隨著業務的發展、數據的融合、需求的深入,只是簡單的查詢已經遠遠不能滿足我們的實戰需求,比如我們要查"張某三"、或者我們不確定"張三"就是姓名還是別的什麼信息,更有甚者,我們想查"張三盜竊"這事有沒有。
這些需求我們原有的關係數據庫查詢就很難響應了,儘管可以做相關的改造和適配,但代價太大。其實在這裡我們的需求就不僅僅是查詢了,而是搜索的需求。我認為搜索與查詢最大的區別就是查詢非結構化數據的能力,搜索是能對非結構化數據進行處理的,就例如搜索"張三盜竊"這個信息。
這種能夠對非結構化數據(主要是文本數據)進行搜索的技術業界叫做全文檢索、全文檢索可以實現對非結構化數據的全文檢索功能。目前市面的百度、谷歌也是採用的這種技術。那麼全文檢索是怎麼實現這樣的功能的呢?
還是引入一個術語"倒排索引",為啥叫倒排索引?倒排索引和我們上文所提到的關係數據庫中的索引是不同的,因為它索引的方式和普通索引相比是轉置的,所以叫倒排索引。
還是舉例說明吧,不知道大家玩沒玩過詩詞的"飛花令",也就是前段時間大火的中國詩詞大會節目的一個項目。
比如出一個字"前",讓你背出所有帶"前"的詩句。這個其實有些難度,即使你會背很多詩詞,很多詩詞裡面有"前"這個詞,但你一時卻很難想起。這是為什麼呢,原因就在於你的大腦中沒有建立"倒排索引"。你大腦中建立的都是正向的索引,比如我提示你說"靜夜思,李白",你立即就能說出來"床前明月光"了。你的大腦中存有這個詩句,它的記憶方式如下圖:
可以看出,這是一個正向的索引,也就是從文檔到細化的信息,但你正向的索引是無法完成"飛花令"這個任務的。
那麼倒排索引是怎樣的呢,倒排索引就是拿細化的信息去索引整個文檔,在這裡就是拿具體的字詞去索引詩句了,也就是如下圖:
這樣的話我們就能輕鬆的由"前"這個索引找到對應的詩句了。
我們可以繼續維護這個索引,比如"日照香爐生紫煙,遙看瀑布掛前川"裡面就有"前"字,我們就把這個詩句也加到這個索引上去。這樣的詩句會越來越多,我們就建立了一個從文檔中字詞"前"到整個文檔《靜夜思》、《望廬山瀑布》的索引,這就叫倒排索引,好理解了吧。
當然如果你想揹帶有"月"字的詩句,那麼也要建立"月"字的倒排索引。在往上,如果你想揹帶有"明月"的詩句,那麼建立"明月"的倒排索引。這裡就有個問題,怎麼建立"明月"這個索引呢,我們知道"明月"是個有意義的詞語,所以我們得把"明月"看做一個整體,把"明月"從原詩句中提取出來,那麼我們就需要對原來的文檔做分詞處理。
分詞就是將連續的字序列按照一定的規範重新組合成詞序列的過程。我們知道,在英文的行文中,單詞之間是以空格作為自然分界符的,而中文只是字、句和段能通過明顯的分界符來簡單劃界,唯獨詞沒有一個形式上的分界符,雖然英文也同樣存在短語的劃分問題,不過在詞這一層上,中文比之英文要複雜得多、困難得多。分詞細研究下去知識很多在這裡不談。
我們繼續。
如果要求背既帶"前"字又帶"月"字的詩句呢。如果我們分別建立了"前"字和"月"字的倒排索引,
從圖中看,"前"字索引到了三個文檔詩句,"月"字也索引到了三個文檔詩句。如果我們要背既帶"前"字又帶"月"字的詩句,那麼只需要將上面的值做一下交集,就能找出來"床前明月光"這句詩了。
到這裡基本的原理就可以用"飛花令"簡單的描述清楚了。我們回過來看我們的業務需求,像搜索"張三",我們就可以將後臺數據項都進行倒排索引,這樣當前臺輸入"張三"時,後臺進行索引匹配,不管索引到那條數據的那個字段,都可以給你搜索出來,哪怕是一段案情描述中出現了"張三"這個詞。
再如搜索"張三盜竊",前端在收到"張三盜竊"這個搜索詞後,會先進行分詞操作,也就是把"張三盜竊"分成"張三"和"盜竊"兩個詞,然後分別去倒排索引中去尋找對應的數據。然後把"張三"匹配到的所有數據記錄和"盜竊"匹配到的數據記錄做交集,最終找到包含"張三盜竊"信息的數據。當然在這裡,"張三盜竊"可以是不連續的詞語。這樣就牽扯到匹配精確度的問題,比如"張三"和"盜竊"兩詞挨在一起的我們認為匹配最精確,"張三"和"盜竊"兩詞不挨在一起的稍弱一些,這些我們都可以設置。所以我們能看出,處理非結構化數據的全文檢索(搜索)的結果也和結構化數據的查詢是不一樣,搜索的結果是精確度匹配,是按照數據對請求匹配程度來響應的,而查詢一般就是精確匹配,是就是,不是就是不是。
現在流行的提供全文檢索能力的工具有elasticsearch,它提供了倒排索引、分詞等等的能力,現在許多的搜索類系統都在採用elasticsearch。有機會再跟大家詳細聊一聊elasticsearch,但現在還沒怎麼學習。