全文检索:Apache Lucene框架入门实例

简介:

一 简介

Lucene属于Apache开源项目的一部分,是一个开源的全文检索引擎工具包,但它不是一个完整的全文检索引擎,而是一个全文检索引擎的架构,提供了完整的查询引擎和索引引擎,部分文本分析引擎(英文与德文两种西方语言)

Lucene的目的是为软件开发人员提供一个简单易用的工具包,以方便在目标系统中实现全文检索的功能,或者是以此为基础建立起完整的全文检索引擎。在Java开发环境里Lucene是一个成熟的免费开源工具。就其本身而言,Lucene是当前以及最近几年最受欢迎的免费Java信息检索程序库。人们经常提到信息检索程序库,虽然与搜索引擎有关,但不应该将信息检索程序库与搜索引擎相混淆

注:以上介绍参考至百度百科

在使用Lucene建立索引时,可以选择将索引文件存储在内存中或者磁盘里。下面我将分别介绍基于这两种存储方式的全文索引的创建与检索

二 基于内存的索引创建与检索

首先需要做的是下载相关的jar包,下载地址是:http://lucene.apache.org/core/downloads.html

其次,在正式介绍下面的内容之前,至少需要导入以下三个jar包:

  • lucene-analyzers-common-6.2.1.jar

  • lucene-core-6.2.1.jar

  • lucene-queryparser-6.2.1.jar

基于内存的全文索引示例代码如下:

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
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
package  cn.zifangsky.lucene;
 
import  java.io.IOException;
import  java.util.HashMap;
import  java.util.Map;
import  java.util.Map.Entry;
 
import  org.apache.lucene.analysis.Analyzer;
import  org.apache.lucene.analysis.standard.StandardAnalyzer;
import  org.apache.lucene.document.Document;
import  org.apache.lucene.document.Field;
import  org.apache.lucene.document.StringField;
import  org.apache.lucene.document.TextField;
import  org.apache.lucene.index.DirectoryReader;
import  org.apache.lucene.index.IndexReader;
import  org.apache.lucene.index.IndexWriter;
import  org.apache.lucene.index.IndexWriterConfig;
import  org.apache.lucene.queryparser.classic.ParseException;
import  org.apache.lucene.queryparser.classic.QueryParser;
import  org.apache.lucene.search.IndexSearcher;
import  org.apache.lucene.search.Query;
import  org.apache.lucene.search.ScoreDoc;
import  org.apache.lucene.store.Directory;
import  org.apache.lucene.store.RAMDirectory;
 
public  class  Demo1 {
 
     /**
      * 创建索引
     
      * @param sourceMap
      *            待索引的内容
      * @return
      */
     public  static  Directory createIndex(Map<String, String> sourceMap) {
         //1 创建一个默认的词法分析器
         Analyzer analyzer =  new  StandardAnalyzer();
 
         //2 设置索引文件存储位置,可以存储到磁盘和内存中,这里设置为存储到内存
         Directory directory =  new  RAMDirectory();  // 存储到内存
 
         //3 索引的写入
         IndexWriterConfig config =  new  IndexWriterConfig(analyzer);
         try  {
             IndexWriter indexWriter =  new  IndexWriter(directory, config);
 
             //将内容添加到索引中,每本书表示一个“文档”,并将每个文档进行存储
             if  (!sourceMap.isEmpty()) {
                 for  (Entry<String, String> source : sourceMap.entrySet()) {
                     Document document =  new  Document();
                     //标题需要分词,使用TextField
                     document.add( new  TextField( "title" , source.getKey(), Field.Store.YES));
                     //作者不需要分词,使用StringField
                     document.add( new  StringField( "author" , source.getValue(), Field.Store.YES));
                     indexWriter.addDocument(document);
                 }
             }
             indexWriter.close();
         catch  (IOException e) {
             e.printStackTrace();
         }
         return  directory;
     }
 
     /**
      * 搜索
     
      * @param directory
      * @param searchWord
      *            搜索关键词
      */
     public  static  void  readIndex(Directory directory, String searchWord) {
         int  preHits =  10 //获取前面多少个结果
 
         try  {
             //1 打开一个文档
             IndexReader indexReader = DirectoryReader.open(directory);
             IndexSearcher indexSearcher =  new  IndexSearcher(indexReader);
             Analyzer analyzer =  new  StandardAnalyzer();
 
             //2 设置使用关键字检索,这里是检索标题
             QueryParser parser =  new  QueryParser( "title" , analyzer);
             Query query = parser.parse(searchWord);
 
             //3 获取检索到的结果
             System.out.println( "总共有 "  + indexSearcher.count(query) +  " 个结果" );
             ScoreDoc[] hits = indexSearcher.search(query, preHits).scoreDocs;
 
             System.out.println( "当前有 "  + hits.length +  " 个结果,内容分别如下:" );
             //遍历检索到的“文档”
             for  ( int  i =  0 ; i < hits.length; i++) {
                 int  docId = hits[i].doc;
                 Document hitDoc = indexSearcher.doc(docId);
                 System.out.println( "《 "  + hitDoc.get( "title" ) +  "》    作者: "  + hitDoc.get( "author" ));
             }
             indexReader.close();
         catch  (IOException | ParseException e) {
             e.printStackTrace();
         }
     }
 
     public  static  void  main(String[] args) {
         Map<String, String> books =  new  HashMap<>();
         books.put( "Java编程思想" "Bruce Eckel" );
         books.put( "Java8实战" "Raoul-Gabriel Urma" );
         books.put( "Spring入门经典" "Mert Caliskan" );
         books.put( "Spring实战" "Craig Walls" );
         books.put( "Spring Boot实战" "汪云飞" );
         books.put( "Redis实战" "Josiah L. Carlson" );
 
         Directory directory = Demo1.createIndex(books);
         Demo1.readIndex(directory,  "Spring" );
//      Demo1.readIndex(directory, "实战");
     }
}

上面代码并不复杂,而且还有详细的注释,因此一些细节流程我就不多做介绍了。只是需要强调的一点是:

在创建索引时使用了这样的代码:

1
document.add( new  TextField( "title" , source.getKey(), Field.Store.YES));

其中,上面的Field.Store.YES表示在创建索引的同时将内容的原文(也就是:source.getKey())也存储到内存中。如果我们选择了Field.Store.NO,在创建索引和检索的时候都是没问题的,但是在最后是没法提取检索关键字所在的原文内容的

最后的输出结果如下:

1
2
3
4
5
总共有 3 个结果
当前有 3 个结果,内容分别如下:
《 Spring Boot实战》    作者: 汪云飞
《 Spring实战》    作者: Craig Walls
《 Spring入门经典》    作者: Mert Caliskan

三 基于磁盘的索引创建与检索

示例代码如下:

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
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
package  cn.zifangsky.lucene;
 
import  java.io.BufferedReader;
import  java.io.File;
import  java.io.FileReader;
import  java.io.FilenameFilter;
import  java.io.IOException;
import  java.util.ArrayList;
import  java.util.List;
 
import  org.apache.lucene.analysis.Analyzer;
import  org.apache.lucene.analysis.standard.StandardAnalyzer;
import  org.apache.lucene.document.Document;
import  org.apache.lucene.document.Field.Store;
import  org.apache.lucene.document.StringField;
import  org.apache.lucene.document.TextField;
import  org.apache.lucene.index.DirectoryReader;
import  org.apache.lucene.index.IndexWriter;
import  org.apache.lucene.index.IndexWriterConfig;
import  org.apache.lucene.queryparser.classic.QueryParser;
import  org.apache.lucene.search.IndexSearcher;
import  org.apache.lucene.search.Query;
import  org.apache.lucene.search.ScoreDoc;
import  org.apache.lucene.store.Directory;
import  org.apache.lucene.store.FSDirectory;
 
public  class  Demo2 {
 
     public  static  void  main(String[] args) {
         Demo2.createIndex( "D:/test/source" "D:/test/index" );
         Demo2.searchIndex( "D:/test/index" "apache" );
     }
 
     /**
      * 给一个目录下的所有文本文件创建索引
     
      * @param sourceDir
      *            待索引的文件目录
      * @param indexDir
      *            索引文件存储目录
      * @return
      */
     public  static  void  createIndex(String sourceDir, String indexDir) {
         List<File> fileList = getFileList(sourceDir);
 
         if  (fileList.size() >  0 ) {
             // 遍历文件并分别创建索引
             for  (File file : fileList) {
                 StringBuffer stringBuffer =  new  StringBuffer();
                 stringBuffer.append(getFileContent(file));
 
//              System.out.println("fileName: " + file.getName() + "    filePath: " + file.getPath());
 
                 Analyzer analyzer =  new  StandardAnalyzer();
                 try  {
                     File indexFile =  new  File(indexDir);
                     if  (!indexFile.exists()) {
                         indexFile.mkdirs();
                     }
 
                     //存储到文件中
                     Directory directory = FSDirectory.open( new  File(indexDir).toPath());
                     IndexWriterConfig config =  new  IndexWriterConfig(analyzer);
                     IndexWriter indexWriter =  new  IndexWriter(directory, config);
 
                     Document document =  new  Document();
                     document.add( new  TextField( "fileName" , file.getName(), Store.YES));
                     document.add( new  TextField( "content" , stringBuffer.toString(), Store.YES));
                     document.add( new  StringField( "path" , file.getPath(), Store.YES));
 
                     indexWriter.addDocument(document);
                     indexWriter.commit();
 
                     indexWriter.close();
                 catch  (IOException e) {
                     e.printStackTrace();
                 }
             }
         }
     }
 
     /**
      * 在索引目录下检索关键字
     
      * @param indexDir
      *            索引文件存储目录
      * @param searchWord
      *            搜索的关键字
      */
     public  static  void  searchIndex(String indexDir, String searchWord) {
         Analyzer analyzer =  new  StandardAnalyzer();
 
         try  {
             //从一个磁盘目录中检索
             Directory directory = FSDirectory.open( new  File(indexDir).toPath());
             DirectoryReader directoryReader = DirectoryReader.open(directory);
             IndexSearcher indexSearcher =  new  IndexSearcher(directoryReader);
 
             // 检索正文
             QueryParser queryParser =  new  QueryParser( "content" , analyzer);
             Query query = queryParser.parse(searchWord);
 
             // 检索前1000个结果
             System.out.println( "总共有 "  + indexSearcher.count(query) +  " 个结果" );
             ScoreDoc[] hits = indexSearcher.search(query,  1000 ).scoreDocs;
 
             System.out.println( "当前有 "  + hits.length +  " 个结果,分别如下:" );
             for  ( int  i =  0 ; i < hits.length; i++) {
                 int  docId = hits[i].doc;
                 Document hitDoc = indexSearcher.doc(docId);
                 System.out.println( "文件名: "  + hitDoc.get( "fileName" ) +  "    路径: "  + hitDoc.get( "path" ));
                 // System.out.println(hitDoc.get("content"));
             }
             directoryReader.close();
             directory.close();
         catch  (Exception e) {
             e.printStackTrace();
         }
     }
 
     /***
      * 获取一个目录下的所有文件
     
      * @param sourceDir
      *            文件目录
      * @return 所有文件的集合
      */
     private  static  List<File> getFileList(String sourceDir) {
         File dir =  new  File(sourceDir);
         if  (dir.isDirectory()) {
             // 返回指定格式的文本文件
             File[] files = dir.listFiles( new  FilenameFilter() {
 
                 public  boolean  accept(File dir, String name) {
                     return  name.endsWith( ".txt" ) || name.endsWith( ".log" ) || name.endsWith( ".xml" );
                 }
             });
 
             List<File> fileList =  new  ArrayList<File>();
             if  (files.length >  0 ) {
                 for  (File tmpFile : files) {
                     fileList.add(tmpFile);
                 }
             }
             return  fileList;
         }
         return  null ;
     }
 
     /**
      * 获取一个文本文件的所有内容
     
      * @param file
      * @return
      */
     private  static  StringBuffer getFileContent(File file) {
         try  {
//          BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream(file), "UTF-8"));
             BufferedReader reader =  new  BufferedReader( new  FileReader(file));
             String line =  null ;
             StringBuffer stringBuffer =  new  StringBuffer();
             while  ((line = reader.readLine()) !=  null ) {
                 stringBuffer.append(line +  "\n" );
             }
 
             reader.close();
             return  stringBuffer;
         catch  (Exception e) {
             e.printStackTrace();
         }
         return  null ;
     }
}

从上面的代码可以看出,索引存储到内存或者磁盘上,其基本步骤是差不多的,只是使用的类稍有区别。同时多了一些基本的IO操作代码,包括文件的读写等操作

运行上面的索引创建方法之后,打开磁盘上的“D:/test/index”,可以发现多了很多这样的文件:

wKioL1hHiFShS6vOAACsEwCrR8w273.png

最后,检索关键字“apache”,检索到的结果如下:

1
2
3
4
5
总共有 3 个结果
当前有 3 个结果,分别如下:
文件名: Lucene简介.txt    路径: D:\test\source\Lucene简介.txt
文件名: H5_ws.log    路径: D:\test\source\H5_ws.log
文件名: Shiro简介.txt    路径: D:\test\source\Shiro简介.txt

可以发现,有三个文件包含“apache”这个关键字

至此,Lucene的入门介绍到此结束




本文转自 pangfc 51CTO博客,原文链接:http://blog.51cto.com/983836259/1880295,如需转载请自行联系原作者

相关文章
|
6月前
|
Java 应用服务中间件 Apache
从零手写实现 apache Tomcat-01-入门介绍
创建简易Tomcat涉及理解其作为Java服务器的角色,建立HTTP服务器,实现Servlet接口处理动态和静态内容,以及启动和关闭服务器。项目mini-cat是一个简化版Tomcat实现,支持Servlet、静态网页和基础功能。可通过maven添加依赖并运行测试类快速体验。开源项目位于[GitHub](https://github.com/houbb/minicat)。
|
27天前
|
存储 数据挖掘 数据处理
Apache Paimon 是一款高性能的数据湖框架,支持流式和批处理,适用于实时数据分析
【10月更文挑战第8天】随着数据湖技术的发展,越来越多企业开始利用这一技术优化数据处理。Apache Paimon 是一款高性能的数据湖框架,支持流式和批处理,适用于实时数据分析。本文分享了巴别时代在构建基于 Paimon 的 Streaming Lakehouse 的探索和实践经验,包括示例代码和实际应用中的优势与挑战。
53 1
|
29天前
|
数据挖掘 物联网 数据处理
深入探讨Apache Flink:实时数据流处理的强大框架
在数据驱动时代,企业需高效处理实时数据流。Apache Flink作为开源流处理框架,以其高性能和灵活性成为首选平台。本文详细介绍Flink的核心特性和应用场景,包括实时流处理、强大的状态管理、灵活的窗口机制及批处理兼容性。无论在实时数据分析、金融服务、物联网还是广告技术领域,Flink均展现出巨大潜力,是企业实时数据处理的理想选择。随着大数据需求增长,Flink将继续在数据处理领域发挥重要作用。
|
2月前
|
前端开发 JavaScript Java
Apache Wicket 框架:踏上从新手到英雄的逆袭之路,成就你的编程传奇!
【9月更文挑战第4天】Apache Wicket是一款基于Java的开源Web应用框架,以简洁、易维护及强大功能著称。它采用组件化设计,让页面开发更为模块化。Wicket的简洁编程模型、丰富的组件库、良好的可维护性以及对Ajax的支持,使其成为高效开发Web应用的理想选择。下文将通过解析Wicket的基本概念与特性,帮助读者深入了解这一框架的优势。
88 1
|
4月前
|
关系型数据库 API Apache
Flink CDC:基于 Apache Flink 的流式数据集成框架
本文整理自阿里云 Flink SQL 团队研发工程师于喜千(yux)在 SECon 全球软件工程技术大会中数据集成专场沙龙的分享。
18123 11
Flink CDC:基于 Apache Flink 的流式数据集成框架
|
3月前
|
开发框架 Dubbo 应用服务中间件
微服务开发框架-----Apache Dubbo
这篇文章介绍了Apache Dubbo微服务开发框架,它提供RPC通信和微服务治理能力,支持服务发现、负载均衡和流量治理等功能,并强调了Dubbo在微服务规模化实践和企业级治理方面的优势。
微服务开发框架-----Apache Dubbo
|
3月前
|
分布式计算 Hadoop 大数据
大数据处理框架在零售业的应用:Apache Hadoop与Apache Spark
【8月更文挑战第20天】Apache Hadoop和Apache Spark为处理海量零售户数据提供了强大的支持
61 0
|
5月前
|
Java 应用服务中间件 Apache
Apache HTTP配置反向代理入门
Apache HTTP配置反向代理入门
397 0
Apache HTTP配置反向代理入门
|
6月前
|
分布式计算 Java Go
Golang深入浅出之-Go语言中的分布式计算框架Apache Beam
【5月更文挑战第6天】Apache Beam是一个统一的编程模型,适用于批处理和流处理,主要支持Java和Python,但也提供实验性的Go SDK。Go SDK的基本概念包括`PTransform`、`PCollection`和`Pipeline`。在使用中,需注意类型转换、窗口和触发器配置、资源管理和错误处理。尽管Go SDK文档有限,生态系统尚不成熟,且性能可能不高,但它仍为分布式计算提供了可移植的解决方案。通过理解和掌握Beam模型,开发者能编写高效的数据处理程序。
230 1
|
6月前
|
XML 安全 Java
从零手写实现 apache Tomcat-02-web.xml 入门详细介绍
`web.xml`是Java Web应用的核心配置文件,描述应用工作方式。它包含Servlet、Filter和Listener的定义。例如,示例展示了如何配置名为`my`的Servlet处理`/my`请求,`LoggingFilter`拦截所有请求,以及`MyServletContextAttrListener`监听应用事件。`web.xml`是服务器理解应用结构与行为的指南。迷你版Tomcat实现——mini-cat(开源:https://github.com/houbb/minicat)。

推荐镜像

更多