es实现上传文件查询

es实现上传文件查询

上传文件,获取文件内容base64,使用es的ingest-attachment文本抽取管道转换为文字存储

安装插件

通过命令行安装(推荐)

1.进入 Elasticsearch 安装目录
2.使用 elasticsearch-plugin 命令安装
bin/elasticsearch-plugin install ingest-attachment
3.重启elasticsearch
# 如果是系统服务
sudo systemctl restart elasticsearch
# 或者直接重启
./bin/elasticsearch查看是否安装成功
1.查看elasticsearch-7.17.12\plugins下是否存在ingest-attachment
2.查看已安装插件列表
bin/elasticsearch-plugin list进入kibana调试控制台,通过 API 检查
GET /_nodes/plugins

可以手动加载插件再解压到plugins目录下

https://artifacts.elastic.co/downloads/elasticsearch-plugins/ingest-attachment/ingest-attachment-7.17.0.zip

新增和es交互的实体FileEsDTO

尽量只留需要参与查询的字段,不经常变动的字段

package com.xiaofei.site.search.model.dto.file;import lombok.Data;
import org.springframework.data.annotation.Id;
import org.springframework.data.elasticsearch.annotations.Document;
import org.springframework.data.elasticsearch.annotations.Field;
import org.springframework.data.elasticsearch.annotations.FieldType;import java.io.Serializable;
import java.util.Date;/*** 文件 ES 包装类**/
@Document(indexName = "file_v3")
@Data
public class FileEsDTO implements Serializable {private static final String DATE_TIME_PATTERN = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'";/*** id*/@Idprivate Long id;/*** 文件名*/@Field(type = FieldType.Text, analyzer = "ik_max_word", searchAnalyzer = "ik_smart")private String fileName;/*** 文件类型*/@Field(type = FieldType.Keyword)private String fileType;/*** 解析后的文本内容*/@Field(type = FieldType.Text, analyzer = "ik_max_word", searchAnalyzer = "ik_smart")private String content;/*** 文件描述*/@Field(type = FieldType.Text, analyzer = "ik_max_word", searchAnalyzer = "ik_smart")private String description;/*** 上传用户ID*/@Field(type = FieldType.Long)private Long userId;/*** 业务类型*/@Field(type = FieldType.Keyword)private String biz;/*** 下载次数*/@Field(type = FieldType.Integer)private Integer downloadCount;/*** 创建时间*/@Field(index = false, store = true, type = FieldType.Date, format = {}, pattern = DATE_TIME_PATTERN)private Date createTime;/*** 更新时间*/@Field(index = false, store = true, type = FieldType.Date, format = {}, pattern = DATE_TIME_PATTERN)private Date updateTime;private Integer isDelete;private static final long serialVersionUID = 1L;
}

新增文本抽取管道

字段就是存储文本的content

#文本抽取管道
PUT /_ingest/pipeline/attachment
{"description": "Extract file content","processors": [{"attachment": {"field": "content","target_field": "attachment","indexed_chars": -1}},{"remove": {"field": "content"}}]
}

新增es文档索引

PUT /file_v3
{"mappings": {"properties": {"id": {"type": "long"},"fileName": {"type": "text","analyzer": "ik_max_word","search_analyzer": "ik_smart","fields":{"keyword":{"type":"keyword","ignore_above":256}}},"fileType": {"type": "keyword"},"content": {"type": "binary"},"attachment": {"properties": {"content": {"type": "text","analyzer": "ik_max_word","search_analyzer": "ik_smart"},"content_type": {"type": "keyword"},"language": {"type": "keyword"},"title": {"type": "text"}}},"description": {"type": "text","analyzer": "ik_max_word","search_analyzer": "ik_smart"},"userId": {"type": "long"},"biz": {"type": "keyword"},"isDelete": {"type": "integer"},"createTime": {"type": "date"},"updateTime": {"type": "date"}}}
}

修改文件上传方法

增加文本内容同步到es

public interface FileService extends IService<FilePo> {
...FilePo uploadFile(MultipartFile file, FileUploadBizEnum fileUploadBizEnum);
...
}
@Service
@Slf4j
public class FileServiceImpl extends ServiceImpl<FileMapper, FilePo> implements FileService {
@Resourceprivate RestHighLevelClient restHighLevelClient;@Overridepublic FilePo uploadFile(MultipartFile file, FileUploadBizEnum fileUploadBizEnum) {try {...int insert = fileMapper.insert(filePo);if (insert > 0) {//上传esboolean esUpload = uploadFileToEs(filePo, dest);// 4. 删除临时文件dest.delete();if (!esUpload) {throw new BusinessException(ErrorCode.OPERATION_ERROR, "文件上传失败");}return filePo;}return null;} catch (IOException e) {log.error("文件上传失败", e);throw new BusinessException(ErrorCode.OPERATION_ERROR, "文件上传失败");}}
/*** 通过pipeline上传文件到es* @param filePo* @param file* @return*/public boolean uploadFileToEs(FilePo filePo, File file) {try {// 1. 读取文件内容并转换为 Base64byte[] fileContent = Files.readAllBytes(file.toPath());String base64Content = Base64.getEncoder().encodeToString(fileContent);// 2. 准备索引文档Map<String, Object> document = new HashMap<>();document.put("id", filePo.getId());document.put("fileName", filePo.getFileName());document.put("fileType", filePo.getFileType());document.put("content", base64Content);  // 使用 content 字段document.put("description", filePo.getDescription());document.put("userId", filePo.getUserId());document.put("biz", filePo.getBiz());document.put("createTime", filePo.getCreateTime());document.put("updateTime", filePo.getUpdateTime());// 3. 创建索引请求IndexRequest indexRequest = new IndexRequest("file_v3").id(filePo.getId().toString()).setPipeline("attachment").source(document);// 4. 执行索引请求IndexResponse indexResponse = restHighLevelClient.index(indexRequest, RequestOptions.DEFAULT);return indexResponse.status() == RestStatus.CREATED|| indexResponse.status() == RestStatus.OK;} catch (Exception e) {log.error("上传文件到 ES 失败", e);return false;}}/*** 生成文件名(防止重复)** @param originalFilename* @return*/private String generateFileName(String originalFilename) {String extension = FilenameUtils.getExtension(originalFilename);String uuid = RandomStringUtils.randomAlphanumeric(8);return uuid + "." + extension;}/*** 计算文件MD5** @param file* @return* @throws IOException*/private String calculateMD5(File file) throws IOException {return DigestUtils.md5Hex(new FileInputStream(file));}
}

上传一个测试文档

通过es查询,看看是否正常解析成文本

content存储着文本

image-20241210220739746

新增es查询方法

public interface FileService extends IService<FilePo> {Page<FileVo> searchFromEs(FileQueryRequest fileQueryRequest);
}
@Service
@Slf4j
public class FileServiceImpl extends ServiceImpl<FileMapper, FilePo> implements FileService {@Resourceprivate ElasticsearchRestTemplate elasticsearchRestTemplate;@Overridepublic Page<FileVo> searchFromEs(FileQueryRequest fileQueryRequest) {String searchText = fileQueryRequest.getSearchText();String fileName = fileQueryRequest.getFileName();String content = fileQueryRequest.getContent();// es 起始页为 0long current = fileQueryRequest.getCurrent() - 1;long pageSize = fileQueryRequest.getPageSize();String sortField = fileQueryRequest.getSortField();String sortOrder = fileQueryRequest.getSortOrder();BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();//boolQueryBuilder.filter(QueryBuilders.termQuery("isDelete", 0));// 按关键词检索if (StringUtils.isNotBlank(searchText)) {boolQueryBuilder.should(QueryBuilders.matchQuery("fileName", searchText));boolQueryBuilder.should(QueryBuilders.matchQuery("description", searchText));boolQueryBuilder.should(QueryBuilders.matchQuery("attachment.content", searchText));boolQueryBuilder.minimumShouldMatch(1);}// 按标题检索if (StringUtils.isNotBlank(fileName)) {boolQueryBuilder.should(QueryBuilders.matchQuery("fileName", fileName));boolQueryBuilder.minimumShouldMatch(1);}// 按内容检索if (StringUtils.isNotBlank(content)) {boolQueryBuilder.should(QueryBuilders.matchQuery("attachment.content", content));boolQueryBuilder.minimumShouldMatch(1);}// 排序SortBuilder<?> sortBuilder = SortBuilders.scoreSort();if (StringUtils.isNotBlank(sortField)) {sortBuilder = SortBuilders.fieldSort(sortField);sortBuilder.order(CommonConstant.SORT_ORDER_ASC.equals(sortOrder) ? SortOrder.ASC : SortOrder.DESC);}// 分页PageRequest pageRequest = PageRequest.of((int) current, (int) pageSize);// 构造查询NativeSearchQuery searchQuery = new NativeSearchQueryBuilder().withQuery(boolQueryBuilder).withPageable(pageRequest).withSorts(sortBuilder).build();SearchHits<FileEsDTO> searchHits = elasticsearchRestTemplate.search(searchQuery, FileEsDTO.class);Page<FileVo> page = new Page<>();page.setTotal(searchHits.getTotalHits());List<FilePo> resourceList = new ArrayList<>();// 查出结果后,从 db 获取最新动态数据if (searchHits.hasSearchHits()) {List<SearchHit<FileEsDTO>> searchHitList = searchHits.getSearchHits();List<Long> fileIdList = searchHitList.stream().map(searchHit -> searchHit.getContent().getId()).collect(Collectors.toList());List<FilePo> fileList = baseMapper.selectBatchIds(fileIdList);if (fileList != null) {Map<Long, List<FilePo>> idPostMap = fileList.stream().collect(Collectors.groupingBy(FilePo::getId));fileIdList.forEach(fileId -> {if (idPostMap.containsKey(fileId)) {resourceList.add(idPostMap.get(fileId).get(0));} else {// 从 es 清空 db 已物理删除的数据String delete = elasticsearchRestTemplate.delete(String.valueOf(fileId), FileEsDTO.class);log.info("delete post {}", delete);}});}}List<FileVo> fileVoList = new ArrayList<>();if (CollUtil.isNotEmpty(resourceList)) {for (FilePo filePo : resourceList) {FileVo fileVo = FilePoToVoUtils.poToVo(filePo);fileVoList.add(fileVo);}}page.setRecords(fileVoList);return page;}
}

po转Vo工具

package com.xiaofei.site.search.utils;import com.xiaofei.site.search.model.entity.FilePo;
import com.xiaofei.site.search.model.vo.FileVo;
import org.springframework.stereotype.Component;/*** @author tuaofei* @description TODO* @date 2024/12/6*/
@Component
public class FilePoToVoUtils {public static FileVo poToVo(FilePo entity) {if (entity == null) {return null;}FileVo vo = new FileVo();vo.setId(entity.getId());vo.setBiz(entity.getBiz());vo.setFileName(entity.getFileName());vo.setFileType(entity.getFileType());vo.setFileSize(entity.getFileSize());vo.setFileSizeFormat(formatFileSize(entity.getFileSize()));vo.setFileExtension("");vo.setUserId(entity.getUserId());vo.setDownloadCount(entity.getDownloadCount());vo.setDescription(entity.getDescription());vo.setCreateTime(entity.getCreateTime());vo.setUpdateTime(entity.getUpdateTime());vo.setContent(entity.getContent());// 设置预览和下载URLvo.setPreviewUrl(generatePreviewUrl(entity));vo.setDownloadUrl(generateDownloadUrl(entity));// 设置权限vo.setCanPreview(checkPreviewPermission(entity));vo.setCanDownload(checkDownloadPermission(entity));return vo;}/*** 格式化文件大小*/private static String formatFileSize(Long size) {if (size == null) {return "0B";}if (size < 1024) {return size + "B";} else if (size < 1024 * 1024) {return String.format("%.2fKB", size / 1024.0);} else if (size < 1024 * 1024 * 1024) {return String.format("%.2fMB", size / (1024.0 * 1024.0));} else {return String.format("%.2fGB", size / (1024.0 * 1024.0 * 1024.0));}}/*** 生成预览URL*/private static String generatePreviewUrl(FilePo entity) {// 根据业务逻辑生成预览URLreturn "/api/file/preview/" + entity.getId();}/*** 生成下载URL*/private static String generateDownloadUrl(FilePo entity) {// 根据业务逻辑生成下载URLreturn "/api/file/download/" + entity.getId();}/*** 检查预览权限*/private static Boolean checkPreviewPermission(FilePo entity) {// 根据业务逻辑检查预览权限return true;}/*** 检查下载权限*/private static Boolean checkDownloadPermission(FilePo entity) {// 根据业务逻辑检查下载权限return true;}
}

测试内容分词是否正常

使用分词器分词后,拿分词后的单个分词结果搜索,应该能搜索到结果

POST /file_v3/_analyze
{"analyzer": "ik_max_word","text": "xxx"
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/diannao/63537.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

采集opc ua转profinet IO项目案例

目录 1 案例说明 1 2 VFBOX网关工作原理 1 3 准备工作 2 4 配置VFBOX网关采集OPC UA的数据 2 5 用PROFINET IO协议转发数据 4 6 案例总结 6 1 案例说明 设置网关采集OPC UA设备数据把采集的数据转成profinet IO协议转发给其他系统。 2 VFBOX网关工作原理 VFBOX网关是协议转换…

linux 安装composer

下载composer curl -sS https://getcomposer.org/installer | php下载后设置环境变量&#xff0c;直接通过命令composer -v mv composer.phar /usr/local/bin/composer查看版本看是否安装成功 composer -v

可视化建模以及UML期末复习----做题篇

一、单项选择题。&#xff08;20小题&#xff0c;每小题2分,共40分&#xff09; 1、UML图不包括&#xff08; &#xff09; A、用例图 B、状态机图 C、流程图 D、类图 E、通信图 答案&#xff1a;C、流程图 UML中不包括传统意义上的流程图&#xff0c;流程图通常是指B…

VBA高级应用30例应用在Excel中的ListObject对象:向表中添加注释

《VBA高级应用30例》&#xff08;版权10178985&#xff09;&#xff0c;是我推出的第十套教程&#xff0c;教程是专门针对高级学员在学习VBA过程中提高路途上的案例展开&#xff0c;这套教程案例与理论结合&#xff0c;紧贴“实战”&#xff0c;并做“战术总结”&#xff0c;以…

Mac mini m4本地跑大模型(ollama + llama + ComfyUI + Stable Diffusion | flux)

安装chat大模型&#xff08;不推荐&#xff0c;本地运行的大模型只能聊废话&#xff0c;不如网页版使用openAI等高效&#xff09; 首先下载ollama的安装包 https://ollama.com/ 点击启动访问&#xff1a;http://localhost:11434 Ollama is running 代表已经运行起来了&#x…

借助Aspose.Cells ,在 Excel 中将文本转换为数字

在 Excel 中将文本转换为数字是一项常见要求。许多用户会遇到以文本形式存储的数字&#xff0c;这可能会导致计算错误。此问题会影响财务、数据分析和报告。将这些文本条目转换为实际数字对于准确的数据处理至关重要。在这篇博文中&#xff0c;我们将探讨如何使用 C# 将以文本形…

2024 年 11 月区块链游戏研报:牛市加持下的 GameFi 破局之路

2024 年 11 月区块链游戏研报 作者&#xff1a;Stella L (stellafootprint.network) 数据来源&#xff1a;Footprint Analytics 区块链游戏 Research 页面 2024 年 11 月 Web3 游戏行业市场增长显著但大规模采用策略仍在演进。随着比特币创下历史新高并接近 10 万美元里程碑…

QT 多级嵌套结构体,遍历成员--半自动。<模板+宏定义>QTreeWidget树结构显示

Qt的QTreeWidget来显示嵌套结构体的成员&#xff0c;并以树形结构展示。 #include <QApplication> #include <QTreeWidget> #include <QTreeWidgetItem> #include <QString> #include <cstdint>// 假设这些是你的结构体定义 struct BaseMeterPa…

【JAVA高级篇教学】第一篇:Springboot对接通义千问大模型

博主今天打算讲解下Java如何对接阿里云的通义千问大模型&#xff0c;可以自己玩玩ai问答之类的&#xff01; 目录 一、发展历程 二、API-KEY的获取与配置 三、引用SDK 四、文本模型 1.代码 2.返回数据 3.官方代码案例 五、通义千问VL 1.计量计费 六、查看API-KEY调用额…

快捷构建AI大模型,源码自取可直接运行

Node.js 和 WebSocket 实现一个基于kimi&#xff08;Moonshot 月之暗大模型&#xff09;的AI工具 前端&#xff1a;前端界面比较容易&#xff0c;只需要简单的额css js即可&#xff0c;本文使用vue作为作为demo。 后端&#xff1a;我java很垃圾&#xff0c;写不出好的代码&am…

R语言 | 峰峦图 / 山脊图

目的&#xff1a;为展示不同数据分布的差异。 1. ggplot2 实现 # 准备数据 datmtcars[, c("mpg", "cyl")] colnames(dat)c("value", "type") head(dat) # value type #Mazda RX4 21.0 6 #Mazda RX4 Wag …

Redis性能优化18招

Redis性能优化的18招 目录 前言选择合适的数据结构避免使用过大的key和value[使用Redis Pipeline](#使用Redis Pipeline)控制连接数量合理使用过期策略使用Redis集群充分利用内存优化使用Lua脚本监控与调优避免热点key使用压缩使用Geo位置功能控制数据的持久化尽量减少事务使…

学习笔记063——通过使用 aspose-words 将 Word 转 PDF 时,遇到的字体改变以及乱码问题

文章目录 1、问题描述&#xff1a;2、解决方法&#xff1a; 1、问题描述&#xff1a; Java项目中&#xff0c;有个需要将word转pdf的需求。本人通过使用aspose-words来转换的。在Windows中&#xff0c;转换是完全正常的。但是当部署到服务器时&#xff0c;会出现转换生成的pdf…

(6)JS-Clipper2之ClipperOffset

1. 描述 ClipperOffset类封装了对打开路径和关闭路径进行偏移(膨胀/收缩)的过程。 这个类取代了现在已弃用的OffsetPaths函数&#xff0c;该函数不太灵活。可以使用不同的偏移量(增量)多次调用Execute方法&#xff0c;而不必重新分配路径。现在可以在一次操作中对开放和封闭路…

SpringCloudAlibaba教程之注册中心Nacos

目录 概念 架构 设计原则 架构分层 用户层 业务层 内核层 插件 单机部署 1.下载安装包 2.安装nacos 3.启动nacos 快速开始 1.添加Maven依赖 2.添加配置 3.启动 集群部署 搭建步骤 1.搭建数据库&#xff0c;初始化数据库表结构 2.配置nacos 3.启动nacos集群…

PostgreSQL 安装部署系列:使用YUM 方式在Centos 7.9 安装指定 PostgreSQL -15版本数据库

一、前言 千里之行始于足下&#xff0c;想学习一门数据库&#xff0c;首先要从安装部署开始&#xff0c;先拥有一套属于自己的学习测试库。为了更好的学习该数据库&#xff0c;可以选择一个在企业界使用率比较普及的操作系统&#xff0c;选择稳定版本的操作系统&#xff1b;如果…

李飞飞的生成式3D场景,对数字孪生的未来影响几何?

大家好&#xff0c;我是日拱一卒的攻城师不浪&#xff0c;致力于技术与艺术的融合。这是2024年输出的第47/100篇文章。 前言 这两天&#xff0c;AI界的教母李飞飞团队重磅发布了空间智能生成式AI大模型。 仅通过一张图片就能够生成一个可操作和交互的3D空间场景。 空间智能的…

Redis面试专题-持久化

目录 前言 持久化相关知识 1.三种持久化机制 2.RDB持久化 3.深入剖析一下RDB持久化过程 4.AOF持久化 5.RDB和AOF对比​编辑 面试题 1.redis持久化机制有哪些&#xff1f; 2.那仔细讲讲你对他们的理解 3.你刚刚说AOF的文件很大&#xff0c;那AOF文件会越来越大&#xf…

数据结构之初始二叉树(1)

找往期文章包括但不限于本期文章中不懂的知识点&#xff1a; 个人主页&#xff1a;我要学编程(ಥ_ಥ)-CSDN博客 所属专栏&#xff1a;数据结构&#xff08;Java版&#xff09; 目录 树型结构 树的概念 与树的有关概念 树的表示形式 树的应用 二叉树 概念 两种特殊的…

酷柚易汛生产管理系统PHP+Uniapp

生产管理系统&#xff0c;帮助企业数字化转型&#xff0c;打造智能工厂&#xff0c;专业为生产企业量身开发的一套完整的生产管理系统。主要包含以下模块&#xff1a;购货模块、生产模块、仓库模块、资料模块&#xff0c;可配合酷柚易汛进销存无缝衔接使用。 产品理念: 共享功…