基于自己的兴趣,利用业务时间在Lucene基础上做的一个搜索框架,请大家多多指教。
一、 介绍
基于Lucene的全文检索框架,提供快速方便的索引创建及查询方式,并提供扩展功能对框架进行扩展。
项目地址:http://code.google.com/p/snoics-retrieval/
二、 使用指南
1、 环境要求
Java1.5+
Lucene 3.0.x+
2、 加载
通过RetrievalApplicationContext 载入配置参数,创建实例,每个被创建出的RetrievalApplicationContext实例中都包含一个完整的、独立的上下文环境。
一般情况下,一个应用只需要在启动时创建一个RetrievalApplicationContext实例,然后由整个应用共享。
有以下几种方式创建RetrievalApplicationContext实例:
l 以默认的方式,通过读取classpath下的retrieval.properties配置文件创建
RetrievalApplicationContext retrievalApplicationContext=
new RetrievalApplicationContext(“c:""index”);
l 使用配置文件的Properties实例加载
Properties properties=...
...
RetrievalApplicationContext retrievalApplicationContext=
new RetrievalApplicationContext(properties,“c:""index”);
l 读取指定的配置文件创建,文件必须放在classpath下
RetrievalApplicationContext retrievalApplicationContext=
new RetrievalApplicationContext(“app-retrieval.properties”,
“c:""index”);
l 通过构建RetrievalProperties对象创建
RetrievalProperties retrievalProperties=…
…
RetrievalApplicationContext retrievalApplicationContext=
new RetrievalApplicationContext(retrievalProperties,
“c:""index”);
3、 参数配置
默认配置文件为classpath下的retrieval.properties,配置参数说明如下
LUCENE_PARAM_VERSION |
Lucene参数,如果不设置则使用默认值 LUCENE_30 |
LUCENE_PARAM_MAX_FIELD_LENGTH |
Lucene参数,如果不设置则使用默认值 DEFAULT_MAX_FIELD_LENGTH=10000 |
LUCENE_PARAM_RAM_BUFFER_SIZE_MB |
Lucene 参数,如果不设置,则使用默认值 DEFAULT_RAM_BUFFER_SIZE_MB=16 |
LUCENE_PARAM_MAX_BUFFERED_DOCS |
Lucene 参数,如果不设置,则使用默认值 |
LUCENE_PARAM_MERGE_FACTOR |
Lucene 参数,如果不设置,则使用默认值 10 |
LUCENE_PARAM_MAX_MERGE_DOCS |
Lucene 参数,如果不设置,则使用默认值 Integer.MAX_VALUE |
INDEX_MAX_FILE_DOCUMENT_PAGE_SIZE |
设置索引创建执行参数,如果不设置,则使用默认值 20 |
INDEX_MAX_INDEX_FILE_SIZE |
设置索引创建执行参数,如果不设置,则使用默认值 3145728(单位:字节) |
INDEX_MAX_DB_DOCUMENT_PAGE_SIZE |
设置索引创建执行参数,如果不设置,则使用默认值 500 |
INDEX_DEFAULT_CHARSET |
设置索引创建执行参数,如果不设置,则使用默认值 utf-8 |
QUERY_RESULT_TOP_DOCS_NUM |
设置索引检索执行参数,如果不设置,则使用默认值 3000 |
RETRIEVAL_EXTENDS_CLASS_FILE_CONTENT_PARSER_MANAGER |
Retrieval扩展,如果不设置,则使用默认值 com.snoics.retrieval.engine.index.create.impl.file.FileContentParserManager |
RETRIEVAL_EXTENDS_CLASS_ANALYZER_BUILDER |
Retrieval扩展,如果不设置,则使用默认值 com.snoics.retrieval.engine.analyzer.CJKAnalyzerBuilder |
RETRIEVAL_EXTENDS_CLASS_HEIGHLIGHTER_MAKER |
Retrieval扩展,如果不设置,则使用默认值 com.snoics.retrieval.engine.query.formatter.HighlighterMaker |
RETRIEVAL_EXTENDS_CLASS_DATABASE_INDEX_ALL |
Retrieval扩展,如果不设置,则使用默认值 com.snoics.retrieval.engine.index.all.impl.DefaultRDatabaseIndexAllImpl |
4、 索引
4.1、初始化索引
retrievalApplicationContext
.getFacade()
.initIndex(new String[]{"DB","FILE"});
4.2、提供5种方式创建索引
l 以普通方式创建索引
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("速度覅藕断丝连房价多少了咖啡卡拉圣诞节");
docItem2.setName("TITLE_FIELD");
normalIndexDocument.addContent(docItem2);
RDocItem docItem3=new RDocItem();
docItem3.setContent("哦瓦尔卡及讨论热离开家");
docItem3.setName("CONTENT_FIELD");
normalIndexDocument.addContent(docItem3);
IRDocOperatorFacade docOperatorFacade=
facade.createDocOperatorFacade();
docOperatorFacade.create(normalIndexDocument);
l 对单条数据库记录内容创建索引
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 对单个文件内容及文件信息创建索引
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 对数据库记录进行批量创建索引
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");
// 如果无论记录是否存在,都新增一条索引内容,
则使用RetrievalType.RIndexOperatorType.INSERT,
// 如果索引中记录已经存在,则只更新索引中的对应的记录,
否则新增记录,则使用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);
//优化索引
retrievalApplicationContext
.getFacade()
.createIndexOperatorFacade("DB")
.optimize();
l 对大量的文件批量创建索引
RFileIndexAllItem fileIndexAllItem=
retrievalApplicationContext
.getFacade()
.createFileIndexAllItem(false,"utf-8");
fileIndexAllItem.setIndexPathType("FILE");
//如果无论记录是否存在,都新增一条索引内容,
则使用RetrievalType.RIndexOperatorType.INSERT,
//如果索引中记录已经存在,则只更新索引中的对应的记录,
否则新增记录,则使用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());
//如果要对所有类型的文件创建索引,则不要做设置一下这些设置,
否则在设置过类型之后,将只对这些类型的文件创建索引
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 支持多线程创建索引,而不会出现索引文件异常
Thread thread1=new Thread(new Runnable(){
publicvoid run() {
do 单条或批量创建索引
}
});
Thread thread2=new Thread(new Runnable(){
publicvoid run() {
do 单条或批量创建索引
}
});
Thread thread3=new Thread(new Runnable(){
publicvoid run() {
do 单条或批量创建索引
}
});
thread1.start();
thread2.start();
thread3.start();
5、 查询
使用RQuery实例,通过传入构造好的QueryItem实例进行查询,并使用QuerySort实例对结果排序
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","过节");
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、 扩展
提供两种途径进行扩展:
1) 在配置文件指定扩展类,在加载时,自动读取和设置配置文件中的扩展类
2) 在RetrievalProperties实例中设置扩展类,
并使用该实例创建RetrievalApplicationContext实例
l IFileContentParserManager
通过实现此接口,并替换整个文件内容解析管理器,扩展文件内容解析方式
或通过以下的方式,在原文件内容解析管理器中替换或新增文件解析器
实现IFileContentParser接口,并使用以下的方式新增或替换文件类型的内容解析器
retrievalApplicationContext
.getRetrievalFactory()
.getFileContentParserManager()
.regFileContentParser(“docx”, fileContentParser)
l IRAnalyzerBuilder
通过实现此接口,并替换分词器构建器
l IHighlighterMaker
通过实现此接口,并替换内容高亮处理器
l IRDatabaseIndexAll
通过实现此接口,实现数据库数据批量读取并写入索引
或直接继承AbstractRDatabaseIndexAll抽象类,并实现其中的抽象方法
/**
* 获取当前页数据库记录,每调用一次这个方法,就返回一页的记录
* @return
*/
publicabstract List<Map> getResultList()
7、 其它
更详细的示例请查阅test中的代码
snoics-retrieval项目中使用了snoics-base.jar,如果需要获取snoics-base.jar的源代码,请到http://code.google.com/p/snoics-base/中下载
三、 关于
项目地址:http://code.google.com/p/snoics-retrieval/
Email : [email protected]
Blog : http://www.blogjava.net/snoics/