这几天项目中用到了luence检索技术,我这里记录一下自己的使用过程,版本号luence为5.5
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
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.*;
import org.apache.lucene.queryparser.classic.MultiFieldQueryParser;
import org.apache.lucene.queryparser.classic.QueryParser;
import org.apache.lucene.search.*;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
import org.apache.lucene.util.Version;
import org.wltea.analyzer.lucene.IKAnalyzer;
import java.nio.file.Paths;
public class Indexer{
private static String[] ids={"1","2","3"};
private static String[] names={"koala世界","alecor","lol"};
private static String[] describes={"数据世界的产物必然会造就不同的进程","I am alecor","This is lol speaking!"};
/**
* 索引存储地址
*/
private static String indexDir="/Users/koala/Documents/temp/a";
/**
* 获取操作索引实体,并添加测试数据
* @param indexDir 索引存储位置
* @return
* @throws Exception
*/
public static IndexWriter getIndexWriter(String indexDir)throws Exception{
IndexWriterConfig writerConfig=new IndexWriterConfig(getAnalyzer());
IndexWriter indexWriter=new IndexWriter(getDirectory(indexDir),writerConfig);
indexWriter.deleteAll();
Document document = null;
//Field.Store.YES或者NO(存储域选项)
//设置为YES表示或把这个域中的内容完全存储到文件中,方便进行文本的还原
//设置为NO表示把这个域的内容不存储到文件中,但是可以被索引,此时内容无法完全还原(doc.get)
for(int i=0;i<ids.length;i++){
document = new Document();
/**
* 其中注意filed 5.5 版本的field 和之前的版本还是有区别的,更加简化了
*
* TextField.TYPE_STORED 索引 分词 存储
*
* TextField.TYPE_NOT_STORED 索引 分词 不存储
*
* 通常情况下 对于大文档部分,不建议存储, 存储它的主键列就行,再根据主键列去找记录(示具体情况耳钉)
*
*/
document.add(new Field("ids", ids[i],TextField.TYPE_STORED));
document.add(new Field("names",names[i], TextField.TYPE_STORED));
document.add(new Field("describes",describes[i], TextField.TYPE_STORED));
indexWriter.addDocument(document);
}
return indexWriter;
}
/**
* 使用分词器
* @return
*/
public static Analyzer getAnalyzer(){
/**
* 这里我使用的是IKAnalyzer分词器(后面的true表示智能分词,通常这种情况就够用了),常见有以下几种
*
* a.1 单字分词:就是按照中文一个字一个字的进行分词,比如:"我们是中国人",分词的效果就是"我","们","是","中","国","人",StandardAnalyzer分词法就是单字分词。
*
* a.2 二分法分词:按照两个字进行切分,比如:"我们是中国人",分词的效果就是:"我们","们是","是中","中国","国人",CJKAnalyzer分词法就是二分法分词
* a.3 词库分词:按照某种算法构造词,然后去匹配已建好的词库集合,如果匹配到就切分出来成为词语,通常词库分词被认为是最好的中文分词算法,如:"我们是中国人",分词的效果就是:"我们","中国人",极易分词
* MMAnalyzer、庖丁分词、IkAnalyzer等分词法就是属于词库分词。
*
*/
return new IKAnalyzer(true);
}
/**
* 得到索引磁盘存储器
* @param indexDir 存储位置
* @return
*/
public static Directory getDirectory(String indexDir){
Directory directory=null;
try {
directory= FSDirectory.open(Paths.get(indexDir));
}catch (Exception e){
e.printStackTrace();
}
return directory;
}
/**
* 获取读索引实体,并打印读到的索引信息
* @return
*/
public static IndexReader getIndexReader(){
IndexReader reader=null;
try {
reader= DirectoryReader.open(getDirectory(indexDir));
//通过reader可以有效的获取到文档的数量
System.out.println("当前存储的文档数::"+reader.numDocs());
System.out.println("当前存储的文档数,包含回收站的文档::"+reader.maxDoc());
System.out.println("回收站的文档数:"+reader.numDeletedDocs());
} catch (Exception e) {
e.printStackTrace();
}
return reader;
}
/**
* 写索引测试,借助Luke观察结果
* @throws Exception
*/
public void Testinsert() throws Exception{
IndexWriter writer=getIndexWriter(indexDir);
writer.close();
// getIndexReader();
}
/**
* 删除索引测试,借助Luke观察结果
* @throws Exception
*/
public void TestDelete(String id)throws Exception{
//测试删除前我们先把上次的索引文件删掉,或者换个目录
IndexWriter writer=getIndexWriter(indexDir);
QueryParser parser=new QueryParser("ids", getAnalyzer());//指定Document的某个属性
Query query=parser.parse(id);//指定索引内容,对应某个分词
// Term term=new Term("names","kl");
//参数是一个选项,可以是一个query,也可以是一个term,term是一个精确查找的值
//此时删除的文档并不会被完全删除,而是存储在一个回收站中的,可以恢复
writer.deleteDocuments(query);
// writer.forceMergeDeletes();//强制合并删除的索引信息,索引量大的时候不推荐使用,真正的删除
writer.commit(); //更改索引要提交,和提交数据库事务一个概念,真正的删除
writer.close();
getIndexReader();
}
/**
* 更新操作测试,借助Luke观察结果
* @throws Exception
*/
public void TestUpdate()throws Exception{
// Lucene并没有提供更新,这里的更新操作相当于新增,他并不会去掉原来的信息
IndexWriter writer = getIndexWriter(indexDir);
try {
Document doc = new Document();
doc.add(new StringField("ids","1",Field.Store.YES));
doc.add(new StringField("names","koalanew",Field.Store.YES));
doc.add(new StringField("describes","chenkailing",Field.Store.YES));
writer.updateDocument(new Term("ids","1"), doc);
} catch (Exception e) {
e.printStackTrace();
} finally {
if(writer!=null) writer.close();
}
}
/**
* 查询测试
*/
public void TestSearchaer(String keyword){
try {
IndexReader reader = getIndexReader();
IndexSearcher searcher = new IndexSearcher(reader);
// 使用IK分词
Analyzer analyzer = new IKAnalyzer(true);
/**
*
* TermQuery 精确查询
* TermRangeQuery 查询一个范围
* PrefixQuery 前缀匹配查询
* WildcardQuery 通配符查询
* BooleanQuery 多条件查询
* PhraseQuery 短语查询
* FuzzyQuery 模糊查询
* Queryparser 万能查询(上面的都可以用这个来查询到)
*
*
*
*
*
*/
// 搜索目标是 contents
// QueryParser parser = new QueryParser( "describes", analyzer );
// 传入关键字,进行分析
// Query query = parser.parse( keyword );
/**
* 多列检索,fields表示需要检索的多列字段, 用数组存储
* clauses 表示的示 字段检索的方式, 有 and not 和 or 三种
*
*/
String[] fields = {"describes"};
// MUST 表示and,MUST_NOT 表示not ,SHOULD表示or
BooleanClause.Occur[] clauses = {BooleanClause.Occur.SHOULD};
// MultiFieldQueryParser表示多个域解析, 同时可以解析含空格的字符串,如果我们搜索"上海 中国"
Query query = MultiFieldQueryParser.parse(keyword, fields, clauses, analyzer);
TopDocs hits = searcher.search(query, 10);
System.out.println(hits.totalHits);
for(ScoreDoc sd:hits.scoreDocs) {
Document doc = searcher.doc(sd.doc);
System.out.println(doc.get("names")+"["+doc.get("describes")+"]-->"+doc.get("ids"));
}
reader.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
其中比较重要的几个知识点 。我都在文中写列备注,如果有不明白的地方可以留言