在构建文档库之后,绝大多数的查询都是基于中文进行查询。使用前面的例子,进行测试时,会发现这样问题:
使用关键词“微信”进行搜索时,能够搜索到没有“微信”这个词出现,但是有“微”和“信”这两个字出现的文档。造成这种错误搜索的原因是,Lucene标准的分析器在分析文档以及查询关键词时,对于英文是基于单词分词的,但是对于中文,是基于汉字进行分词的,所以就造成了上述的查询问题。为了解决这个问题,我们只需要将Lucene的标准分析器换成中文的分析器就可以了,IK Analyzer是基于lucene实现的分词开源框架具有如下特性:
- 算法采用“正向迭代最细粒度切分算法”, 支持细粒度和最大词长两种分词 方式,速度最大支持 80W 字/秒(1600KB/秒) 。
- 支持多子处理器分析模式:中文、数字、字母,并兼容日文、韩文。
- 较小的内存占用,优化词库占有空间,用户可自定义扩展词库。 采用歧义分析算法优化查询关键字的搜索排列组
- 基于lucene 的扩展实现,提高 lucene 检索命中率。
比如对于“基于java语言开发的轻量级的中文分词工具包”,使用IK Analyzer分析后的结果为:
基于|java|语言|开发|的|轻量级|的|中文|分词|工具包|
基本上符合中文的分词,而使用Lucene标准的分析器,则分析的结果就是逐字分开。所以使用IK Analyzer作为分析器构建中文文档库时,不但能减少索引文件大小,还可以加快搜索速度。在使用时,只需要将构建索引时和查询时的分析器修改为IK Analyzer,就可以了。使用步骤如下:
1.在http://code.google.com/p/ik-analyzer/downloads/list下载IK Analyzer 2012FF_hf1.zip(该版本与
Lucene4.x兼容)
2.解压下载文件在classpath加入IKAnalyzer2012FF_u1.jar,并且将stopword.dic和IKAnalyzer.cfg.xml文件
添加到src的根路径下(保证编译时这两个文件copy到classes的根路径下)。
3.在代码中使用IK Analyzer作为分析器。
代码如下,为了便于调试,此处将全部代码贴出:
package com.hsdl.lucene;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.document.Field;
import org.apache.lucene.document.StringField;
import org.apache.lucene.document.TextField;
import org.apache.lucene.index.CorruptIndexException;
import org.apache.lucene.index.DirectoryReader;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.index.IndexWriterConfig.OpenMode;
import org.apache.lucene.queryparser.classic.ParseException;
import org.apache.lucene.queryparser.classic.QueryParser;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.Query;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
import org.apache.lucene.store.LockObtainFailedException;
import org.apache.lucene.util.Version;
import org.apache.tika.Tika;
import org.wltea.analyzer.lucene.IKAnalyzer;
public class LuceneDemo2 {
private static String contentFieldName = "content";
private static Tika tika = new Tika();
public static void main(String[] args) {
//Analyzer analyzer = new StandardAnalyzer(Version.LUCENE_45);
//使用IKAnalyzer构建分析器
Analyzer analyzer = new IKAnalyzer(true);
try {
String docPath = "D:/work/lucene/tika/doc";
String indexPath = "D:/work/lucene/tika/index";
//创建索引
createIndex(analyzer, indexPath, docPath);
//搜索
search(analyzer, indexPath, "微信");
} catch (CorruptIndexException e) {
e.printStackTrace();
} catch (LockObtainFailedException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
} catch (ParseException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
/**
* 创建索引
*
* @param analyzer
* @param indexPath
* @param docPath
* @throws IOException
* @throws CorruptIndexException
* @throws LockObtainFailedException
*/
private static void createIndex(Analyzer analyzer, String indexPath,
String docPath) throws IOException, CorruptIndexException,
LockObtainFailedException {
IndexWriter iwriter;
Directory directory = FSDirectory.open(new File(indexPath));
// 配置IndexWriterConfig
IndexWriterConfig iwConfig = new IndexWriterConfig(Version.LUCENE_45,
analyzer);
iwConfig.setOpenMode(OpenMode.CREATE_OR_APPEND);
iwriter = new IndexWriter(directory, iwConfig);
File file = new File(docPath);
indexDocs(iwriter, file);
iwriter.close();
}
/**
* 搜索
*
* @param analyzer
* @param indexPath
* @param queryStr
* @throws CorruptIndexException
* @throws IOException
* @throws ParseException
*/
private static void search(Analyzer analyzer, String indexPath,
String queryStr) throws CorruptIndexException, IOException,
ParseException {
Directory directory = FSDirectory.open(new File(indexPath));
// 搜索过程**********************************
// 实例化搜索器
IndexReader ireader = DirectoryReader.open(directory);
IndexSearcher isearcher = new IndexSearcher(ireader);
// 使用QueryParser查询分析器构造Query对象
QueryParser qp = new QueryParser(Version.LUCENE_45, contentFieldName, analyzer);
qp.setDefaultOperator(QueryParser.AND_OPERATOR);
Query query = qp.parse(queryStr);
// 搜索相似度最高的5条记录
TopDocs topDocs = isearcher.search(query, 10);
System.out.println("命中:" + topDocs.totalHits);
// 输出结果
ScoreDoc[] scoreDocs = topDocs.scoreDocs;
System.out.println(scoreDocs.length);
for (int i = 0; i < scoreDocs.length; i++) {
Document targetDoc = isearcher.doc(scoreDocs[i].doc);
System.out.println("内容:" + targetDoc.toString());
System.out.println(targetDoc.get("fileName") + "["
+ targetDoc.get("path") + "]");
}
}
/**
* 根据指定存放内容的文件或目录创建索引
* @param iwriter
* @param file
* @throws IOException
*/
public static void indexDocs(IndexWriter iwriter, File file) throws IOException {
if (file.canRead())
if (file.isDirectory()) {
String[] files = file.list();
if (files != null)
for (int i = 0; i < files.length; i++)
indexDocs(iwriter, new File(file, files[i]));
} else {
Document doc = null;
FileInputStream fis=null;
try {
doc = new Document();
doc.add(new StringField("ID", "10000", Field.Store.YES));
fis = new FileInputStream(file);
//此处添加文件内容时,需要根据tika获取Reader对象
doc.add(new TextField(contentFieldName, tika.parse(file)));
doc.add(new StringField("fileName", file.getName(),
Field.Store.YES));
doc.add(new StringField("path", file.getAbsolutePath(),
Field.Store.YES));
iwriter.addDocument(doc);
} finally {
if(fis!=null){
fis.close();
}
}
}
}
}