Lucene流程分析
一、什么是全文检索
1、数据的分类
1)结构化数据:格式固定、长度固定、数据类型固定。例如数据库中的数据
2)非结构化数据:格式不固定、长度不固定、数据类型不固定。例如Word文档、pdf文档、邮件、html、txt
2、数据的查询
1)结构化数据的查询:SQL语句,查询结构化数据的方法。简单、速度快。
2)非结构化数据的查询:从文本文件中找出包含spring单词的文件。
1、目测
2.使用程序吧文档读取到内存中,然后匹配字符串。顺序扫描。
3、把非结构化数据变成结构化数据
先跟根据空格进行字符声拆分,得到一个单词列表,基于单词列表创建一个索引。然后查询索引,根据单词和文档的对应关系找到文档列表。这个过程叫做全文检索。
索引:一个为了提高查询速度,创建某种数据结构的集合.
3.全文检索:先创建索引然后查询索引的过程叫做全文检索.
索引一次创建可以多次使用。表现为每次查询速度很快。
二、全文检索的应用场景
1、搜索引擎:百度、360搜索、谷歌、搜狗
2、站内搜索:论坛搜索、微博、文章搜索
3、电商搜索:淘宝搜索、京东搜索
4、只要是有搜索的地方就可以使用全文检索技术
三、什么是Lucene
Lucene是一个基于Java开发全文检索工具包
四、Lucene实现全文检索的流程
A、绿色表示索引过程,对要搜索的原始内容进行索引构建一个索引库,索引过程包括:
确定原始内容即要搜索的内容→采集文档→创建文档→分析文档→索引文档
B、红色表示搜索过程,从索引库中搜索内容,搜索过程包括:
用户通过搜索界面>创建查询≥执行搜索,从索引库搜索>渲染搜索结果
1、创建索引
1)获得文档
原始文档:要基于那些数据来进行搜索,那么这些数据就是原始文档。
搜索引擎:使用爬虫获得原始文档
站内搜索:数据库中的数据
2)构建文档对象
对应每个原始文档创建一个Document对 象,每个document对象中包含多个域(field)。域中保存就是原始文档数据。
域的名称
域的值
每个文档都有一一个唯一的编号, 就是文档id
3)分析文档
就是分词的过程
1.根据空格进行字符串拆分,得到一-个单词列表。
2.把单词统一转换成小写。
3、去除标点符号。
4、去除停用词。停用词:无意义的词
每个关键词都封装成一个Term对象中
Term中包含两部分内容:关键词所在的域、关键词本身。
不同的域中拆分出来的相同的关键词是不同的Term
4)创建索引
基于关键词列表创建一个索引,保存到索引库中。
索引库中:索引、document对象、关键词文档的对应关系。
通过词语找文档,这种索引的结构叫做倒排索引结构
2、查询索引
1)用户查询接口:用户输入查询条件的地方。例如:百度的搜索框
2)把关键词封装成一个查询对象:要查询的域、要搜索的关键词
3)执行查询:根据要查询的关键词到对应的城上进行搜索。找到关键词,根据关键词找到对应的文档。
4)渲染结果
根据文档的id找到文档对象,对关键词进行高亮显示;分页处理,最终展示给用户看。|
入门程序
1、导入jar坐标
<packaging>jar</packaging>
<dependencies>
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-core</artifactId>
<version>7.4.0</version>
</dependency>
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-analyzers-common</artifactId>
<version>4.10.0</version>
</dependency>
<dependency>
<groupId></groupId>
<artifactId></artifactId>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.6</version>
</dependency>
</dependencies>
2、编写代码
创建索引的步骤
1、创建一个Directory对象,指定索引库保存的位置
2、基于Directory对象创建一个IndexWriter对象
3、取磁盘上的文件,对每一个文件创建一个文档对象
4、向文档对象中添加域
5、把文档对象写入索引
6、关闭IndexWriter对象
实现步骤如下:
public void createIndex() throws Exception{
//1、创建一个Directory对象,指定索引库保存的位置
//Directory directory = new RAMDirectory(); //把索引库保存到内存中
Directory directory = FSDirectory.open(new File("D:\\Downloads\\java_project\\lucene\\index").toPath()); //把索引库保存到磁盘中
//2、基于Directory对象创建一个IndexWriter对象
IndexWriter indexWriter = new IndexWriter(directory, new IndexWriterConfig());
//3、取磁盘上的文件,对每一个文件创建一个文档对象
File dir = new File("D:\\Downloads\\java_project\\index\\index\\src\\test\\day10");
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();
//4、向文档对象中添加域
document.add(fieldName);
document.add(fieldPath);
document.add(fieldContent);
document.add(fieldSize);
//5、把文档对象写入索引
indexWriter.addDocument(document);
}
//6、关闭IndexWriter对象
indexWriter.close();
}
成功的显示效果如下:
会在指定的目录下生成以下五个二进制文件
查询索引的步骤
1、创建一个Directory对象,指定索引库的位置
2、创建一个IndexReader对象
3、创建一个IndexSearcher对象,构造方法中的参数就是IndexReader对象
4、创建一个Query对象,TermQuery
5、执行查询,得到一个TopDocs对象
6、获取查询结果的总记录数
7、获取文档列表
8、打印文档文档的内容
9、关闭IndexReader对象
实现代码如下
public void searchIndex() throws Exception {
//1、创建一个Directory对象,指定索引库的位置
Directory directory = FSDirectory.open(new File("D:\\Downloads\\java_project\\lucene\\index").toPath()); //把索引库保存到磁盘中
//2、创建一个IndexReader对象
IndexReader indexReader = DirectoryReader.open(directory);
//3、创建一个IndexSearcher对象,构造方法中的参数就是IndexReader对象
IndexSearcher indexSearcher = new IndexSearcher(indexReader);
//4、创建一个Query对象,TermQuery
//表示在content域中查找包含public关键字的Team
Query query = new TermQuery(new Term("content", "public"));
//5、执行查询,得到一个TopDocs对象
//参数一:查询对象、参数二:结果返回的最大记录条数
TopDocs topDecs = indexSearcher.search(query, 10);
//6、获取查询结果的总记录数
System.out.println("查询总记录数" + topDecs.totalHits);
//7、获取文档列表
ScoreDoc[] scoreDocs = topDecs.scoreDocs;
//8、打印文档文档的内容
for (ScoreDoc scoreDoc : scoreDocs) {
//取文档ID
int docId = scoreDoc.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("================华丽的分割线===============");
}
//9、关闭IndexReader对象
indexReader.close();
}
成功显示效果如下:
会在控制台显示文件中的内容
分析器
默认使用的是标准分析器StandAnalyzer
1、如何查看分析器的分析效果
使用Analyzer对象的tokenStream方法,可以返回一个TokenStream对象(包含了最终的分词结果)。
实现步骤:
1、创建一个Analyzer对象,StandardAnalyzer对象
2、使用过分析器对象的tokenStream方法获取一个TokenStream对象
3、向TokenStream对象中设置一个引用,相当于一个指针
4、调用TokenStream对象的reset方法,将指针置顶,不过不调用会抛出异常
5、使用while循环遍历TokenStream对象
6、释放资源,关闭TokenStream对象
实现代码如下:
public void testTokenStream() throws Exception {
//1、创建一个Analyzer对象,StandardAnalyzer对象
StandardAnalyzer analyzer = new StandardAnalyzer();
//2、使用过分析器对象的tokenStream方法获取一个TokenStream对象
TokenStream tokenStream = analyzer.tokenStream("", "我在等风的同时,也在等你!!I am here waitting for you !!");
//3、向TokenStream对象中设置一个引用,相当于一个指针
CharTermAttribute charTermAttribute = tokenStream.addAttribute(CharTermAttribute.class);
//4、调用TokenStream对象的reset方法,将指针置顶,不过不调用会抛出异常
tokenStream.reset();
//5、使用while循环遍历TokenStream对象
while (tokenStream.incrementToken()) {
System.out.println(charTermAttribute.toString());
}
//6、释放资源,关闭TokenStream对象
tokenStream.close();
}
成功显示效果如下:
IKAnalyze的使用方法
1)添加IKAnalyze的坐标
<dependency>
<groupId>com.jianggujin</groupId>
<artifactId>IKAnalyzer-lucene</artifactId>
<version>8.0.0</version>
</dependency>
2)把配置文件和扩展词典添加到工程的classpath下
这些配置文件可以再jar包中找到
注意:扩展词典严禁使用windows记事本嫡辑保证扩展词典的嫡码格式是utf-8
扩展词典:添加一些新词
停用词词典:无意义的词或者是敏感词汇