MongoDB中如何使用JOIN操作详解

前言

MongoDB是由C++语言所编写的一种面向文档的非关系型数据库(是一种NoSql数据库实现),也是介于关系型数据库和非关系型数据库之间的数据存储产品,而众所周知SQL与NoSQL最大的不同之一就是不支持JOIN,在传统的数据库中,SQL JOIN子句允许你使用普通的字段,在两个或者是更多表中的组合表中的每行数据。例如,如果你有表books和publishers,你可以像下面这样写命令:

SELECT book.title, publisher.name
FROM book
LEFT JOIN book.publisher_id ON publisher.id;

换句话说,book表中的publisher_id字段引用了publishers表中的id字典。这些都是很常见的例子:对于每个publisher都可以拥有成千上万本书,如果你想更新publisher的信息的时候,我们只需要更改一条记录。数据的冗余是很小的,因为我们不需要为每本书来重复更新他的publisher信息,这种技术已基本当做一种规范化的东西了。SQL数据库提供了一些列的规范与约束条件来保障数据关联性。

--------------------------------------------------------------------------------

NoSQL == No JOIN?

并不都是这样吧。。。。。

--------------------------------------------------------------------------------

面向文档的数据库,例如MongoDB,被设计用来存储非结构化的数据,理想情况下,这些数据是在数据集合中是相互没有关联的,如果一条数据包含两次或者更多次,那数据就重复了。因为大部分情况下我们还是需要数据关联的,只有很少的情况下才会不需要关联数据,看来NoSQL这些特性看来让人失望啊。幸运的是MongoDB 3.2 介绍了一个新的$lookup操作,这个操作可以提供一个类似于LEFT OUTER JOIN的操作在两个或者是更多的条件下。

--------------------------------------------------------------------------------

MongoDB Aggregation

$lookup仅仅在 aggregation操作中才被允许使用,想想他作为一个管道操作:查询,过滤,组合结果。一个操作的输出被作为下一个的输入。Aggregation比简单的查询操作更难于理解,而且这些操作通常运行很慢,然而他们很高效,Aggregation可以使用一个很好的例子来解释,假设我们使用user数据集合来创建一个社交平台,在每个独立的文档中存储没个用户的信息,例如:

{
 "_id": ObjectID("45b83bda421238c76f5c1969"),
 "name": "User One",
 "email: "userone@email.com",
 "country": "UK",
 "dob": ISODate("1999-09-13T00:00:00.000Z")
}

我们可以向user这个集合中添加足够多的用户,但是每个MongoDB文档都必须有一个为一个_id字段值,这个_id字段值就像SQL中的键,在我们没有明确指定_id的时候会被自动的加入到文档中。我们的社交网站现在需要一个post集合,这个结合存储用户的评论,这个文档存储纯文本,时间,评分,一个被写到user_id字段的玩家引用。

{
 "_id": ObjectID("17c9812acff9ac0bba018cc1"),
 "user_id": ObjectID("45b83bda421238c76f5c1969"),
 "date: ISODate("2016-09-05T03:05:00.123Z"),
 "text": "My life story so far",
 "rating": "important"
}

我们现在想要显示最近具有important评论的二十条数据,这些数据来自所有的用户,并且是按照时间排序的。每一个返回的文档中应该包含评论的文本,发布评论的时间,以及相关的用户的名字和国家。

MongoDB数据库的aggregate查询是通过传递管道操作的数组,这个数组中顺序的定了每个操作。首先,我们需要从所有的post集合中提取出所有的文档,这些文档使用$match记性准确rating过滤。

{ "$match": { "rating": "important" } }

我们现在需要对过滤出来的文档按照时间,使用$sort操作进行排序。

{ "$sort": { "date": -1 } }

因为我们要仅仅返回二十条数据,我们可以使用$limit来限制我们需要处理的文档数量。

{ "$limit": 20 }

我们现在使用$lookup操作从user集合中连接数据,这个操作需要一个四个参数的对象:

1、localField:在输入文档中的查找字段

2、from:需要连接的集合

3、foreignField:需要在from集合中查找的字段

4、as:输出的字段名字

所以我们的操作是这样的:

{ "$lookup": {
 "localField": "user_id",
 "from": "user",
 "foreignField": "_id",
 "as": "userinfo"
} }

在我们的输出中将会创建一个名为userinfo的新字段,他是一个数组,其中每个元素都是在user集合中匹配的元素。

"userinfo": [
 { "name": "User One", ... }
]

在post.user_id与user._id之间,我们具有一对一的关系,因为对于每一个post只有一个用户。因此我们的userinfo数组将会仅仅包含一个元素,我们可以说使用 $unwind操作来解构他并插入到一个自文档中。

{ "$unwind": "$userinfo" }

现在的输出将会转化成更加常用的结构:

"userinfo": {
 "name": "User One",
 "email: "userone@email.com",
 …
}

最终我们可以在管道中使用 $project操作返回评论信息,评论的时间,评论的用户名,国家等。

{ "$project": {
 "text": 1,
 "date": 1,
 "userinfo.name": 1,
 "userinfo.country": 1
} }

合并上面所有的操作

我们最终的聚合查询匹配的评论,按照顺序排序,限制最新的二十条信息,连接用户的数据,扁平用户数组,最后只返回我们需要的必须数据,总的命令如下:

db.post.aggregate([
 { "$match": { "rating": "important" } },
 { "$sort": { "date": -1 } },
 { "$limit": 20 },
 { "$lookup": {
 "localField": "user_id",
 "from": "user",
 "foreignField": "_id",
 "as": "userinfo"
 } },
 { "$unwind": "$userinfo" },
 { "$project": {
 "text": 1,
 "date": 1,
 "userinfo.name": 1,
 "userinfo.country": 1
 } }
]);

结果是一个拥有二十个文档的集合,例如:

[
 {
 "text": "The latest post",
 "date: ISODate("2016-09-27T00:00:00.000Z"),
 "userinfo": {
  "name": "User One",
  "country": "UK"
 }
 },
 {
 "text": "Another post",
 "date: ISODate("2016-09-26T00:00:00.000Z"),
 "userinfo": {
  "name": "User One",
  "country": "UK"
 }
 }
 ...
]

MongoDB的$lookup很好用而且很高效,但是上面这个基础的例子只是一个组合的集合查询。他不是一个对SQL中的更加高效的JOIN子句的替代。而且MongoDB也提供了一些限制,如果user集合被删除了,post文档还是会保留。

理想情况下,这个$lookup操作应该不会经常使用,如果你需要经常使用它,那么你就使用了错误的数据存储了(数据库):如果你有相关联的数据,应该使用关联数据库(SQL)。

也就是说$lookup是一个MongoDB 3.2新加入的,他解决了当在Nosql数据库中使用一些小的相关联的数据查询的时候一些令人失望的问题。

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作能带来一定的帮助,如果有疑问大家可以留言交流,谢谢大家对我们的支持。

(0)

相关推荐

  • MongoDB中如何使用JOIN操作详解

    前言 MongoDB是由C++语言所编写的一种面向文档的非关系型数据库(是一种NoSql数据库实现),也是介于关系型数据库和非关系型数据库之间的数据存储产品,而众所周知SQL与NoSQL最大的不同之一就是不支持JOIN,在传统的数据库中,SQL JOIN子句允许你使用普通的字段,在两个或者是更多表中的组合表中的每行数据.例如,如果你有表books和publishers,你可以像下面这样写命令: SELECT book.title, publisher.name FROM book LEFT JO

  • 对pyqt5中QTabWidget的相关操作详解

    首先,下面贴上designer处理的界面文件(转换成py后的): # -*- coding: utf-8 -*- # Form implementation generated from reading ui file 'TabWidget.ui' # # Created by: PyQt5 UI code generator 5.12.1 # # WARNING! All changes made in this file will be lost! from PyQt5 import QtC

  • 对vuex中getters计算过滤操作详解

    getter这个概念其实我们写的时候感觉好像和Mutations修改状态一样,实际上它们是有区别的: getters比较死板,如果你的百度钱包只有在金额为100才能提现,那么你在写提现页面,它是早已固定好的,而Mutation不一样,当你点击百度钱包提现,你哪怕是一元,它只要你点击了便可以提现,而且getters它是不需要什么点击,它就存在,只要你写了,这是什么意思,就是说假设你百度钱包为0,你存在了getter它就有100元,而你如果写许多百度经验,百度再次发红包0.5元时它就是100+0.5

  • Python Numpy中数组的集合操作详解

    我们知道两个 set 对象之间,可以取交集.并集.差集.对称差集,举个例子: s1 = {1, 2, 3} s2 = {2, 3, 4} """ &: 交集 |: 并集  -: 差集 ^: 对称差集 """ # 以下几种方式是等价的 # 但是一般我们都会使用操作符来进行处理,因为比较方便 print(s1 & s1) print(s1.intersection(s2)) print(set.intersection(s1, s2)

  • MongoDB中MapReduce的使用方法详解

    前言 玩过Hadoop的小伙伴对MapReduce应该不陌生,MapReduce的强大且灵活,它可以将一个大问题拆分为多个小问题,将各个小问题发送到不同的机器上去处理,所有的机器都完成计算后,再将计算结果合并为一个完整的解决方案,这就是所谓的分布式计算.本文我们就来看看MongoDB中MapReduce的使用. 打算用mongodb mapreduce之前一定要知道的事!!! mapreduce其实是分批处理数据的,每一百次重新reduce处理,所以到reduce里的数据如果是101条,那就会分

  • nodejs中密码加密处理操作详解

    本文实例讲述了nodejs中密码加密处理操作.分享给大家供大家参考,具体如下: 一.关于node加密模块crypto的介绍 其实就是使用MD5加密的,不太安全,在实际开发中根据自己的方案进行加盐处理 二.在路由视图中使用加密方式 1.导入node自带的加密模块(不需要安装) //导入加密模块 const crypto = require("crypto"); 2.做一个用户注册,密码加密的视图 <div class="col-md-6"> <h4&

  • MongoDB中的定时索引示例详解

    MongoDB中存在一种索引,叫做TTL索引(time-to-live index,具有生命周期的索引),这种索引允许为每一个文档设置一个超时时间.一个文档达到预设置的老化程度后就会被删除. 数据到期对于某些类型的信息非常有用,例如机器生成的事件数据,日志和会话信息,这些信息只需要在数据库中保存有限的时间. 在createIndex中指定expireAfterSeconds选项就可以创建一个TTL索引: // 超时时间为24小时,默认是前台运行,可以通过background:true设置为后台模

  • Python中xlsx文件转置操作详解(行转列和列转行)

    目录 1.原始数据是这样的 2.脚本如下: 3.运行脚本后生成的xlsx文件,如下: 附:pivot方法即可完成行转列哦 总结 1.原始数据是这样的 2.脚本如下: import pandas as pd df = pd.read_excel(r'E:\untitled1\带宽测试\temp.xlsx') # 读取需要转置的文件 df = df.T # 转置 df.to_excel(r'E:\untitled1\带宽测试\TestResult.xlsx') # 另存为xlsx文件 3.运行脚本后

  • Go语言中的上下文取消操作详解

    前言 许多使用Go的人,都会用到它的上下文库.大多数使用 context 进行下游操作,比如发出HTTP调用,或者从数据库获取数据,或者在协程中执行异步操作.最常见的用法是传递可由所有下游操作使用的公共数据.然而,一个不太为人所知,但非常有用的上下文特性是,它能够在中途取消或停止一个操作. 本篇文章将解释我们如何利用上下文库的取消特性,并通过一些模式和最佳实践来使用取消,使你的程序更快.更健壮. 为什么需要取消? 简而言之,我们需要取消,以防止我们的系统做不不需要的工作. 考虑HTTP服务器对数

  • Python中列表的常用操作详解

    目录 打印出列表的数据 一.列表的循环遍历 1.for循环遍历 2.while循环遍历 二.列表的添加操作 1.append() 2.extend() 3.insert() 三.列表的修改操作 四.列表的查找 1.in 2.not in 3.index 4.count 五.列表中的删除 1.del 2.pop 3.remove 六.列表的排序操作 1.排序sort() 2.reverse方法是将内容顺序反转 总结 列表的格式:变量A的类型为列表 namesList = ['xiaoWang','

随机推荐