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

MongoDB中的嵌套List操作

前言

MongoDB区别Mysql的地方,就是MongoDB支持文档嵌套,比如最近业务中就有一个在音频转写结果中进行对话场景,一个音频中对应多轮对话,这些音频数据和对话信息就存储在MongoDB中文档中。集合结构大致如下

{"_id":23424234234324234,"audioId": 2689944,"contextId": "cht000d24ab@dx187d1168a449a4b540","dialogues": [{"ask": "今天是礼拜天?","answer": "是的","createTime": 1697356990966}, {"ask": "你也要加油哈","answer": "奥利给!","createTime": 1697378011483}, {"ask": "下周见","answer": "拜拜!","createTime": 1697378072063}]
}

下面简单介绍几个业务中用到的简单操作。

查询嵌套List的长度大小

    public Integer getDialoguesSize(Long audioId) {Integer datasSize = 0;List<Document> group = Arrays.asList(new Document("$match",new Document("audioId",new Document("$eq", audioId))), new Document("$match",new Document("dialogues",new Document("$exists", true))), new Document("$project",new Document("datasSize",new Document("$size", "$dialogues"))));AggregateIterable<Document> aggregate = generalCollection.aggregate(group);Document document = aggregate.first();if (document != null) {datasSize = (Integer) document.get("datasSize");}return datasSize;}

根据嵌套List中属性查询

下面的代码主要查询指定audioId中的dialogues集合中小于createTime,并且根据limit分页查询,这里用到了MongoDB中的Aggregates和unwind来进行聚合查询,具体使用细节,可以参见MongoDB官方文档

    public AIDialoguesResultDTO queryAiResult(Long audioId, Long createTime, Integer limit) {AIDialoguesResultDTO aiDialoguesResultDTO = new AIDialoguesResultDTO();List<Bson> pipeline = Arrays.asList(Aggregates.match(Filters.eq("audioId", audioId)),Aggregates.unwind("$dialogues"),Aggregates.match(Filters.lt("dialogues.createTime", createTime)),Aggregates.sort(Sorts.descending("dialogues.createTime")),Aggregates.limit(limit));AggregateIterable<Document> aggregate = generalCollection.aggregate(pipeline);List<AIDialoguesResult> aiDialoguesResultList = new ArrayList<>();String contextId = Constant.EMPTY_STR;for (Document document : aggregate) {AIDialoguesResult aiDialoguesResult = new AIDialoguesResult();List<String> key = Collections.singletonList("dialogues");aiDialoguesResult.setAnswer(document.getEmbedded(key, Document.class).getString("answer"));aiDialoguesResult.setAsk(document.getEmbedded(key, Document.class).getString("ask"));aiDialoguesResult.setCreateTime(document.getEmbedded(key, Document.class).getLong("createTime"));aiDialoguesResultList.add(aiDialoguesResult);contextId = document.getString("contextId");}if (!CollectionUtils.isEmpty(aiDialoguesResultList)) {aiDialoguesResultList = aiDialoguesResultList.stream().sorted(Comparator.comparingLong(AIDialoguesResult::getCreateTime)).collect(Collectors.toList());}aiDialoguesResultDTO.setCount(aiDialoguesResultList.size());aiDialoguesResultDTO.setContextId(contextId);aiDialoguesResultDTO.setResult(aiDialoguesResultList);return aiDialoguesResultDTO;}

当然,我们还有一种比较简单的写法

    public AIDialoguesResultDTO queryAiResultBackupVersion(Long audioId, Long createTime, Integer limit) {Bson query = and(eq("audioId", audioId));AITextResult aiTextResult = mongoDao.findSingle(query, AITextResult.class);AIDialoguesResultDTO aiDialoguesResultDTO = new AIDialoguesResultDTO();if (Objects.isNull(aiTextResult)) {aiDialoguesResultDTO.setResult(Collections.emptyList());aiDialoguesResultDTO.setCount(0);aiDialoguesResultDTO.setContextId("");}List<AIDialoguesResult> aiDialoguesResultList = aiTextResult.getDialogues();if (CollectionUtils.isEmpty(aiDialoguesResultList)) {return aiDialoguesResultDTO;}Long finalCreateTime = createTime;List<AIDialoguesResult> afterFilterAiDialoguesResultList =aiDialoguesResultList.stream().filter(t -> t.getCreateTime()< finalCreateTime).sorted(Comparator.comparingLong(AIDialoguesResult::getCreateTime).reversed()).limit(limit).collect(Collectors.toList());if (CollectionUtils.isEmpty(afterFilterAiDialoguesResultList)) {aiDialoguesResultDTO.setCount(0);} else {aiDialoguesResultDTO.setCount(afterFilterAiDialoguesResultList.size());}afterFilterAiDialoguesResultList = afterFilterAiDialoguesResultList.stream().sorted(Comparator.comparingLong(AIDialoguesResult::getCreateTime)).collect(Collectors.toList());aiDialoguesResultDTO.setResult(afterFilterAiDialoguesResultList);aiDialoguesResultDTO.setContextId(aiTextResult.getContextId());return aiDialoguesResultDTO;}

上面这种写法比较直接,就是直接audioId进行匹配查询, 然后将当前文档中的dialogues全部加载到内存中,然后在内存中进行排序,分页返回,显然如果dialogues集合长度很大,对内存占用会比较高。

嵌套List的增量追加

对于dialogues数组,如果我们要向dialogues追加元素,我们可以把audioId对应的dialogues全部取出来,然后在List后面追加一个元素,大致代码如下

    public void saveAiResult(SaveAIResultDTO saveAIResultDTO) {Long audioId = saveAIResultDTO.getAudioId();Bson filter = Filters.eq("audioId", audioId);AITextResult aiTextResult = mongoDao.findSingle(filter, AITextResult.class);if (Objects.isNull(aiTextResult)) {aiTextResult = AITextResult.buildAiTextResult(saveAIResultDTO);mongoDao.saveOrUpdate(aiTextResult);return;}List<AIDialoguesResult> aiDialoguesResults = aiTextResult.getDialogues();AIDialoguesResult aiDialoguesResult = new AIDialoguesResult();aiDialoguesResult.setCreateTime(new Date().getTime());aiDialoguesResult.setAsk(saveAIResultDTO.getAsk());aiDialoguesResult.setAnswer(saveAIResultDTO.getAnswer());aiDialoguesResults.add(aiDialoguesResult);aiTextResult.setDialogues(aiDialoguesResults);mongoDao.saveOrUpdate(aiTextResult);}

上面这种写法本身没有什么问题,但是如果dialogues集合大小比较大,每次追加都将dialogues全部取出来进行追加操作,可能比较占用内存,我们可以利用MongoDB中的push操作,直接追加

    public void saveAiResultIncremental(SaveAIResultDTO saveAIResultDTO) {Long audioId = saveAIResultDTO.getAudioId();Document query = new Document("audioId", audioId);Bson projection = Projections.fields(Projections.include("contextId"), Projections.excludeId());FindIterable<Document> result = generalCollection.find(query).projection(projection);AITextResult aiTextResult;if (!result.iterator().hasNext()) {aiTextResult = AITextResult.buildAiTextResult(saveAIResultDTO);mongoDao.saveOrUpdate(aiTextResult);return;}AIDialoguesResult aiDialoguesResult = new AIDialoguesResult();aiDialoguesResult.setCreateTime(new Date().getTime());aiDialoguesResult.setAsk(saveAIResultDTO.getAsk());aiDialoguesResult.setAnswer(saveAIResultDTO.getAnswer());Bson update = push("dialogues", aiDialoguesResult);Bson filter = Filters.eq("audioId", audioId);generalCollection.updateOne(filter, update);}

总结

既然选择了MongoDB,就不能继续沿用Mysql的查询风格,要学会利用MongoDB的特性,否则往往达不到预期效果。

相关文章:

MongoDB中的嵌套List操作

前言 MongoDB区别Mysql的地方&#xff0c;就是MongoDB支持文档嵌套&#xff0c;比如最近业务中就有一个在音频转写结果中进行对话场景&#xff0c;一个音频中对应多轮对话&#xff0c;这些音频数据和对话信息就存储在MongoDB中文档中。集合结构大致如下 {"_id":234…...

【C#】什么是并发,C#常规解决高并发的基本方法

给自己一个目标&#xff0c;然后坚持一段时间&#xff0c;总会有收获和感悟&#xff01; 在实际项目开发中&#xff0c;多少都会遇到高并发的情况&#xff0c;有可能是网络问题&#xff0c;连续点击鼠标无反应快速发起了N多次调用接口&#xff0c; 导致极短时间内重复调用了多次…...

MySQL双主一从高可用

MySQL双主一从高可用 文章目录 MySQL双主一从高可用环境说明1.配置前的准备工作2.配置yum源 1.在部署NFS服务2.安装主数据库的数据库服务&#xff0c;并挂载nfs3.初始化数据库4.配置两台master主机数据库5.配置m1和m2成为主数据库6.安装、配置keepalived7.安装部署从数据库8.测…...

#力扣:2894. 分类求和并作差@FDDLC

2894. 分类求和并作差 - 力扣&#xff08;LeetCode&#xff09; 一、Java class Solution {public int differenceOfSums(int n, int m) {return (1n)*n/2-n/m*(mn/m*m)/2;} } 二、C class Solution { public:int differenceOfSums(int n, int m) {return (1n)*n/2-n/m*(mn…...

【网络协议】聊聊从物理层到MAC层 ARP 交换机

物理层 物理层其实就是电脑、交换器、路由器、光纤等。组成一个局域网的方式可以使用集线器。可以将多台电脑连接起来&#xff0c;然后进行将数据转发给别的端口。 数据链路层 Hub其实就是广播模式&#xff0c;如果A电脑发出一个包&#xff0c;B、C电脑也可以收到。那么数据…...

WordPress插件 WP-PostViews 汉化语言包

WP-PostViews汉化语言包 WP-PostViews是一款很受欢迎的文章浏览次数统计插件&#xff0c;记录每篇文章展示次数、根据展示次数显示历史最热或最衰的文章排行、展示范围可以是全部文章和页面&#xff0c;也可以是某些目录下的文章和页面。本文还介绍了一些隐藏的功能&#xff0…...

基础课2——自然语言处理

1.概念 自然语言处理&#xff08;Natural Language Processing, NLP&#xff09;是计算机科学领域与人工智能领域中的一个重要方向&#xff0c;它研究能实现人与计算机之间用自然语言进行有效通信的各种理论和方法。 自然语言处理的主要研究方向包括&#xff1a; 语言学研究&…...

有趣的GPT指令

1 从现在开始&#xff0c;你的回答必须把所有字替换emoji&#xff0c;并保持原来的含义。你不能使用任何汉字或英文。如果有不适当的词语&#xff0c;将它们替换成对应的emoji。下面是一个例子&#xff1a; 原文&#xff1a;爷吐啦 翻译&#xff1a;&#x1f474;&#x1f43…...

小样本学习--(1)概论

目录 一、概述 二、小样本学习的数据集 1、Omniglot 2、MiniimageNet 三、孪生网络 四、三元组损失函数 一、概述 小样本学习用于处理训练数据集中样本数量少的情况&#xff0c;一般来说&#xff0c;小样本学习流程是这样的&#xff0c;从一个多种类少量样本的巨大数据集…...

数据结构之手撕顺序表(讲解➕源代码)

0.引言 在本章之后&#xff0c;就要求大家对于指针、结构体、动态开辟等相关的知识要熟练的掌握&#xff0c;如果有小伙伴对上面相关的知识还不是很清晰&#xff0c;要先弄明白再过来接着学习哦&#xff01; 那进入正题&#xff0c;在讲解顺序表之前&#xff0c;我们先来介绍…...

小微企业是怎样从客户管理系统中获益的?

大企业普遍拥有成熟的客户管理系统&#xff0c;而对小微企业而言&#xff0c;客户管理系统的重要性更为突出。这是因为小微企业管理相对薄弱&#xff0c;资源有限&#xff0c;人力资金需要更加精细化的管理。那么&#xff0c;小微企业如何从客户管理系统中获益&#xff1f; 一…...

mysql整库备份表结构和数据

命令 mysqldump -P 端口 -h 主机 -u 用户名 -p 数据库 > xxxxbak.sql 将导出数据库的表结构及数据&#xff08;建表语句和insert语句&#xff09; 举例 mysqldump -P 3306 -h 100.120.56.23 -u my_username-p sys > system-230510.sql...

LinkedHashMap与LRU缓存

序、慢慢来才是最快的方法。 背景 LinkedHashMap 是继承于 HashMap 实现的哈希链表&#xff0c;它同时具备双向链表和散列表的特点。事实上&#xff0c;LinkedHashMap 继承了 HashMap 的主要功能&#xff0c;并通过 HashMap 预留的 Hook 点维护双向链表的逻辑。 1.缓存淘汰算法…...

2023大联盟6比赛总结

比赛链接 反思 A 为什么打表就我看不出规律&#xff01;&#xff01;&#xff01; 定式思维太严重了T_T B 纯智障分块题&#xff0c;不知道为什么 B 100 B100 B100 比理论最优 B 300 B300 B300 更优&#xff08;快了 3 倍&#xff09;&#xff0c;看来分块还是要学习一…...

05_51单片机led流水线的实现

1:step创建一个新的项目并将程序烧录进入51单片机 以下是51单片机流水线代码的具体实现 #include <REGX52.H>void Delay500ms() //11.0592MHz {unsigned char i, j, k;i 4;j 129;k 119;do{do{while (--k);} while (--j);} while (--i); }void main(){while(1){P1 0…...

Java系列 | 如何讲自己的JAR包上传至阿里云maven私有仓库【云效制品仓库】

什么是云效 云效是云原生时代一站式 BizDevOps 平台&#xff0c;产研数字化同行者&#xff0c;支持公共云、专有云和混合云多种部署形态&#xff0c;通过云原生新技术和研发新模式&#xff0c;助力创新创业和数字化转型企业快速实现产研数字化&#xff0c;打造“双敏”组织&…...

小程序技术加速信创操作系统国产化替换

随着信息技术的不断发展&#xff0c;信息技术应用创新&#xff08;简称“信创”&#xff09;已经成为了当今企业数字化转型的重要趋势之一。信创是指在信息技术领域&#xff0c;以自主可控的国产软硬件产品和服务为核心&#xff0c;构建起一套完整的信息技术生态体系&#xff0…...

免费:实时 AI 编程助手 Amazon CodeWhisperer

点 &#xff0c;一起程序员弯道超车之路 现已正式推出实时 AI 编程助手 Amazon CodeWhisperer&#xff0c;包括 CodeWhisperer 个人套餐&#xff0c;所有开发人员均可免费使用。最初于去年推出的预览版 CodeWhisperer 让开发人员能够保持专注、高效&#xff0c;帮助他们快速、安…...

面试准备-深入理解计算机系统-信息的表示与处理1

浮点运算是不可结合的&#xff08;由于表示的精度有限&#xff09;。比如(3.141e20)-1e20是0.0而3.14(1e20-1e20)是3.14。整数虽然只能编码一个较小的取值范围&#xff0c;但是是准确的&#xff1b;浮点数虽然能编码更大的范围&#xff0c;但是是近似的。 二进制转十六进制转换…...

搭建Atlas2.2.0 集成CDH6.3.2 生产环境+kerberos

首先确保环境的干净&#xff0c;如果之前有安装过清理掉相关残留 确保安装atlas的服务器有足够的内存&#xff08;至少16G&#xff09;&#xff0c;有必要的hadoop角色 HDFS客户端 — 检索和更新Hadoop使用的用户组信息&#xff08;UGI&#xff09;中帐户成员资格的信息。对调…...

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

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

Android Bitmap治理全解析:从加载优化到泄漏防控的全生命周期管理

引言 Bitmap&#xff08;位图&#xff09;是Android应用内存占用的“头号杀手”。一张1080P&#xff08;1920x1080&#xff09;的图片以ARGB_8888格式加载时&#xff0c;内存占用高达8MB&#xff08;192010804字节&#xff09;。据统计&#xff0c;超过60%的应用OOM崩溃与Bitm…...

【安全篇】金刚不坏之身:整合 Spring Security + JWT 实现无状态认证与授权

摘要 本文是《Spring Boot 实战派》系列的第四篇。我们将直面所有 Web 应用都无法回避的核心问题&#xff1a;安全。文章将详细阐述认证&#xff08;Authentication) 与授权&#xff08;Authorization的核心概念&#xff0c;对比传统 Session-Cookie 与现代 JWT&#xff08;JS…...

第八部分:阶段项目 6:构建 React 前端应用

现在&#xff0c;是时候将你学到的 React 基础知识付诸实践&#xff0c;构建一个简单的前端应用来模拟与后端 API 的交互了。在这个阶段&#xff0c;你可以先使用模拟数据&#xff0c;或者如果你的后端 API&#xff08;阶段项目 5&#xff09;已经搭建好&#xff0c;可以直接连…...

sshd代码修改banner

sshd服务连接之后会收到字符串&#xff1a; SSH-2.0-OpenSSH_9.5 容易被hacker识别此服务为sshd服务。 是否可以通过修改此banner达到让人无法识别此服务的目的呢&#xff1f; 不能。因为这是写的SSH的协议中的。 也就是协议规定了banner必须这么写。 SSH- 开头&#xff0c…...

验证redis数据结构

一、功能验证 1.验证redis的数据结构&#xff08;如字符串、列表、哈希、集合、有序集合等&#xff09;是否按照预期工作。 2、常见的数据结构验证方法&#xff1a; ①字符串&#xff08;string&#xff09; 测试基本操作 set、get、incr、decr 验证字符串的长度和内容是否正…...

【Java多线程从青铜到王者】单例设计模式(八)

wait和sleep的区别 我们的wait也是提供了一个还有超时时间的版本&#xff0c;sleep也是可以指定时间的&#xff0c;也就是说时间一到就会解除阻塞&#xff0c;继续执行 wait和sleep都能被提前唤醒(虽然时间还没有到也可以提前唤醒)&#xff0c;wait能被notify提前唤醒&#xf…...

Easy Excel

Easy Excel 一、依赖引入二、基本使用1. 定义实体类&#xff08;导入/导出共用&#xff09;2. 写 Excel3. 读 Excel 三、常用注解说明&#xff08;完整列表&#xff09;四、进阶&#xff1a;自定义转换器&#xff08;Converter&#xff09; 其它自定义转换器没生效 Easy Excel在…...

C# WPF 左右布局实现学习笔记(1)

开发流程视频&#xff1a; https://www.youtube.com/watch?vCkHyDYeImjY&ab_channelC%23DesignPro Git源码&#xff1a; GitHub - CSharpDesignPro/Page-Navigation-using-MVVM: WPF - Page Navigation using MVVM 1. 新建工程 新建WPF应用&#xff08;.NET Framework) 2.…...

ffmpeg(三):处理原始数据命令

FFmpeg 可以直接处理原始音频和视频数据&#xff08;Raw PCM、YUV 等&#xff09;&#xff0c;常见场景包括&#xff1a; 将原始 YUV 图像编码为 H.264 视频将 PCM 音频编码为 AAC 或 MP3对原始音视频数据进行封装&#xff08;如封装为 MP4、TS&#xff09; 处理原始 YUV 视频…...