Lucene 实战教程第十六章 SpringBoot 整合 Lucene

JAVA herman 975浏览
公告:“业余草”微信公众号提供免费CSDN下载服务(只下Java资源),关注业余草微信公众号,添加作者微信:xttblog,发送下载链接帮助你免费下载!
本博客日IP超过1800,PV 2600 左右,急需赞助商。
极客时间所有课程通过我的二维码购买后返现24元微信红包,请加博主新的微信号:xttblog,之前的微信号好友位已满,备注:返现
所有面试题(java、前端、数据库、springboot等)一网打尽,请关注文末小程序
视频教程免费领

网上搜索了一下关于 Lucene 教程的文章非常多,但是关于 SpringBoot 整合 Lucene 的非常少,可能一些涉及到搜索的项目都比较老,使用 Lucene 的比较少,使用 Solr 和 Elasticsearch 的可能比较多。但是文章少,并不代表没有人用,所以本文我教大家如何把 Lucene 和 SpringBoot 整合到一起。

至于,你们还不懂 Lucene 是什么的,建议看我前面的文章。有详细的教程。

SpringBoot 不论整合什么框架,都是先从 pom.xml 文件开始。同样的整合 Lucene 也需要引入 Lucene 相关的 jar 文件。

<!--对分词索引查询解析-->
<dependency>
	<groupId>org.apache.lucene</groupId>
	<artifactId>lucene-queryparser</artifactId>
	<version>7.1.0</version>
</dependency>
<!--高亮 -->
<dependency>
	<groupId>org.apache.lucene</groupId>
	<artifactId>lucene-highlighter</artifactId>
	<version>7.1.0</version>
</dependency>
<!--smartcn 中文分词器 SmartChineseAnalyzer  smartcn分词器 需要lucene依赖 且和lucene版本同步-->
<dependency>
	<groupId>org.apache.lucene</groupId>
	<artifactId>lucene-analyzers-smartcn</artifactId>
	<version>7.1.0</version>
</dependency>
<!--ik-analyzer 中文分词器-->
<dependency>
	<groupId>cn.bestwu</groupId>
	<artifactId>ik-analyzers</artifactId>
	<version>5.1.0</version>
</dependency>
<!--MMSeg4j 分词器-->
<dependency>
	<groupId>com.chenlb.mmseg4j</groupId>
	<artifactId>mmseg4j-solr</artifactId>
	<version>2.4.0</version>
	<exclusions>
		<exclusion>
			<groupId>org.apache.solr</groupId>
			<artifactId>solr-core</artifactId>
		</exclusion>
	</exclusions>
</dependency>

Lucene 目前还没有提供 SpringBoot 方面的自动配置的 starter,所以引入的 jar 文件可能较多。

实例化 IndexReader 需要加载索引文件,所以实例化它是非常耗资源的。 

IndexReader 是线程安全的,通常一个索引目录,我们只实例化一个 IndexReader 就够了。 

@Configuration
public class LuceneConfig {

	// 创建一个 Analyzer 实例
	@Bean
    public Analyzer analyzer() {
        return new IKAnalyzer(false);
    }

    // 索引位置
    @Bean
    public Directory directory() {
        return FSDirectory.open(Paths.get("indexDir/"));
    }

	// 创建 IndexReader
    @Bean
    public IndexReader indexReader(Directory directory) {
        return DirectoryReader.open(directory);
    }

    // 创建 SearcherManager
    @Bean
    public SearcherManager searcherManager(Directory directory) throws IOException {
        return new SearcherManager(directory, null);
    }

    // 创建 IndexSearcher
	@Bean
    public IndexSearcher indexSearcher(IndexReader indexReader){
    	return new IndexSearcher(indexReader);
    }
}

同样的 IndexSearcher 实例一个也就够了。如果不够,可以使用 searcherManager 实例。

searcherManager 实例的用法,一般如下:

@Service
public class XttblogService(){
	@Autowired
    private SearcherManager searcherManager;
	
    public Set<Map<String, Object>> search(String keyword, int page) {
        IndexSearcher searcher = null;

        Set<Map<String, Object>> data = newLinkedHashSetWithExpectedSize(DEFAULT_PAGE_SIZE);
        try {
            searcher = searcherManager.acquire();

            BooleanQuery query = ......
            ......
        } catch (IOException e) {
            logger.error("acquire beer searcher failed");
            logger.error(e.getLocalizedMessage(), e);
        } catch (ParseException e) {
            logger.error("failed to build query for \"{}\"", keyword);
        } finally {
            if (searcher != null) {
                try {
                    searcherManager.release(searcher);
                } catch (IOException e) {
                    logger.error("release beer searcher failed");
                    logger.error(e.getLocalizedMessage(), e);
                }
            }
        }
        return data;
    }
}

封装好了各个 bean 只有,查询搜索,创建索引,更新,删除索引之类的就非常简单了。

下面看几个贱的查询例子。

**
 * 执行查询,并打印查询到的记录数
 * @param query
 * @throws IOException
 */
public void executeQuery(Query query) throws IOException {
	TopDocs topDocs = indexSearcher.search(query, 100);
	//打印查询到的记录数
	System.out.println("总共查询到" + topDocs.totalHits + "个文档");
	for (ScoreDoc scoreDoc : topDocs.scoreDocs) {
		//取得对应的文档对象
		Document document = indexSearcher.doc(scoreDoc.doc);
		System.out.println("id:" + document.get("id"));
		System.out.println("title:" + document.get("title"));
		System.out.println("content:" + document.get("content"));
	}
}

/**
 * 分词打印
 * @param analyzer
 * @param text
 * @throws IOException
 */
public void printAnalyzerDoc(Analyzer analyzer, String text) throws IOException {
	TokenStream tokenStream = analyzer.tokenStream("content", new StringReader(text));
	CharTermAttribute charTermAttribute = tokenStream.addAttribute(CharTermAttribute.class);
	try {
		tokenStream.reset();
		while (tokenStream.incrementToken()) {
			System.out.println(charTermAttribute.toString());
		}
		tokenStream.end();
	} finally {
		tokenStream.close();
		analyzer.close();
	}
}

public void indexWriterBook(Book book) throws IOException {
	long start = System.currentTimeMillis();

	// indexWriterConfig 也可以通过引入的方式来实现
	//创建索引写入配置
	IndexWriterConfig indexWriterConfig = new IndexWriterConfig(analyzer);

	//创建索引写入对象
	IndexWriter indexWriter = new IndexWriter(directory, indexWriterConfig);

	//创建Document对象,存储索引

	Document doc = new Document();

	int id = 1;

	//将字段加入到doc中
	doc.add(new IntPoint("id", book.getId()));
	doc.add(new StringField("title", book.getTitle(), Field.Store.YES));
	doc.add(new TextField("content", book.getContent(), Field.Store.YES));
	doc.add(new StoredField("id", book.getId()));

	//将doc对象保存到索引库中
	indexWriter.addDocument(doc);

	indexWriter.commit();
	//关闭流
	indexWriter.close();

	long end = System.currentTimeMillis();
	System.out.println("索引花费了" + (end - start) + " 毫秒");
}

public void termQueryTitle(String title) throws IOException {
	String searchField = "title";
	//这是一个条件查询的api,用于添加条件
	TermQuery query = new TermQuery(new Term(searchField, title));

	//执行查询,并打印查询到的记录数
	executeQuery(query);
}

@Test
public void BooleanQueryTest(String t, String t2) throws IOException {

	String searchField1 = "title";
	String searchField2 = "content";
	Query query1 = new TermQuery(new Term(searchField1, t));
	Query query2 = new TermQuery(new Term(searchField2, t2));
	BooleanQuery.Builder builder = new BooleanQuery.Builder();

	// BooleanClause用于表示布尔查询子句关系的类,
	// 包 括:
	// BooleanClause.Occur.MUST,
	// BooleanClause.Occur.MUST_NOT,
	// BooleanClause.Occur.SHOULD。
	// 必须包含,不能包含,可以包含三种.有以下6种组合:
	//
	// 1.MUST和MUST:取得连个查询子句的交集。
	// 2.MUST和MUST_NOT:表示查询结果中不能包含MUST_NOT所对应得查询子句的检索结果。
	// 3.SHOULD与MUST_NOT:连用时,功能同MUST和MUST_NOT。
	// 4.SHOULD与MUST连用时,结果为MUST子句的检索结果,但是SHOULD可影响排序。
	// 5.SHOULD与SHOULD:表示“或”关系,最终检索结果为所有检索子句的并集。
	// 6.MUST_NOT和MUST_NOT:无意义,检索无结果。

	builder.add(query1, BooleanClause.Occur.SHOULD);
	builder.add(query2, BooleanClause.Occur.SHOULD);

	BooleanQuery query = builder.build();

	//执行查询,并打印查询到的记录数
	executeQuery(query);
}

看吧,整合 SpringBoot 也是非常的简单。并没有大家想象的那么难。

业余草公众号

最后,欢迎关注我的个人微信公众号:业余草(yyucao)!可加QQ1群:135430763(2000人群已满),QQ2群:454796847(已满),QQ3群:187424846(已满)。QQ群进群密码:xttblog,想加微信群的朋友,之前的微信号好友已满,请加博主新的微信号:xttblog,备注:“xttblog”,添加博主微信拉你进群。备注错误不会同意好友申请。再次感谢您的关注!后续有精彩内容会第一时间发给您!原创文章投稿请发送至532009913@qq.com邮箱。商务合作可添加助理微信进行沟通!

本文原文出处:业余草: » Lucene 实战教程第十六章 SpringBoot 整合 Lucene