使用 Lucene 代替数据库搜索

Lucene 是 Solr 和 Elasticsearch 的底层基础。打个比方,相当于 JDBC 与 Hibernate、Mybatis之间的关系。

这里就不再过多介绍 Lucene,想了解的可以自行查阅资料。

1、环境配置

我这里是使用 Maven 搭建的 Demo,如果不是的话,需要自己去网上下载相关 jar包。

<!-- Lucene 核心 -->
<dependency>
	<groupId>org.apache.lucene</groupId>
	<artifactId>lucene-core</artifactId>
	<version>4.10.2</version>
</dependency>
<!-- Lucene 默认分词器 -->
<dependency>
	<groupId>org.apache.lucene</groupId>
	<artifactId>lucene-analyzers-common</artifactId>
	<version>4.10.2</version>
</dependency>
<!-- Lucene 查询解析器 -->
<dependency>
	<groupId>org.apache.lucene</groupId>
	<artifactId>lucene-queryparser</artifactId>
	<version>4.10.2</version>
</dependency>
<!-- Lucene 高亮提示 -->
<dependency>
	<groupId>org.apache.lucene</groupId>
	<artifactId>lucene-highlighter</artifactId>
	<version>4.10.2</version>
</dependency>
<!-- Lucene 索引库优化策略 -->
<dependency>
	<groupId>org.apache.lucene</groupId>
	<artifactId>lucene-memory</artifactId>
	<version>4.10.2</version>
</dependency>
<!-- 中文分词器 -->
<dependency>
	<groupId>com.janeluo</groupId>
	<artifactId>ikanalyzer</artifactId>
	<version>2012_u6</version>
</dependency>

因为 IKAnalyzer 很久没有维护了,最高只能支持 Lucene4.x 的版本。

2、创建索引

具体的创建步骤

  1. 创建文档
  2. 创建存储目录
  3. 创建分词器
  4. 创建索引写入器配置
  5. 创建索引写入器
  6. 将文档交给索引写入器
  7. 索引写入器提交
  8. 索引写入器关闭
@Test
public void createIndexDB() throws Exception{

    //创建文档,并添加字段
    Document document = new Document();
    document.add(new StringField("id", "1", Field.Store.YES));
    document.add(new TextField("title", "Lucene是apache软件基金会发布的一个开放源代码的全文检索引擎工具包", Field.Store.YES));

    //创建存储目录,高版本需要使用 FSDirectory.open(Paths.get("E:\\workspace\\spring-redis\\lucene"))
    Directory directory = FSDirectory.open(new File("E:\\workspace\\spring-redis\\lucene"));

    //创建中文分词器
    Analyzer analyzer = new IKAnalyzer();

    //创建索引写入器配置,选择最新版本(Version.LATEST)和中文分词器
    IndexWriterConfig config = new IndexWriterConfig(Version.LATEST, analyzer);

    //创建索引写入器
    IndexWriter indexWriter = new IndexWriter(directory, config);
    //清空后再提交
    config.setOpenMode(IndexWriterConfig.OpenMode.CREATE);
    //在原来的基础上追加新索引
    config.setOpenMode(IndexWriterConfig.OpenMode.APPEND);


    //添加文档、提交、关闭
    indexWriter.addDocument(document);
    indexWriter.commit();
    indexWriter.close();
}

常用的 Field 类

运行成功后,就可以在存储目录设置的路径中看到索引文件

如果需要提交新的索引,可以设置索引写入配置的打开方式

//清空后再提交
config.setOpenMode(IndexWriterConfig.OpenMode.CREATE);
//在原来的基础上追加新索引
config.setOpenMode(IndexWriterConfig.OpenMode.APPEND);

3、查询索引

具体的查询步骤

  1. 创建读取目录
  2. 创建索引读取器
  3. 创建索引搜索器
  4. 创建查询解析器
  5. 创建查询对象
  6. 搜索数据
  7. 对数据的相关操作
@Test
public void findIndexDB() throws Exception{

    //创建读取目录,高版本需要使用 FSDirectory.open(Paths.get("E:\\workspace\\spring-redis\\lucene"))
    Directory directory = FSDirectory.open(new File("E:\\workspace\\spring-redis\\lucene"));

    //创建索引读取器
    IndexReader reader = DirectoryReader.open(directory);

    //创建索引搜索器
    IndexSearcher indexSearcher = new IndexSearcher(reader);

    //创建查询解析器,这里是对 title 字段查询,分词器选择中文分词器
    QueryParser queryParser = new QueryParser("title", new IKAnalyzer());

    //通过解析器返回需要查询的对象
    Query query = queryParser.parse("软件");

    //获取前10条数据,根据匹配度排序
    TopDocs topDocs = indexSearcher.search(query, 10);

    System.out.println("本次搜索共" + topDocs.totalHits + "条数据");

    //根据 ScoreDoc 中的 doc 获取对应的文档 ID
    for (ScoreDoc scoreDoc: topDocs.scoreDocs){
        int docID = scoreDoc.doc;
        Document document = indexSearcher.doc(docID);
        String title = document.get("title");
        System.out.println(title);
    }
}

QueryParser 只能单字段查询,MultiFieldQueryParser 提供多字段查询功能

MultiFieldQueryParser multiFieldQueryParser = new MultiFieldQueryParser(new String[]{"id", "title"}, new IKAnalyzer());
Query query = multiFieldQueryParser.parse("软件");

搜索结果 TopDocs 有两个主要属性

int totalHits 结果总数
ScoreDoc[] scoreDocs 得分文档数组

ScoreDoc 有文档的存储文档的相关信息

int doc 文档 ID (唯一)
float score 文档分数

除了使用解析器,还可以使用词条创建查询对象

Query query = new TermQuery(new Term("title", "软件"));

4、更新、删除索引

更新操作与添加类似,只要创建新的文档,并使用索引写入器更新即可

Document document = new Document();
document.add(new StringField("id", "1", Field.Store.YES));
document.add(new TextField("title", "Lucene是当前以及最近几年最受欢迎的免费Java信息检索程序库", Field.Store.YES));

indexWriter.updateDocument(new Term("id", "1"), document);
indexWriter.commit();
indexWriter.close();

删除操作

//根据词条删除
indexWriter.deleteDocuments(new Term("id", "1"));
//全部删除
indexWriter.deleteAll();

indexWriter.commit();
indexWriter.close();

5、高亮显示

在查询的基础上,增加高亮工具,对关键词进行设置

@Test
public void findIndexDB() throws Exception{

    //创建读取目录,高版本需要使用 FSDirectory.open(Paths.get("E:\\workspace\\spring-redis\\lucene"))
    Directory directory = FSDirectory.open(new File("E:\\workspace\\spring-redis\\lucene"));

    //创建索引读取器
    IndexReader reader = DirectoryReader.open(directory);

    //创建索引搜索器
    IndexSearcher indexSearcher = new IndexSearcher(reader);

    //创建查询解析器,这里是对 title 字段查询,分词器选择中文分词器
    QueryParser queryParser = new QueryParser("title", new IKAnalyzer());

    //通过解析器返回需要查询的对象
    Query query = queryParser.parse("软件");

    Formatter formatter = new SimpleHTMLFormatter("<span style = 'color: red'>", "</span>");
    QueryScorer scorer = new QueryScorer(query);
    Highlighter highlighter = new Highlighter(formatter, scorer);

    //获取前10条数据,根据匹配度排序
    TopDocs topDocs = indexSearcher.search(query, 10);

    System.out.println("本次搜索共" + topDocs.totalHits + "条数据");

    //根据 ScoreDoc 中的 doc 获取对应的文档 ID
    for (ScoreDoc scoreDoc: topDocs.scoreDocs){
        int docID = scoreDoc.doc;
        Document document = indexSearcher.doc(docID);
        String title = document.get("title");
        System.out.println(title);
        String hTitle = highlighter.getBestFragment(new IKAnalyzer(), "title", title);
        System.out.println(hTitle);
    }
}

运行完后会看到 “软件” 被 span 标签包裹

本次搜索共1条数据
Lucene是apache软件基金会发布的一个开放源代码的全文检索引擎工具包
Lucene是apache<span style = 'color: red'>软件</span>基金会发布的一个开放源代码的全文检索引擎工具包
  • 用支付宝打我
  • 用微信打我

Long may the sunshine

发表评论

电子邮件地址不会被公开。 必填项已用*标注

召唤蕾姆