【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 ...
随机推荐
- Apache POI组件操作Excel,制作报表(四)
Apache POI组件操作Excel,制作报表(四) 博客分类: 探索实践 ExcelApacheSpringMVCServlet 上一篇我们介绍了如何制作复杂报表的分析和设计,本篇结合S ...
- frameset使用总结
frameset使用总结 2009-06-15 00:59 213人阅读 评论(0) 收藏 举报 框架bordermicrosoftinternethtml文档 FRAMESET 元素是 FRAME ...
- 函数,#include <>和#include " "区别
l 4..函数定义到使用分三步:1.声明.2.定义函数.3.调用函数. 2.函数的定义格式:返回值 函数名(形参列表){ 函数体; return } 命名规则:1.只能由字母.数字.下划线或者美元符号 ...
- POJ3468:A Simple Problem with Integers (线段树||树状数组||Splay解决基本问题的效率对比)
You have N integers, A1, A2, ... , AN. You need to deal with two kinds of operations. One type of op ...
- [USACO 2017DEC] Greedy Gift Takers
[题目链接] https://www.lydsy.com/JudgeOnline/problem.php?id=5139 [算法] 二分答案 时间复杂度 : O(NlogN^2) [代码] #incl ...
- bzoj1007 [HNOI2008]水平可见直线——单调栈
题目:https://www.lydsy.com/JudgeOnline/problem.php?id=1007 可以把直线按斜率从小到大排序,用单调栈维护,判断新直线与栈顶的交点和栈顶与它之前直线的 ...
- bzoj 4521: [Cqoi2016]手机号码【数位dp】
比较基础的数位dp,dfs的时候带上上一位,上上位,是否已经有连续3个相同位,是否有4,是否有8即可 但是要注意两点(在洛谷上一直70) 当l=1e10的时候,直接输出clc(r)即可,因为如果再减去 ...
- 洛谷 P4011 孤岛营救问题【bfs】
注意: 一个点可能有多把钥匙,所以把每个点有钥匙的情况状压一下 两个点之间有障碍的情况只给出了单向,存的时候记得存一下反向 b[i][j]表示当前点拥有钥匙的状态,g[x1][y1][x2][y2]表 ...
- 只需3步,即可将你的Chromium Edge 浏览器设置成中文
最近,Chromium Edge 浏览器推出了添加语言包的功能,于是我们可以轻松将其界面设置成中文的. 第1步: 升级浏览器到最新版 在Chromium Edge 浏览器的地址栏中输入: edge:/ ...
- CF767C Garland 【树形dp】By cellur925
一句话题意:给定一个树,树有点权,要求把树的某些边删去,使树变成三个部分,每部分点权值和相等. 我们很容易想到,再读入的时候记录所有点的点权之和,点权除以3是最后权值相等的值.如果不能整除3一定无解, ...