基于自己的興趣,利用業(yè)務(wù)時(shí)間在Lucene基礎(chǔ)上做的一個(gè)搜索框架,請(qǐng)大家多多指教。
一、 介紹
基于Lucene的全文檢索框架,提供快速方便的索引創(chuàng)建及查詢方式,并提供擴(kuò)展功能對(duì)框架進(jìn)行擴(kuò)展。
項(xiàng)目地址:http://code.google.com/p/snoics-retrieval/
二、 使用指南
1、 環(huán)境要求
Java1.5+
Lucene 3.0.x+
2、 加載
通過(guò)RetrievalApplicationContext 載入配置參數(shù),創(chuàng)建實(shí)例,每個(gè)被創(chuàng)建出的RetrievalApplicationContext實(shí)例中都包含一個(gè)完整的、獨(dú)立的上下文環(huán)境。
一般情況下,一個(gè)應(yīng)用只需要在啟動(dòng)時(shí)創(chuàng)建一個(gè)RetrievalApplicationContext實(shí)例,然后由整個(gè)應(yīng)用共享。
有以下幾種方式創(chuàng)建RetrievalApplicationContext實(shí)例:
l 以默認(rèn)的方式,通過(guò)讀取classpath下的retrieval.properties配置文件創(chuàng)建
RetrievalApplicationContext retrievalApplicationContext=
new RetrievalApplicationContext(“c:""index”);
l 使用配置文件的Properties實(shí)例加載
Properties properties=...
...
RetrievalApplicationContext retrievalApplicationContext=
new RetrievalApplicationContext(properties,“c:""index”);
l 讀取指定的配置文件創(chuàng)建,文件必須放在classpath下
RetrievalApplicationContext retrievalApplicationContext=
new RetrievalApplicationContext(“app-retrieval.properties”,
“c:""index”);
l 通過(guò)構(gòu)建RetrievalProperties對(duì)象創(chuàng)建
RetrievalProperties retrievalProperties=…
…
RetrievalApplicationContext retrievalApplicationContext=
new RetrievalApplicationContext(retrievalProperties,
“c:""index”);
3、 參數(shù)配置
默認(rèn)配置文件為classpath下的retrieval.properties,配置參數(shù)說(shuō)明如下
LUCENE_PARAM_VERSION |
Lucene參數(shù),如果不設(shè)置則使用默認(rèn)值 LUCENE_30 |
LUCENE_PARAM_MAX_FIELD_LENGTH |
Lucene參數(shù),如果不設(shè)置則使用默認(rèn)值 DEFAULT_MAX_FIELD_LENGTH=10000 |
LUCENE_PARAM_RAM_BUFFER_SIZE_MB |
Lucene 參數(shù),如果不設(shè)置,則使用默認(rèn)值 DEFAULT_RAM_BUFFER_SIZE_MB=16 |
LUCENE_PARAM_MAX_BUFFERED_DOCS |
Lucene 參數(shù),如果不設(shè)置,則使用默認(rèn)值 |
LUCENE_PARAM_MERGE_FACTOR |
Lucene 參數(shù),如果不設(shè)置,則使用默認(rèn)值 10 |
LUCENE_PARAM_MAX_MERGE_DOCS |
Lucene 參數(shù),如果不設(shè)置,則使用默認(rèn)值 Integer.MAX_VALUE |
INDEX_MAX_FILE_DOCUMENT_PAGE_SIZE |
設(shè)置索引創(chuàng)建執(zhí)行參數(shù),如果不設(shè)置,則使用默認(rèn)值 20 |
INDEX_MAX_INDEX_FILE_SIZE |
設(shè)置索引創(chuàng)建執(zhí)行參數(shù),如果不設(shè)置,則使用默認(rèn)值 3145728(單位:字節(jié)) |
INDEX_MAX_DB_DOCUMENT_PAGE_SIZE |
設(shè)置索引創(chuàng)建執(zhí)行參數(shù),如果不設(shè)置,則使用默認(rèn)值 500 |
INDEX_DEFAULT_CHARSET |
設(shè)置索引創(chuàng)建執(zhí)行參數(shù),如果不設(shè)置,則使用默認(rèn)值 utf-8 |
QUERY_RESULT_TOP_DOCS_NUM |
設(shè)置索引檢索執(zhí)行參數(shù),如果不設(shè)置,則使用默認(rèn)值 3000 |
RETRIEVAL_EXTENDS_CLASS_FILE_CONTENT_PARSER_MANAGER |
Retrieval擴(kuò)展,如果不設(shè)置,則使用默認(rèn)值 com.snoics.retrieval.engine.index.create.impl.file.FileContentParserManager |
RETRIEVAL_EXTENDS_CLASS_ANALYZER_BUILDER |
Retrieval擴(kuò)展,如果不設(shè)置,則使用默認(rèn)值 com.snoics.retrieval.engine.analyzer.CJKAnalyzerBuilder |
RETRIEVAL_EXTENDS_CLASS_HEIGHLIGHTER_MAKER |
Retrieval擴(kuò)展,如果不設(shè)置,則使用默認(rèn)值 com.snoics.retrieval.engine.query.formatter.HighlighterMaker |
RETRIEVAL_EXTENDS_CLASS_DATABASE_INDEX_ALL |
Retrieval擴(kuò)展,如果不設(shè)置,則使用默認(rèn)值 com.snoics.retrieval.engine.index.all.impl.DefaultRDatabaseIndexAllImpl |
4、 索引
4.1、初始化索引
retrievalApplicationContext
.getFacade()
.initIndex(new String[]{"DB","FILE"});
4.2、提供5種方式創(chuàng)建索引
l 以普通方式創(chuàng)建索引
RFacade facade=retrievalApplicationContext.getFacade();
NormalIndexDocument normalIndexDocument=
facade.createNormalIndexDocument(false);
RDocItem docItem1=new RDocItem();
docItem1.setContent("搜索引擎");
docItem1.setName("KEY_FIELD");
normalIndexDocument.addKeyWord(docItem1);
RDocItem docItem2=new RDocItem();
docItem2.setContent("速度覅藕斷絲連房?jī)r(jià)多少了咖啡卡拉圣誕節(jié)");
docItem2.setName("TITLE_FIELD");
normalIndexDocument.addContent(docItem2);
RDocItem docItem3=new RDocItem();
docItem3.setContent("哦瓦爾卡及討論熱離開(kāi)家");
docItem3.setName("CONTENT_FIELD");
normalIndexDocument.addContent(docItem3);
IRDocOperatorFacade docOperatorFacade=
facade.createDocOperatorFacade();
docOperatorFacade.create(normalIndexDocument);
l 對(duì)單條數(shù)據(jù)庫(kù)記錄內(nèi)容創(chuàng)建索引
IRDocOperatorFacade docOperatorHelper=
retrievalApplicationContext
.getFacade()
.createDocOperatorFacade();
String tableName="TABLE1";
String recordId="849032809432490324093";
DatabaseIndexDocument databaseIndexDocument=
retrievalApplicationContext
.getFacade()
.createDatabaseIndexDocument(false);
databaseIndexDocument.setIndexPathType("DB");
databaseIndexDocument.setIndexInfoType("TABLE1");
databaseIndexDocument.setTableNameAndRecordId(tableName,
recordId);
RDocItem docItem1=new RDocItem();
docItem1.setName("TITLE");
docItem1.setContent("SJLKDFJDSLK F");
RDocItem docItem2=new RDocItem();
docItem2.setName("CONTENT");
docItem2.setContent("RUEWOJFDLSKJFLKSJGLKJSFLKDSJFLKDSF");
RDocItem docItem3=new RDocItem();
docItem3.setName("field3");
docItem3.setContent("adsjflkdsjflkdsf");
RDocItem docItem4=new RDocItem();
docItem4.setName("field4");
docItem4.setContent("45432534253");
RDocItem docItem5=new RDocItem();
docItem5.setName("field5");
docItem5.setContent("87987yyyyyyyy");
RDocItem docItem6=new RDocItem();
docItem6.setName("field6");
docItem6.setContent("87987yyyyyyyy");
databaseIndexDocument.addContent(docItem1);
databaseIndexDocument.addContent(docItem2);
databaseIndexDocument.addContent(docItem3);
databaseIndexDocument.addContent(docItem4);
databaseIndexDocument.addContent(docItem5);
databaseIndexDocument.addContent(docItem6);
docOperatorHelper.create(databaseIndexDocument);
l 對(duì)單個(gè)文件內(nèi)容及文件信息創(chuàng)建索引
IRDocOperatorFacade docOperatorHelper=
retrievalApplicationContext
.getFacade()
.createDocOperatorFacade();
FileIndexDocument fileIndexDocument=
retrievalApplicationContext
.getFacade()
.createFileIndexDocument(false,"utf-8");
fileIndexDocument.setFileBasePath("c:""doc");
fileIndexDocument.setFileId("fileId_123");
fileIndexDocument.setFile(new File("c:""doc""1.doc"));
fileIndexDocument.setIndexPathType("FILE");
fileIndexDocument.setIndexInfoType("SFILE");
docOperatorHelper.create(fileIndexDocument,3*1024*1024);
l 對(duì)數(shù)據(jù)庫(kù)記錄進(jìn)行批量創(chuàng)建索引
String tableName = "TABLE1";
String keyField = "ID";
String sql = "SELECT ID,"
+ "TITLE,"
+ "CONTENT,"
+ "FIELD3,"
+ "FIELD4,"
+ "FIELD5,"
+ "FIELD6 FROM TABLE1 ORDER BY ID ASC";
RDatabaseIndexAllItem databaseIndexAllItem =
retrievalApplicationContext
.getFacade()
.createDatabaseIndexAllItem(false);
databaseIndexAllItem.setIndexPathType("DB");
databaseIndexAllItem.setIndexInfoType("TABLE1");
// 如果無(wú)論記錄是否存在,都新增一條索引內(nèi)容,
則使用RetrievalType.RIndexOperatorType.INSERT,
// 如果索引中記錄已經(jīng)存在,則只更新索引中的對(duì)應(yīng)的記錄,
否則新增記錄,則使用RetrievalType.RIndexOperatorType.UPDATE
databaseIndexAllItem
.setIndexOperatorType(RetrievalType.
RIndexOperatorType.INSERT);
databaseIndexAllItem.setTableName(tableName);
databaseIndexAllItem.setKeyField(keyField);
databaseIndexAllItem.setDefaultTitleFieldName("TITLE");
databaseIndexAllItem.setDefaultResumeFieldName("CONTENT");
databaseIndexAllItem.setPageSize(500);
databaseIndexAllItem.setSql(sql);
databaseIndexAllItem.setParam(new Object[] {});
databaseIndexAllItem
.setDatabaseRecordInterceptor(new TestDatabaseRecordInterceptor());
IRDocOperatorFacade docOperatorFacade =
retrievalApplicationContext
.getFacade()
.createDocOperatorFacade();
long indexCount = docOperatorFacade.
createAll(databaseIndexAllItem);
//優(yōu)化索引
retrievalApplicationContext
.getFacade()
.createIndexOperatorFacade("DB")
.optimize();
l 對(duì)大量的文件批量創(chuàng)建索引
RFileIndexAllItem fileIndexAllItem=
retrievalApplicationContext
.getFacade()
.createFileIndexAllItem(false,"utf-8");
fileIndexAllItem.setIndexPathType("FILE");
//如果無(wú)論記錄是否存在,都新增一條索引內(nèi)容,
則使用RetrievalType.RIndexOperatorType.INSERT,
//如果索引中記錄已經(jīng)存在,則只更新索引中的對(duì)應(yīng)的記錄,
否則新增記錄,則使用RetrievalType.RIndexOperatorType.UPDATE
FileIndexAllItem
.setIndexOperatorType(RetrievalType
.RIndexOperatorType.INSERT);
fileIndexAllItem.setIndexInfoType("SFILE");
fileIndexAllItem
.setFileBasePath("D:""workspace""resources""docs");
fileIndexAllItem.setIncludeSubDir(true);
fileIndexAllItem.setPageSize(100);
fileIndexAllItem
.setIndexAllFileInterceptor(
new TestFileIndexAllInterceptor());
//如果要對(duì)所有類(lèi)型的文件創(chuàng)建索引,則不要做設(shè)置一下這些設(shè)置,
否則在設(shè)置過(guò)類(lèi)型之后,將只對(duì)這些類(lèi)型的文件創(chuàng)建索引
fileIndexAllItem.addFileType("doc");
fileIndexAllItem.addFileType("docx");
fileIndexAllItem.addFileType("sql");
fileIndexAllItem.addFileType("html");
fileIndexAllItem.addFileType("htm");
fileIndexAllItem.addFileType("txt");
fileIndexAllItem.addFileType("xls");
long count=docOperatorHelper.createAll(fileIndexAllItem);
retrievalApplicationContext
.getFacade()
.createIndexOperatorFacade("FILE")
.optimize();
l 支持多線程創(chuàng)建索引,而不會(huì)出現(xiàn)索引文件異常
Thread thread1=new Thread(new Runnable(){
publicvoid run() {
do 單條或批量創(chuàng)建索引
}
});
Thread thread2=new Thread(new Runnable(){
publicvoid run() {
do 單條或批量創(chuàng)建索引
}
});
Thread thread3=new Thread(new Runnable(){
publicvoid run() {
do 單條或批量創(chuàng)建索引
}
});
thread1.start();
thread2.start();
thread3.start();
5、 查詢
使用RQuery實(shí)例,通過(guò)傳入構(gòu)造好的QueryItem實(shí)例進(jìn)行查詢,并使用QuerySort實(shí)例對(duì)結(jié)果排序
public QueryItem createQueryItem(
RetrievalType.RDocItemType docItemType,
Object name,
String value){
QueryItem queryItem=
retrievalApplicationContext
.getFacade()
.createQueryItem(docItemType,
String.valueOf(name),
value);
return queryItem;
}
IRQueryFacade queryFacade=
retrievalApplicationContext
.getFacade()
.createQueryFacade();
RQuery query=queryFacade.createRQuery(indexPathType);
QueryItem queryItem0=
testQuery
.createQueryItem(RetrievalType.RDocItemType.CONTENT,
"TITLE","啊啊");
QueryItem queryItem1=
testQuery
.createQueryItem(RetrievalType.RDocItemType.CONTENT,
"TITLE","哦");
QueryItem queryItem2=
testQuery
.createQueryItem(RetrievalType.RDocItemType.CONTENT,
"CONTENT","工作");
QueryItem queryItem3=
testQuery
.createQueryItem(RetrievalType.RDocItemType.CONTENT,
"CONTENT","地方");
QueryItem queryItem4=
testQuery
.createQueryItem(RetrievalType.RDocItemType.CONTENT,
"FIELD3","過(guò)節(jié)");
QueryItem queryItem5=
testQuery
.createQueryItem(RetrievalType.RDocItemType.CONTENT,
"FIELD4","高興");
QueryItem queryItem=
queryItem0
.should(QueryItem.SHOULD,queryItem1)
.should(queryItem2)
.should(queryItem3.mustNot(QueryItem.SHOULD,queryItem4)).should(queryItem5);
QuerySort querySort=new QuerySort(QueryUtil.createScoreSort());
QueryResult[] queryResults=
query.getQueryResults(queryItem,querySort);
query.close();
6、 擴(kuò)展
提供兩種途徑進(jìn)行擴(kuò)展:
1) 在配置文件指定擴(kuò)展類(lèi),在加載時(shí),自動(dòng)讀取和設(shè)置配置文件中的擴(kuò)展類(lèi)
2) 在RetrievalProperties實(shí)例中設(shè)置擴(kuò)展類(lèi),
并使用該實(shí)例創(chuàng)建RetrievalApplicationContext實(shí)例
l IFileContentParserManager
通過(guò)實(shí)現(xiàn)此接口,并替換整個(gè)文件內(nèi)容解析管理器,擴(kuò)展文件內(nèi)容解析方式
或通過(guò)以下的方式,在原文件內(nèi)容解析管理器中替換或新增文件解析器
實(shí)現(xiàn)IFileContentParser接口,并使用以下的方式新增或替換文件類(lèi)型的內(nèi)容解析器
retrievalApplicationContext
.getRetrievalFactory()
.getFileContentParserManager()
.regFileContentParser(“docx”, fileContentParser)
l IRAnalyzerBuilder
通過(guò)實(shí)現(xiàn)此接口,并替換分詞器構(gòu)建器
l IHighlighterMaker
通過(guò)實(shí)現(xiàn)此接口,并替換內(nèi)容高亮處理器
l IRDatabaseIndexAll
通過(guò)實(shí)現(xiàn)此接口,實(shí)現(xiàn)數(shù)據(jù)庫(kù)數(shù)據(jù)批量讀取并寫(xiě)入索引
或直接繼承AbstractRDatabaseIndexAll抽象類(lèi),并實(shí)現(xiàn)其中的抽象方法
/**
* 獲取當(dāng)前頁(yè)數(shù)據(jù)庫(kù)記錄,每調(diào)用一次這個(gè)方法,就返回一頁(yè)的記錄
* @return
*/
publicabstract List<Map> getResultList()
7、 其它
更詳細(xì)的示例請(qǐng)查閱test中的代碼
snoics-retrieval項(xiàng)目中使用了snoics-base.jar,如果需要獲取snoics-base.jar的源代碼,請(qǐng)到http://code.google.com/p/snoics-base/中下載
三、 關(guān)于
項(xiàng)目地址:http://code.google.com/p/snoics-retrieval/
Email : snoics@gmail.com
Blog : http://www.aygfsteel.com/snoics/