mongodb的多表联查与后续的数据处理_spring data mongodb 多表关联-程序员宅基地

背景与简述

  1. 背景

    使用nosql作多表操作时很麻烦的,所以平时都没使用过多表,但最近遇到一个项目必须使用多表,没法,就研究了一下mongodb的多表联查功能.
    mongodb的多表联查主要通过聚合完场,使用的是关键子 l o o k u p , 而 后 续 处 理 中 lookup,而后续处理中 lookup,unwind则是关键的一环.以下是这次的记录:

  2. 版本

    mongodb:3.6
    spring:5.0.7
    spring-data:2.0.7
    mongo-java-driver:3.6.3
    版本问题需要注意一下,如果版本不兼容会出现:The ‘cursor’ option is required, except for aggregate…的问题,解决办法是升级版本,spring-data2.x的运行环境是spring5.x以及jdk8+,mongodb-java-driver也应该升级到3.6以上

数据

  1. user表
{
    "_id" : ObjectId("5b69062240a6d80a6cece003"),
    "name" : "小明",
    "age" : 28,
    "createtime" : ISODate("2018-08-07T02:38:26.601Z"),
    "_class" : "com.xiangpeng.bo.UserBo"
}
  1. orders表
/* 1 */
{
    "_id" : ObjectId("5b69062240a6d80a6cece004"),
    "uid" : ObjectId("5b69062240a6d80a6cece003"),
    "money" : 10.0,
    "createtime" : ISODate("2018-08-07T02:38:26.601Z"),
    "produce" : "产品1",
    "_class" : "com.xiangpeng.bo.OrderBo"
}

/* 2 */
{
    "_id" : ObjectId("5b6a5711c2eee4295c63768e"),
    "uid" : ObjectId("5b69062240a6d80a6cece003"),
    "money" : 20.0,
    "produce" : "产品2",
    "createtime" : ISODate("2018-08-07T02:38:26.601Z"),
    "_class" : "com.xiangpeng.bo.OrderBo"
}

查询

  1. mongodb的多表查询比较简单,使用$lookup关键字即可:
db.user.aggregate([{$lookup:{from:"orders",localField:"_id",foreignField:"uid",as:"orders"}}])

结果:

{
    "_id" : ObjectId("5b69062240a6d80a6cece003"),
    "name" : "小明",
    "age" : 28,
    "createtime" : ISODate("2018-08-07T02:38:26.601Z"),
    "_class" : "com.xiangpeng.bo.UserBo",
    "orders" : [ 
        {
            "_id" : ObjectId("5b69062240a6d80a6cece004"),
            "uid" : ObjectId("5b69062240a6d80a6cece003"),
            "money" : 10.0,
            "createtime" : ISODate("2018-08-07T02:38:26.601Z"),
            "produce" : "产品1",
            "_class" : "com.xiangpeng.bo.OrderBo"
        }, 
        {
            "_id" : ObjectId("5b6a5711c2eee4295c63768e"),
            "uid" : ObjectId("5b69062240a6d80a6cece003"),
            "money" : 20.0,
            "produce" : "产品2",
            "createtime" : ISODate("2018-08-07T02:38:26.601Z"),
            "_class" : "com.xiangpeng.bo.OrderBo"
        }
    ]
}

参数解释:
form:需要关联的外表名,$lookup的多变查询使用的是左外连接
localField:本表的外表关联字段;
foreignField:外表的关联字段;
as:参考查询结果,使用$lookup进行查询后会将所有符合条件的文档封装为一个list,as参数定义这个list的名字;

数据处理

  • 使用$unwind将数据打散:
db.user.aggregate([
		{$lookup:{from:"orders",localField:"_id",foreignField:"uid",as:"orders"},
		{$unwind:"$orders"}
])

$unwind的作用是将文档中的数组拆分为多条,拆分结果为:

/* 1 */
{
    "_id" : ObjectId("5b69062240a6d80a6cece003"),
    "name" : "小明",
    "age" : 28,
    "createtime" : ISODate("2018-08-07T02:38:26.601Z"),
    "_class" : "com.xiangpeng.bo.UserBo",
    "orders" : {
        "_id" : ObjectId("5b69062240a6d80a6cece004"),
        "uid" : ObjectId("5b69062240a6d80a6cece003"),
        "money" : 10.0,
        "createtime" : ISODate("2018-08-07T02:38:26.601Z"),
        "produce" : "产品1",
        "_class" : "com.xiangpeng.bo.OrderBo"
    }
}

/* 2 */
{
    "_id" : ObjectId("5b69062240a6d80a6cece003"),
    "name" : "小明",
    "age" : 28,
    "createtime" : ISODate("2018-08-07T02:38:26.601Z"),
    "_class" : "com.xiangpeng.bo.UserBo",
    "orders" : {
        "_id" : ObjectId("5b6a5711c2eee4295c63768e"),
        "uid" : ObjectId("5b69062240a6d80a6cece003"),
        "money" : 20.0,
        "produce" : "产品2",
        "createtime" : ISODate("2018-08-07T02:38:26.601Z"),
        "_class" : "com.xiangpeng.bo.OrderBo"
    }
}
  • 数据过滤

    现在可以对数据进行过滤,数据过滤的步骤应该尽可能提前,但如果过滤条件中也需要筛选外表条件的话就没办法放前面了,过滤在聚合中使用$match

db.user.aggregate([
	{$lookup:{from:"orders",localField:"_id",foreignField:"uid",as:"orders"}},
	{$unwind:"$orders"},
	{$match:{name:"小明","orders.produce":"产品2"}}
])

查出小明买的产品2订单
结果展示

/* 1 */
{
    "_id" : ObjectId("5b69062240a6d80a6cece003"),
    "name" : "小明",
    "age" : 28,
    "createtime" : ISODate("2018-08-07T02:38:26.601Z"),
    "_class" : "com.xiangpeng.bo.UserBo",
    "orders" : {
        "_id" : ObjectId("5b6a5711c2eee4295c63768e"),
        "uid" : ObjectId("5b69062240a6d80a6cece003"),
        "money" : 20.0,
        "produce" : "产品2",
        "createtime" : ISODate("2018-08-07T02:38:26.601Z"),
        "_class" : "com.xiangpeng.bo.OrderBo"
    }
}
  • 如果对字段结果有要求可以使用$project进行字段筛选:
db.user.aggregate([
	{$lookup:{from:"orders",localField:"_id",foreignField:"uid",as:"orders"}},
	{$unwind:"$orders"},
	{$match:{name:"小明","orders.produce":"产品2"}},
	{$project:{name:"$name",age:"$age",produce:"$orders.produce",money:"$orders.money"}}])

再聚合中$可以用作引用相应字段的值
结果为:

/* 1 */
{
    "_id" : ObjectId("5b69062240a6d80a6cece003"),
    "name" : "小明",
    "age" : 28,
    "produce" : "产品2",
    "money" : 20.0
}

使用spring-data-mongodb在dao层的代码

@Repository("aggregateDao")
public class AggregateDaoImpl implements IAggregateDao {
	@Autowired
	private MongoTemplate mongoTemplate;

	public AggregationResults<Document> aggregateLookup() {
		// 创建条件
		AggregationOperation lookup = Aggregation.lookup("orders", "_id", "uid", "orders");
		AggregationOperation unwind = Aggregation.unwind("orders");
		AggregationOperation match = Aggregation.match(Criteria.where("name").is("小明").and("orders.produce").is("产品2"));
		AggregationOperation project = Aggregation.project("name", "age", "orders.produce", "orders.money");
		
		// 将条件封装到Aggregate管道
		Aggregation aggregation = Aggregation.newAggregation(lookup, unwind, match, project);
		
		// 查询
		AggregationResults<Document> aggregate = mongoTemplate.aggregate(aggregation, "user", Document.class);
		
		return aggregate;
	}
}

ps:一些小坑

  1. $lookup是如果涉及关联"_id",注意两个字段的类型,用string类型匹配ObjectId类型是没有结果的~~
  2. _class字段也是有些作用的,比如说使用$sum时用作分组
  3. 数据处理后续:Document返回的值,如果用作前端返回,ObjectId是会被当成BSON解析的~
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/DDKii/article/details/81504805

智能推荐

浅谈WPF页间导航-程序员宅基地

文章浏览阅读106次。浅谈WPF页间导航 使用导航的目的是从一个页面进入到另一个页面。无论是预先决定的线性顺序(向导)还是基于层次的用户驱动程序(大部分网站的形式),或者动态生成的路径,主要有3种方法实现:调用Navigate方法,使用Hyperlinks,使用导航日志。 Navigate方法: 导航容器支持Navigate方法,它允许改变当前页,可以用目标页的示例:..._"wpf navigated=\"mainframe_navigated"

猫头虎分享已解决Bug || Error: ImagePullBackOff (K8s) ‍-程序员宅基地

文章浏览阅读928次,点赞19次,收藏21次。嘿,云原生小伙伴们,猫头虎博主在此!今天我们要聊聊Kubernetes(K8s)中一个常见但头疼的问题 ——错误。这个小怪兽常常在我们尝试部署容器时悄悄出现,让我们的应用启动失败。在这篇博客里,我会用我那猫头虎般敏锐的眼光,深入挖掘这个问题的根源,展示如何一步步地解决它,并提供避免未来类似问题的策略。准备好了吗?让我们开始吧!问题原因解决步骤网络问题检查网络连接认证错误核实凭证信息镜像名/标签错误确认镜像信息资源限制检查资源配额处理。_error: imagepullbackoff

【贪心法求解最小生成树之Kruskal算法详细分析】---Greedy Algorithm for MST_greedy mst demo-程序员宅基地

文章浏览阅读1.4k次。初衷:最近在看算法相关的东西,看到贪心法解决mst的问题,可惜树上讲解的不是很清新,到网上找了很多资料讲解的也不透彻只是随便带过就草草了事、这几天抽空看了下,总算基本思路理清楚了主要还是得感谢强大的google,帮我找到一个很好的英文资料。(下面有链接,有兴趣的同学可以看看)理顺了思路,就和大家分享下~希望对学习贪心法的同学会有所帮助。 这篇博客的主要内容是贪心法求解Min_greedy mst demo

存储器讲述工作原理及作用_电阻式随机存取存储器原理-程序员宅基地

文章浏览阅读3.1w次,点赞12次,收藏64次。转载路径  介绍  存储器(Memory)是现代信息技术中用于保存信息的记忆设备。其概念很广,有很多层次,在数字系统中,只要能保存二进制数据的都可以是存储器;在集成电路中,一个没有实物形式的具有存储功能的电路也叫存储器,如RAM、FIFO等;在系统中,具有实物形式的存储设备也叫存储器,如内存条、TF卡等。计算机中全部信息,包括输入的原始数据、计算机程序、中间运行结果和最终运行结果都保存在存_电阻式随机存取存储器原理

css3实现六边形-程序员宅基地

文章浏览阅读103次。实现原理:这个效果的主要css样式有:1.>transform: rotate(120deg); 图片旋转2.>overflow:hidden; 超出隐藏3.>visibility: hidden; 也是隐藏,与display:none;相似,但不同的是,它虽然隐藏了,但依然会在网页中占有位置 我们要用到3层div进行旋转来得到这个效果(ps:3层div的大小是一样的)。..._css3 6边型

linux查询内存命令-程序员宅基地

文章浏览阅读100次。1、Linux查看内存的详细信息 [root@c0235 ~]# dmidecode --type memory # dmidecode 2.10 SMBIOS 2.4 present. Handle 0x004A, DMI type 16, 15 bytes Physical Memory ..._内存error correction type: multi-bit ecc

随便推点

smartfroms word编辑_word如何改变smartart word如何编辑smartart-程序员宅基地

文章浏览阅读1.3k次。word怎样修改smartart请在“插入”选项中的“插图”中SmartArt图形中,根据自己的任务需要,选择“图形类型”,如列表、流程、循环、关系等。详见附图:如何在Word2010中更改SmartArt图形几何形状在Word2010文档中设置SmartArt图形样式的步骤如下所述:第1步,打开Word2010文档窗口,选中SmartArt图形。第2步,打开“SmartArt工具/设计”功能区,..._smartart样式文档的最佳匹配对象

像学历史课本一样学习Perl_像学教材一样学习-程序员宅基地

文章浏览阅读351次。转自http://www.cnblogs.com/baiyanhuang/archive/2010/05/16/1736436.html第一次接触Perl,还是2008年10月份的时候,当时因为项目重构,需要进行大量的文本操作,于是便拾起了以“文本操作为己任”的Perl语言。当然,带我入门的还是那本赫赫有名的The Llama Book, 即 第四版。 然而,由于主要是为了使用,所以只是快餐式的学习,并没有系统的去了解Perl,这几年_像学教材一样学习

数据预处理_数据预处理csdn-程序员宅基地

文章浏览阅读406次,点赞7次,收藏9次。数据转换:数据转换包括对数据进行平滑、聚合、标准化和归一化等操作,以便更好地适应模型的要求。平滑可以通过滤波或函数拟合来实现,聚合可以将数据按照一定的规则进行合并,标准化可以将数据按照均值和标准差进行缩放,归一化可以将数据缩放到固定的范围内。它是数据分析的关键步骤之一,可以提高数据质量、减少错误,以及为后续的模型训练和分析提供更准确、可靠的数据。在数据预处理中,可以将类别型数据进行编码,例如使用独热编码、标签编码或频率编码等方法,将其转换为数值型数据。_数据预处理csdn

Android 百度地图开发(三)--- 实现比例尺功能和替换自带的缩放组件_安卓百度地图比例尺-程序员宅基地

文章浏览阅读740次。转载请注明出处:http://blog.csdn.net/xiaanming/article/details/11821523貌似有些天没有写博客了,前段时间在忙找工作的事,面试了几家公司,表示反响还不错,过完国庆节去新公司报道,期待新的公司,新的同事,而且新公司还有很多女孩子,哈哈,我可是一年多没和女孩子一起工作过了,之前的公司全是男的,你没听错,真的全是男的,我还以为我自己不会在爱了,_安卓百度地图比例尺

基于Matlab分析分期付款陷阱_利率计算matlab代码-程序员宅基地

文章浏览阅读179次。通过Matlab结算可以看出,在费率为0.5%的情况下,但每期实际贷款费率为0.85%,而年利率竟高达10.2%。一般的银行存款年利率在1%到3%之间。_利率计算matlab代码

自由变量和约束变量-程序员宅基地

文章浏览阅读8.8k次。在程序设计语言中,变量可分为自由变量与约束变量两种。简单来说,局部变量和参数都被认为是约束变量;而不是约束变量的则是自由变量。 在冯·诺依曼计算机体系结构的内存中,变量的属性可以视为一个六元组:(名字,地址,值,类型,生命期,作用域)。地址属性具有明显的冯·诺依曼体系结构的色彩,代表变量所关联的存储器地址。类型规定了变量的取值范围和可能的操作。生命期表示变量与某个存储区地址绑定的过程。根据生命期的不_自由变量和约束变量

推荐文章

热门文章

相关标签