本博客日IP超过2000,PV 3000 左右,急需赞助商。
极客时间所有课程通过我的二维码购买后返现24元微信红包,请加博主新的微信号:xttblog2,之前的微信号好友位已满,备注:返现
受密码保护的文章请关注“业余草”公众号,回复关键字“0”获得密码
所有面试题(java、前端、数据库、springboot等)一网打尽,请关注文末小程序
腾讯云】1核2G5M轻量应用服务器50元首年,高性价比,助您轻松上云
网上搜索了一下关于 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)!可加作者微信号:xttblog2。备注:“1”,添加博主微信拉你进微信群。备注错误不会同意好友申请。再次感谢您的关注!后续有精彩内容会第一时间发给您!原创文章投稿请发送至532009913@qq.com邮箱。商务合作也可添加作者微信进行联系!
本文原文出处:业余草: » Lucene 实战教程第十六章 SpringBoot 整合 Lucene