Lucene[‘lusen] 是一个高性能的 java 全文检索工具包(引擎),现阶段 Apache 的顶级的开源项目,可基于它开发出各种全文搜索的应用。
一个全文检索系统需要做的可以分为两部分,第一部分是建立索引,第二部分是进行检索。下面就结合代码对这两部分进行讲解。
先创建一个由 maven 管理的 java 项目,在 pom 中添加 lucene 依赖
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-core</artifactId>
<version>${lucene.version}</version>
</dependency>
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-queryparser</artifactId>
<version>${lucene.version}</version>
</dependency>
</dependencies>
然后
mvn clean install
把各种依赖导入
建立索引
建立索引是从原始文档到索引文件的过程,索引中包含索引和文档对象。每个文档对象有唯一的 id,文档对象里包含了很多 field,每个 field 有自己的名字(可重复)和相应的值。索引的最小单位是 Term,可以理解为分词之后的每个单词或者不进行分词的整体,也可以建立 n-gram 的 term。
常用的 Field 类如下:
Field 类 | 数据类型 | 是否分词 | 是否索引 | 是否存储 | 备注 |
---|---|---|---|---|---|
StringField(FieldName, FieldValue,Store.YES)) | String | No | Yes | Yes or No | 用于不进行分词的字符串,如订单号 |
LongField(FieldName, FieldValue,Store.YES) | long | Yes | Yes | Yes or No | |
StoredField(FieldName, FieldValue) | 重载方法,支持多种类型 | No | No | Yes | 需要储存的文档信息,但不进行索引和分词 |
TextField(FieldName, FieldValue, Store.NO) | String | Yes | Yes | Yes or No | 需要储存的正文内容 |
TextField(FieldName, reader) | streamming | Yes | Yes | No | 因为是流,lucene猜测内容比较多,会采用Unstored |
建立索引,第一步当然就是创建索引对象
Directory index = FSDirectory.open(Paths.get(indexPath));
因为有些内容需要分词,所以也需要建立文本分析器对象,并把它加入到索引 Writer 的配置中配置 IndexWriter,也对增量更新(IndexWriterConfig.OpenMode.APPEND、IndexWriterConfig.OpenMode.CREATE_OR_APPEND)还是全量更新(IndexWriterConfig.OpenMode.CREATE)进行设置:
Analyzer analyzer = new StandardAnalyzer();
IndexWriterConfig config = new IndexWriterConfig(analyzer);
config.setOpenMode(IndexWriterConfig.OpenMode.CREATE);
IndexWriter writer = new IndexWriter(index, config);
下面就可以对索引进行写操作了,对于每个文档:
// 先创建一个文档(Document 对象)
Document doc = new Document();
// 解析原始文本并往文档中添加相应的 Field
Field pathField = new StringField("path", file.toString(), Field.Store.YES);
Field contentsField = new TextField("contents", buffer.toString(),Field.Store.YES);
doc.add(pathField);
doc.add(contentsField);
writer.addDocument(doc);
最后记得把该关的都关了
index.close();
writer.close();
建立后查看索引内内用
有一个开源项目可以查看索引文件,项目地址:https://github.com/DmitryKey/luke
克隆后进入文件夹“mvn install”,然后
java target/luke-swing-with-deps.jar
会弹出UI界面,选择索引所在文件夹,就可饮查看索引和进行一些简单的操作了。
进行查询
进行查询,首先要创建 IndexReader 对象来读取索引,并且把它交给 IndexSearcher 对象来进行搜索
IndexReader reader = DirectoryReader.open(FSDirectory.open(Paths.get(indexPath)));
IndexSearcher searcher = new IndexSearcher(reader);
对于输入的 String 类型的 query 我们需要对其解析成 IndexSearcher 可以查询的 query 类型
Analyzer analyzer = new StandardAnalyzer();
QueryParser parser = new QueryParser(field, analyzer);
Query query = parser.parse(raw_input);
进行搜索
TopDocs results = searcher.search(query, 10);// 搜索结果,包含了一些搜索的信息
ScoreDoc[] hits = results.scoreDocs;// 搜索所得结果的列表
int numTotalHits = Math.toIntExact(results.totalHits);//一共所得的结果
for (ScoreDoc hit : hits) {
int docID = hit.doc;
Document document = searcher.doc(docID);
System.out.println("文档:" + document.get("path"));
System.out.println(document.getField("contents").stringValue());
System.out.println("相关度:" + hit.score);
System.out.println("================================");
}
搜索常用的方法,评分应该是根据idf来算的
方法 | 说明 |
---|---|
IndexSearcher.search(query, n) | 根据 query 搜索评分最高的 n 条记录 |
IndexSearcher.search(query, filter, n) | 添加过滤策略 |
IndexSearcher.search(query, n, sort) | 添加排序策略 |
IndexSearcher.search(query, filter, n, sort) | 添加过滤策略和排序策略 |
最后把该关的都关了
reader.close();