上一個系列尚未完結,我又來開新坑啦~java
接觸搜索/推薦相關工做,也有兩年了。工做裏對lucene的接觸很多,卻也不精。最近工做裏沒有那麼忙,所以想經過學習源碼的方式,來對lucene進行一個系統的學習。apache
此外,聽聞lucene源碼堪稱面對對象設計屆的典範,也想從中吸取一些代碼設計/開發方面的知識。最近總是感受本身寫的代碼有問題,想嘗試優化卻感受很是吃力,常常一頓操做下來提高的頗有限。後端
如下內容來自維基百科:數組
Lucene是一套用於全文檢索和搜索的開放源碼程序庫,由Apache軟件基金會支持和提供。Lucene提供了一個簡單卻強大的應用程序接口,可以作全文索引和搜索。Lucene是如今最受歡迎的免費Java信息檢索程序庫。微信
全文檢索(Full Text Retrieval)全文檢索是指以所有文本信息做爲檢索對象的一種信息檢索技術。最爲常見的全文檢索搜索引擎就是google和百度了,他們經過對互聯網上的全部網頁內容進行分析,索引,提供給咱們秒級的搜索體驗。其次,當前移動端各類APP,不少都內置了搜索功能,這些也是垂直領域的搜索實現。他們與google/百度的區別就是,只提供當前APP內信息的搜索,而不是互聯網上的全部網頁。markdown
假設有10篇文章,每一篇都有標題和正文。當咱們想找到正文中包含原子能的對應文章時,咱們應該怎麼作?架構
首先,最粗暴的辦法,咱們能夠順序讀取每一篇文章,逐個字符進行判斷,若是其中有連續的三個字符是 原子能,咱們就記錄下來這篇文章的標題,如此所有掃描一遍,咱們就完成了一次搜索。工具
這個方法是至關簡單粗暴,且有效的。在計算機性能十分強勁的狀況下,對於1G的文件進行搜索,均可以使用這個方法(Linux下的grep命令,常用的話應該知道即便在GB級別的文件作些簡單的搜索,一般性能也是能接受的)。oop
可是,數據量會遠大於1G,搜索的要求也更加複雜,不是簡單的字符串匹配,而是多種條件的組合。此時就須要全文搜索了。性能
像google這種搜索引擎,能夠在0.5s的時間,搜索到與"全文搜索引擎"相關的1230w結果,這顯然使用的不是順序的逐個字符對比,而是相似於lucene的全文搜索了。
lucene能作到在秒級對大量數據進行查詢,依賴的就是被稱之爲索引的結構。對於索引的理解,有不少現成的例子,好比在不少書籍後,都會提供一個關鍵詞到頁碼的映射,這就是一種索引,可讓咱們不用通讀整本書,就能找到本身關心的部分。
在《數學之美》這本書中,做者認爲全文檢索的本質就是布爾代數。隨着對全文檢索的逐漸深刻了解,愈來愈以爲這句話的精準,在全文檢索的索引/搜索階段,根本原理就是最簡單的布爾代數,剩下的只是工程實現的複雜度問題了。
lucene 目前已經在開發9.0版本了,整個工程分爲多個模塊,十分複雜。
在學習lucene源碼以前,我一直在想,應該以什麼路線去學習lucene,總不能隨機找一個類開始看吧,那樣怕是會陷入細節的汪洋大海中。
最初的想法是,從構建索引開始,走構建索引->寫入磁盤->搜索請求->query分析->相關性打分->返回結果這條路線,逐步學習。
後來忽然產生了一個大膽的想法,我想嘗試抽象全文檢索的本質,寫一個各方面都最簡單的全文檢索工具(最好只有一兩個類的那麼簡單),以後就這個工具的各個方面如何進化成lucene的對應模塊,各類缺陷lucene是如何改進的, 來進行lucene的學習。
這就是這節的標題lucene-beta的來源。
在個人預期中,這樣作應該會有兩個優勢:
public class LuceneBeta {
private static final Logger logger = LoggerFactory.getLogger(LuceneBeta.class);
public static void main(String[] args) {
LuceneBeta beta = new LuceneBeta();
String[] arr = new String[]{"原子能研究所", "原子彈威力很大"};
Map<Character, int[]> index = beta.build(arr);
int[] searchRet = beta.search('威', index);
System.out.println(Arrays.toString(searchRet));
}
/** * 對傳入的字符串數組進行字符級別的構建索引 */
public Map<Character, int[]> build(String[] arr) {
Set<Character> all = new HashSet<>();
for (String s : arr) {
for (char c : s.toCharArray()) {
all.add(c);
}
}
Map<Character, int[]> index = new HashMap<>();
for (Character c : all) {
int[] perContains = new int[arr.length];
for (int w = 0; w < arr.length; w++) {
if (arr[w].contains(String.valueOf(c))) {
perContains[w] = 1;
} else {
perContains[w] = 0;
}
}
index.put(c, perContains);
}
logger.info("build {} strings. indexed: ", arr.length);
for (Map.Entry<Character, int[]> e : index.entrySet()) {
logger.info("{} ==> {}", e.getKey(), Arrays.toString(e.getValue()));
}
return index;
}
/** * 查詢目標字符都在哪些字符串中出現過 */
public int[] search(char target, Map<Character, int[]> index) {
if (!index.containsKey(target)) {
return null;
}
int[] ints = index.get(target);
int[] tmp = new int[index.size()];
int j = 0;
for (int i = 0; i < ints.length; i++) {
if (ints[i] == 1) {
tmp[j] = i;
j++;
}
}
int[] ret = new int[j];
System.arraycopy(tmp, 0, ret, 0, j);
return ret;
}
}
複製代碼
說要簡單,那就要簡單到底,所有代碼70行。它實現了什麼功能呢?
在給定的一系列字符串中,能夠搜索某個字符出現的全部字符串編號
google能夠根據你給的關鍵字找到對應的網頁, 上面的代碼能夠根據你提供的關鍵字符,查找對應的字符串, 源碼已經開發了,就等融資上市了,我就是下一個google...
雖然上面的代碼極其簡單,可是爲了後續對應lucene的分析,我仍是要認真的概括其中的每個步驟。
上面的程序中,分爲兩個部分,即兩個方法build 和 search.
首先是build過程:
search過程
lucene 做爲一個成熟的開源軟件,其包括了多個模塊,其中最核心的是lucene.core包。其中又分爲如下幾個目錄:
其中:
本文實現了極簡版的lucene-beta, 固然不是爲了真的替代lucene。只是對全文搜索作一個簡單的抽象,用簡單的功能映射lucene優秀的實現. 逐一的去學習。
最後一個小節簡單的介紹了lucene.core包下的幾個目錄,後續的主要源碼學習,將以lucene-beta中的問題爲引導,分模塊的逐步進行。
lucene 源碼學習,正式開始啦~
完。
最後,歡迎關注個人我的公衆號【 呼延十 】,會不按期更新不少後端工程師的學習筆記。 也歡迎直接公衆號私信或者郵箱聯繫我,必定知無不言,言無不盡。
以上皆爲我的所思所得,若有錯誤歡迎評論區指正。
歡迎轉載,煩請署名並保留原文連接。
更多學習筆記見我的博客或關注微信公衆號 <呼延十 >------>呼延十