本文主要介紹Lucene的常用概念,並自定義一個分詞器
1 環境介紹
系統:win10
lucene版本:7.3.0 https://lucene.apache.org/
jdk:1.8
2 lucene 簡介
lucene是最受歡迎的java開源全文搜索引擎開發工具包,提供了完整的查詢引擎和索引引擎,是Apache的子項目。在應用中為數據庫中的數據提供全文檢索實現
也可以開發獨立的搜索引擎服務,系統。
架構圖如下:
上層application層,左邊 為lucene提供數據收集,右邊為用戶提供搜索的入口
下層lucene,為數據提供索引的存儲,索引的查詢等功能
3 分詞器
分析器(org.apache.lucene.analysis.Analyzer),分詞器組件的核心api ,用來構建真正的對文本進行分詞處理的TokenStrem (分詞處理器),在Analyzer 這個類中我們看到,有唯一的一個可以擴展的抽象方法
我們在擴展自己的analyzer的時候 要重載這個方法,改方法的參數fieldName表示字段名。不同的字段有不同的處理方式,根據字段名來區分。
TokenStreamComponents 是一個內部類,提供了兩個構造方法
TokenStream
從上面的構造方法的參數中,我們可以看到,我們至少要提供一個Tokenizer 參數。那現在看看Tokenizer 和TokenStream 這兩個類。TokenStream,負責對輸入的文本進行分詞和處理,分詞分出的每一項叫token
實際上TokenStream 有兩種類型的子類分別用於分詞和處理 。 一類是 Tokenizer分詞器,完成從輸入的reader字符流中分出分項, 還有一類是 TokenFilter,分項過濾器,對分出的分項進行特性處理。
TokenFilter是采用的裝飾器模式。如果我們需要對分詞進行各種處理,我們只需要按照我們的處理順序一層層包裹即可。
在TokenStrem中有個抽象方法
在我們實現自己的分詞器的時候要實現這個方法,來告訴我們自己的分詞規則和處理規則。返回值為false的時候表示分詞結束。
AttributeSource
TokenStream 繼承AttributeSource. 該方法用來存放Attribute 並且提供了對應的設置和取值方法。那Attribute是用來干什么的呢?
在我們用Tokenizer 和TokenStream 對分詞進行了分項,並且對每個分項進行了了處理了之后,每個分項都會產生相應的信息,比如這個分項的文本是什么,位置是多少。這些都是要存儲起來的,那這些信息就是
存儲在Attribute中的,AttributeFactory是用來創建attribute的工廠方法。不需要我們去創建。
TokenStream的使用步驟:
1 從tokenStream獲得你想要的分項屬性對象(attribute)用來存放分項信息。
2 調用tokenStream的reset方法,進行重置,因為tokenStream是重復利用的。
3 循環調用tokenStream的increamentToken方法,一個個分項,知道返回false
4 在循環中取出你每個分項想要的屬性
5 調用tokenStream的end方法,接受處理。
5 調用tokenStream的close方法,釋放資源。
4 實現自己的分詞器
4.1定義自己的分詞屬性
**
* 每個分詞的屬性
*/
public interface MyCharAttribute extends Attribute {
/**
* 賦值
* @param buffer 要被復制的數組
* @param length 復制的長度
*/
void setChars(char[] buffer, int length);
/**
* 獲取分詞數組
* @return
*/
char[] getChars();
/**
* 獲取分詞長度
* @return
*/
int getLength();
/**
* 獲取分詞的字符串
* @return
*/
String getString();
}
4.2 實現上面抽象方法
主要命名規則必須是 xxxImpl,lucence內部是以字符串拼接的形式去實現的
**
* 命名規則必須是 xxxImpl
*/
public class MyCharAttributeImpl extends AttributeImpl implements MyCharAttribute {
//單根分詞的字符數組
private char[] chatTerm = new char[255];
//單根分詞的數組長度
private int length = 0;
@Override
public void setChars(char[] buffer, int length) {
this.length = length;
if (length > 0) {
System.arraycopy(buffer, 0, chatTerm, 0, length);
}
}
@Override
public char[] getChars() {
return chatTerm;
}
@Override
public int getLength() {
return length;
}
@Override
public String getString() {
if (this.length > 0) {
return new String(this.chatTerm, 0, length);
}
return null;
}
@Override
public void clear() {
length = 0;
}
@Override
public void reflectWith(AttributeReflector attributeReflector) {
}
@Override
public void copyTo(AttributeImpl attribute) {
}
}
4.3 實現自己的分詞器
/**
* 定義如何分詞
*/
public class MyTokenizer extends Tokenizer {
//里面是通過工廠方法 得到實例
MyCharAttribute attribute = this.addAttribute(MyCharAttribute.class);
char[] buffer = new char[255];
int length = 0;
int c;
/**
* 判斷是否有需要分詞的項 每次讀取一個單詞
* 如果有 會把這個分出來的詞 設置到MyCharAttribute中
*/
@Override
public boolean incrementToken() throws IOException {
// 清除所有的詞項屬性
clearAttributes();
length = 0;
while (true) {
//這個input 就是要解析的文本流
c = input.read();
//是否到末尾
if (c == -1) {
if (length > 0) {
// 復制到charAttr
attribute.setChars(buffer, length);
return true;
} else {
return false;
}
}
//是否是空格 英文單詞之間通過空格區分
if (Character.isWhitespace(c)) {
if (length > 0) {
// 復制到charAttr
attribute.setChars(buffer, length);
return true;
}
}
buffer[length++] = (char) c;
}
}
}
4.4 定義分詞處理器
**
* 分詞處理器 這里是全部轉小寫
*/
public class MyTokenFilter extends TokenFilter {
protected MyTokenFilter(TokenStream input) {
super(input);
}
MyCharAttribute charAttr = this.addAttribute(MyCharAttribute.class);
@Override
public boolean incrementToken() throws IOException {
boolean res = input.incrementToken();
if (res){
char[] chars = charAttr.getChars();
int length = charAttr.getLength();
if (length > 0) {
for (int i = 0; i < length; i++) {
chars[i] = Character.toLowerCase(chars[i]);
}
}
}
return res;
}
}
4.5 使用自定義分詞器
public class MyAnalyzer extends Analyzer{
@Override
protected TokenStreamComponents createComponents(String s) {
Tokenizer source = new MyTokenizer();
//攔截連1 轉小寫
TokenStream filter1 = new MyTokenFilter(source);
return new TokenStreamComponents(source,filter1);
}
public static void main(String[] args) {
String text = "An AttributeSource contains a list of different AttributeImpls, and methods to add and get them. ";
try (Analyzer ana = new MyAnalyzer(); TokenStream ts = ana.tokenStream("aa", text);) {
//得到屬性對象 每個屬性對象是通過工廠創建 單例
MyCharAttribute ca = ts.getAttribute(MyCharAttribute.class);
ts.reset();
while (ts.incrementToken()) {
System.out.print(ca.getString() + "|");
}
ts.end();
System.out.println();
} catch (IOException e) {
e.printStackTrace();
}
}
}
4.6 運行結果