上回书说到已经为txt文件进行了简单的预处理,接下来就要开始建立索引了:)。
步骤:1.为要处理的内容建立索引 2.构建查询对象 3.在索引中查找
首先我们要在firstProject的“firstPoject.lucene.process”包下创建一个IndexProcesser。
代码如下
package firstProject.lucenedemo.process;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import jeasy.analysis.MMAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.index.IndexWriter;
public class IndexProcesser {
// 成员变量存储创建的索引文件存放的位置
private String INDEX_STORE_PATH = "D:\\index";
// 创建索引
public void createIndex(String inputDir) {
try {
// MMAnalyzer作为分词工具创建一个IndexWriter
IndexWriter writer = new IndexWriter(INDEX_STORE_PATH,
new MMAnalyzer(), true);
File filesDir = new File(inputDir);
// 取得所有需要建立索引的文件数组
File[] files = filesDir.listFiles();
// 遍历数组
for (int i = 0; i < files.length; i++) {
// 获取文件名
String fileName = files[i].getName();
// 判断文件是否为txt类型的文件
if (fileName.substring(fileName.lastIndexOf("."))
.equals(".txt")) {
// 创建一个新的Document
Document doc = new Document();
// 为文件名创建一个Field
Field field = new Field("filename", files[i].getName(),
Field.Store.YES, Field.Index.TOKENIZED);
doc.add(field);
// 为文件内容创建一个Filed
field = new Field("content", loadFileToString(files[i]),
Field.Store.NO, Field.Index.TOKENIZED);
doc.add(field);
// 把Document加入IndexWriter
writer.addDocument(doc);
}
}
// 关闭IndexWriter
writer.close();
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
IndexProcesser processor = new IndexProcesser();
processor.createIndex("D:\\TDDOWNLOAD\\niceFolder");
}
public String loadFileToString(File file) {
try {
BufferedReader br = new BufferedReader(new FileReader(file));
StringBuffer sb = new StringBuffer();
String line = br.readLine();
while (line != null) {
sb.append(line);
line = br.readLine();
}
br.close();
return sb.toString();
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
}
上述代码有两个方法
1.createIndex:该方法接受一个参数inputDir,该参数表示等待被建立索引的文件的存放路径。方法会从该路径中取出所有.txt文件,为每一个文件创建一个Lucene的Document文档,并向每个Document文档内加入了一些文件信息,包括文件名以及文件内容。可能你和我一样,现在对这些Document之类的术语朦胧着,不要紧,慢慢来。
2.LoadFileToString:在将文件内容加入到Document时,调用了类中另一个公有方法loadFileToString。它被用来从一个文件中读出其全部内容,并返回一个String类型的对象。该方法较简单,在BufferReader文件夹中,将每行的内容读取并装入StringBuffer中,最后一行返回。
createIndex中使用了Lucene的API,将建立的Document对象加入到索引中去,也将文件名和文件内容等信息加入了Lucene的索引。
现在在查看D:\indexr文件夹就可以发现所有的文本文件建立了索引文本文件越大,.cfs文件越多 欧了,这样索引就建立成功了
接下来我们就要建立一个有搜索功能的类 (右键单击"firstProject.lucenedemo.process"->New->class->命名为Search)
代码如下
package firstProject.lucenedemo.process;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import org.apache.lucene.index.Term;
import org.apache.lucene.index.TermDocs;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.TermQuery;
public class Search {
private String INDEX_STORE_PATH = "D:\\index";
public void indexSearch(String searchType, String searchKey) {
try {
// 根据索引位置建立IndexSearcher
IndexSearcher searcher = new IndexSearcher(INDEX_STORE_PATH);
// 建立搜索单元,searchType代表要搜索的Filed,searchKey代表关键字
Term t = new Term(searchType, searchKey);
// 由Term生成一个Query
Query q = new TermQuery(t);
// 搜索开始时间
Date beginTime = new Date();
// 获取一个<document, frequency>的枚举对象TermDocs
TermDocs termDocs = searcher.getIndexReader().termDocs(t);
while (termDocs.next()) {
// 输出在文档中出现关键词的次数和搜索到关键词的文档
System.out.println("find "+termDocs.freq()+" matches in "+searcher.getIndexReader().document(termDocs.doc()).getField("filename").stringValue());
}
// 搜索完成时间
Date endTime = new Date();
// 搜索所耗时间
long timeOfSearch = endTime.getTime() - beginTime.getTime();
System.out.println("The time For indexsearch is " + timeOfSearch + " ms");
} catch (IOException e) {
e.printStackTrace();
}
}
public void StringSearch(String keyword, String searchDir) {
File filesDir = new File(searchDir);
// 返回目录文件夹所有文件数组
File[] files = filesDir.listFiles();
// HashMap保存文件名和匹配次数对
Map rs = new HashMap();
// 记录搜索开始时间
Date beginTime = new Date();
// 遍历所有文件
for (int i = 0; i < files.length; i++) {
// 初始化匹配次数
int hits = 0;
try {
// 读取文件内容
BufferedReader br = new BufferedReader(new FileReader(files[i]));
StringBuffer sb = new StringBuffer();
String line = br.readLine();
while (line != null) {
sb.append(line);
line = br.readLine();
}
br.close();
// 将StringBuffer转化成String,以便于搜索
String stringToSearch = sb.toString();
// 初始化fromIndex
int fromIndex = -keyword.length();
// 逐个匹配关键词
while ((fromIndex = stringToSearch.indexOf(keyword, fromIndex
+ keyword.length())) != -1) {
hits++;
}
// 将文件名和匹配次数加入HashMap
rs.put(files[i].getName(), new Integer(hits));
} catch (IOException e) {
e.printStackTrace();
}
}
// 输出查询结果
Iterator it = rs.keySet().iterator();
while (it.hasNext()) {
String fileName = (String) it.next();
Integer hits = (Integer) rs.get(fileName);
System.out.println("find " + hits.intValue() + " matches in "
+ fileName);
}
// 记录结束时间
Date endTime = new Date();
// 得到搜索耗费时间
long timeOfSearch = endTime.getTime() - beginTime.getTime();
System.out.println("The time For string search is " + timeOfSearch + " ms");
}
}
然后再从"first.lucene.test"中建立一个测试类 (右键单击“first.lucene.test"->New->class->命名为SearchTimeCompareTest)
代码如下
package firstProject.lucenedemo.test;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import firstProject.lucenedemo.process.Search;
public class SearchTimeCompareTest {
public static void main(String[] args) {
Search search = new Search();
// 通过索引搜索关键词
search.indexSearch("content", "她");
//插入一个分隔行
System.out.println();
// 通过String的API搜索关键词
search.StringSearch("她", "D:\\TDDOWNLOAD\\niceFolder");
}
}
可以看出用索引来找几乎不用时间,而字符串匹配需要10ms。这只是个很小很小的txt,上兆呢?答案不言而喻。
但可以发现两者结果有差异,因为建立索引时,要先分词后建立索引,而分词过程中对于一些词的处理与我们理解的内容不太一样(语义偏差或二义),因此会出现分词不正确的情况。(摘自《开发自己的搜索引擎》人民邮电出版社)