《Lucene由潜入深教程系列》之Lucene入门实例
Posted 嗣金杂谈
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了《Lucene由潜入深教程系列》之Lucene入门实例相关的知识,希望对你有一定的参考价值。
一、Lucene简介
最初Lucene是 Apahce软件基金会Jakarta项目组的一个子项目,它是一个完全开放源码的全文检索工具包。Lucene 是一个基于 Java 的全文信息检索工具包,它不是一个完整的搜索应用程序,而是为你的应用程序提供索引和搜索功能。Lucene的其最初阶段是使用JAVA开发的,不过由于它的强大,逐渐被翻译成了多种语言,如C++、C#、Perl等。目前已经有很多应用程序的搜索功能是基于 Lucene 的,比如 Eclipse 的帮助系统的搜索功能,Nutch(一个WebCrawler工具)、Hadoop(一个基于Lucene的分布式计算平台)。
搜索应用程序和 Lucene 之间的关系图
二、入门实例
1、实例说明
首先,将引入一个文件,利用Lucene来查找其中的关键字,最后使用普通IO流的方式查找,并对两者的查找效率进行比较。
2、开发思路
a、对要进行查找的文档进行预处理
在该例中,主要处理全角标点与半角标点的转换。
b、将大文档切分成多个小文档
这一步主要是为了更好的展示Lucene的一些功能,将文档切分为多个小文档,并给每一个文档一个唯一的ID号。
c、建立关键字索引
d、通过索引查找
3、编码实现
a、引入Lucene的Maven依赖
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-core</artifactId>
<version>6.5.1</version>
</dependency>
<!-- 中文分词器 SmartChineseAnalyzer -->
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-analyzers-smartcn</artifactId>
<version>6.5.1</version>
</dependency>
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-queryparser</artifactId>
<version>6.5.1</version>
</dependency>
b、创建一个StringUtils工具类实现全角标点与半角标点的转换
/**
* String工具类
* Created by Administrator on 2017/5/7.
*/
public class StringUtils {
/**
* 全角/半角转换
* @param line
* @return
*/
public static String replace(String line){
HashMap map = new HashMap();
map.put(",",",");
map.put("。",".");
map.put("(","(");
map.put(")",")");
map.put("|","|");
map.put("《","<");
map.put("》",">");
map.put("【","[");
map.put("】","]");
map.put("?","?");
map.put("“","\"");
map.put("”","\"");
map.put(":",":");
map.put("、",",");
map.put("——","-");
map.put("—","-");
map.put("~","~");
map.put("!","!");
int length = line.length();
for (int i=0;i<length;i++){
//逐个取得长度为1的String,作为查询条件
String charat = line.substring(i,i+1);
//判断Hashmap的key里面是否在出现该String
if (map.containsKey(charat)){
line = line.replace(charat,(String)map.get(charat));
}
}
return line;
}
}
c、创建一个文档工具类FileUtils,实现全角与半角的转换和文件的切割
public class FileUtils {
/**
* 将文件中的全角符转换成半角符
* @param file 原文件
* @param destFile 目标文件
* @return
* @throws IOException
*/
public static File charactorProcess(File file,String destFile) throws IOException {
//创建一个输出流,用于写新文件
BufferedWriter writer = new BufferedWriter(new FileWriter(destFile));
//创建一个输入流,用于读取文件
BufferedReader reader = new BufferedReader(new FileReader(file));
String line = reader.readLine();
while (line!=null){
//替换所有的全角
String newline = StringUtils.replace(line);
writer.write(newline);
//写入行分隔符
writer.newLine();
line = reader.readLine();
}
//关闭输入输出流,将缓冲区数据写入文件
reader.close();
writer.close();
return new File(destFile);
}
/**
* 文件切割
* @param file 待分割的文件
* @param outputPath 分隔文件路径
* @param max_szie 每个文件最大字节大小(byte)
*
*/
public static void splitToSmallFiles(File file,String outputPath,int max_szie) throws IOException {
//文件计数器
int filePointer = 0;
BufferedWriter writer = null;
BufferedReader reader = new BufferedReader(new FileReader(file));
StringBuffer buffer = new StringBuffer();
String line = reader.readLine();
while (line!=null){
//如果读取字符串不为空,则将字符串加入缓冲区,并在每行字符串后面加上回车换行
buffer.append(line).append("\r\n");
//判断当前Buffer内容是否超过最大长度
if (buffer.toString().getBytes().length>max_szie){
//如果达到最大长度,则将缓冲区内数据写入文件
writer = new BufferedWriter(new FileWriter(outputPath+"/output"+filePointer+".txt"));
writer.write(buffer.toString());
writer.close();
filePointer++;
buffer = new StringBuffer();
}
line = reader.readLine();
}
//文件读取完毕,直接将缓冲区数据写入文件
writer = new BufferedWriter(new FileWriter(outputPath+"/output"+filePointer+".txt"));
writer.write(buffer.toString());
writer.close();
}
}
d、创建文件处理类FileProcessor
public class FileProcessor {
/**
* 文件分割处理
* @param file 需要做处理的源文件
* @param outputDir 文件输出路径
* @param max_size 每个文件最大大小
*/
public static void process(File file,String outputDir,int max_size){
try {
FileUtils.splitToSmallFiles(file,outputDir,max_size);
} catch (IOException e) {
e.printStackTrace();
}
}
}
e、执行Main测试:
public static void main( String[] args )
{
String inputFile = "book.txt";
String outputDir = "F:\\work\\LunceneDemo\\test\\";
if(!new File(outputDir).exists()){
new File(outputDir).mkdir();
}
FileProcessor fileProcessor = new FileProcessor();
fileProcessor.process(new File(inputFile),outputDir,1024);
}
此时在F:\work\LunceneDemo\test看到被分割的文件。
f、创建索引类:IndexProcesser
public class IndexProcesser {
private static String INDEX_STORE_PATH = "F:\\work\\LunceneDemo\\index";
/**
* 创建索引
* @param inputDir
*/
public void createIndex(String inputDir){
try {
//创建IndexWriterConfig
IndexWriterConfig indexWriterConfig = new IndexWriterConfig(new
SmartChineseAnalyzer());
//创建索引目录
Directory dir = FSDirectory.open(Paths.get(INDEX_STORE_PATH));
// 创建IndexWriter
IndexWriter writer = new IndexWriter(dir,indexWriterConfig);
File fileDir = new File(inputDir);
//取得所有需要建立索引的文件数组
File[] files = fileDir.listFiles();
for(int i=0;i<files.length;i++){
String fileName = files[i].getName();
//判断文件是否为txt文件
if(fileName.substring(fileName.lastIndexOf(".")).equals(".txt")){
//创建Document
Document document = new Document();
//为文件名创建一个Field
StringField stringField = new StringField("filename",files[i].getName(), Field.Store.YES);
document.add(stringField);
//对于内容只索引不存储
TextField contentField = new TextField("content",
loadFileToString(files[i]),Field.Store.NO);
document.add(contentField);
//把Document加入IndexWriter
writer.addDocument(document);
}
}
writer.close();
} catch (IOException e) {
e.printStackTrace();
}
}
/**
* 从文件中读取内容
* @param file
* @return
*/
public String loadFileToString(File file){
try {
BufferedReader reader = new BufferedReader(new FileReader(file));
StringBuffer buffer = new StringBuffer();
String line = reader.readLine();
while(line!=null){
buffer.append(line);
line = reader.readLine();
}
reader.close();
return buffer.toString();
} catch (IOException e) {
e.printStackTrace();
return null;
}
}
}
g、写一个Main方法执行索引的创建
public static void main(String[] args) {
IndexProcesser processer = new IndexProcesser();
processer.createIndex("F:\\work\\LunceneDemo\\test");
}
此时在F:\work\LunceneDemo\test目录下,生成索引文件
h、创建一个检索类Search,检索要查找的关键字,包含两种检索方式,一种是通过Lucene索引来检索,另一种方式,直接通过IO流读取文件来检索。
public class Search {
private static String INDEX_STORE_PATH = "F:\\work\\LunceneDemo\\index";
/**
* 搜索
* @param searchType 代表要搜索的Filed
* @param searchKey 关键字
*/
public void IndexSearch(String searchType,String searchKey) throws IOException {
System.out.println("==========使用索引方式搜索===========");
IndexReader reader=null;
try {
Directory directory = FSDirectory.open(Paths.get(INDEX_STORE_PATH));
if (directory==null) {
System.out.println("The Lucene index is not exist");
return;
}
reader = DirectoryReader.open(directory);
System.out.println("max num:" + reader.maxDoc());
System.out.println("index num:" + reader.numDocs());
//创建搜索器
IndexSearcher searcher = new IndexSearcher(reader);
/*//创建搜索单元
Term term = new Term(searchType,searchKey);
//由Term生成一个Query
Query query = new TermQuery(term);*/
//搜索开始时间
Date beginTime = new Date();
/* //使用query进行查询,1000为限制返回的结果
TopDocs topDocs = searcher.search(query,1000);
//当有检索到结果时进入循环,打印出文档路径
for(int i=0;i<topDocs.totalHits;i++){
Document docHit = searcher.doc(doc.scoreDocs[i].doc);
System.out.println(docHit.get("fileName"));
}*/
//TopDocs topDocs = searcher.search(query,1000);
QueryParser parse = new QueryParser(searchType, new SmartChineseAnalyzer());
Query query = parse.parse(searchKey);
TopDocs topDocs = searcher.search(query, 1000);
ScoreDoc[] docs = topDocs.scoreDocs;
// 碰撞结果
if(null == docs || docs.length == 0) {
System.out.println("No results for this query.");
return;
}
System.out.println("find "+topDocs.totalHits+" file matches." );
for (ScoreDoc scoreDoc : docs) {
int docID = scoreDoc.doc;
float score = scoreDoc.score;
Document document = searcher.doc(docID);
System.out.println("score: "+score);
System.out.println("find matches in" +document.get("filename"));
}
Date endTime = new Date();
long timeOfSearch = endTime.getTime()-beginTime.getTime();
System.out.println("使用索引方式搜索总耗时 "+timeOfSearch+" ms");
}catch (Exception e){
e.printStackTrace();
}finally {
if(reader!=null) reader.close();
}
}
public void stringSearch(String keyword,String searchDir) throws IOException {
System.out.println("==========使用字符串匹配方式搜索===========");
File filesDir = new File(searchDir);
File[] files = filesDir.listFiles();
//保存文件名和匹配的次数
Map map = new LinkedHashMap();
Date beginTime = new Date();
for(int i=0;i<files.length;i++){
//初始化匹配次数
int hits = 0;
try {
BufferedReader reader = new BufferedReader(new FileReader(files[i]));
StringBuffer buffer = new StringBuffer();
String line = reader.readLine();
while (line!=null){
buffer.append(line);
line = reader.readLine();
}
reader.close();
String stringToSearch = buffer.toString();
int fromIndex = keyword.length();
while((fromIndex=stringToSearch.indexOf(keyword,fromIndex+keyword.length()))!=-1){
hits++;
}
map.put(files[i].getName(),new Integer(hits));
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
Iterator it = map.keySet().iterator();
while(it.hasNext()){
String fileName = (String) it.next();
Integer hits = (Integer) map.get(fileName);
System.out.println("find "+hits.intValue()+" matches in "+fileName);
}
Date endTimes = new Date();
//得到搜索耗费时间
long timeOfSearch = endTimes.getTime()-beginTime.getTime();
System.out.println("使用字符串匹配方式搜索总耗时 "+timeOfSearch+" ms");
}
}
i、执行测试,并对两种实现所花费的时间做对比。
public static void main(String[] args) throws IOException {
Search search = new Search();
search.IndexSearch("content","丁义珍");
System.out.println();
search.stringSearch("丁义珍","F:\\work\\LunceneDemo\\test");
}
三、小结
以上主要通过对一个文档的处理,演示了如何使用Lucene来从文档中检索关键字,关于具体Lucene的API使用细节,后续章节再给大家讲解。在使用Lucene从文档中检索关键字时,具体步骤如下:
(1) 文档预处理
(2) 对要处理的文件内容建立索引
(3) 构建查询对象
(4) 在索引中查找
以上是关于《Lucene由潜入深教程系列》之Lucene入门实例的主要内容,如果未能解决你的问题,请参考以下文章