全文检索: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,如需转载请自行联系原作者

相关文章
|
2月前
|
消息中间件 Kafka Apache
Apache Flink 是一个开源的分布式流处理框架
Apache Flink 是一个开源的分布式流处理框架
482 5
|
5月前
|
SQL 算法 Java
Apache Calcite入门
Apache Calcite入门
159 0
|
5月前
|
分布式计算 Java 大数据
Apache SeaTunnel 3 分钟入门指南
Apache SeaTunnel 3 分钟入门指南
237 0
|
5月前
|
数据采集 分布式计算 Hadoop
开源数据质量解决方案——Apache Griffin入门宝典(上)
开源数据质量解决方案——Apache Griffin入门宝典
165 0
|
6月前
|
SQL 消息中间件 分布式计算
Apache Doris 系列: 入门篇-数据导入及查询
Apache Doris 系列: 入门篇-数据导入及查询
157 0
|
6月前
|
SQL 关系型数据库 Apache
Apache Doris 系列: 入门篇-创建数据表
Apache Doris 系列: 入门篇-创建数据表
191 0
|
3月前
|
分布式计算 Java 大数据
IO流【Java对象的序列化和反序列化、File类在IO中的作用、装饰器模式构建IO流体系、Apache commons-io工具包的使用】(四)-全面详解(学习总结---从入门到深化)
IO流【Java对象的序列化和反序列化、File类在IO中的作用、装饰器模式构建IO流体系、Apache commons-io工具包的使用】(四)-全面详解(学习总结---从入门到深化)
53 0
|
1月前
|
监控 测试技术 Linux
性能工具之 Apache Bench 入门使用
ab 全称为:apache bench,ab 为小型压力工具,对于在 Linux 中简单压测 HTTP 接口轻巧灵活。
23 1
|
1月前
|
SQL 分布式计算 HIVE
Apache Hudi入门指南(含代码示例)
Apache Hudi入门指南(含代码示例)
56 0
|
6月前
|
存储 固态存储 关系型数据库
Apache Doris 系列: 入门篇-安装部署
Apache Doris 系列: 入门篇-安装部署
367 0

热门文章

最新文章

推荐镜像

更多