Lucene BooleanQuery相关算法
BooleanQuery對兩種不同查詢場景執行不同的算法:
場景1:
所有的子句都必須滿足,而且所有的子句里沒有嵌套BooleanQuery。
例:
a AND b AND c
上面語句表示要同時包含a,b,c三個字符(詞元)的文檔,假如現在索引里包含a的文檔有4,6,8;b的文檔有:2,4,6;c的文檔有:3,4,5,這個語句就是找出編號為4的這個文檔。
注:在倒排索引里存儲的包含某個詞元的文檔列表都是從小到大排列的。
初始狀態如下:
| a | b | c |
| -> 4 | -> 2 | -> 3 |
| 6 | 4 | 4 |
| 8 | 6 | 5 |
指針表示當前遍歷到哪個文檔
第一步:按照每個詞元文檔列表的第一個文檔對詞元排序。排序以后的狀態如下:
| b | c | a |
| -> 2 | -> 3 | -> 4 |
| 4 | 4 | 6 |
| 6 | 5 | 8 |
第二步:判斷第一個詞元(b)的當前遍歷文檔(2)是否小于最后一個詞元(a)的當前遍歷文檔(4)。如果小于,則表示第一個詞元的當前遍歷文檔不是符合的文檔,如果是符合的最后一個詞元的當前文檔應該和第一個詞元的相同。
第三步: 第一個詞元文檔位置(2)跳轉到最后一個詞元的當前遍歷的文檔(4),跳轉以后的狀態如下:
| b | c | a |
| 2 | -> 3 | -> 4 |
| -> 4 | 4 | 6 |
| 6 | 5 | 8 |
第四步: 將第一個詞元放到詞元列表最后,重置位置后狀態如下:
| c | a | b |
| -> 3 | -> 4 | 2 |
| 4 | 6 | -> 4 |
| 5 | 8 | 6 |
重復第二、三、四步,直到找到第一個詞元的當前遍歷文檔ID和最后一個詞元的相同,則這個文檔就是符合查詢要求的文檔。
這種場景的代碼實現在ConjunctionScorer類里,主要的代碼邏輯在方法doNext里:
while (more && first().doc() < last().doc()) { // find doc w/ all clauses
more = first().skipTo(last().doc()); // skip first upto last
scorers.addLast(scorers.removeFirst()); // move first to last
}
這里會不斷的判斷第一個詞元的當前文檔是否小于最后一個詞元的當前文檔,如果不相同,則第一個詞元的文檔跳轉到最后一個詞元的文檔位置。
排序是在第一次進去的時候在init方法里做的。
場景2:
除了上面第一種場景就是第二種場景,第一種場景因為排序的原因,不需要遍歷所有的文檔,第二種場景需要遍歷所有的文檔。
第二種場景的實現在BooleanScorer類里,
每次往BooleanScorer類里添加一個子句,會記錄當前這個子句的序號和這個子句的定義,是必須滿足還是必須不滿足。這個數據記錄在requiredMask和prohibitedMask中,這兩個數是int類型,里面每一位都代表一個子句。requiredMask記錄了哪些子句必須滿足;prohibitedMask記錄了哪些子句必須不能滿足。比如一共有5個子句,1、3、5必須滿足,2、4必須不能滿足,則requiredMask二進制的值為: 10101。prohibitedMask的值為: 01010。
當調用next的時候會批量從各子句中取出符合這些子句的部分文檔(文檔ID批范圍,1024為一批)到內存中的一個緩存BucketTabke里,在這個緩存里會根據文檔ID進行聚合(Bucket),每個文檔ID都有個滿足哪些子句的屬性bits。
然后遍歷這些文檔,那些bits里包含所有必須符合子句且不包含所有必須排查子句的文檔是最終符合的文檔。這個判斷是通過bits和上面requiredMask和prohibitedMask做位運算實現的。
if ((current.bits & prohibitedMask) == 0 &&
(current.bits & requiredMask) == requiredMask) {
return true;
}
總結
以上是生活随笔為你收集整理的Lucene BooleanQuery相关算法的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: Ubuntu 14.04开发环境
- 下一篇: Day 03--设计与完善(一)