mongo对文档中数组进行过滤的三种方法

news/2024/10/12 10:58:42

前言

在mongo中数据类型有很多种,常见的包括:

数据类型 例子 描述
String { "x" : "foot" } 字符串。存储数据常用的数据类型。在 MongoDB 中,UTF-8 编码的字符串才是合法的。
Integer { "x" : 1 } 整型数值。用于存储数值。根据你所采用的服务器,可分为 32 位或 64 位。
Object { "x" : { "y" : "foot" } } 用于内嵌文档
Array { "x" : [ "a" , "b" ] } 用于将数组或列表或多个值存储为一个键。

有一种很常见的查询,就是过滤数组中的一些数据,只返回符合要求的数据。数据如下,将下面travel中的vehicle=train的记录保留,过滤掉其他的元素,并返回整个文档。

{"name": "tom", "travel": [{"vehicle" : "train","city" : "北京"},{"vehicle" : "plane","city" : "上海"}, {"vehicle" : "train","city" : "深圳"}]
}

想要实现数组的过滤有三种方法,包括:

  1. 聚合查询 使用$unwindtravel数组打散,获取结果集后用$match筛选符合条件的数据,最后使用$group进行聚合获取最终结果集
  2. 聚合查询 使用$match过滤符合条件的根文档结果集,然后使用$project返回对应字段的同时,在travel数组中使用$filter进行内部过滤,返回最终结果集
  3. 普通查询 先筛选记录,然后通过投影查询过滤数组

下面来分析这三种方法能否实现需求。

添加数据

假设有两条记录,每条记录是一个人的信息,包括姓名、职业、旅游过的城市。旅游过的城市是一个数组,包含城市的名字以及交通工具。

db.test.insertOne({"uid" : "1000001","name" : "zhangsan","job": "coder","travel" : [ {"vehicle" : "train","city" : "北京"}, {"vehicle" : "plane","city" : "上海"}, {"vehicle" : "train","city" : "深圳"}]
})
db.test.insertOne({"uid" : "1000002","name" : "lisi","job": "coder","travel" : [ {"vehicle" : "plane","city" : "北京"}, {"vehicle" : "car","city" : "上海"}, {"vehicle" : "train","city" : "深圳"}]
})
db.test.find()
{ _id: ObjectId("6708d3e646d2075ca11e88ce"),uid: '1000001',name: 'zhangsan',job: 'coder',travel: [ { vehicle: 'train', city: '北京' },{ vehicle: 'plane', city: '上海' },{ vehicle: 'train', city: '深圳' } ] }
{ _id: ObjectId("6708d3f646d2075ca11e88cf"),uid: '1000002',name: 'lisi',job: 'coder',travel: [ { vehicle: 'plane', city: '北京' },{ vehicle: 'car', city: '上海' },{ vehicle: 'train', city: '深圳' } ] }

验证三种方法

需求说明

现在的目标是:筛选的出所有记录中通过火车去旅游的城市,也就是travel数组中vehicle=train的记录,过滤掉非目标记录。

方法一

方法一:使用$unwindtravel数组打散,获取结果集后用match筛选符合条件的数据,最后使用$group进行聚合获取最终结果集。

db.getCollection('test').aggregate([{   $unwind: "$travel" },{ $match : {"job":"coder", "travel.vehicle": "train" } },{ $group : { "_id" : "$uid", "travel": { $push: "$travel" } } } ]
)

结果:

{ _id: '1000002', travel: [ { vehicle: 'train', city: '深圳' } ] }
{ _id: '1000001', travel: [ { vehicle: 'train', city: '北京' }, { vehicle: 'train', city: '深圳' } ] }

分析:

unwind 可以将一个数组拆分,例如unwind的效果如下:

{ _id: ObjectId("6708d3e646d2075ca11e88ce"),uid: '1000001',name: 'zhangsan',job: 'coder',travel: { vehicle: 'train', city: '北京' } }
{ _id: ObjectId("6708d3e646d2075ca11e88ce"),uid: '1000001',name: 'zhangsan',job: 'coder',travel: { vehicle: 'plane', city: '上海' } }
{ _id: ObjectId("6708d3e646d2075ca11e88ce"),uid: '1000001',name: 'zhangsan',job: 'coder',travel: { vehicle: 'train', city: '深圳' } }
{ _id: ObjectId("6708d3f646d2075ca11e88cf"),uid: '1000002',name: 'lisi',job: 'coder',travel: { vehicle: 'plane', city: '北京' } }
{ _id: ObjectId("6708d3f646d2075ca11e88cf"),uid: '1000002',name: 'lisi',job: 'coder',travel: { vehicle: 'car', city: '上海' } }
{ _id: ObjectId("6708d3f646d2075ca11e88cf"),uid: '1000002',name: 'lisi',job: 'coder',travel: { vehicle: 'train', city: '深圳' } }

然后通过match筛选出符合条件的数据

{ _id: ObjectId("6708d3e646d2075ca11e88ce"),uid: '1000001',name: 'zhangsan',job: 'coder',travel: { vehicle: 'train', city: '北京' } }
{ _id: ObjectId("6708d3e646d2075ca11e88ce"),uid: '1000001',name: 'zhangsan',job: 'coder',travel: { vehicle: 'train', city: '深圳' } }
{ _id: ObjectId("6708d3f646d2075ca11e88cf"),uid: '1000002',name: 'lisi',job: 'coder',travel: { vehicle: 'train', city: '深圳' } }

最后通过group进行聚合,以_id为聚合依赖,合并相同_id的数据。

总结:

这种方法是能够达到过滤数组的要求,但是有一个问题,拆分数组比较简单,想要再合并起来就不容易了。group只能以某一个变量为基准聚合,其他变量都会丢失。比如最后的结果只保留了_id和travel,其他变量都丢失了。

方法二

方法二:使用$match过滤符合条件的根文档结果集,然后使用$project返回对应字段的同时,在travel数组中使用$filter进行内部过滤,返回最终结果集

db.getCollection('test').aggregate([{ $match : { "job": "coder" } },{$project: {"uid": 1,"name": 1,"travel": {$filter: {input: "$travel",as: "item",cond: { $eq : ["$$item.vehicle","train"] }}}}}]
)

结果分析:

{ _id: ObjectId("6708d3e646d2075ca11e88ce"),uid: '1000001',name: 'zhangsan',travel: [ { vehicle: 'train', city: '北京' },{ vehicle: 'train', city: '深圳' } ] }
{ _id: ObjectId("6708d3f646d2075ca11e88cf"),uid: '1000002',name: 'lisi',travel: [ { vehicle: 'train', city: '深圳' } ] }

分析:

mongo中查询分为两种:普通查询和高级查询。高级查询包括聚合查询,用aggregate关键字实现。

MongoDB的聚合管道将MongoDB文档在一个管道处理完毕后将结果传递给下一个管道处理。管道操作是可以重复的。

这里我们介绍一下聚合框架中常用的几个操作:

  • $project:修改输入文档的结构。可以用来重命名、增加或删除域,也可以用于创建计算结果以及嵌套文档。
  • $match:用于过滤数据,只输出符合条件的文档。$match使用MongoDB的标准查询操作。
  • $limit:用来限制MongoDB聚合管道返回的文档数。
  • $skip:在聚合管道中跳过指定数量的文档,并返回余下的文档。
  • $unwind:将文档中的某一个数组类型字段拆分成多条,每条包含数组中的一个值。
  • $group:将集合中的文档分组,可用于统计结果。
  • $sort:将输入文档排序后输出。
  • $geoNear:输出接近某一地理位置的有序文档。

这里首先使用match过滤所有job=coder,然后使用project修改输出的结构。在project中使用了filter来过滤数组中的元素。

filter的定义如下:

根据指定条件选择要返回的数组的子集。返回仅包含与条件匹配的那些元素的数组。返回的元素按原始顺序。

$filter 具有以下语法:

{ $filter: { input: <array>, as: <string>, cond: <expression> } }
领域 规格
input 解析为数组的表达式
as 可选的。代表数组中每个单独元素的变量名称<u><font style="color:rgb(199, 37, 78);background-color:rgb(249, 242, 244);">input</font></u>。如果未指定名称,则变量名称默认为<u><font style="color:rgb(199, 37, 78);background-color:rgb(249, 242, 244);">this</font></u>
cond 表达式可解析为布尔值,该布尔值用于确定输出数组中是否应包含元素。该表达式<u><font style="color:rgb(199, 37, 78);background-color:rgb(249, 242, 244);">input</font></u>使用在中指定的变量名称分别引用数组的每个元素<u><font style="color:rgb(199, 37, 78);background-color:rgb(249, 242, 244);">as</font></u>

https://mongodb.net.cn/manual/reference/operator/aggregation/filter/

在cond将vehicle=train的元素留下,排除其他元素。

总结:

这种方法可以完成查询目标,既可以过滤掉数组中的元素,也可以返回完整的文档。

方法三

方法三:

通过投影查询,先选择符合条件的记录,在通过使用投影操作符,需要返回的字段,以及排除特定的字段。

db.test.find({job: "coder"}, {  uid: 1, name: 1, travel: {$filter: {input: "$travel",as: "item",cond: { $eq : ["$$item.vehicle","train"] }} } }
)

结果:

{ _id: ObjectId("6708d3e646d2075ca11e88ce"),uid: '1000001',name: 'zhangsan',travel: [ { vehicle: 'train', city: '北京' },{ vehicle: 'train', city: '深圳' } ] }
{ _id: ObjectId("6708d3f646d2075ca11e88cf"),uid: '1000002',name: 'lisi',travel: [ { vehicle: 'train', city: '深圳' } ] }

分析:

什么是投影查询?

在MongoDB中,投影查询是一种查询操作,用于选择性地返回文档中的字段。通过使用投影操作符,我们可以指定需要返回的字段,以及是否要排除特定的字段。

投影查询语法如下所示:

db.collection.find({ <query> }, { <projection> })

其中, 是一个查询表达式,用于筛选满足条件的文档。 是一个可选参数,用于指定要返回的字段。

在projection中保留字段、排除字段、选择或排除数组中的特定元素。利用选择或排除数组中的特定元素的特性也可以达到目的。

例如:

如果我们只想返回每个文档中的第一个标签,我们可以这样做:

db.products.find({}, { tags: { $slice: 1 } })

在本篇中通过filter方法来过滤数组,保留符合条件的元素。

总结:

该方法能够完成查询目标,并且是一种简洁的实现,普通查询复杂度低,而且没有太多关键字的使用。

参考文档

https://geek-docs.com/mongodb/mongodb-questions/393_mongodb_mongo_query_with_projection.html

https://segmentfault.com/a/1190000016629733

https://mongodb.net.cn/manual/reference/operator/aggregation/filter/

https://blog.csdn.net/weixin_44009447/article/details/115479348

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈,一经查实,立即删除!

相关文章

sshd 启动失败

解决方法 yum remove opensshyum install openssh openssh-server openssh-clientssystemctl start sshdsystemctl status sshd

ubuntu20 运行playwright

步骤 pip install playwrightplaywright installplaywright install-deps若弹出这个参考:https://www.cnblogs.com/code3/p/18458533 解决tip 但是貌似有2s的延迟。。本文来自博客园,作者:__username,转载请注明原文链接:https://www.cnblogs.com/code3/p/18460038

海康大华宇视等摄像头/执法记录仪等设备通过GB28181注册到LiveGBS流媒体平台,如何实时获取GB28181设备和通道的在线状态

@目录1、如何监听设备状态2、device订阅2.1、设备上线消息2.2、设备离线消息2.2、通道上线消息2.2、通道离线消息3、订阅示例3.1、连接REDIS3.2、订阅device示例3.3、设备上线示例3.3.1、注册上线后3.4、设备离线示例3.4.1、注销离线后4、更多4.1、如何切换redis5、搭建GB28181…

DIKI:清华提出基于残差的可控持续学习方案,完美保持预训练知识 | ECCV24

本研究解决了领域-类别增量学习问题,这是一个现实但富有挑战性的持续学习场景,其中领域分布和目标类别在不同任务中变化。为应对这些多样化的任务,引入了预训练的视觉-语言模型(VLMs),因为它们具有很强的泛化能力。然而,这也引发了一个新问题:在适应新任务时,预训练VL…

redis闪退

由于我长期开启redis,它突然关了,我再打开,就闪退了。通过网上大佬的解决方案,成功解决: 1.打开redis文件,点击redis-cli.exe程序:2.依次输入: shutdawn exit退出redis之后,再次打开就好了。 阅读文档:https://blog.csdn.net/m0_73379880/article/details/128942115

[Java/Spring] 深入理解 : SpringBoot PropertyMapper

1 概述: SpringBoot PropertyMapper 简介PropertyMapper是Spring提供的一个工具类,主要用于重新赋值,转换等操作位于: org.springframework.boot.context.properties.PropertyMapper2 应用场景 场景 :2个异构数据对象的转换在实际工作中,经常会遇到将数据库的实体类 Entit…

--Nacos服务注册与发现的概述与原理--

什么是 Nacos 官网中的概述:Nacos官网链接 Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service的首字母简称,一个更易于构建云原生应用的动态服务发现、配置管理和服务管理平台。 Nacos 致力于帮助您发现、配置和管理微服务。Nacos 提供了一组简单易用的特性集…

E64 树形DP P3174 [HAOI2009] 毛毛虫

视频链接:E64 树形DP P3174 [HAOI2009] 毛毛虫_哔哩哔哩_bilibili P3174 [HAOI2009] 毛毛虫 - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)// 树形DP O(n) #include <iostream> #include <cstring> #include <algorithm> using namespace std;const int …