ElasticSearch&Lucene

百亿级数据搜索引擎,Lucene,有关java的实用操作!

2020-01-04  本文已影响0人  javap

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);
上一篇下一篇

猜你喜欢

热点阅读