【Lucene】实现全文索引
2. Lucene 实现全文检索的流程
2.1.索引和搜索流程图
绿色表示索引过程,对要搜索的原始内容进行索引构建一个索引库,索引过程包括:确定原始内容即要搜索的内容 -> 采集文档 -> 创建文档 -> 分析文档 ->索引文档
红色表示搜索过程,从索引库中搜索内容,搜索过程包括:用户通过搜索界面 -> 创建查询 -> 执行搜索,从索引库搜索 -> 渲染搜索结果
2.2.创建索引
步骤:
获得文档
原始文档:要基于那些数据来进行搜索,那么这些数据就是原始文档。
搜索引擎:使用爬虫获得原始文档
站内搜索:数据库中的数据。
案例:直接使用 io 流读取磁盘上的文件。
构建文档对象
对应每个原始文档创建一个 Document 对象
每个 document 对象中包含多个域(field)
域中保存的就是原始文档的数据
域的名称、域的值
每个文档都有一个唯一的编号,就是文档id。
注意:每个Document可以有多个Field,不同的Document可以有不同的Field,同一个Document可以有相同的Field(域名和域值都相同)
分析文档
就是分词的过程
根据空格进行字符串拆分,得到一个单词列表
把单词统一转换成小写。
去除标点符号。
去除停用词(无意义的词)
每个关键词都封装成一个 Term 对象中
Term 中包含两部分内容:
关键词所在的域
关键词本身
不同的域中拆分出来的相同的关键词是不同的 Term 。
创建索引
基于关键词列表创建一个索引。保存到索引库中。
索引库中:
索引
document 对象
关键词和文档的对应关系
通过词语找文档,这种索引的结构叫倒排索引结构。如下图:
倒排索引结构也叫反向索引结构,包括索引和文档两部分,索引即词汇表,它的规模较小,而文档集合较大。
2.3.查询索引
用户查询接口
用户输入查询条件的地方
例如:百度的搜索框
把关键词封装成一个查询对象(创建查询)
要查询的域
要搜索的关键词
执行查询
根据要查询的关键词到对应的域上进行搜索。
找到关键词,根据关键词找到 对应的文档
渲染结果
根据文档的 id 找到文档的对象
对关键词进行高亮显示
分页处理
最终展示给用户看。
3.入门程序
3.1.配置开发环境
Lucene 下载
Lucene是开发全文检索功能的工具包,从官方网站下载lucene-7.4.0,并解压。
官方网站:http://lucene.apache.org/
版本:lucene-7.4.0
Jdk要求:1.8以上
使用的jar包
lucene-core-7.4.0.jar
lucene-analyzers-common-7.4.0.jar
3.2.需求
实现一个文件的搜索功能,通过关键字搜索文件,凡是文件名或文件内容包括关键字的文件都需要找出来。还可以根据中文词语进行查询,并且需要支持多个条件查询。
本案例中的原始内容就是磁盘上的文件,如下图:
3.3.创建索引
实现步骤:
第一步:创建一个java工程,并导入jar包。
第二步:创建一个indexwriter对象。
1)指定索引库的存放位置Directory对象
2)指定一个IndexWriterConfig对象。
第二步:创建document对象。
第三步:创建field对象,将field添加到document对象中。
第四步:使用indexwriter对象将document对象写入索引库,此过程进行索引创建。并将索引和document对象写入索引库。
第五步:关闭IndexWriter对象。
代码实现:
/**
* @Auther: lss
* @Date: 2019/5/7 17:27
* @Description:
*/
public class LuceneFirst {
@Test
public void createIndex() throws IOException {
// 创建一个 Directory 对象,指定索引库保存的位置
// 把索引库保存在内存中
// Directory directory = new RAMDirectory();
// 把索引库保存在磁盘中
Directory directory = FSDirectory.open(new File("D:\\IDEA1\\lelucene\\index").toPath());
// 基于 Directory 对象创建一个 IndexWriter 对象
IndexWriter writer = new IndexWriter(directory, new IndexWriterConfig());
// 读取磁盘上的文件,对应每个文件创建一个文档对象。
File dir = new File("D:\\searchsource");
File[] files = dir.listFiles();
for (File file : files) {
// 取文件名
String fileName = file.getName();
// 文件的路径
String filePath = file.getPath();
// 文件的路径
String fileContent = FileUtils.readFileToString(file, "utf-8");
// 文件的大小
long fileSize = FileUtils.sizeOf(file);
// 创建 Field
Field fieldName = new TextField("name", fileName, Field.Store.YES);
Field fieldPath = new TextField("path", filePath, Field.Store.YES);
Field fieldContent = new TextField("content", fileContent, Field.Store.YES);
Field fieldSize = new TextField("size", fileSize + "", Field.Store.YES);
// 创建文档对象
Document document = new Document();
// 向文档对象中添加域
document.add(fieldName);
document.add(fieldPath);
document.add(fieldContent);
document.add(fieldSize);
// 把文档对象写入索引库
writer.addDocument(document);
}
// 关闭 indexWriter 对象
writer.close();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
执行测试: 生成的索引库
这里生成的了一堆看不懂的文件,这些文件没办法用普通的文本编辑器查看,这该怎么办,有个小软件 luck 可以查看索引库。我会把文件上传到下载上。下载
使用 luke 查看索引库中的内容
我们使用的luke的版本是luke-7.4.0,跟lucene的版本对应的。可以打开7.4.0版本的lucene创建的索引库。需要注意的是此版本的Luke是jdk9编译的,所以要想运行此工具还需要jdk9才可以(PS:jdk 1.8貌似也是可以的)。
3.4.查询索引
实现步骤:
第一步:创建一个Directory对象,也就是索引库存放的位置。
第二步:创建一个indexReader对象,需要指定Directory对象。
第三步:创建一个indexsearcher对象,需要指定IndexReader对象
第四步:创建一个TermQuery对象,指定查询的域和查询的关键词。
第五步:执行查询。
第六步:返回查询结果。遍历查询结果并输出。
第七步:关闭IndexReader对象
代码实现:
@Test
public void searchIndex() throws Exception {
// 创建一个 Directory 对象,指定索引库的位置
Directory directory = FSDirectory.open(new File("D:\\IDEA1\\lelucene\\index").toPath());
// 创建一个 IndexReader 对象
IndexReader indexReader = DirectoryReader.open(directory);
// 创建一个Indexsearcher 对象,构造方法中的参数 indexReader 对象。
IndexSearcher indexSearcher = new IndexSearcher(indexReader);
// 创建一个 Query 对象,TermQuery
Query query = new TermQuery(new Term("name", "spring"));
// 执行查询,得到一个 TopDocs 对象
// 参数1:查询对象 参数2:查询结果返回的最大记录数
TopDocs topDocs = indexSearcher.search(query, 10);
// 取查询结果的总记录数
System.out.println("查询总记录数:" + topDocs.totalHits);
// 取文档列表
ScoreDoc[] scoreDocs = topDocs.scoreDocs;
// 打印文档中的内容
for (ScoreDoc doc : scoreDocs) {
// 取文档 id
int docId = doc.doc;
// 根据 id 取文档对象
Document document = indexSearcher.doc(docId);
System.out.println(document.get("name"));
System.out.println(document.get("path"));
// System.out.println(document.get("content"));
System.out.println(document.get("size"));
System.out.println("------------------华丽的分割线");
}
// 关闭 IndexReader 对象
indexReader.close();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
执行测试:
这里没有打印内容,太多了不好截屏,同学们可以自行测试。
我们使用 luck 查看分词的结果,发现对于中文是不友好的,英文以按照单词分,而中文只能是一个字一个字的,
搜索英文是没有问题的,中文就不行了,单个字搜索还可以。这就有了问题,这就引入了一个分析器。
4.分析器
4.1.分析器的分词效果
代码实现:
@Test
public void testTokenStream() throws Exception {
// 创建一个标准分析器对象
Analyzer analyzer = new StandardAnalyzer();
// 获得tokenStream对象
// 第一个参数:域名,可以随便给一个
// 第二个参数:要分析的文本内容
TokenStream tokenStream = analyzer.tokenStream("", "The Spring Framework provides a comprehensive programming and configuration model.");
// TokenStream tokenStream = analyzer.tokenStream("", "单字分词:就是按照中文一个字一个字地进行分词。如:“我爱中国”");
// 添加一个引用,可以获得每个关键词
CharTermAttribute charTermAttribute = tokenStream.addAttribute(CharTermAttribute.class);
// 添加一个偏移量的引用,记录了关键词的开始位置以及结束位置
OffsetAttribute offsetAttribute = tokenStream.addAttribute(OffsetAttribute.class);
// 将指针调整到列表的头部
tokenStream.reset();
// 遍历关键词列表,通过incrementToken方法判断列表是否结束
while (tokenStream.incrementToken()) {
// 关键词的起始位置
// System.out.println("start->" + offsetAttribute.startOffset());
// 取关键词
System.out.println(charTermAttribute);
// 结束位置
// System.out.println("end->" + offsetAttribute.endOffset());
}
// 关闭 tokenStream 对象
tokenStream.close();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
**执行测试:**英文
中文:
对中文不友好,这不行啊,我们查看源码发现 创建索引库我们没有指定分词器,使用的是默认的分词器 StandardAnalyzer
下面介绍中文分析器
4.2.中文分析器
4.2.1.Lucene 自带中文分词器
StandardAnalyzer:
单字分词:就是按照中文一个字一个字地进行分词。如:“我爱中国”,
效果:“我”、“爱”、“中”、“国”。
SmartChineseAnalyzer:
对中文支持较好,但扩展性差,扩展词库,禁用词库和同义词库等不好处理。
4.2.2.IKAnalyzer
使用方法:
第一步:把jar包添加到工程中
第二步:把配置文件和扩展词典和停用词词典添加到classpath下(hotword.dic 和 stopword.dic,配文件 IKAnalyzer.cfg.xml)
注意:hotword.dic和ext_stopword.dic文件的格式为UTF-8,注意是无BOM 的UTF-8 编码也就是说禁止使用windows记事本编辑扩展词典文件
使用EditPlus.exe保存为无BOM 的UTF-8 编码格式,如下图:
-**扩展词典:**添加一些新词
**停用词词典:**无意义的词或者是敏感词汇
4.3.使用自定义分析器
代码实现:
@Test
public void addDocument() throws Exception {
// 创建一个 IndexWriter 对象,需要使用 IKAnalyzer 作为分析器
Directory directory = FSDirectory.open(new File("D:\\IDEA1\\lelucene\\index").toPath());
// 创建一个 IndexWriter 对象,需要使用 IKAnalyzer 作为分析器
IndexWriterConfig indexWriterConfig = new IndexWriterConfig(new IKAnalyzer());
IndexWriter writer = new IndexWriter(directory, indexWriterConfig);
// 创建一个 document 对象
Document document = new Document();
// 向 document 对象添加域
document.add(new TextField("name", "新添加的文件", Field.Store.YES));
document.add(new TextField("content", "新添加的文件内容", Field.Store.NO));
document.add(new StoredField("path", "C:/temp/hello"));
// 把文档添写入索引库
writer.addDocument(document);
// 关闭索引库
writer.close();
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
使用上面分词效果的代码测试:
以后我们在创建索引库时,使用 IKAnalyzer 就可以了。
---------------------
【Lucene】实现全文索引的更多相关文章
- 深度解析 Lucene 轻量级全文索引实现原理
一.Lucene简介 1.1 Lucene是什么? Lucene是Apache基金会jakarta项目组的一个子项目: Lucene是一个开放源码的全文检索引擎工具包,提供了完整的查询引擎和索引引擎, ...
- Lucene就是这么简单
什么是Lucene?? Lucene是apache软件基金会发布的一个开放源代码的全文检索引擎工具包,由资深全文检索专家Doug Cutting所撰写,它是一个全文检索引擎的架构,提供了完整的创建索引 ...
- Lucene原理之概念
概念: 数据分两种: 1.结构化数据:指具有固定格式或有限长度的数据,如数据库,元数据等. 2.非结构化数据:指不定长或无固定格式的数据,如邮件,word文档等.(半结构化数据:如XML,HTML等, ...
- Lucene Index Search
转发自: https://my.oschina.net/u/3777556/blog/1647031 什么是Lucene?? Lucene 是 apache 软件基金会发布的一个开放源代码的全文检索 ...
- NoSQL 35 个非主流数据库
几乎每个Web开发人员都有自己喜欢的数据库,或自己最熟悉的数据库,但最常见的无外乎以下几种: MySQL PostgreSQL MSSQL SQLite MS Access 或是更简单的XML,文本文 ...
- Sql 调优总结
1前言 Sql 语句调优对应用性能非常重要,看了几篇文章,总结了一下数据库优化的方法. 2 数据库 Sql 优化 1 对查询进行优化,要尽量避免全表扫描,首先应考虑在 where 及 order by ...
- Neo4j 3.5发布,在索引方面大幅增强
Neo4j 3.5版本已正式发布,这也是Neo4j宣布企业版闭源以来发布的第一个版本. 这个版本在性能.资源使用率以及安全方面均有增强,我们可以先快速浏览一下这个版本: 全文索引 基于Index的快速 ...
- 全文索引-lucene,solr,nutch,hadoop之nutch与hadoop
全文索引-lucene.solr.nutch,hadoop之lucene 全文索引-lucene.solr,nutch,hadoop之solr 我在去年的时候,就想把lucene,solr.nutch ...
- 全文索引Elasticsearch,Solr,Lucene
最近项目组安排了一个任务,项目中用到了全文搜索,基于全文搜索 Solr,但是该 Solr 搜索云项目不稳定,经常查询不出来数据,需要手动全量同步,而且是其他团队在维护,依赖性太强,导致 Solr 服务 ...
- 5分钟了解lucene全文索引
一.Lucene介绍及应用 Apache Lucene是当下最为流行的开源全文检索工具包,基于JAVA语言编写. 目前基于此工具包开源的搜索引擎,成熟且广为人知的有Solr和Elasticsearch ...
随机推荐
- +Java中的native关键字浅析(Java+Native+Interface)++
JNI是Java Native Interface的 缩写.从Java 1.1开始,Java Native Interface (JNI)标准成为java平台的一部分,它允许Java代码和其他语言写的 ...
- HDU1151 Air Raid —— 最小路径覆盖
题目链接:https://vjudge.net/problem/HDU-1151 Air Raid Time Limit: 2000/1000 MS (Java/Others) Memory L ...
- linux设备驱动第三篇:如何写一个简单的字符设备驱动?
在linux设备驱动第一篇:设备驱动程序简介中简单介绍了字符驱动,本篇简单介绍如何写一个简单的字符设备驱动.本篇借鉴LDD中的源码,实现一个与硬件设备无关的字符设备驱动,仅仅操作从内核中分配的一些内存 ...
- SQL Server 语法注意
1.order by select a1,a1 from temp order by a1 此语句在高版本中执行提示a1列名不明确,在低版本中执行成功!
- 上帝造题的七分钟2/花神游历各国/GSS4 线段树维护区间开方 By cellur925
题目传送门 或者 另一个传送门 询问区间和都好说.但是开方?? 其实是这样的,一个数(1e9)以内连续开方6次就会变成1,于是我们就可在开方操作上进行暴力修改.暴力修改的意思其实也就是找到叶子节点进行 ...
- C语言经典程序1
//打印2-200的所有素数 (除了1和它本身,不能被小于它的其它数整除的数称为素数) ; ;i<;i++) //i遍历2-200 { ; //先默认这个数为素数 ;j<i-;j++) / ...
- ACM二分查找模板
int main(){ == key int m; while ( l <= r ) { m = ( l + r ) >> 1; if ( x[m] == key ) return ...
- 51nod 1024 矩阵中不重复的元素
1024 矩阵中不重复的元素 题目来源: Project Euler 基准时间限制:1 秒 空间限制:131072 KB 分值: 10 难度:2级算法题 收藏 关注 一个m*n的矩阵. 该矩阵的 ...
- 对数组随机赋值,并输出(Arrays.toString(arr))
import java.util.Arrays; public class Demo { public static void main(String[] args) { int[] arr = ne ...
- 在Windows上部署Zabbix客户端
将Zabbix for Windows客户端拷贝到windows系统的c盘,修改配置文件的相关配置项后,打开cmd窗口执行: # 安装服务 c:\zabbix\bin\win32\zabbix_age ...