lucene hello world

简介

Lucene是一个全文搜索框架,而不是应用产品,它只是提供了一种工具让你实现这些产品。主要由3个部分组成:索引部分,分词部分,搜索部分。

相关概念

  • Analyzer是分析器,它的作用是把一个字符串按某种规则划分成一个个词语,并去除其中的无效词语,这里说的无效词语是指英文中的“of”、“the”,中文中的“的”、“地”等词语,这些词语在文章中大量出现,但是本身不包含什么关键信息,去掉有利于缩小索引文件、提高效率、提高命中率。分词的规则千变万化,但目的只有一个:按语义划分。这点在英文中比较容易实现,因为英文本身就是以单词为单位的,已经用空格分开;而中文则必须以某种方法将连成一片的句子划分成一个个词语。
  • Document是指用户提供的一条条记录,它们可以是文本文件,字符串或者数据库的一条记录等等,一条记录经过索引之后,就是以一个Document的形式存储在索引文件中的。用户进行搜索,也是以Document列表的形式返回。
  • Field是指Document的域,一个Document可以有多个域。例如一篇文章可以包含“标题”、“正文”、“最后修改时间”等信息域,这些信息域就是通过Field在Document中存储的。Field有两个属性可选:存储和索引。通过存储属性你可以控制是否对这个Field进行存储;通过索引属性你可以控制是否对该Field进行索引。这看起来似乎有些废话,事实上对这两个属性的正确组合很重要,下面举例说明:还是以刚才的文章为例子,我们需要对标题和正文进行全文搜索,所以我们要把索引属性设置为真,同时我们希望能直接从搜索结果中提取文章标题,所以我们把标题域的存储属性设置为真,但是由于正文域太大了,我们为了缩小索引文件大小,将正文域的存储属性设置为假,当需要时再直接读取文件;我们只是希望能从搜索解果中提取最后修改时间,不需要对它进行搜索,所以我们把最后修改时间域的存储属性设置为真,索引属性设置为假。上面的三个域涵盖了两个属性的三种组合,还有一种全为假的没有用到,事实上Field不允许你那么设置,因为既不存储又不索引的域是没有意义的。
  • term是搜索的最小单位,它表示文档的一个词语,term由两部分组成:它表示的词语和这个词语所出现的field。
  • tocken是term的一次出现,它包含term文本和相应的起止偏移,以及一个类型字符串。一句话中可以出现多次相同的词语,它们都用同一个term表示,但是用不同的tocken,每个tocken标记该词语出现的地方。
  • Segment是存储索引的文件,它可以有多个,因为 添加索引时并不是每个document都马上添加到同一个索引文件,它们首先被写入到不同的小文件,然后再合并成一个大索引文件,这里每个小文件都是一个segment。

Hello world

加入依赖

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-core</artifactId>
<version>6.6.0</version>
</dependency>
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-queryparser</artifactId>
<version>6.6.0</version>
</dependency>
<dependency>
<groupId>org.apache.lucene</groupId>
<artifactId>lucene-demo</artifactId>
<version>6.6.0</version>
</dependency>

创建索引以及合并索引

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
public void () throws Exception {
if (!Files.exists(path)) {
Files.createDirectory(path);
}
Directory dir = FSDirectory.open(path);
Analyzer analyzer = new StandardAnalyzer();
IndexWriterConfig iwc = new IndexWriterConfig(analyzer);
iwc.setOpenMode(IndexWriterConfig.OpenMode.CREATE_OR_APPEND);
Date start = new Date();
IndexWriter writer = new IndexWriter(dir, iwc);
Document doc = new Document();
doc.add(new StringField("name", "zhangsan", Field.Store.YES));
doc.add(new IntRange("age", new int[]{10, 20}, new int[]{60, 80}));
doc.add(new TextField("content", "chinahubeiwuhangjianlifenyanhuishizhangsanok zhangsan", Field.Store.NO));
writer.addDocument(doc);
Document doc1 = new Document();
doc1.add(new StringField("name", "lisi", Field.Store.YES));
doc1.add(new IntRange("age", new int[]{10, 20}, new int[]{60, 80}));
doc1.add(new TextField("content", "chinahubeiwuhangjianlifenyanhuishilisiok", Field.Store.NO));
writer.addDocument(doc1);
writer.close();
Date end = new Date();
System.out.println(end.getTime() - start.getTime() + " total milliseconds");
}
public void testMerge() throws Exception {
Directory dir = FSDirectory.open(path);
Analyzer analyzer = new StandardAnalyzer();
IndexWriterConfig iwc = new IndexWriterConfig(analyzer);
iwc.setOpenMode(IndexWriterConfig.OpenMode.CREATE_OR_APPEND);
IndexWriter writer = new IndexWriter(dir, iwc);
writer.forceMerge(1);
writer.close();
}

Search data

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public void testSearch() throws Exception {
IndexReader reader = DirectoryReader.open(FSDirectory.open(path));
IndexSearcher searcher = new IndexSearcher(reader);
Analyzer analyzer = new StandardAnalyzer();
QueryParser parser = new QueryParser("name", analyzer);
Query query = parser.parse("zhangsan");
TopDocs topDocs = searcher.search(query, 3);
for (ScoreDoc scoreDoc : topDocs.scoreDocs) {
Document document = searcher.doc(scoreDoc.doc);
System.out.println(document.toString());
}
reader.close();
}