一、lucene索引
1、文档层次结构
- 索引(Index):一个索引放在一个文件夹中;
- 段(Segment):一个索引中可以有很多段,段与段之间是独立的,添加新的文档可能产生新段,不同的段可以合并成一个新段;
- 文档(Document):文档是创建索引的基本单位,不同的文档保存在不同的段中,一个段可以包含多个文档;
- 域(Field):一个文档包含不同类型的信息,可以拆分开索引;
- 词(Term):词是索引的最小单位,是经过词法分析和语言处理后的数据;
文档是Lucene索引和搜索的原子单位,文档为包含一个或多个域的容器,而域则依次包含“真正的”被搜索内容,域值通过分词技术处理,得到多个词元。如一篇小说信息可以称为一个文档;小说信息又包含多个域,比如标题,作者、简介、最后更新时间等;对标题这一个域采用分词技术,又可以等到一个或多个词元。
2、正向索引与反向索引
- 正向索引:文档占据了中心的位置,每个文档指向了一个它所包含的索引项的序列。正向信息就是按层次保存了索引一直到词的包含关系: 索引 -> 段-> 文档 -> 域 -> 词
- 反向索引:一种以索引项为中心来组织文档的方式,每个索引项指向一个文档序列,这个序列中的文档都包含该索引项。反向信息保存了词典的倒排表映射:词 -> 文档
lucene使用到的就是反向索引。如下图所示:

二、索引操作
相关示例如下:
1 package com.test.lucene;
2
3 import java.io.IOException;
4 import java.nio.file.Paths;
5
6 import org.apache.lucene.analysis.Analyzer;
7 import org.apache.lucene.analysis.cn.smart.SmartChineseAnalyzer;
8 import org.apache.lucene.document.Document;
9 import org.apache.lucene.document.Field.Store;
10 import org.apache.lucene.document.IntField;
11 import org.apache.lucene.document.StringField;
12 import org.apache.lucene.document.TextField;
13 import org.apache.lucene.index.DirectoryReader;
14 import org.apache.lucene.index.IndexWriter;
15 import org.apache.lucene.index.IndexWriterConfig;
16 import org.apache.lucene.index.Term;
17 import org.apache.lucene.index.IndexWriterConfig.OpenMode;
18 import org.apache.lucene.queryparser.classic.ParseException;
19 import org.apache.lucene.queryparser.classic.QueryParser;
20 import org.apache.lucene.search.IndexSearcher;
21 import org.apache.lucene.search.Query;
22 import org.apache.lucene.search.TopDocs;
23 import org.apache.lucene.store.Directory;
24 import org.apache.lucene.store.FSDirectory;
25
26 /**
27 * 索引增删改查
28 */
29 public class IndexTest {
30 /**
31 * 创建索引
32 *
33 * @param path
34 * 索引存放路径
35 */
36 public static void create(String path) {
37 System.out.println("创建开始=============================》");
38 Analyzer analyzer = new SmartChineseAnalyzer();// 指定分词技术,这里使用的是中文分词
39
40 IndexWriterConfig indexWriterConfig = new IndexWriterConfig(analyzer);// indexWriter的配置信息
41
42 indexWriterConfig.setOpenMode(OpenMode.CREATE_OR_APPEND); // 索引的打开方式:没有则创建,有则打开
43
44 Directory directory = null;
45 IndexWriter indexWriter = null;
46 // 文档一
47 Document doc1 = new Document();
48 doc1.add(new StringField("id", "1111", Store.YES));
49 doc1.add(new TextField("content", "中国广州", Store.YES));
50 doc1.add(new IntField("num", 1, Store.YES));
51
52 // 文档二
53 Document doc2 = new Document();
54 doc2.add(new StringField("id", "2222", Store.YES));
55 doc2.add(new TextField("content", "中国上海", Store.YES));
56 doc2.add(new IntField("num", 2, Store.YES));
57
58 try {
59 directory = FSDirectory.open(Paths.get(path));// 索引在硬盘上的存储路径
60 indexWriter = new IndexWriter(directory, indexWriterConfig);
61 indexWriter.addDocument(doc1);
62 indexWriter.addDocument(doc2);
63 // 将indexWrite操作提交,如果不提交,之前的操作将不会保存到硬盘
64 // 但是这一步很消耗系统资源,索引执行该操作需要有一定的策略
65 indexWriter.commit();
66 } catch (IOException e) {
67 e.printStackTrace();
68 } finally { // 关闭资源
69 try {
70 indexWriter.close();
71 directory.close();
72 } catch (IOException e) {
73 e.printStackTrace();
74 }
75 }
76 System.out.println("创建索引完成=================================");
77 }
78
79 /**
80 * 添加索引
81 *
82 * @param path
83 * 索引存放路径
84 * @param document
85 * 添加的文档
86 */
87 public static void add(String path, Document document) {
88 System.out.println("增加索引开始=============================》");
89 Analyzer analyzer = new SmartChineseAnalyzer();// 指定分词技术,这里使用的是中文分词
90
91 IndexWriterConfig indexWriterConfig = new IndexWriterConfig(analyzer);// indexWriter的配置信息
92
93 indexWriterConfig.setOpenMode(OpenMode.CREATE_OR_APPEND); // 索引的打开方式:没有则创建,有则打开
94 Directory directory = null;
95 IndexWriter indexWriter = null;
96 try {
97 directory = FSDirectory.open(Paths.get(path));// 索引在硬盘上的存储路径
98
99 indexWriter = new IndexWriter(directory, indexWriterConfig);
100 indexWriter.addDocument(document);
101 indexWriter.commit();
102 } catch (IOException e) {
103 e.printStackTrace();
104 } finally { // 关闭资源
105 try {
106 indexWriter.close();
107 directory.close();
108 } catch (IOException e) {
109 e.printStackTrace();
110 }
111 }
112 System.out.println("增加索引完成=================================");
113 }
114
115 /**
116 * 删除索引
117 *
118 * @param indexpath
119 * 索引存放路径
120 * @param id
121 * 文档id
122 */
123 public static void delete(String indexpath, String id) {
124 System.out.println("删除索引开始=============================》");
125 Analyzer analyzer = new SmartChineseAnalyzer();// 指定分词技术,这里使用的是中文分词
126
127 IndexWriterConfig indexWriterConfig = new IndexWriterConfig(analyzer);// indexWriter的配置信息
128
129 indexWriterConfig.setOpenMode(OpenMode.CREATE_OR_APPEND); // 索引的打开方式:没有则创建,有则打开
130 IndexWriter indexWriter = null;
131 Directory directory = null;
132 try {
133 directory = FSDirectory.open(Paths.get(indexpath));// 索引在硬盘上的存储路径
134 indexWriter = new IndexWriter(directory, indexWriterConfig);
135 indexWriter.deleteDocuments(new Term("id", id));// 删除索引操作
136 } catch (IOException e) {
137 e.printStackTrace();
138 } finally { // 关闭资源
139 try {
140 indexWriter.close();
141 directory.close();
142 } catch (IOException e) {
143 e.printStackTrace();
144 }
145 }
146 System.out.println("删除索引完成=================================");
147 }
148
149 /**
150 * Lucene没有真正的更新操作,通过某个fieldname,可以更新这个域对应的索引,但是实质上,它是先删除索引,再重新建立的。
151 *
152 * @param indexpath
153 * 索引存放路径
154 * @param newDoc
155 * 更新后的文档
156 * @param oldDoc
157 * 需要更新的目标文档
158 */
159 public static void update(String indexpath, Document newDoc, Document oldDoc) {
160 System.out.println("更新索引开始=============================》");
161 Analyzer analyzer = new SmartChineseAnalyzer();
162 IndexWriterConfig config = new IndexWriterConfig(analyzer);
163 config.setOpenMode(OpenMode.CREATE_OR_APPEND);
164 IndexWriter indexWriter = null;
165 Directory directory = null;
166 try {
167 directory = FSDirectory.open(Paths.get(indexpath));
168 indexWriter = new IndexWriter(directory, config);
169 indexWriter.updateDocument(new Term("id", oldDoc.get("id")), newDoc);
170 } catch (IOException e) {
171 e.printStackTrace();
172 } finally { // 关闭资源
173 try {
174 indexWriter.close();
175 directory.close();
176 } catch (IOException e) {
177 e.printStackTrace();
178 }
179 }
180 System.out.println("更新索引完成=================================");
181 }
182
183 /**
184 * 搜索
185 *
186 * @param keyword
187 * 关键字
188 * @param indexpath
189 * 索引存放路径
190 */
191 public static void search(String keyword, String indexpath) {
192 Directory directory = null;
193 try {
194 directory = FSDirectory.open(Paths.get(indexpath));// 索引硬盘存储路径
195
196 DirectoryReader directoryReader = DirectoryReader.open(directory);// 读取索引
197
198 IndexSearcher searcher = new IndexSearcher(directoryReader);// 创建索引检索对象
199
200 Analyzer analyzer = new SmartChineseAnalyzer();// 分词技术
201
202 QueryParser parser = new QueryParser("content", analyzer);// 创建Query
203 Query query = parser.parse(keyword);// 查询content为广州的
204 // 检索索引,获取符合条件的前10条记录
205 TopDocs topDocs = searcher.search(query, 10);
206 if (topDocs != null) {
207 System.out.println("符合条件的记录为: " + topDocs.totalHits);
208 for (int i = 0; i < topDocs.scoreDocs.length; i++) {
209 Document doc = searcher.doc(topDocs.scoreDocs[i].doc);
210 System.out.println("id = " + doc.get("id"));
211 System.out.println("content = " + doc.get("content"));
212 System.out.println("num = " + doc.get("num"));
213 }
214 }
215 directory.close();
216 directoryReader.close();
217 } catch (IOException e) {
218 e.printStackTrace();
219 } catch (ParseException e) {
220 e.printStackTrace();
221 }
222 }
223 /**
224 * 测试代码
225 * @param args
226 */
227 public static void main(String[] args) {
228 String indexpath = "D://index/test";
229 create(indexpath);// 创建索引
230 search("广州", indexpath);
231 Document doc = new Document();
232 doc.add(new StringField("id", "3333", Store.YES));
233 doc.add(new TextField("content", "中国北京广州", Store.YES));
234 doc.add(new IntField("num", 2, Store.YES));
235 add(indexpath, doc);// 添加索引
236 search("广州", indexpath);
237 Document newDoc = new Document();
238 newDoc.add(new StringField("id", "3333", Store.YES));
239 newDoc.add(new TextField("content", "中国北京广州我的顶顶顶顶顶顶顶顶顶顶顶顶", Store.YES));
240 newDoc.add(new IntField("num", 3, Store.YES));
241 update(indexpath, newDoc, doc);// 更新索引
242 search("广州", indexpath);
243 delete(indexpath, "3333");// 删除索引
244 search("广州", indexpath);
245 }
246 }
运行结果如下:

来源:https://www.cnblogs.com/always-online/p/4698519.html