JAVA代码生成lucene索引
import org.apache.commons.io.FileUtils;
import org.apache.lucene.analysis.cn.smart.SmartChineseAnalyzer;
import org.apache.lucene.document.*;
import org.apache.lucene.index.*;
import org.apache.lucene.queryparser.classic.ParseException;
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 java.io.File;
import java.io.IOException;
import java.nio.file.Paths;
public class LuceneIndexDemo {
// 创建索引(首先创建data数据目录和index生成目录)
public void createIndex() throws IOException {
// 原始文档的路径
File path = new File("D:/Lucene/data");
// 索引存放路径
Directory directory = FSDirectory.open(Paths.get("D:/Lucene/index"));
// 创建SmartChinese分析器
SmartChineseAnalyzer analyzer = new SmartChineseAnalyzer();
// 创建IndexWriterConfig
IndexWriterConfig iwc = new IndexWriterConfig(analyzer);
// 是否使用复合索引格式(默认true 使用复合索引格式)
iwc.setUseCompoundFile(true);
//创建IndexWriter(第三个参数boolean TRUE时是会删除同一个目录下的索引,新建全量索引,FALSE 新建增量索引添加进索引文件内)
IndexWriter indexWriter = new IndexWriter(directory, iwc);
for (File file : path.listFiles()) {
// 创建Document对象
Document document = new Document();
// 文件名称
document.add(new TextField("filename", file.getName(), Field.Store.YES));
// 文件内容
document.add(new TextField("content", FileUtils.readFileToString(file, "GBK"), Field.Store.YES));
// 文件路径
document.add(new StoredField("path", file.getPath()));
// 文件大小
document.add(new NumericDocValuesField("size", FileUtils.sizeOf(file)));
// 写入索引
indexWriter.addDocument(document);
}
// 写入完毕,清理工作
if (indexWriter != null) {
indexWriter.close();
indexWriter = null;
}
}
}
使用复合索引生成的文件目录格式(index目录如下) iwc.setUseCompoundFile(true);
文件名称 | 说明 |
---|---|
_N.cfe | 采用混合格式下该文件包括其他所有格式的文件 |
_N.cfs | 采用混合格式下该文件包括其他所有格式的文件 |
_N.si | 保存段的元数据信息 |
segments_N | 保存提交点的信息 |
write.lock | 写文件锁,用于防止多个IndexWriters同时写一个文件,也就是说同一时间,创建索引的只可能是一个线程 |
不使用复合索引生成的文件目录格式(index目录如下) iwc.setUseCompoundFile(false);
文件名称 | 扩展名 | 说明 |
---|---|---|
Fields | .fnm | 保存域信 |
Field Index | .fdx | 保存指向域数据的指针 |
Field Data | .fdt | 保存域数据 |
Term Dictionary | .tim | 保存词项信息 |
Term Index | .tip | Term Dictionary的索引信息 |
Frequencies | .doc | 记录文档信息,以及文档中词项对应的词频 |
Positions | .pos | 记录词项的位置信息 |
Payloads | .pay | 全文索引的字段,使用了一些像payloads的高级特性会有该文件,保存了term在doc中的一些高级特性 |
Norms | .nvd, .nvm | 文件保存索引字段加权数据 |
Per-Document Values | .dvd, .dvm | lucene的docvalues文件,即数据的列式存储,用作聚合和排序 |
Term Vector Index | .tvx | 记录文档数据记录中的偏移量 |
Term Vector Documents | .tvd | 记录有词项向量的文档的信息 |
Term Vector Fields | .tvf | 词项向量的域级别信息 |
Live Documents | .liv | 存活文件的信息 |
Point values | .dii .dim | 记录被索引的点 |
segments_N | 保存提交点的信息 | |
write.lock | lock | 写文件锁,用于防止多个IndexWriters同时写一个文件,也就是说同一时间,创建索引的只可能是一个线程 |
Lucene Query索引查询
Lucene元数据补充
选项 | 说明 |
---|---|
IndexOptions.NONE | 不索引 |
IndexOptions.DOCS | 只索引文档,词频和位置信息不保存 |
IndexOptions.DOCS_AND_FREQS | 只索引文档和词频,位置信息不保存 |
IndexOptions.DOCS_AND_FREQS_AND_POSITIONS | 索引文档、词频和位置信息 |
IndexOptions.DOCS_AND_FREQS_AND_POSITIONS_AND_OFFSETS | 索引文档、词频、位置信息和偏移量 |
Field类型
Lucene除了LongPoint、IntPoint、StringField、TextField等常用Field类型外,还有基于一些非常实用的类型
类型 | 说明 |
---|---|
SortedDocValuesField | 存储为文本内容的DocValue字段。 |
StoredDocValuesField | 适合索引字段值为文本并且需要按值进行排序的字段 |
SortedSetDocValuesField | 存储多域值的DocValues字段。 |
StoredSetDocValuesField | 适合索引字段值为文本并且需要按值进行分组、聚合等排序的字段 |
NumericDocValuesField | 存储单个数值型的DocValues字段,主要包括int,long,float,double |
SortedNumericDocValuesField | 存储数值型有序数组列表的DocValues字段 |
StoredField | StoredField适合索引只需要保存字段值而不进行其他操作的字段 |
Lucene构建查询
Lucene处理用户输入的查询关键字就是构建Query对象的过程。Lucene搜索需要实例化IndexSearch对象,由IndexSearch对象中的search()方法完成搜索过程,而Query对象作为search()方法的参数。搜索结果保存在TopDocs类型的文档集合中,遍历TopDocs就可以得到文档信息。
构建Query对象
初始化IndexSearch
使用IndexSearch.search()方法完成查询,参数为Query对象
查询结果得到TopDocs文档集合
遍历TopDocs得到文档信息
ScoreDoc:是得分文档对象,其score属性就是相关度。相关度得分是0到1之间的值。1表示相关度最高,0表示不相关。
TopDocs:是匹配搜索条件的前N个搜索结果。
Lucene的搜索结果默认按相关度排序,这个相关度排序是基于内部的Score和DocID,Score又基于关键词的内部评分和索引机制。默认Score高的排前面,如果Score一样,再按索引顺序,先索引的排前面。
TermQuery 词项查询
TermQuery是最简单的,也是最基础的Query。TermQuery可以理解为“词项搜索”,也就是说在索引中搜索某一个词项。如:某个Field中是否包含某个Term。
public void searchIndex() throws Exception {
// 索引存放路径
Directory directory = FSDirectory.open(Paths.get("D:/Lucene/index"));
// 创建IndexReader
IndexReader indexReader = DirectoryReader.open(directory);
// 创建IndexSearcher
IndexSearcher indexSearcher = new IndexSearcher(indexReader);
// 创建查询
Query query = new TermQuery(new Term("content","考试"));
// 获取搜索的结果,指定返回document返回的个数
ScoreDoc[] hits = indexSearcher.search(query, 10).scoreDocs;
// 遍历ScoreDoc
for (ScoreDoc scoreDoc : hits) {
// 根据Document的id找到document对象
Document doc = indexSearcher.doc(scoreDoc.doc);
System.out.print(doc.get("filename") + ": ");
System.out.println(doc.get("path"));
}
indexReader.close();
directory.close();
}
BooleanQuery 布尔查询
BooleanQuery也是时间开发中最常用的一种Query,它的作用可以组合多个查询条件。BooleanQyer本身是一个布尔子句的容器,我们需要使用BooleanClause.Occur连接多个查查询项并表示它们间的关系:
Term term1 = new Term("content", "考试");
Query query1 = new TermQuery(term1);
Term term2 = new Term("content", "全国");
Query query2 = new TermQuery(term2);
// BooleanClause bc = new BooleanClause(query1, BooleanClause.Occur.MUST);
// 合集查询
BooleanQuery bQuery = new BooleanQuery.Builder()
.add(query1, BooleanClause.Occur.MUST)
.add(query2, BooleanClause.Occur.MUST)
.build();
RangeQuery 范围查询
有时用户需要搜索满足某个范围条件内的文档,如:某一时间段内的所有文档。Lucene提供了RangeQuery查询来满足这样的场景需求。
Query rangeQuery = LongPoint.newRangeQuery("size", 10, 50);
PrefixQuery前缀搜索
PrefixQuery就是通过前缀来进行搜索。如果要搜索的Field中包含要查找关键字的前缀,则可以使用该查询。
Term term = new Term("content", "考试");
PrefixQuery query = new PrefixQuery(term);
PhraseQuery 短语搜索
在使用搜索的时候,我们常常查找的并发是一个简单的单词,而是几个不同的关键字,这些关键字紧密相连可以构成一个精确的短语(或者几个关键字中还有其他不相关的内容),要么可能是一句简短的话。PhraseQuery可以满足我们上面的这种搜索需求。
使用PhraseQuery.add方法可以让用户向其内部添加关键字,添加完毕后,还可以通过setSlop()方法来设定关键字之间是否允许或者允许多少个无关词汇存在。
PhraseQuery.Builder builder = new PhraseQuery.Builder();
builder.add(new Term("content","全国"), 2);
builder.setSlop(5);
builder.add(new Term("content","考试"), 4);
PhraseQuery query = builder.build();
PhraseQuery要求短语中的所有次都存在才能匹配上,所以有时我们需要一个比较宽松版本的PhraseQuery —— 虽然对词的顺序敏感,但允许缺少个别词(缺少词的文档分值降低)
FuzzyQuery模糊搜索
FuzzyQuery是一种模糊搜索,它可以简单地识别两个相近的词。如:拼写错误Boss拼成Boos,使用FuzzyQuery可以搜索到正确的结果。
这种模糊搜索的方法是根据用户输入的单个字进行字符串间的查找,这种算法被称为levenshtein算法。
这种算法在比较两个字符串时会会将动作分为三种:加上一个字母,删一个字母,改变一个字母。两个字符串之间进行比较时就是在执行将其中一个字符串,转变为另一个字符串的操作。
每执行一次上述的操作,则相应的就会扣除一定的分数。当比较完毕后,也就是转变完成,此时的得分被称为两者之间的距离也可以称为模糊度。
FuzzyQuery query = new FuzzyQuery(term);
ScoreDoc[] hits = indexSearcher.search(query, 10).scoreDocs;
WildcardQuery 通配符查询
通配符搜索即WildcardQuery。我们可以使用通配符来模糊搜索,WildcardQuery中的通配符有?, *, \ 三种。
Term term = new Term("content", "考试?");
WildcardQuery query = new WildcardQuery(term);