当前位置: 首页 > news >正文

springboot 集成 lucene

简介

  1. 数据每分钟产生200条,使用mysql储存。
  2. 目前有数据超过700M。
  3. 按照日期查询,按月查询包含每次超过20w条以上,时间比较长。
  4. 计划使用lucene优化查询,不适用es是因为项目较小,没有更富裕的资源。

基本步骤

  1. 引入依赖。
  2. 开发工具类。
  3. 开发索引功能,完成索引。
  4. 开发定时任务,完成数据增量更新。
  5. 开发搜索功能,可以搜索数据。

引入依赖

  1. 修改pom文件
<!-- Lucence核心包 -->
<dependency><groupId>org.apache.lucene</groupId><artifactId>lucene-core</artifactId><version>9.7.0</version>
</dependency><!-- Lucene查询解析包 -->
<dependency><groupId>org.apache.lucene</groupId><artifactId>lucene-queryparser</artifactId><version>9.7.0</version>
</dependency>
  • 注:没有使用更多的包是因为这次优化是以long类型区间计算为主,不需要全文索引,所以有基础的包就够了。

工具类

  1. 实现基本的生成、删除和查询。

import com.xxx.common.ResponseCode;
import com.xxx.common.exception.SystemException;
import com.xxx.common.util.ValidUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.analysis.standard.StandardAnalyzer;
import org.apache.lucene.document.Document;
import org.apache.lucene.index.DirectoryReader;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.IndexWriterConfig;
import org.apache.lucene.search.IndexSearcher;
import org.apache.lucene.search.ScoreDoc;
import org.apache.lucene.search.TopDocs;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.FSDirectory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;import java.io.File;
import java.io.IOException;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.List;@Component
@Slf4j
public class LuceneUtil {//索引文件存放路径@Value("${lucene.index.path}")private String luceneIndexPath;/**生成索引方法*/public <T> void createIndex(List<T> list, CreateDocumentHandler handler) {File file = new File(luceneIndexPath);if (!file.exists()) {file.mkdir();}if (ValidUtil.isEmpty(list)) {return;}long startTime = System.currentTimeMillis();IndexWriter writer = null;try {Directory dir = FSDirectory.open(Paths.get(luceneIndexPath));//标准分词器,会自动去掉空格啊,is a the等单词Analyzer analyzer = new StandardAnalyzer();//将标准分词器配到写索引的配置中IndexWriterConfig config = new IndexWriterConfig(analyzer);//实例化写索引对象writer = new IndexWriter(dir, config);for (T t : list) {Document doc = handler.createDocument(t);writer.addDocument(doc);}writer.commit();} catch (Exception e) {throw new SystemException(ResponseCode.ERROR, e);} finally {try {if (null != writer) {writer.close();}} catch (Exception e) {throw new SystemException(ResponseCode.ERROR, e);}}//记录索引结束时间long endTime = System.currentTimeMillis();log.info("建立索引耗时" + (endTime - startTime) + "毫秒");}/**清楚所有索引*/public void clean() {File file = new File(luceneIndexPath);if (!file.exists()) {return;}long startTime = System.currentTimeMillis();IndexWriter writer = null;try {Directory dir = FSDirectory.open(Paths.get(luceneIndexPath));//标准分词器,会自动去掉空格啊,is a the等单词Analyzer analyzer = new StandardAnalyzer();//将标准分词器配到写索引的配置中IndexWriterConfig config = new IndexWriterConfig(analyzer);//实例化写索引对象writer = new IndexWriter(dir, config);writer.deleteAll();} catch (Exception e) {throw new SystemException(ResponseCode.ERROR, e);} finally {try {if (null != writer) {writer.close();}} catch (Exception e) {throw new SystemException(ResponseCode.ERROR, e);}}//记录索引结束时间long endTime = System.currentTimeMillis();log.info("清除索引耗时" + (endTime - startTime) + "毫秒");}/**查询*/public List<Document> search(CreateQueryParamsHandler handler) {File file = new File(luceneIndexPath + File.separator + "write.lock");if (!file.exists()) {return new ArrayList<>();}IndexReader reader = null;try {//获取要查询的路径,也就是索引所在的位置Directory dir = FSDirectory.open(Paths.get(luceneIndexPath));reader = DirectoryReader.open(dir);if (reader == null) {return new ArrayList<>();}//构建IndexSearcherIndexSearcher searcher = new IndexSearcher(reader);//记录索引开始时间long startTime = System.currentTimeMillis();//开始查询,查询前10条数据,将记录保存在docs中TopDocs docs = handler.handler(searcher);//记录索引结束时间long endTime = System.currentTimeMillis();log.info("索引查询耗时" + (endTime - startTime) + "毫秒");List<Document> result = new ArrayList<>(Long.valueOf(docs.totalHits.value).intValue());//取出每条查询结果for(ScoreDoc scoreDoc : docs.scoreDocs) {Document doc = searcher.doc(scoreDoc.doc);result.add(doc);}return result;} catch (Exception e) {throw new SystemException(ResponseCode.ERROR, e);} finally {try {assert reader != null;reader.close();} catch (IOException e) {throw new SystemException(ResponseCode.ERROR, e);}}}
}

生成索引功能

public void index(Date startDate) {log.info("start index! Date : " + DateUtil.format(DateUtil.now()));Date curStartDate = startDate;while (true) {Date curEndDate = DateUtil.datePlusDays(curStartDate, 1);List<CurrencyData> list = currencyDataMapper.queryLuceneList(CurrencyDataForm.builder().createTimeBegin(curStartDate.getTime()).createTimeEnd(curEndDate.getTime()).build());log.info(String.format("index startDate = %s, endDate = %s, size = %s", DateUtil.format(curStartDate), DateUtil.format(curEndDate), list.size()));if (list.size() == 0) {CurrencyDataForm countForm = CurrencyDataForm.builder().createTimeBegin(curStartDate.getTime()).build();List<CurrencyData> one = currencyDataMapper.getOne(countForm);log.info("has more begin:" + DateUtil.format(curEndDate) + ", result: " + (one.size() > 0 ? "yes" : "no"));if (one.size() == 0) {break;}}luceneUtil.createIndex(list, (CreateDocumentHandler<Data>) data -> {Document doc = new Document();//开始添加字段doc.add(new TextField("dId", data.getDId(), Field.Store.YES));doc.add(new TextField("typeId", data.getTypeId(), Field.Store.YES));//区间查询需要doc.add(new LongPoint("createTime", data.getCreateTime()));//储存需要doc.add(new StoredField("createTime", data.getCreateTime()));// 排序需要doc.add(new NumericDocValuesField("sortTime", data.getCreateTime()));// 第二个参数需要处理非空的情况doc.add(new TextField("value", (ValidUtil.isEmpty(data.getValue()) ? "" : data.getValue()) , Field.Store.YES));doc.add(new TextField("unit", (ValidUtil.isEmpty(data.getUnit()) ? "" : data.getUnit()) , Field.Store.YES));return doc;});curStartDate = curEndDate;}log.info("finish index!");
}
  • 注:每次生成1天的索引,如果本轮没数据,并且大于结束时间也没数据,结束索引。

定时任务

private ThreadPoolTaskExecutor tpe;tpe.execute(() -> {Date startDate = null;try {startDate = getLastDate();} catch (SystemException s) {luceneUtil.clean();startDate = DateUtil.parse(initStartTime);}try {index(startDate);} catch (Exception e) {log.info("生成索引异常。", e);} finally {ScheduledExecutorService executor = Executors.newScheduledThreadPool(1);executor.schedule(this::init, 60, TimeUnit.SECONDS);executor.shutdown();}
});
  • 注:使用线程池+延时任务,实现每60s执行一次功能。

搜索

public List<Data> queryIndex(Form form) {List<Data> result = new ArrayList<>();List<Document> documentList = luceneUtil.search((searcher) -> {BooleanQuery.Builder builder = new BooleanQuery.Builder();if (ValidUtil.isNotEmpty(form.getDId())) {TermQuery deviceIdQuery = new TermQuery(new Term("dId", form.getDId()));builder.add(deviceIdQuery, BooleanClause.Occur.MUST);}if (ValidUtil.isNotEmpty(form.getTypeId())) {TermQuery typeQuery = new TermQuery(new Term("typeId", form.getTypeId()));builder.add(deviceIdQuery, BooleanClause.Occur.MUST);}if (ValidUtil.isNotEmpty(form.getBegin()) && ValidUtil.isNotEmpty(form.getEnd())) {Query timeQuery = LongPoint.newRangeQuery("time", form.getBegin().getTime(), form.getEnd().getTime());builder.add(timeQuery, BooleanClause.Occur.MUST);}Sort sort = new Sort(new SortField("sortTime", SortField.Type.LONG, false));// 执行查询return searcher.search(builder.build(), form.getSize(), sort);});for (Document document : documentList) {Data data = new Data();data.setTypeId(Integer.valueOf(document.get("typeId")));data.setDId(Integer.valueOf(document.get("dId")));data.setTime(document.getField("time").numericValue().longValue());data.setValue(document.get("value"));data.setUnit(document.get("unit"));result.add(data);}return result;
}

相关文章:

springboot 集成 lucene

简介 数据每分钟产生200条&#xff0c;使用mysql储存。目前有数据超过700M。按照日期查询&#xff0c;按月查询包含每次超过20w条以上&#xff0c;时间比较长。计划使用lucene优化查询&#xff0c;不适用es是因为项目较小&#xff0c;没有更富裕的资源。 基本步骤 引入依赖。…...

Android开机动画

Android开机动画 1、BootLoader开机图片2、Kernel开机图片3、系统启动时&#xff08;BootAnimation&#xff09;动画3.1 bootanimation.zip位置3.2 bootanimation启动3.3 SurfaceFlinger启动bootanimation3.4 播放开机动画playAnimation3.6 开机动画退出检测3.7 简易时序图 4、…...

vue中使用wow.js

一、安装 npm install wowjs --save-dev 二、main中引入 animate.css会自动安装 因为wow.js在animate.css基础上 main.js中引入animate.css import "animate.css" 三、 页面使用 有两种引入使用方式&#xff1a;1. import {WOW} from wowjs mounted() { n…...

网站edge -- 油猴 -> IDM

一、百度网盘限速 未解决 软件&#xff1a;IDM 安装路径&#xff1a; 1.1如果&#xff1a;edge 出问题打不开其他网站&#xff0c; 解决方法&#xff1a; 以管理员的身份&#xff0c;右击载这个软件&#xff0c;就好了 1.2使用这个软件 应该是右击这个软件 以管理员的身…...

Android片段

如果你希望应用根据不同的环境有不同的外观和行为&#xff0c;这种情况下就需要片段&#xff0c;片段是可以由不同活动重用的模块化代码组件。 片段&#xff08;Fragment&#xff09;是活动&#xff08;Activity&#xff09;的一种模块化部分&#xff0c;表示活动中的行为或界面…...

iOS实时监控与报警器

在现代信息化社会中&#xff0c;即使我们不在电脑前面也能随时获取到最新的数据。而苹果公司提供的iOS推送通知功能为我们带来了一种全新的方式——通过手机接收实时监控和报警信息。 首先让我们了解一下iOS推送通知。它是一个强大且灵活可定制化程度高、适用于各类应用场景&a…...

Git小白入门——上手实操之创建仓库和代码提交

版本库 什么是版本库呢&#xff1f;版本库又名仓库&#xff0c;英文名repository&#xff0c;简单理解成一个目录&#xff0c;目录里的所有文件都可以被Git管理&#xff0c;每个文件的修改、删除&#xff0c;Git都能跟踪&#xff0c;以便任何时刻都可以追踪历史&#xff0c;或…...

JS数组迭代方法实操

数组迭代方法有 1. every() 2.some() 3.foreach() 4.map() 5.filter 逐一操作&#xff0c;并简要区分之。 1 every() every() 方法使用指定的函数测试数组中所有的项&#xff0c;在数组的所有项都满足该条件时&#xff0c;才返回true&#xff0c;否则返回false&#xff1b; …...

基于snat+dnat发布内网K8S及Jenkins+gitlab+Harbor模拟CI/CD的综合项目

目录 项目名称 项目架构图 项目环境 项目概述 项目准备 项目步骤 一、修改每台主机的ip地址&#xff0c;同时设置永久关闭防火墙和selinux&#xff0c;修改好主机名&#xff0c;在firewalld服务器上开启路由功能并配置snat策略。 1. 在firewalld服务器上配置ip地址、设…...

时序预测 | MATLAB实现PSO-LSSVM粒子群算法优化最小二乘支持向量机时间序列预测未来

时序预测 | MATLAB实现PSO-LSSVM粒子群算法优化最小二乘支持向量机时间序列预测未来 目录 时序预测 | MATLAB实现PSO-LSSVM粒子群算法优化最小二乘支持向量机时间序列预测未来预测效果基本介绍模型描述程序设计参考资料 预测效果 基本介绍 1.Matlab实现PSO-LSSVM时间序列预测未…...

java IO流(二) 字符流 缓冲流 原始流与缓冲流性能分析

字符流 前面学习的字节流虽然可以读取文件中的字节数据&#xff0c;但是如果文件中有中文&#xff0c;使用字节流来读取&#xff0c;就有可能读到半个汉字的情况&#xff0c;这样会导致乱码。虽然使用读取全部字节的方法不会出现乱码&#xff0c;但是如果文件过大又不太合适。…...

复现XSS漏洞及分析

XSS漏洞概述&#xff1a; 类型一&#xff1a;反射型 类型二&#xff1a;存储型 类型三&#xff1a;DOM型 复现20字符短域名绕过 一、安装BEEF 1、在Kali中运行apt install beef-xss 2、运行beef 3、在浏览器访问 二、安装galleryCMS *遇到一点小问题 提示"last…...

Vue组件之间传值

聊一聊vue里面组件之间的传值 首先总结一下vue里面传值的几种关系&#xff1a; 如上图所示, A与B、A与C、B与D、C与F组件之间是父子关系&#xff1b; B与C之间是兄弟关系&#xff1b;A与D、A与E之间是隔代关系&#xff1b; D与F是堂兄关系&#xff0c;针对以上关系 我们把组件…...

windows查看端口占用,通过端口找进程号(查找进程号),通过进程号定位应用名(查找应用)(netstat、tasklist)

文章目录 通过端口号查看进程号netstat通过进程号定位应用程序tasklist 通过端口号查看进程号netstat 在Windows系统中&#xff0c;可以使用 netstat 命令来查看端口的占用情况。以下是具体的步骤&#xff1a; 打开命令提示符&#xff08;CMD&#xff09;&#xff1a;按WinR组…...

Weblogic SSRF【漏洞复现】

文章目录 漏洞测试注入HTTP头&#xff0c;利用Redis反弹shell redis不能启动问题解决 Path : vulhub/weblogic/ssrf 编译及启动测试环境 docker compose up -dWeblogic中存在一个SSRF漏洞&#xff0c;利用该漏洞可以发送任意HTTP请求&#xff0c;进而攻击内网中redis、fastcgi…...

文件读取漏洞复现(Metinfo 6.0.0)

文章目录 安装环境启动环境漏洞复现代码审计 安装环境 安装phpstudy&#xff0c;下载MetInfo 6.0.0版本软件&#xff0c;复制到phpstudy目录下的www目录中。 打开phpstudy&#xff0c;访问浏览器127.0.0.1/MetInfo6.0.0/install/index.php&#xff0c;打开Meinfo 6.0.0主页&a…...

【工程实践】使用git clone 批量下载huggingface模型文件

前言 经常需要下载模型到服务器&#xff0c;使用git clone方法可以快速实现模型下载。 1.选定要下载的模型 以下载moka-ai/m3e-base为例&#xff0c;切换到Files and versions。 2.更改下载网页的url 如上图所示&#xff0c;当前要下载模型网页的url为&#xff1a; https://hu…...

2020 杭电多校第三场 H Triangle Collision(反射套路 + 绕点旋转 + 矢量

2020 杭电多校第三场 H. Triangle Collision(反射套路 绕点旋转 矢量分解) 大意&#xff1a;给出一个等边三角形 &#xff0c; 以底边中线建立坐标系 &#xff0c; 给出三角形中一点 &#xff0c; 和其初始速度 &#xff0c; 小球在等边三角形中做完全弹性碰撞 &#xff0c; …...

Servlet属性、监听者和会话

没有servlet能单独存在。在当前的现代Web应用中&#xff0c;许多组件都是在一起协作共同完成一个目标。怎么让这些组件共享信息&#xff1f;如何隐藏信息&#xff1f;怎样让信息做到线程安全&#xff1f; 1 属性和监听者 1.1 初始化 容器初始化一个servlet时&#xff0c;会为…...

Gin学习记录2——路由

路由 一. 常规路由二. 动态路由三. 带参数的路由3.1 GET3.2 POST3.3 绑定 四. 简单的路由组五. 文件分组 一. 常规路由 package mainimport ("net/http""github.com/gin-gonic/gin" )func index(ctx *gin.Context) {ctx.String(http.StatusOK, "Hell…...

iOS 26 携众系统重磅更新,但“苹果智能”仍与国行无缘

美国西海岸的夏天&#xff0c;再次被苹果点燃。一年一度的全球开发者大会 WWDC25 如期而至&#xff0c;这不仅是开发者的盛宴&#xff0c;更是全球数亿苹果用户翘首以盼的科技春晚。今年&#xff0c;苹果依旧为我们带来了全家桶式的系统更新&#xff0c;包括 iOS 26、iPadOS 26…...

SciencePlots——绘制论文中的图片

文章目录 安装一、风格二、1 资源 安装 # 安装最新版 pip install githttps://github.com/garrettj403/SciencePlots.git# 安装稳定版 pip install SciencePlots一、风格 简单好用的深度学习论文绘图专用工具包–Science Plot 二、 1 资源 论文绘图神器来了&#xff1a;一行…...

Opencv中的addweighted函数

一.addweighted函数作用 addweighted&#xff08;&#xff09;是OpenCV库中用于图像处理的函数&#xff0c;主要功能是将两个输入图像&#xff08;尺寸和类型相同&#xff09;按照指定的权重进行加权叠加&#xff08;图像融合&#xff09;&#xff0c;并添加一个标量值&#x…...

质量体系的重要

质量体系是为确保产品、服务或过程质量满足规定要求&#xff0c;由相互关联的要素构成的有机整体。其核心内容可归纳为以下五个方面&#xff1a; &#x1f3db;️ 一、组织架构与职责 质量体系明确组织内各部门、岗位的职责与权限&#xff0c;形成层级清晰的管理网络&#xf…...

鸿蒙中用HarmonyOS SDK应用服务 HarmonyOS5开发一个医院查看报告小程序

一、开发环境准备 ​​工具安装​​&#xff1a; 下载安装DevEco Studio 4.0&#xff08;支持HarmonyOS 5&#xff09;配置HarmonyOS SDK 5.0确保Node.js版本≥14 ​​项目初始化​​&#xff1a; ohpm init harmony/hospital-report-app 二、核心功能模块实现 1. 报告列表…...

【android bluetooth 框架分析 04】【bt-framework 层详解 1】【BluetoothProperties介绍】

1. BluetoothProperties介绍 libsysprop/srcs/android/sysprop/BluetoothProperties.sysprop BluetoothProperties.sysprop 是 Android AOSP 中的一种 系统属性定义文件&#xff08;System Property Definition File&#xff09;&#xff0c;用于声明和管理 Bluetooth 模块相…...

Rapidio门铃消息FIFO溢出机制

关于RapidIO门铃消息FIFO的溢出机制及其与中断抖动的关系&#xff0c;以下是深入解析&#xff1a; 门铃FIFO溢出的本质 在RapidIO系统中&#xff0c;门铃消息FIFO是硬件控制器内部的缓冲区&#xff0c;用于临时存储接收到的门铃消息&#xff08;Doorbell Message&#xff09;。…...

优选算法第十二讲:队列 + 宽搜 优先级队列

优选算法第十二讲&#xff1a;队列 宽搜 && 优先级队列 1.N叉树的层序遍历2.二叉树的锯齿型层序遍历3.二叉树最大宽度4.在每个树行中找最大值5.优先级队列 -- 最后一块石头的重量6.数据流中的第K大元素7.前K个高频单词8.数据流的中位数 1.N叉树的层序遍历 2.二叉树的锯…...

均衡后的SNRSINR

本文主要摘自参考文献中的前两篇&#xff0c;相关文献中经常会出现MIMO检测后的SINR不过一直没有找到相关数学推到过程&#xff0c;其中文献[1]中给出了相关原理在此仅做记录。 1. 系统模型 复信道模型 n t n_t nt​ 根发送天线&#xff0c; n r n_r nr​ 根接收天线的 MIMO 系…...

招商蛇口 | 执笔CID,启幕低密生活新境

作为中国城市生长的力量&#xff0c;招商蛇口以“美好生活承载者”为使命&#xff0c;深耕全球111座城市&#xff0c;以央企担当匠造时代理想人居。从深圳湾的开拓基因到西安高新CID的战略落子&#xff0c;招商蛇口始终与城市发展同频共振&#xff0c;以建筑诠释对土地与生活的…...