Lucene3.0全文搜索简单应用
由于项目的要求,用到了全文搜索技术,上网查了许多资料,结合本项目实际情况,现将lucene3.0总结如下:
1.首先了解一下lucene中的一些基本概念:
假设我们的电脑的目录中含有很多文本文档,我们需要查找哪些文档含有某个关键词。为了实现这种功能,我们首先利用 Lucene 对这个目录中的文档建立索引,然后在建立好的索引中搜索我们所要查找的文档。
(1)建立索引
为了对文档进行索引,Lucene 提供了五个基础的类,他们分别是 Document, Field, IndexWriter, Analyzer, Directory。下面我们分别介绍一下这五个类的用途:
Document
Document 是用来描述文档的,这里的文档可以指一个 HTML 页面,一封电子邮件,或者是一个文本文件。一个 Document 对象由多个 Field 对象组成的。可以把一个 Document 对象想象成数据库中的一个记录,而每个 Field 对象就是记录的一个字段。
Field
Field 对象是用来描述一个文档的某个属性的,比如一封电子邮件的标题和内容可以用两个 Field 对象分别描述。
Analyzer
在一个文档被索引之前,首先需要对文档内容进行分词处理,这部分工作就是由 Analyzer 来做的。Analyzer 类是一个抽象类,它有多个实现。针对不同的语言和应用需要选择适合的 Analyzer。Analyzer 把分词后的内容交给 IndexWriter 来建立索引。
IndexWriter
IndexWriter 是 Lucene 用来创建索引的一个核心的类,他的作用是把一个个的 Document 对象加到索引中来。
Directory
这个类代表了 Lucene 的索引的存储的位置,这是一个抽象类,它目前有两个实现,第一个是 FSDirectory,它表示一个存储在文件系统中的索引的位置。第二个是 RAMDirectory,它表示一个存储在内存当中的索引的位置。
熟悉了建立索引所需要的这些类后,我们开始对某个目录下面的htm文件l建立索引了,在建立的过程中,需将这些html文件转化为文本文件,下面给出建立索引类的源代码。
import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.index.CorruptIndexException;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriter.MaxFieldLength;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.LockObtainFailedException;
import org.apache.lucene.store.SimpleFSDirectory;
import org.htmlparser.Parser;
import org.htmlparser.util.ParserException;
import org.htmlparser.visitors.HtmlPage;
import org.wltea.analyzer.lucene.IKAnalyzer;
public class LuceneIndex
{
public static final String CONTENT_KEY_NAME = "content";//内容的键
public static final String FILE_KEY_NAME = "filenamddde";//文件名的键
public IndexWriter mWriter = null;//用于写入索引的对象
/**
* @功能描述 打开Lucene实例
* @输入参数 indexDirectory为索引存放目录,create为创建索引
* @反馈值 无
*/
public void openLuceneIndex(Directory indexDirectory, boolean create) throws CorruptIndexException, LockObtainFailedException, IOException
{
close();
mWriter = new IndexWriter(indexDirectory, new IKAnalyzer(), create, MaxFieldLength.LIMITED);
mWriter.setUseCompoundFile(true);
}
/**
* @功能描述 将HTML转化为Text
* @输入参数
* @反馈值
*/
public static String extractHtml(String inputHtml) throws ParserException
{
Parser htmlParser = Parser.createParser(inputHtml,"utf-8");
HtmlPage thePage = new HtmlPage(htmlParser);
htmlParser.visitAllNodesWith(thePage);
String body = thePage.getBody().asString();
body = body.replaceAll("[ \\t\\n\\r\\f( |gt) ]+", " ");
System.out.println(body);
String title = thePage.getTitle();
return title + " " + body;
}
/**
* @throws Exception
* @功能描述 向已经打开的Lucene实例里面添加新的索引
* @输入参数
* @反馈值
*/
public void indexTxt(File f) throws Exception
{
if (mWriter == null)
{
throw new Exception("Lucene Index not Opened!");
}
BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(f), "GBK"));
StringBuffer sb = new StringBuffer();
String temp = null;
while ((temp = reader.readLine()) != null)
{
sb.append(temp);
}
reader.close();
indexString(f.getName(), sb.toString());
}
/**
* @功能描述 索引HTML文件
* @输入参数
* @反馈值
*/
public void indexHtml(File f) throws Exception
{
BufferedReader br = null;
try
{
StringBuffer sb = new StringBuffer();
br = new BufferedReader(new InputStreamReader(new FileInputStream(f),"utf-8"));
String temp = null;
while ((temp = br.readLine()) != null)
{
sb.append(temp + "\r\n");
}
String theHtml = sb.toString();
String thePlainText = extractHtml(theHtml);
indexString(f.getName(), thePlainText);
} catch (Exception ex)
{
throw ex;
} finally
{
if (br != null)
{
br.close();
}
}
}
/**
* @功能描述 索引字符串
* @输入参数
* @反馈值
*/
public void indexString(String key, String content) throws Exception
{
if (mWriter == null)
{
throw new Exception("Lucene Index not Opened!");
}
Document doc = new Document();
doc.add(new Field(CONTENT_KEY_NAME, content, Field.Store.YES, Field.Index.ANALYZED));
doc.add(new Field(FILE_KEY_NAME, key, Field.Store.YES, Field.Index.ANALYZED));
mWriter.addDocument(doc);
mWriter.optimize();
}
public void close()
{
try
{
if (mWriter != null)
{
mWriter.close();
mWriter = null;
}
} catch (Exception e)
{
}
}
/**
* @功能描述 建立索引
* @输入参数 indexDirectory为索引存放路径,docDirectory为文档存放路径
* @反馈值
*/
public static void indexService(String indexDirectory, String docDirectory) throws Exception
{
LuceneIndex index = new LuceneIndex();
Directory directory = null;
try
{
directory = new SimpleFSDirectory(new File(indexDirectory));// new
index.openLuceneIndex(directory, true);
indexDirectory(index, new File(docDirectory));
System.out.println("索引完成");
} catch (Exception e)
{
throw e;
} finally
{
index.close();
}
}
/**
* @功能描述 遍历索引一个目录下面的所有HTML文件
* @输入参数
* @反馈值
*/
public static void indexDirectory(LuceneIndex index, File directory) throws Exception
{
File[] fileList = directory.listFiles();
for (File f : fileList)
{
if (f.isFile())
{
if (f.getName().endsWith(".html") || f.getName().endsWith(".htm"))
{
System.out.println("现在开始索引:" + f.getName());
index.indexHtml(f);
}
} else if (f.isDirectory())
{
indexDirectory(index, f);
}
}
}
}
(2)搜索文档
利用 Lucene 进行搜索就像建立索引一样也是非常方便的。在上面一部分中,我们已经为一个目录下的文本文档建立好了索引,现在我们就要在这个索引上进行搜索以找到包含某个关键词或短语的文档。Lucene 提供了几个基础的类来完成这个过程,它们分别是呢 IndexSearcher, Term, Query, TermQuery, Hits. 下面我们分别介绍这几个类的功能。
Query
这是一个抽象类,他有多个实现,比如 TermQuery, BooleanQuery, PrefixQuery. 这个类的目的是把用户输入的查询字符串封装成 Lucene 能够识别的 Query。
Term
Term 是搜索的基本单位,一个 Term 对象有两个 String 类型的域组成。生成一个 Term 对象可以有如下一条语句来完成:Term term = new Term(“fieldName”,”queryWord”); 其中第一个参数代表了要在文档的哪一个 Field 上进行查找,第二个参数代表了要查询的关键词。
TermQuery
TermQuery 是抽象类 Query 的一个子类,它同时也是 Lucene 支持的最为基本的一个查询类。生成一个 TermQuery 对象由如下语句完成: TermQuery termQuery = new TermQuery(new Term(“fieldName”,”queryWord”)); 它的构造函数只接受一个参数,那就是一个 Term 对象。
IndexSearcher
IndexSearcher 是用来在建立好的索引上进行搜索的。它只能以只读的方式打开一个索引,所以可以有多个 IndexSearcher 的实例在一个索引上进行操作。
Hits
Hits 是用来保存搜索的结果的。
介绍完这些搜索所必须的类之后,我们就开始在之前所建立的索引上进行搜索了,下面给出搜索类的源代码:
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import org.apache.lucene.document.Document;
import org.apache.lucene.index.CorruptIndexException;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.store.Directory;
import org.wltea.analyzer.lucene.IKQueryParser;
public class LuceneSearch
{
public IndexSearcher mSearch = null;//用于搜索的对象
/**
* @功能描述 打开一个索引目录
* @输入参数 indexDirectory 索引目录
* @反馈值
*/
public void openLuceneSearch(Directory indexDirectory) throws CorruptIndexException, IOException
{
mSearch = new IndexSearcher(indexDirectory, true);
}
public void close()
{
try
{
if (mSearch != null)
{
mSearch.close();
}
} catch (Exception e)
{
}
}
/**
* @功能描述 搜索关键字
* @输入参数 keyword为搜索关键字,start为起始点,top为返回多少条数据
* @反馈值
*/
public List<Document> search(String keyword, int start, int top) throws Exception
{
if (mSearch == null)
{
throw new Exception("没有初始化 Lucene Index Search");
}
Query query = IKQueryParser.parse(LuceneIndex.CONTENT_KEY_NAME, keyword);
return search(query, start, top);
}
/**
* @功能描述 根据已经定制好的查询返回查询结果
* @输入参数 query 已经定制好的查询,start 起始结果,count 结果数量
* @反馈值
*/
public List<Document> search(Query query, int start, int count) throws Exception
{
if (mSearch == null)
{
throw new Exception("没有初始化 Lucene Index Search");
}
List<Document> docs = new ArrayList<Document>();
TopDocs theDocs = mSearch.search(query, 1000);
int resultCount = theDocs.scoreDocs.length;
start = Math.min(start, resultCount);
int end = Math.min(start + count, resultCount);
for (int i = start; i < end; ++i)
{
Document doc = mSearch.doc(theDocs.scoreDocs[i].doc);
docs.add(doc);
}
return docs;
}
}
(3)主方法进行测试
import java.io.File;
import java.util.List;
import org.apache.lucene.document.Document;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.SimpleFSDirectory;
public class TestMain
{
public static void main(String[] args) throws Exception
{
if (args.length == 3)
{
if (args[0].equalsIgnoreCase("createindex"))
{
String indexDirectory = args[1];//最终放置索引位置D:\2
String docDirectory = args[2];//文档路径D:\1
LuceneIndex.indexService(indexDirectory, docDirectory);
}
} else if (args.length == 4)
{
if (args[0].equalsIgnoreCase("search"))
{
LuceneSearch search = new LuceneSearch();
try
{
String indexDirectory = args[1];//D:\2
String keyword = args[2];//要查找的字
int top = Integer.parseInt(args[3]);//查找范围
System.out.println("关键字为 :" + keyword);
Directory directory = new SimpleFSDirectory(new File(indexDirectory));
search.openLuceneSearch(directory);
List<Document> docs = search.search(keyword, 0, top);
System.out.println("结果总数" + docs.size());
for (Document doc : docs)
{
System.out.println("结果:" + doc.getField(LuceneIndex.FILE_KEY_NAME).stringValue());
}
} catch (Exception e)
{
e.printStackTrace();
return;
} finally
{
search.close();
}
}
}
}
}
点击myeclipse中,绿色有个向右的箭头旁边的向下的按钮,选择open run dialog对话框,在java Application上点击右键,选择new,新建一个起名为index,然后选择index,在Arguments中的Program arguments中输入,
createindex D:\2 D:\1 点应用,就开始创建索引。
同理创建一个search应用,在Arguments 中的Program arguments中输入
search D:\2 要搜索的关键字 搜索范围(默认值,随便给1000)
点应用,开始搜索,查看控制台有没有输出,如果有,说明成功,如果没有,在看看是不是编码的问题。