莱芜网站优化方案,零食网站建设的必要性,网站制作心得体会200字,企业怎么做网站建设笑小枫的专属目录 1. 项目背景2. 什么是Lucene3. 引入依赖#xff0c;配置索引3.1 引入Lucene依赖和分词器依赖3.2 表结构和数据准备3.3 创建索引3.4 修改索引3.5删除索引 4. 数据检索4.1 基础搜索4.2 一个关键词#xff0c;在多个字段里面搜索4.3 搜索结果高亮显示4.4 分页检… 笑小枫的专属目录 1. 项目背景2. 什么是Lucene3. 引入依赖配置索引3.1 引入Lucene依赖和分词器依赖3.2 表结构和数据准备3.3 创建索引3.4 修改索引3.5删除索引 4. 数据检索4.1 基础搜索4.2 一个关键词在多个字段里面搜索4.3 搜索结果高亮显示4.4 分页检索4.5多个关键词搜索 5. IK扩展词处理6. 项目源码 1. 项目背景
同样本文的出现也是我的个人网站笑小枫搭建的过程中产生的作为一个技术博客为主的网站Mysql的搜索已经满足不了我的野心了于是我便瞄上了全文检索。最初是打算直接使用比较熟悉的ES但是考虑到部署ES额外的服务器资源开销最后选择了Lucene搭配IK分词器直接在项目中整合。
2. 什么是Lucene
看看官网上的介绍吧~ Apache Lucene™ is a high-performance, full-featured search engine library written entirely in Java. It is a technology suitable for nearly any application that requires structured search, full-text search, faceting, nearest-neighbor search across high-dimensionality vectors, spell correction or query suggestions. Apache Lucene is an open source project available for free download. 看不懂翻译过来就是 Apache Lucene™是一个完全用Java编写的高性能、全功能的搜索引擎库。它是一种几乎适用于任何需要结构化搜索、全文搜索、切面、跨高维向量的最近邻搜索、拼写纠正或查询建议的应用程序的技术。Apache Lucene是一个免费下载的开源项目。 没错它就是我们需要的全文搜索引擎接下来让我们一起看看怎么在SpringBoot项目中集成使用它吧。
3. 引入依赖配置索引
3.1 引入Lucene依赖和分词器依赖
先看看需要的依赖吧。
算了还是先说说我的需求吧算了没有需求具体参考百度搜索框吧~反正就是那样
直接上依赖吧默认分词器对中文不友好。这里使用IK分词器不多介绍
!-- Lucene核心库 --dependencygroupIdorg.apache.lucene/groupIdartifactIdlucene-core/artifactIdversion7.6.0/version/dependency!-- Lucene的查询解析器 --dependencygroupIdorg.apache.lucene/groupIdartifactIdlucene-queryparser/artifactIdversion7.6.0/version/dependency!-- Lucene的默认分词器库 --dependencygroupIdorg.apache.lucene/groupIdartifactIdlucene-analyzers-common/artifactIdversion7.6.0/version/dependency!-- Lucene的高亮显示 --dependencygroupIdorg.apache.lucene/groupIdartifactIdlucene-highlighter/artifactIdversion7.6.0/version/dependency!-- ik分词器 --dependencygroupIdcom.jianggujin/groupIdartifactIdIKAnalyzer-lucene/artifactIdversion8.0.0/version/dependency
!-- dependency--
!-- groupIdcom.janeluo/groupId--
!-- artifactIdikanalyzer/artifactId--
!-- version2012_u6/version--
!-- /dependency--这里使用com.jianggujin:IKAnalyzer-lucene:8.0.0可以兼容新版本的lucene。
新版本的lucene和com.janeluo:ikanalyzer:2012_u6版本冲突会报以下错误。
解决方案放在源码中了这里不展开了。使用com.janeluo:ikanalyzer:2012_u6版本把com.maple.lucene.util.MyIKAnalyzer和MyIKTokenizer的注释放开就行。 3.2 表结构和数据准备
准备表结构这里是简化过的表结构只提供演示效果。
CREATE TABLE blog_title (id BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT ID,title VARCHAR(255) NOT NULL COMMENT 标题,description VARCHAR(255) NULL DEFAULT NULL COMMENT 描述,PRIMARY KEY (id) USING BTREE
)
COMMENT博客标题 COLLATEutf8_general_ci ENGINEInnoDB;准备测试数据
INSERT INTO blog_title (id, title, description) VALUES
(808, 0.SpringBoot目录, https://xiaoxiaofeng.com),
(809, 1.SpringBoot项目创建, 大家好我是笑小枫跟我一起玩转SpringBoot项目吧本文讲一下如何搭建SpringBoot项目。),
(810, 10.SpringBoot处理请求跨域问题, CORS全称Cross-Origin Resource Sharing意为跨域资源共享。当一个资源去访问另一个不同域名或者同域名不同端口的资源时就会发出跨域请求。如果此时另一个资源不允许其进行跨域资源访问那么访问就会遇到跨域问题。跨域指的是由于浏览器的安全性限制不允许前端页面访问协议不同、域名不同、端口号不同的http接口。),
(811, 11.SpringBoot接口日志信息统一记录, 为什么要记录接口日志\n至于为什么详细看到这里的小伙伴心里都有一个答案吧我这里简单列一下常用的场景吧用户登录记录统计、重要增删改操作留痕、需要统计用户的访问次数、接口调用情况统计、线上问题排查、等等等...既然有这么多使用场景那我们该怎么处理总不能一条一条的去记录吧面试是不是老是被问Spring的Aop的使用场景那这个典型的场景就来了我们可以使用Spring的Aop完美的实现这个功能接下来上代码),
(812, 12.SpringBoot导入Excel, 在java处理excel方便从简单的实现功能到自己封装工具类一路走了好多阿里的easyExcel对POI的封装更加精简这里介绍一下简单使用。),
(813, 13.SpringBoot导出Excel, 在java处理excel方便从简单的实现功能到自己封装工具类一路走了好多阿里的easyExcel对POI的封装更加精简这里介绍一下简单使用。),
(814, 14.SpringBoot发送邮件, 本文主要介绍了使用SpringBoot发送邮件主要包含如何获取发送邮件的授权码这里以QQ邮箱为例然后介绍了功能如何实现包括通过模板发送邮件发送带图片的邮件发送带附件的邮件发送带有多个附件的邮件。),
(815, 15.SpringBoot根据模板生成Word, 本文主要讲了SpringBoot基于模板的形式生成word的功能实现感兴趣或有类似功能需求的小伙伴可以看一下包括word模板制作功能代码实现支持导出图片、表格等功能。),
(816, 16.SpringBoot生成PDF, 本文主要介绍了在SpringBoot项目下通过代码和操作步骤详细的介绍了如何操作PDF。希望可以帮助到准备通过JAVA操作PDF的你。\n本文涉及pdf操作如下\nPDF模板制作、 基于PDF模板生成并支持下载、自定义中文字体、完全基于代码生成并保存到指定目录、合并PDF并保存到指定目录、合并PDF并支持下载\n),
(817, 17.SpringBoot文件上传下载, 在java开发中文件的上传、下载、删除功能肯定是很常见的本文主要基于上传图片或文件到指定的位置展开通过详细的代码和工具类讲述java如何实现文件的上传、下载、删除。),
(818, 18.SpringBoot中的Properties配置, springboot在使用过程中我们有很多配置比如mysql配置、redis配置、mybatis-plus、调用第三方的接口配置等等...\n\n我们现在都是放在一个大而全的配置里面的如果我们想根据功能分为不同的配置文件管理让配置更加清晰应该怎么做呢),
(819, 19.使用Docker部署最佳实践, 使用Docker部署最佳实践),
(820, 2.SpringBoot配置基于swagger2的knife4j接口文档, SpringBoot项目如果前后端分离怎么把写好了的接口返回给前端的小伙伴呢试试这款基于Swagger2的knife4j吧简直好用到爆),
(821, 3.SpringBoot集成Mybatis Plus, 本文主要介绍了SpringBoot集成mysql数据库、集成Mybatis Plus框架通过一个简单的例子演示了一下使用Mybatis Plus进行数据插入和查询使用Knife4j进行接口调试集成阿里巴巴Druid数据连接池通过Druid页面进行执行sql查询、分析。),
(822, 4.SpringBoot返回统一结果包装, 前后端分离的时代如果没有统一的返回格式给前端的结果各式各样估计前端的小伙伴就要骂娘了。 \n我们想对自定义异常抛出指定的状态码排查错误对系统的不可预知的异常抛出友好一点的异常信息。 \n我们想让接口统一返回一些额外的数据例如接口执行的时间等等。 那就进来一起康康吧~......),
(823, 5.SpringBoot返回统一异常处理, 如果程序抛异常了我们是否也可以返回统一的格式呢\n答案是当然可以的不光可以抛出我们想要的格式还可以对指定的异常类型进行特殊处理\n例如使用Validated对入参校验的异常我们自定义的异常等等...),
(824, 6.SpringBoot日志打印Logback详解, Logback 旨在作为流行的 log4j 项目的继承者是SpringBoot内置的日志处理框架spring-boot-starter其中包含了spring-boot-starter-logging该依赖内容就是 Spring Boot 默认的日志框架 logback。这里给大家介绍一下在SpraingBoot中Logback的配置。),
(825, 7.SpringBoot控制台自定义banner, 熬夜整理完logback相关的内容突然发现我们的《笑小枫系列-玩转SpringBoot》已经6篇文章了我们的配套程序居然没有一个属于自己的log这简直说不过去了我这处女座的小暴脾气赶紧整一个于是便有了此文。好了接下来言归正传毕竟本文也是属于我们系列的一份子嘛不能落下),
(826, 8.SpringBoot集成Redis, SpringBoot中怎么使用Redis做缓存机制呢本文为大家揭开Redis的面纱内容偏基础但详细。本文核心SpringBoot继承redis、SpringBoot常用的redis操作演示、监听Redis的key过期机制。),
(827, 9.SpringBoot用户登录拦截器, 本文主要介绍了SpringBoot实现登录功能使用JWTRedis进行功能实现从最基础的建表开始详细的介绍了功能的实现。学习完本文你将掌握登录功能的核心技能。),
(832, 【笑小枫的按步照搬系列】JDK8下载安装配置, 本文主要讲解了JDK8在windows环境下的下载、安装、已经环境变量的配置参照本文你只需要按步照搬便可快速的安装好JAVA环境。),
(833, 【笑小枫的按步照搬系列】Maven环境配置, 本文主要介绍了maven的安装配置包括配置本地仓库配置阿里镜像等。安装maven环境之前要先安装java jdk环境没有安装java环境的可以先去看安装JAVA环境的教程Maven 3.3 require JDK 1.7 及以上。),
(834, 【笑小枫的按步照搬系列】Node.js安装, Node.js安装),
(835, 【笑小枫的按步照搬系列】Redis可视化工具-RedisInsight, RedisInsight是Redis官方出品的可视化管理工具可用于设计、开发、优化你的Redis应用。支持深色和浅色两种主题界面非常炫酷可支持String、Hash、Set、List、JSON等多种数据类型的管理同时支持远程使用CLI功能功能非常强大),
(836, 【笑小枫的按步照搬系列】Redis多系统安装Windows、Linux、Ubuntu, RedisRemote Dictionary Server )即远程字典服务是一个开源的使用ANSI C语言编写、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库并提供多种语言的API。本文主要讲述了Redis如何安装。),
(837, 【笑小枫的按步照搬系列】开源的服务器远程工具-FinalShell, 之前一直使用 xshell ftp 组合的方式来部署项目后来发现了FinalShell 这款软件瞬间就爱上了。FinalShell 相当于 xshell ftp 的组合即FinalShell xshell ftp FinalShell 只用一个程序将xshell 、ftp同屏显示既可以输入命令也可以传输数据还能以树的形式展示文件路径。),
(840, 【笑小枫的按步照搬系列】本地安装Mysql数据库, 本文主要介绍了在windows环境下如何下载安装mysql8版本你只需要按步照搬就可以完美解决你安装软件的困扰。本文主要包括mysql的下载、安装、配置my.ini文件、修改初始化密码等。),
(841, 【笑小枫的按步照搬系列】版本控制工具git安装过程详解, Git 是个免费的开源分布式版本控制系统下载地址为git-scm.com 或者 gitforwindows.org本文介绍 Git-2.35.1.2-64-bit.exe 版本的安装方法需要的小伙伴可以看一看。);对数据库的操作使用的Mybatis Plus这里演示比较简单只是单纯的取数据不贴详细代码了需要的去源码里面获取。不想连数据库可以直接用个List模拟掉简单的贴个对象吧。
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;/*** p* blog标题* /p** author 笑小枫 https://xiaoxiaofeng.com/* since 2023-01-30*/
Data
TableName(blog_title)
public class BlogTitle {private Long id;private String title;private String description;
}3.3 创建索引
这里直接从数据库查询所有数据然后创建索引了只为演示实际使用中根据数据量大小业务需要哪些字段是否需要回表查询等等考虑生产方案钓无定法技术多彩。
直接上代码了索引建在d:\\indexDir目录下实际使用该封装封装该放配置放配置哈。这里为了演示效果好方便你们copy集中都放在这里了。 注释比较详细不单独介绍功能了。
如果新增数据追加的话使用conf.setOpenMode(IndexWriterConfig.OpenMode.APPEND);模式即可。 GetMapping(/createIndex)public String createIndex() {ListBlogTitle list blogTitleMapper.selectList(Wrappers.lambdaQuery(BlogTitle.class));// 创建文档的集合CollectionDocument docs new ArrayList();for (BlogTitle blogTitle : list) {// 创建文档对象Document document new Document();// StringField: 这个 Field 用来构建一个字符串Field不分析会索引Field.Store控制存储// LongPoint、IntPoint 等类型存储数值类型的数据。会分析会索引不存储如果想存储数据还需要使用 StoredField// StoredField: 这个 Field 用来构建不同类型不分析不索引会存储// TextField: 如果是一个Reader, 会分析会索引Field.Store控制存储document.add(new StringField(id, String.valueOf(blogTitle.getId()), Field.Store.YES));// Field.Store.YES, 将原始字段值存储在索引中。这对于短文本很有用比如文档的标题它应该与结果一起显示。// 值以其原始形式存储即在存储之前没有使用任何分析器。document.add(new TextField(title, blogTitle.getTitle(), Field.Store.YES));// Field.Store.NO可以索引分词不将字段值存储在索引中。// 个人理解说白了就是为了省空间如果回表查询其实无所谓如果不回表查询需要展示就要保存设为YES无需展示设为NO即可。document.add(new TextField(description, blogTitle.getDescription(), Field.Store.NO));docs.add(document);}// 引入IK分词器如果需要解决上面版本冲突报错的问使用new MyIKAnalyzer()即可Analyzer analyzer new IKAnalyzer();// 索引写出工具的配置对象IndexWriterConfig conf new IndexWriterConfig(analyzer);// 设置打开方式OpenMode.APPEND 会在索引库的基础上追加新索引。OpenMode.CREATE会先清空原来数据再提交新的索引conf.setOpenMode(IndexWriterConfig.OpenMode.CREATE);// 索引目录类,指定索引在硬盘中的位置我的设置为D盘的indexDir文件夹// 创建索引的写出工具类。参数索引的目录和配置信息try (Directory directory FSDirectory.open(FileSystems.getDefault().getPath(d:\\indexDir));IndexWriter indexWriter new IndexWriter(directory, conf)) {// 把文档集合交给IndexWriterindexWriter.addDocuments(docs);// 提交indexWriter.commit();} catch (Exception e) {log.error(创建索引失败, e);return 创建索引失败;}return 创建索引成功;}创建索引后在d:\\indexDir目录下会出现索引文件如下图 3.4 修改索引
数据变更时索引应该怎么变更呢该如何怎么设计呢
在程序中数据变更的时候更新索引但是对业务的侵入性比较大。新增、修改、删除时都要多一套操作Lucene的接口。监听数据库数据变更然后更新索引引入额外中间件复杂度变高。
有舍有得吧看权衡点在哪了大家有什么好的方案可以留言哟。 GetMapping(/updateIndex)public String update() {// 创建配置对象IndexWriterConfig conf new IndexWriterConfig(new IKAnalyzer());// 创建目录对象// 创建索引写出工具try (Directory directory FSDirectory.open(FileSystems.getDefault().getPath(d:\\indexDir));IndexWriter writer new IndexWriter(directory, conf)) {// 获取更新的数据这里只是演示BlogTitle blogTitle blogTitleMapper.selectById(808);// 创建新的文档数据Document doc new Document();doc.add(new StringField(id, 808, Field.Store.YES));doc.add(new TextField(title, blogTitle.getTitle(), Field.Store.YES));doc.add(new TextField(description, blogTitle.getDescription(), Field.Store.YES));writer.updateDocument(new Term(id, 808), doc);// 提交writer.commit();} catch (Exception e) {log.error(更新索引失败, e);return 更新索引失败;}return 更新索引成功;}修改前搜索 然后将id808的title修改为0.SpringBoot不是目录更新索引。可以看到数据已变更但是分词查询数据仍然查询出来了。 3.5删除索引 GetMapping(/deleteIndex)public String deleteIndex() {// 创建配置对象IndexWriterConfig conf new IndexWriterConfig(new IKAnalyzer());// 创建目录对象// 创建索引写出工具try (Directory directory FSDirectory.open(FileSystems.getDefault().getPath(d:\\indexDir));IndexWriter writer new IndexWriter(directory, conf)) {// 根据词条进行删除writer.deleteDocuments(new Term(id, 808));// 提交writer.commit();} catch (Exception e) {log.error(删除索引失败, e);return 删除索引失败;}return 删除索引成功;}只能删除id808的索引然后再进行查询可以看到数据消失了。 4. 数据检索
4.1 基础搜索
最基础的模糊搜索功能不用文字解释了写个sql的案例吧很明显就能懂。
当然走Lucene支持分词检索计算得分展示等等只为了容易懂不杠…
select * from blog_title where title like (%#{title}%)/*** 简单搜索*/RequestMapping(/searchText)public ListBlogTitle searchText(String text) throws IOException, ParseException {Directory directory FSDirectory.open(FileSystems.getDefault().getPath(d:\\indexDir));// 索引读取工具IndexReader reader DirectoryReader.open(directory);// 索引搜索工具IndexSearcher searcher new IndexSearcher(reader);// 创建查询解析器,两个参数默认要查询的字段的名称分词器QueryParser parser new QueryParser(title, new IKAnalyzer());// 创建查询对象Query query parser.parse(text);// 获取前十条记录TopDocs topDocs searcher.search(query, 10);// 获取总条数log.info(本次搜索共找到 topDocs.totalHits 条数据);// 获取得分文档对象ScoreDoc数组.SocreDoc中包含文档的编号、文档的得分ScoreDoc[] scoreDocs topDocs.scoreDocs;ListBlogTitle list new ArrayList();for (ScoreDoc scoreDoc : scoreDocs) {// 取出文档编号int docId scoreDoc.doc;// 根据编号去找文档Document doc reader.document(docId);BlogTitle content blogTitleMapper.selectById(doc.get(id));list.add(content);}return list;}GET http://localhost:8080/lucene/searchText?text笑小枫可以看到 title 中包含笑小枫的数据都搜索出来了 4.2 一个关键词在多个字段里面搜索
关键词在title和description两个字段里面检索类似于下面的sql。
select * from blog_title where title like (%#{searchPram}%) or description like (%#{searchPram}%)/*** 一个关键词在多个字段里面搜索*/RequestMapping(/searchTextMore)public ListBlogTitle searchTextMore(String text) throws IOException, ParseException {String[] str {title, description};Directory directory FSDirectory.open(FileSystems.getDefault().getPath(d:\\indexDir));// 索引读取工具IndexReader reader DirectoryReader.open(directory);// 索引搜索工具IndexSearcher searcher new IndexSearcher(reader);// 创建查询解析器,两个参数默认要查询的字段的名称分词器MultiFieldQueryParser parser new MultiFieldQueryParser(str, new IKAnalyzer());// 创建查询对象Query query parser.parse(text);// 获取前十条记录TopDocs topDocs searcher.search(query, 100);// 获取总条数log.info(本次搜索共找到 topDocs.totalHits 条数据);// 获取得分文档对象ScoreDoc数组.SocreDoc中包含文档的编号、文档的得分ScoreDoc[] scoreDocs topDocs.scoreDocs;ListBlogTitle list new ArrayList();for (ScoreDoc scoreDoc : scoreDocs) {// 取出文档编号int docId scoreDoc.doc;// 根据编号去找文档Document doc reader.document(docId);BlogTitle content blogTitleMapper.selectById(doc.get(id));list.add(content);}return list;}GET http://localhost:8080/lucene/searchTextMore?text笑小枫可以看到title和description中包含笑小枫的数据都搜索出来了 4.3 搜索结果高亮显示
这个功能基本必备吧让用户明确知道搜索的匹配程度 /*** 搜索结果高亮显示*/RequestMapping(/searchTextHighlighter)public ListBlogTitle searchTextHighlighter(String text) throws IOException, ParseException, InvalidTokenOffsetsException {String[] str {title, description};Directory directory FSDirectory.open(FileSystems.getDefault().getPath(d:\\indexDir));// 索引读取工具IndexReader reader DirectoryReader.open(directory);// 索引搜索工具IndexSearcher searcher new IndexSearcher(reader);// 创建查询解析器,两个参数默认要查询的字段的名称分词器MultiFieldQueryParser parser new MultiFieldQueryParser(str, new IKAnalyzer());// 创建查询对象Query query parser.parse(text);// 获取前十条记录TopDocs topDocs searcher.search(query, 100);// 获取总条数log.info(本次搜索共找到 topDocs.totalHits 条数据);//高亮显示SimpleHTMLFormatter simpleHTMLFormatter new SimpleHTMLFormatter(span stylecolor:red, /span);Highlighter highlighter new Highlighter(simpleHTMLFormatter, new QueryScorer(query));//高亮后的段落范围在100字内Fragmenter fragmenter new SimpleFragmenter(100);highlighter.setTextFragmenter(fragmenter);// 获取得分文档对象ScoreDoc数组.SocreDoc中包含文档的编号、文档的得分ScoreDoc[] scoreDocs topDocs.scoreDocs;ListBlogTitle list new ArrayList();for (ScoreDoc scoreDoc : scoreDocs) {// 取出文档编号int docId scoreDoc.doc;// 根据编号去找文档Document doc reader.document(docId);BlogTitle content blogTitleMapper.selectById(doc.get(id));//处理高亮字段显示String title highlighter.getBestFragment(new IKAnalyzer(), title, doc.get(title));if (title null) {title content.getTitle();}// 因为创建索引的时候description设置的Field.Store.NO所以这里doc没有description数据取不出来值设为YES则可以可以断点看一下直接设置content.getDescription()也可以高亮显示
// String description highlighter.getBestFragment(new IKAnalyzer(), description, doc.get(description));
// if (description null) {
// description content.getDescription();
// }
// content.setDescription(description);content.setDescription(content.getDescription());content.setTitle(title);list.add(content);}return list;}
GET http://localhost:8080/lucene/searchTextHighlighter?text笑小枫因为创建索引的时候description设置的Field.Store.NO所以这里doc没有description数据取不出来值故不做高亮当然从数据库中查询出来再做高亮也是可以的。 4.4 分页检索
不多说你需要的我都整活直接上代码分页直接再程序中写死了正常需要传分页参数返回分页数据总条数等不利于演示和普通分页一样自己封装吧
/*** 分页搜索*/RequestMapping(/searchTextPage)public ListBlogTitle searchTextPage(String text) throws IOException, ParseException, InvalidTokenOffsetsException {String[] str {title, description};int page 1;int pageSize 5;Directory directory FSDirectory.open(FileSystems.getDefault().getPath(d:\\indexDir));// 索引读取工具IndexReader reader DirectoryReader.open(directory);// 索引搜索工具IndexSearcher searcher new IndexSearcher(reader);// 创建查询解析器,两个参数默认要查询的字段的名称分词器MultiFieldQueryParser parser new MultiFieldQueryParser(str, new IKAnalyzer());// 创建查询对象Query query parser.parse(text);// 分页获取数据TopDocs topDocs searchByPage(page, pageSize, searcher, query);// 获取总条数log.info(本次搜索共找到 topDocs.totalHits 条数据);//高亮显示SimpleHTMLFormatter simpleHTMLFormatter new SimpleHTMLFormatter(span stylecolor:red, /span);Highlighter highlighter new Highlighter(simpleHTMLFormatter, new QueryScorer(query));//高亮后的段落范围在100字内Fragmenter fragmenter new SimpleFragmenter(100);highlighter.setTextFragmenter(fragmenter);// 获取得分文档对象ScoreDoc数组.SocreDoc中包含文档的编号、文档的得分ScoreDoc[] scoreDocs topDocs.scoreDocs;ListBlogTitle list new ArrayList();for (ScoreDoc scoreDoc : scoreDocs) {// 取出文档编号int docId scoreDoc.doc;// 根据编号去找文档Document doc reader.document(docId);BlogTitle content blogTitleMapper.selectById(doc.get(id));//处理高亮字段显示String title highlighter.getBestFragment(new IKAnalyzer(), title, doc.get(title));if (title null) {title content.getTitle();}String description highlighter.getBestFragment(new IKAnalyzer(), description, content.getDescription());content.setDescription(description);content.setTitle(title);list.add(content);}return list;}private TopDocs searchByPage(int page, int perPage, IndexSearcher searcher, Query query) throws IOException {TopDocs result;if (query null) {log.info( Query is null return null );return null;}ScoreDoc before null;if (page ! 1) {TopDocs docsBefore searcher.search(query, (page - 1) * perPage);ScoreDoc[] scoreDocs docsBefore.scoreDocs;if (scoreDocs.length 0) {before scoreDocs[scoreDocs.length - 1];}}result searcher.searchAfter(before, query, perPage);return result;}
GET http://localhost:8080/lucene/searchTextPage?text笑小枫第一页数据 第二页数据
手动修改int page 2;保证没偷懒~ 4.5多个关键词搜索
最起码满足你的日常使用吧。 /*** 多关键词搜索*/GetMapping(/searchTextMoreParam)public ListBlogTitle searchTextMoreParam(String text) throws IOException, ParseException, InvalidTokenOffsetsException {String[] str {title, description};Directory directory FSDirectory.open(FileSystems.getDefault().getPath(d:\\indexDir));// 索引读取工具IndexReader reader DirectoryReader.open(directory);// 索引搜索工具IndexSearcher searcher new IndexSearcher(reader);//多条件查询构造BooleanQuery.Builder builder new BooleanQuery.Builder();// 条件一MultiFieldQueryParser parser new MultiFieldQueryParser(str, new IKAnalyzer());// 创建查询对象Query query parser.parse(text);builder.add(query, BooleanClause.Occur.MUST);// 条件二// TermQuery不使用分析器所以建议匹配不分词的Field域(StringField, )查询比如价格、分类ID号等。这里只能演示个ID了。。。Query termQuery new TermQuery(new Term(id, 839));builder.add(termQuery, BooleanClause.Occur.MUST);// 获取前十条记录TopDocs topDocs searcher.search(builder.build(), 100);// 获取总条数log.info(本次搜索共找到 topDocs.totalHits 条数据);//高亮显示SimpleHTMLFormatter simpleHTMLFormatter new SimpleHTMLFormatter(span stylecolor:red, /span);Highlighter highlighter new Highlighter(simpleHTMLFormatter, new QueryScorer(query));//高亮后的段落范围在100字内Fragmenter fragmenter new SimpleFragmenter(100);highlighter.setTextFragmenter(fragmenter);// 获取得分文档对象ScoreDoc数组.SocreDoc中包含文档的编号、文档的得分ScoreDoc[] scoreDocs topDocs.scoreDocs;ListBlogTitle list new ArrayList();for (ScoreDoc scoreDoc : scoreDocs) {// 取出文档编号int docId scoreDoc.doc;// 根据编号去找文档Document doc reader.document(docId);BlogTitle content blogTitleMapper.selectById(doc.get(id));//处理高亮字段显示String title highlighter.getBestFragment(new IKAnalyzer(), title, doc.get(title));if (title null) {title content.getTitle();}String description highlighter.getBestFragment(new IKAnalyzer(), description, content.getDescription());content.setDescription(description);content.setTitle(title);list.add(content);}return list;}GET http://localhost:8080/lucene/searchTextMoreParam?textmysql数据库5. IK扩展词处理
什么是扩展词呢字面意思。
就如笑小枫我认为它是一个完整的词汇但是人家IK不认呀怎么办呢
还有就是的和了这些分词检索没有太大意义的词我们可以过滤掉不参与检索。
不说废话怎么做呢看图~ 添加上图文件即可生不生效看高亮就很明显下文演示。
说说坑哈
坑一注意打包后有没有文件如果没有打进去的话就会不生效
坑二设置后需要重新创建索引不然可能会查不到数据
注意这个名字不能错IKAnalyzer.cfg.xml放在resources目录下
?xml version1.0 encodingUTF-8?
!DOCTYPE properties SYSTEM http://java.sun.com/dtd/properties.dtd
propertiescommentIKAnalyzer扩展配置/comment!--用户的扩展字典 --entry keyext_dictextend.dic/entry!--用户扩展停止词字典 --entry keyext_stopwordsstop.dic/entry
/propertiesextend.dic对应上面文件中的名字名字可以自定义同步IKAnalyzer.cfg.xml修改和路径输入多个回车即可
笑小枫系列
笑小枫
按步照搬stop.dic对应上面文件中的名字名字可以自定义和路径输入多个回车即可
的
好
了设置前 设置后 6. 项目源码
本文到此就结束了如果帮助到你了帮忙点个赞
本文源码https://github.com/hack-feng/maple-product/tree/main/maple-lucene 我是笑小枫全网皆可搜的【笑小枫】