Lucene入门

释放双眼,带上耳机,听听看~!

一、介绍

Lucene是一款高性能的、可扩展的
信息检索(IR)工具库。信息检索是指文档搜索、文档内信息搜索或者文档相关的元数据搜索等操作。

简单的说,搜索就是搜寻、查找,在IT行业中就是指用户输入关键字,通过相应的算法,查询并返回用户所需要的信息。

传统的模糊查询方法在处理大数据的时候效率低、耗时长(select * from 表名 where 字段名 like ‘%关键字%’),所以lucene应运而生,也有了倒排索引的概念

二、倒排索引:

**        又称为反向索引: 以字或者词,甚至是一句话一段话作为一个关键字进行索引, 每一个关键字都会对应着一个记录项, 记录项中记录了这个关键字出现在那些文档中, 在此文档的什么位置上**

Lucene入门

在实际的运用中,我们可以对数据库中原始的数据结构(左图),在业务空闲时事先根据左图内容,创建新的倒排索引结构的数据区域(右图)。用户有查询需求时,先访问倒排索引数据区域(右图),得出文档id后,通过文档id即可快速,准确的通过左图找到具体的文档内容。

Lucene、Solr、Elasticsearch关系

Lucene:底层的API,工具包

Solr:基于Lucene开发的企业级的搜索引擎产品

Elasticsearch:基于Lucene开发的企业级的搜索引擎产品

创建索引流程:

Lucene入门

文档Document:数据库中一条具体的记录

字段Field:数据库中的每个字段

目录对象Directory:物理存储位置

写出器的配置对象:需要分词器和lucene的版本

三、Java API实现

API来实现对索引的增(创建索引)、删(删除索引)、改(修改索引)、查(搜索数据)。

**1、IndexWriter:**索引写入器对象,添加索引, 修改索引和删除索引。

                             需要传入DirectoryindexWriterConfig对象 ,即索引的目录和配置
**2、FSDirectory:**用来指定文件系统的目录, 将索引信息保存到磁盘上
RAMDriectory: 内存目录, 将索引库信息存放到内存中
3、IndexWriterConfig: 索引写入器的配置类,需要传递Lucene的版本和分词器

4、QueryParser(查询解析器)

5、Query(查询对象,包含要查询的关键词信息)

6、IndexSearch(索引搜索对象,执行搜索功能)

7、TopDocs(查询结果对象)

8、 ScoreDoc(得分文档对象)

代码实现:

依赖:


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
1<properties>
2        <lunece.version>4.10.2</lunece.version>
3
4    </properties>
5    <dependencies>
6        <dependency>
7            <groupId>junit</groupId>
8            <artifactId>junit</artifactId>
9            <version>4.12</version>
10        </dependency>
11        <!-- lucene核心库 -->
12        <dependency>
13            <groupId>org.apache.lucene</groupId>
14            <artifactId>lucene-core</artifactId>
15            <version>${lunece.version}</version>
16        </dependency>
17        <!-- Lucene的查询解析器 -->
18        <dependency>
19            <groupId>org.apache.lucene</groupId>
20            <artifactId>lucene-queryparser</artifactId>
21            <version>${lunece.version}</version>
22        </dependency>
23        <!-- lucene的默认分词器库 -->
24        <dependency>
25            <groupId>org.apache.lucene</groupId>
26            <artifactId>lucene-analyzers-common</artifactId>
27            <version>4.10.1</version>
28        </dependency>
29        <!-- lucene的高亮显示 -->
30        <dependency>
31            <groupId>org.apache.lucene</groupId>
32            <artifactId>lucene-highlighter</artifactId>
33            <version>${lunece.version}</version>
34        </dependency>
35        <!-- IK分词器 中文 -->
36        <dependency>
37            <groupId>com.janeluo</groupId>
38            <artifactId>ikanalyzer</artifactId>
39            <version>2012_u6</version>
40        </dependency>
41    </dependencies>
42

1、创建倒排索引


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
1//创建只有一条文档的文件
2    @Test
3    public void simpleCreate() {
4        try {
5        //1 创建文档对象
6            Document ducument = new Document();
7            // 添加字段信息。参数:字段的名称、字段的值、是否存储,yes储存,no不储存
8            ducument.add(new StringField("id", "1", Field.Store.YES));
9            // title字段用TextField,创建索引被分词。StringField创建索引,但不会被分词
10            ducument.add(new TextField("title", "谷歌地图之父跳槽facebook", Field.Store.YES));
11
12        //2 创建存储目录,本地
13            FSDirectory directory = FSDirectory.open(new File("e:/hadoop/hdpdata/lucene"));
14        //3 创建分词器
15            StandardAnalyzer analyzer = new StandardAnalyzer();
16        //4 创建索引写入器的配置对象
17            IndexWriterConfig config = new IndexWriterConfig(Version.LATEST, analyzer);
18
19        //5 创建索引写入器对象
20            IndexWriter indexWriter = new IndexWriter(directory, config);
21        //6 将文档交给索引写入器
22            indexWriter.addDocument(ducument);
23        //7 提交
24            indexWriter.commit();
25        //8 关闭
26            indexWriter.close();
27        }catch (Exception e){
28            e.printStackTrace();
29        }
30    }
31
32    //创建多个文档的文件
33    @Test
34    public void manyCreate() {
35        try {
36            //1 创建文档对象集合
37            Collection<Document> documents = new ArrayList<Document>();
38
39            Document ducument = new Document();
40            // 添加字段信息。参数:字段的名称、字段的值、是否存储,yes储存,no不储存
41            ducument.add(new StringField("id", "1", Field.Store.YES));
42            // title字段用TextField,创建索引被分词。StringField创建索引,但不会被分词
43            ducument.add(new TextField("title", "谷歌地图之父跳槽facebook", Field.Store.YES));
44            documents.add(ducument);
45
46            Document ducument2 = new Document();
47            // 添加字段信息。参数:字段的名称、字段的值、是否存储,yes储存,no不储存
48            ducument2.add(new StringField("id", "1", Field.Store.YES));
49            // title字段用TextField,创建索引被分词。StringField创建索引,但不会被分词
50            ducument2.add(new TextField("title", "谷歌地图之父加盟FaceBook", Field.Store.YES));
51            documents.add(ducument2);
52
53            Document ducument3 = new Document();
54            // 添加字段信息。参数:字段的名称、字段的值、是否存储,yes储存,no不储存
55            ducument3.add(new StringField("id", "1", Field.Store.YES));
56            // title字段用TextField,创建索引被分词。StringField创建索引,但不会被分词
57            ducument3.add(new TextField("title", "谷歌地图创始人拉斯离开谷歌加盟Facebook", Field.Store.YES));
58            documents.add(ducument3);
59
60            Document ducument4 = new Document();
61            // 添加字段信息。参数:字段的名称、字段的值、是否存储,yes储存,no不储存
62            ducument4.add(new StringField("id", "1", Field.Store.YES));
63            // title字段用TextField,创建索引被分词。StringField创建索引,但不会被分词
64            ducument4.add(new TextField("title", "谷歌地图之父跳槽Facebook与Wave项目取消有关", Field.Store.YES));
65            documents.add(ducument4);
66
67            Document ducument5 = new Document();
68            // 添加字段信息。参数:字段的名称、字段的值、是否存储,yes储存,no不储存
69            ducument5.add(new StringField("id", "1", Field.Store.YES));
70            // title字段用TextField,创建索引被分词。StringField创建索引,但不会被分词
71            ducument5.add(new TextField("title", "谷歌地图之父拉斯加盟社交网站Facebook", Field.Store.YES));
72            documents.add(ducument5);
73
74            //2 创建存储目录,本地
75            FSDirectory directory = FSDirectory.open(new File("e:/hadoop/hdpdata/lucene"));
76            //3 创建分词器
77            StandardAnalyzer analyzer = new StandardAnalyzer();
78//            IKAnalyzer ikAnalyzer = new IKAnalyzer();
79            //4 创建索引写入器的配置对象
80            IndexWriterConfig config = new IndexWriterConfig(Version.LATEST, analyzer);
81            //如果有数据,覆盖
82            config.setOpenMode(IndexWriterConfig.OpenMode.CREATE);
83
84            //5 创建索引写入器对象
85            IndexWriter indexWriter = new IndexWriter(directory, config);
86            //6 将文档交给索引写入器
87            indexWriter.addDocuments(documents);
88            //7 提交
89            indexWriter.commit();
90            //8 关闭
91            indexWriter.close();
92        }catch (Exception e){
93            e.printStackTrace();
94        }
95    }
96

2、查询索引


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
1//索引查询代码
2    @Test
3    public void search() {
4        try {
5            //1 创建查询文件
6             Directory dir = FSDirectory.open(new File("e:/hadoop/hdpdata/lucene"));
7            //2 索引读取工具
8            DirectoryReader reader = DirectoryReader.open(dir);
9            //搜索工具
10            IndexSearcher indexSearcher = new IndexSearcher(reader);
11
12            // 创建查询解析器,参数:查询的字段的名称,分词器
13            QueryParser queryParser = new QueryParser("title", new IKAnalyzer());  //new StandardAnalyzer()
14            //查询对象
15            Query query = queryParser.parse("谷歌");
16
17            //搜索工具查询,参数:查询对象,最大结果条数
18            //结果topdoc:按照匹配度排名得分前N名的文档信息(包含查询到的总条数信息、所有符合条件的文档的编号信息)
19            TopDocs search = indexSearcher.search(query, 10);
20
21            //获取总条数
22            System.out.println("一共有:"+search.totalHits+"条数据");
23
24            // 获取得分文档对象(ScoreDoc)数组.SocreDoc中包含:文档的编号、文档的得分
25            ScoreDoc[] scoreDocs = search.scoreDocs;
26            for(ScoreDoc scoreDoc : scoreDocs){
27                //文档编号
28                int id=scoreDoc.doc;
29                //文档编号对应文档
30                Document doc = reader.document(id);
31
32                System.out.println("id: "+doc.get("id")+"   title: "+doc.get("title"));
33            // 取出文档得分
34            System.out.println("得分: " + scoreDoc.score);
35            }
36
37        }catch (Exception e){
38            e.printStackTrace();
39        }
40    }
41

3高亮显示


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
1   // 高亮显示
2    @Test
3    public void testHighlighter() throws Exception {
4        // 目录对象
5        Directory directory = FSDirectory.open(new File("e:/hadoop/hdpdata/lucene"));
6        // 创建读取工具
7        IndexReader reader = DirectoryReader.open(directory);
8        // 创建搜索工具
9        IndexSearcher searcher = new IndexSearcher(reader);
10
11        QueryParser parser = new QueryParser("title", new StandardAnalyzer());
12        Query query = parser.parse("谷歌");
13
14        // 格式化器
15        Formatter formatter = new SimpleHTMLFormatter("<body>", "</body>");
16        QueryScorer scorer = new QueryScorer(query);
17        // 准备高亮工具
18        Highlighter highlighter = new Highlighter(formatter, scorer);
19        // 搜索
20        TopDocs topDocs = searcher.search(query, 10);
21        System.out.println("本次搜索共" + topDocs.totalHits + "条数据");
22
23        ScoreDoc[] scoreDocs = topDocs.scoreDocs;
24        for (ScoreDoc scoreDoc : scoreDocs) {
25            // 获取文档编号
26            int docID = scoreDoc.doc;
27            Document doc = reader.document(docID);
28            System.out.println("id: " + doc.get("id"));
29
30            String title = doc.get("title");
31            // 用高亮工具处理普通的查询结果,参数:分词器,要高亮的字段的名称,高亮字段的原始值
32            String hTitle = highlighter.getBestFragment(new StandardAnalyzer(), "title", title);
33
34            System.out.println("title: " + hTitle);
35            // 获取文档的得分
36            System.out.println("得分:" + scoreDoc.score);
37        }
38    }
39

4、排序


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
1    // 排序
2    @Test
3    public void testSortQuery() throws Exception {
4        // 目录对象
5        Directory directory = FSDirectory.open(new File("e:/hadoop/hdpdata/lucene"));
6        // 创建读取工具
7        IndexReader reader = DirectoryReader.open(directory);
8        // 创建搜索工具
9        IndexSearcher searcher = new IndexSearcher(reader);
10
11        QueryParser parser = new QueryParser("title", new StandardAnalyzer());
12        Query query = parser.parse("谷歌");
13
14        // 创建排序对象,需要排序字段SortField,参数:字段的名称、字段的类型、是否反转如果是false,升序。true降序
15        Sort sort = new Sort(new SortField("id", SortField.Type.LONG, true));
16        // 搜索
17        TopDocs topDocs = searcher.search(query, 10,sort);
18        System.out.println("本次搜索共" + topDocs.totalHits + "条数据");
19
20        ScoreDoc[] scoreDocs = topDocs.scoreDocs;
21        for (ScoreDoc scoreDoc : scoreDocs) {
22            // 获取文档编号
23            int docID = scoreDoc.doc;
24            Document doc = reader.document(docID);
25            System.out.println("id: " + doc.get("id"));
26            System.out.println("title: " + doc.get("title"));
27        }
28    }
29

5、分页


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
1    // 分页
2    @Test
3    public void testPageQuery() throws Exception {
4        // 实际上Lucene本身不支持分页。因此我们需要自己进行逻辑分页。我们要准备分页参数:
5        int pageSize = 2;// 每页条数
6        int pageNum = 3;// 当前页码
7        int start = (pageNum - 1) * pageSize;// 当前页的起始条数
8        int end = start + pageSize;// 当前页的结束条数(不能包含)
9
10        // 目录对象
11        Directory directory = FSDirectory.open(new File("e:/hadoop/hdpdata/lucene" ));
12        // 创建读取工具
13        IndexReader reader = DirectoryReader.open(directory);
14        // 创建搜索工具
15        IndexSearcher searcher = new IndexSearcher(reader);
16
17        QueryParser parser = new QueryParser("title", new StandardAnalyzer());
18        Query query = parser.parse("谷歌");
19
20        // 创建排序对象,需要排序字段SortField,参数:字段的名称、字段的类型、是否反转如果是false,升序。true降序
21        Sort sort = new Sort(new SortField("id", SortField.Type.LONG, false));
22        // 搜索数据,查询0~end条
23        TopDocs topDocs = searcher.search(query, end,sort);
24        int count = topDocs.totalHits;
25        System.out.println("本次搜索共" + count + "条数据"+"当前页: "+pageNum);
26
27        ScoreDoc[] scoreDocs = topDocs.scoreDocs;
28            for (int i = start; i < end; i++) {
29                if(i == count ){
30                    return;
31                }
32                ScoreDoc scoreDoc = scoreDocs[i];
33                // 获取文档编号
34                int docID = scoreDoc.doc;
35                Document doc = reader.document(docID);
36                System.out.println("id: " + doc.get("id"));
37                System.out.println("title: " + doc.get("title"));
38            }
39    }
40

 

给TA打赏
共{{data.count}}人
人已打赏
安全运维

OpenSSH-8.7p1离线升级修复安全漏洞

2021-10-23 10:13:25

安全运维

设计模式的设计原则

2021-12-12 17:36:11

个人中心
购物车
优惠劵
今日签到
有新私信 私信列表
搜索