前端开发-MongoDB快速掌握上手知识总结

01_MongoDB 基础入门

数据库概述

数据管理的发展历史

数据库概念的演变与诞生经历了漫长的发展过程,从最开始的人工管理,到文件系统,再到数据库系统。每一个阶段的到来都伴随着新的技术突破。

人工管理阶段

20 世纪 50 年代,那是还没有诞生操作系统,计算机只是用于进行大规模复杂运算的机器,所有的数据都是通过外部磁带、卡带手工存储。

导致的问题是,数据只归属于某一个程序,数据没有结构之分,所有的数据都以二进制的方式顺序存储在物理存储设备上,读取时也只能以固定的字节数进行读取,否则就会数据错乱。

除此之外的是,人工管理下的程序员工作量巨大。

1、数据不保存在计算机内。
2、没有专用软件对数据进行管理
3、只有程序的概念,没有文件的概念
4、数据面向程序
由于在这一阶段,计算机主要用于计算,并不存储数据。重要的原因是没有存储设备,软件工程也不成熟。数据和程序并不是相互独立的,即一组数据对应着一个程序。

文件系统阶段

再往后,操作系统与磁盘的诞生使得数据管理进入了新的阶段,操作系统中实现了专门处理数据管理的模块,可以将虚拟文件映射到磁盘等实际物理设备上。

我们不再需要直接面对二进制,转而可以通过操作系统对数据进行简单的文件读写,管理数据更加方便了。

1、数据可以长期保存在磁盘上。
2、数据的逻辑结构与物理结构有了区别
3、文件组织呈现多样化
4、数据不再属于某个特定程序,可以重复使用。
经过技术的发展,出现了存储设备。并且软件工程也得到了发展,出现的文件系统是专门用于管理外存的数据的。而文件系统的出现的意义是将数据独立开来,使数据不依赖某个特定的程序。

但是,当数据量不断扩大时,文件系统就显露出了三个缺陷

  1. 数据冗余:因为每个应用程序都对应着一个文件,由于这些文件缺乏联系,有可能同样的数据在多个文件中重复存储。

  2. 数据不一致:这是用于数据冗余问题引发的一个问题,当进行数据更新操作时,修改了某个文件的数据可能造成另外一个文件的数据不一致的情况。

  3. 数据联系弱:由于文件之间缺乏联系造成的。

    为了解决文件系统所出现的问题,对数据更高级、更有效的进行管理。出现了数据管理系统,这也是我们所熟悉的数据库技术。

数据库阶段

为了解决多应用、多用户高度共享数据,数据存储的结构化、以及数据的多样化查询和保存,诞生了数据库系统。

数据库相比于文件系统具有如下特点:

  1. 数据的结构化存储
  2. 配备有专门的数据库管理系统

结构化的数据存储意味着我们可以结合面向对象的思想定制化我们程序使用的数据,更方便的读取存储。

专门的数据库管理系统意味着多程序、多用户访问下,仍然能控制并保证数据库中数据的安全性与完整性。

有关数据库和数据库管理系统之间的区别联系,我们后文还会做详尽的解释。

1、采用数据模型表示复杂的数据结构
2、有较高的数据独立性
3、数据库系统为用户提供方便的用户接口
4、数据库系统提供了四个方面的数据控制功能
	数据库的恢复
	数据库的并发控制
	数据的完整性
	数据的安全性
在数据库阶段中,我们将应用程序与数据相互独立了开来,当数据库提供了统一的应用程序的接口。使得应用程序改变时,不需要关心数据库;当数据库改变时,不需要考虑应用程序。并且数据库也提供了许多防止应用程序误操作导致数据丢失,损坏等问题。

数据库的基本概念

数据库

维基百科上是这样定义的:

所谓“数据库”系以一定方式储存在一起、能予多个用户共享、具有尽可能小的冗余度、与应用程序彼此独立的数据集合。一个数据库由多个表空间(Tablespace)构成。

好,这个不难理解,数据库就是一个存储结构化数据的仓库。

数据库是按照数据结构来组织、存储和管理数据的仓库。 
我们的程序都是在内存中运行的,一旦程序运行结束或者计算机断电,程序运行中的数据都会丢失。 
所以我们就需要将一些程序运行的数据持久化到硬盘之中,以确保数据的安全性。而数据库就是数据持久化的最佳选择。 
说白了,数据库就是存储数据的仓库。

数据库管理系统

维基百科这样说:

数据库管理系统(英语:Database Management System,简称DBMS)是为管理数据库而设计的电脑软件系统,一般具有存储、截取、安全保障、备份等基础功能。

看似差不多,都能读取存储数据,但实际上他们是上下级关系。

数据库用于存储数据,提供基本的数据查询保存等功能,而数据库管理系统则是在此基础之上封装了额外的功能。

例如:数据的备份与导出,安全访问拦截,甚至使用一个可视化的点击操作映射了基本的命令操作。

简单一句话,数据库管理系统是为了我们更方便的使用数据库而诞生的。​

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fmINRj4i-1637594662699)(images/微信截图_20200915211659.png)]

数据库分类

在很多人眼里,就只知道 MySQL,SQLServer,Oracle,听过 Sybase 和 DB2 的人或许都不多,但实际上这些都只是关系型数据库这一种数据库类型的实现者,数据库其实有很多类型。

数据库类型的区分主要参照的指标是数据的存储模型,而常用的数据模型其实有很多:

  • 层次模型
  • 网状模型
  • 关系模型
  • 面向对象模型
  • 半结构化模型

由于关系模型在很长一段时间内成为主流的数据模型,所以我们也习惯性将数据库类型分为两类,关系型数据库和非关系型数据库

数据模型:https://zhuanlan.zhihu.com/p/101890657?from=timeline

关系型数据库

这是我们目前至今主流的数据库类型(MySQL、Oracle、DB2、SQL Server …… ),其对应的数据存储模型就是关系型模型,数据以表格形式存储,字段关联数据。

二维表结构是非常贴近逻辑世界的一个概念,它更容易理解,这是关系型数据库能够成为主流的其中一个重要原因。通过 SQL 进行表与表之间的联接查询非常的方便自然。

缺点也是很显而易见的,海量数据下,对表的查询会显得很力不从心,就是因为数据的存储不具备特殊的数据结构,例如有些非关系型数据库的数据存储结构是类似树的结构,就使得查询上具有天然的优势。

所以个人认为,虽然现在是关系型数据库的天下,但相信以后会出现一些优秀的非关系型数据库取代传统的关系型数据库。因为以后必然是大数据的时代,那么海量数据下,传统的关系型数据的效率问题就会被逐渐放大。

SQL(结构化查询语言)是一种特定目的编程语言,用于管理关系数据库管理系统(RDBMS),或在关系流数据管理系统(RDSMS)中进行流处理

简而言之,SQL 是一门编程语言,它很特殊,它是一门帮助我们管理数据的标准语言。

为什么强调标准语言?

关系型数据的主要三大实现者分别是,Mysql,MS SQLServer,Oracle。

它们实现数据存储的底层引擎或许不同,但提供出来管理数据的编程语言必须遵循 SQL 规范,但可以定制添加属于自己的额外语法,这些额外的、SQL 之外的语法又被称作它们各自的『SQL方言』。

非关系型数据库

非关系型数据库也被称为 NoSQL 数据库,NoSQL 并不是某个具体数据库,它泛指所有非关系型数据库。

什么是NoSQL?

NoSQL,指的是非关系型的数据库。NoSQL有时也称作Not Only SQL的缩写,是对不同于传统的关系型数据库的数据库管理系统的统称。

NoSQL用于超大规模数据的存储。(例如谷歌或Facebook每天为他们的用户收集万亿比特的数据)。这些类型的数据存储不需要固定的模式,无需多余操作就可以横向扩展。

为什么使用NoSQL ?

今天我们可以通过第三方平台(如:Google,Facebook等)可以很容易的访问和抓取数据。用户的个人信息,社交网络,地理位置,用户生成的数据和用户操作日志已经成倍的增加。我们如果要对这些用户数据进行挖掘,那SQL数据库已经不适合这些应用了, NoSQL 数据库的发展却能很好的处理这些大的数据。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VFJGOqwh-1637594662701)(images/微信截图_20200915170605.png)]

非关系型数据库种类有很多,我们列举其中较为流行的几种。

键值(Key-Value)存储数据库

键值数据库主要是使用一个哈希表,表中有一个特定的键和一个指针指向特定的数据。Key/value 模型的键值数据库的优势在于,通过键的 hash 码可以快速查询到 value,并且能够应对高并发。

市面上成熟的产品有,MemcachedRedis、MemcacheDB、Berkeley DB。前两个可能比较有名,做缓存的数据库。

列存储(Column-oriented)数据库

列存储数据库又被称为面向可扩展性的分布式数据库,它反转了传统的行存储数据库。

传统的行存储数据库:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UJBd6IEo-1637594662704)(images/微信截图_20200915165530.png)]

列存储数据库:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xovDoeoO-1637594662705)(images/微信截图_20200915165614.png)]

因为是以列字段作为作为表格的行,那么同一行记录取的就是该表中所有记录的的某一个列数据集合,必然是同一类型的数据,要么都是 int 类型,要么都是 varchar 类型。

行存储如果要去表中某一列的所有数据集合,就会复杂的多,所以在大部分场景下,列存储的解析过程更有利于分析大数据的数据分析。

当然了,这只是其中一个区别,他们之间的优劣对比有很多个方面,这里不可能都进行列举,你们可以自行去搜索了解。

最典型的产品应用就是,Hbase,大数据存储用的非常多。

面向文档数据库

文档数据库是一种非关系数据库,旨在将半结构化数据存储为文档,其中文档包括 XML、YAML、JSON、BSON、office 文档等。

简而言之,就是将数据保存到以上类似格式的文档中,数据库中的每个记录都是以文档形式存在的,相互之间不再存在关联关系。

典型的应用就是,MongoDBCouchDB

NoSQL 数据库分类表

类型 部分代表 特点
列存储 Hbase、Cassandra、Hypertable 顾名思义,是按列存储数据的。最大的特点是方便存储结构化和半结构化数据,方便做数据压缩,对针对某一列或者某几列的查询有非常大的IO优势。
文档存储 MongoDB、CouchDB 文档存储一般用类似json的格式(BSON)存储,存储的内容是文档型的。这样也就有机会对某些字段建立索引,实现关系数据库的某些功能。
key-value存储 Tokyo Cabinet / TyrantBerkeley 、DBMemcacheDB、Redis 可以通过key快速查询到其value。一般来说,存储不管value的格式,照单全收。(Redis包含了其他功能)
图存储 Neo4J、FlockDB 图形关系的最佳存储。使用传统关系数据库来解决的话性能低下,而且设计使用不方便。
对象存储 db4o、Versant 通过类似面向对象语言的语法操作数据库,通过对象的方式存取数据。
xml数据库 Berkeley DB XML、BaseX 高效的存储XML数据,并支持XML的内部查询语法,比

RDBMS vs NoSQL

RDBMS 
- 高度组织化结构化数据 
- 结构化查询语言(SQL) 
- 数据和关系都存储在单独的表中。 
- 数据操纵语言,数据定义语言 
- 严格的一致性
- 基础事务

NoSQL 
- 代表着不仅仅是SQL
- 没有声明性查询语言
- 没有预定义的模式
- 键-值对存储,列存储,文档存储,图形数据库等
- 最终一致性,而非ACID属性
- 非结构化和不可预知的数据
- CAP定理 
- 高性能,高可用性和可伸缩性

CAP定理(CAP theorem):

​ http://www.ruanyifeng.com/blog/2018/07/cap.html

MongoDB概述

简介

MongoDB 是由C++语言编写的,是一个基于分布式文件存储、为快速开发互联网Web应用而设计的开源数据库系统。

在高负载的情况下,添加更多的节点,可以保证服务器性能。

MongoDB 旨在为WEB应用提供可扩展的高性能数据存储解决方案。

MongoDB 将数据存储为一个文档,文档数据结构由键值(key=>value)对组成。MongoDB 文档类似于 JSON 对象。字段值可以包含其他文档,数组及文档数组。简单理解MongoDB这个数据库中存的是各种各样的JSON。(BSON)

BSON:
	BSON(/ˈbiːsən/)是一种计算机数据交换格式,主要被用作MongoDB数据库中的数据存储和网络传输格式。它是一种二进制表示形式,能用来表示简单数据结构、关联数组(MongoDB中称为“对象”或“文档”)以及MongoDB中的各种数据类型。BSON之名缘于JSON,含义为Binary JSON(二进制JSON)。

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lQOxIwVc-1637594662708)(images/微信截图_20200915171331.png)]

主要特点

  • MongoDB 是一个面向文档存储的数据库,操作起来比较简单和容易。
  • 你可以在MongoDB记录中设置任何属性的索引 (如:FirstName=“Sameer”,Address=“8 Gandhi Road”)来实现更快的排序。
  • 你可以通过本地或者网络创建数据镜像,这使得MongoDB有更强的扩展性。
  • 如果负载的增加(需要更多的存储空间和更强的处理能力) ,它可以分布在计算机网络中的其他节点上这就是所谓的分片。
  • Mongo支持丰富的查询表达式。查询指令使用JSON形式的标记,可轻易查询文档中内嵌的对象及数组。
  • MongoDb 使用update()命令可以实现替换完成的文档(数据)或者一些指定的数据字段 。
  • Mongodb中的Map/reduce主要是用来对数据进行批量处理和聚合操作。
  • Map和Reduce。Map函数调用emit(key,value)遍历集合中所有的记录,将key与value传给Reduce函数进行处理。
  • Map函数和Reduce函数是使用Javascript编写的,并可以通过db.runCommand或mapreduce命令来执行MapReduce操作。
  • GridFS是MongoDB中的一个内置功能,可以用于存放大量小文件。
  • MongoDB允许在服务端执行脚本,可以用Javascript编写某个函数,直接在服务端执行,也可以把函数的定义存储在服务端,下次直接调用即可。
  • MongoDB支持各种编程语言:RUBY,PYTHON,JAVA,C++,PHP,C#等多种语言。
  • MongoDB安装简单。

历史

  • 2007年10月,MongoDB由10gen团队所发展。2009年2月首度推出。
  • 2012年05月23日,MongoDB2.1 开发分支发布了! 该版本采用全新架构,包含诸多增强。
  • 2012年06月06日,MongoDB 2.0.6 发布,分布式文档数据库。
  • 2013年04月23日,MongoDB 2.4.3 发布,此版本包括了一些性能优化,功能增强以及bug修复。
  • 2013年08月20日,MongoDB 2.4.6 发布。
  • 2013年11月01日,MongoDB 2.4.8 发布。
  • ……

目前MongoDB的最新版本是:4.4

MongoDB安装

MongoDB提供了其强大的分布式文档数据库的企业版和社区版。

MongoDB的版本偶数版本为稳定版,奇数版本为开发版。

在 MongoDB 2.2 版本后已经不再支持 Windows XP 系统。4.X版本后不再支持Win7

MongoDB对于32位系统支持不佳,所以 3.2版本以后没有再对32位系统的支持。

Windows 平台安装 MongoDB

下载安装

MongoDB 提供了可用于 32 位和 64 位系统的预编译二进制包,你可以从MongoDB官网下载安装,MongoDB 预编译二进制包下载地址:https://www.mongodb.com/try/download/community

注:如官网地址下载失败,使用https://www.mongodb.org/dl/win32/

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UKISlucv-1637594662709)(images/微信截图_20200915200912.png)]

  • 下载 .msi 文件,下载后双击该文件,按操作提示安装即可。

    注:4.x版本的MongoDB不支持Win7;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vW37JI5j-1637594662714)(images/微信截图_20200915214700.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NSjfwfY6-1637594662715)(images/微信截图_20200915214727.png)]

  • 选择安装类型,选择Custom安装

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WYyuTHWx-1637594662718)(images/微信截图_20200915214900.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hN96GKdA-1637594662720)(images/微信截图_20200915215001.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zC62oaPi-1637594662722)(images/微信截图_20200915215031.png)]

注:可以去除Install MongoDB Compass选项,去官网下载安装;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xKhxOMrv-1637594662723)(images/微信截图_20200915215048.png)]

  • 安装完毕

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7VUrb6zg-1637594662727)(images/微信截图_20200915220447.png)]

配置环境变量

计算机 --> 属性 --> 高级系统设置;

将MongoDB的bin目录添加到path下

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-55oiIzwb-1637594662728)(images/微信截图_20200915220907.png)]

启动MongoDB

1、创建数据库目录

​ 创建MongoDB存储数据的数据目录。MongoDB的默认数据目录路径是启动MongoDB的驱动器上的绝对路径\data\db。

​ 在C盘根目录下创建data文件夹,在data下创建db文件夹 ;

cd C:\
md "\data\db"

2、启动MongoDB数据库

​ 要启动MongoDB,运行mongod.exe。

​ 打开CMD命令行窗口,输入mongod

​ 32位系统第一次启动:mongod --storageEngine=mmapv1

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MA50WTSc-1637594662732)(images/微信截图_20200915221756.png)]

出现如下输出,表示启动成功:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BkMRU0pD-1637594662733)(images/微信截图_20200915221841.png)]

3、客户端连接到MongoDB

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1bTXEDv8-1637594662737)(images/微信截图_20200915222022.png)]

- 数据库的服务器
	- 服务器用来保存数据
	- mongod 用来启动服务器
			
- 数据库的客户端
	- 客户端用来操作服务器,对数据进行增删改查的操作
	- mongo 用来启动客户端

指定端口和路径

在控制台启动MongoDB

# 语法:
mongod --dbpath 路径 --port 端口号

# 示例:
mongod --dbpath C:\Users\Administrator\Desktop\mongo\data\db --port 10086

注意:打开的命令行窗口不能关闭,如果窗口关闭,则连接不上;

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-81exP1Cx-1637594662738)(images/微信截图_20200915222216.png)]

配置mongo的windows服务

将MongoDB设置为系统服务,可以自动在后台启动,不需要每次都手动启动

1、在c盘根目录创建data

在data下创建db和log文件夹
	db文件夹是数据库
	log文件夹是日志

注:最好先清空db文件夹

2、创建配置文件

​ 在安装目录下添加一个配置文件mongod.cfg

​ 我的安装目录:D:\Tools\MongoDB\Server\3.6

​ mongod.cfg配置文件夹内容如下:

systemLog:
  destination: "file"
  path: "c:\\data\\log\\mongod.log"
storage:
  dbPath: "c:\\data\\db"

3、以管理员的身份打开命令行窗口,并执行如下的指令

# 语法
sc.exe create MongoDB binPath= "\"mongod的bin目录\mongod.exe\" --service --config=\"mongo的安装目录\mongod.cfg\"" DisplayName= "MongoDB" start= "auto"

# 示例
sc.exe create MongoDB binPath= "\"D:\Tools\MongoDB\Server\3.6\bin\mongod.exe\" --service --config=\"D:\Tools\MongoDB\Server\3.6\mongod.cfg\"" DisplayName= "MongoDB" start= "auto"

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1HJZgib8-1637594662741)(images/微信截图_20200915223757.png)]

注:杀毒软件拦截,记得允许所有操作!

4、在计算机服务中,启动mongodb服务,如果启动失败,证明上边的操作有误

net start MongoDB

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6mpPAHeD-1637594662743)(images/微信截图_20200915224123.png)]

​ 那么需要执行如下指令,删除之前配置的服务;然后从第一步再来一次

sc delete MongoDB

关闭MongoDB

1、打开新的命令行窗口,登录服务器

mongo

2、切换管理员用户

use admin

3、关闭数据库

db.shutdownServer()

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-o5CZcU9Q-1637594662747)(images/微信截图_20200916162936.png)]

Linux平台安装MongoDB

MongoDB 提供了 linux 各个发行版本 64 位的安装包,你可以在官网下载安装包。

安装前我们需要安装各个 Linux 平台依赖包。

Red Hat/CentOS:

sudo yum install libcurl openssl

Ubuntu 18.04 LTS (“Bionic”)/Debian 10 “Buster”:

sudo apt-get install libcurl4 openssl

Ubuntu 16.04 LTS (“Xenial”)/Debian 9 “Stretch”:

sudo apt-get install libcurl3 openssl

MongoDB 下载地址:https://www.mongodb.com/try/download

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rSrvBJrW-1637594662749)(images/微信截图_20200915202217.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lhRTsmYV-1637594662752)(images/微信截图_20200915202519.png)]

这里我们选择 tgz 下载,下载完安装包,并解压 tgz(以下演示的是 64 位 Linux上的安装) 。

# 下载
wget https://fastdl.mongodb.org/linux/mongodb-linux-x86_64-ubuntu1604-4.2.8.tgz    

 # 解压
tar -zxvf mongodb-linux-x86_64-ubuntu1604-4.2.8.tgz                                   

# 将解压包拷贝到指定目录
mv mongodb-src-r4.2.8  /usr/local/mongodb4                          

MongoDB 的可执行文件位于 bin 目录下,所以可以将其添加到 PATH 路径中:

export PATH=<mongodb-install-directory>/bin:$PATH

为你 MongoDB 的安装路径。如本文的 /usr/local/mongodb4

export PATH=/usr/local/mongodb4/bin:$PATH

创建数据库目录

默认情况下 MongoDB 启动后会初始化以下两个目录:

  • 数据存储目录:/var/lib/mongodb
  • 日志文件目录:/var/log/mongodb

我们在启动前可以先创建这两个目录并设置当前用户有读写权限:

sudo mkdir -p /var/lib/mongo
sudo mkdir -p /var/log/mongodb
sudo chown `whoami` /var/lib/mongo     # 设置权限
sudo chown `whoami` /var/log/mongodb   # 设置权限

接下来启动 Mongodb 服务:

mongod --dbpath /var/lib/mongo --logpath /var/log/mongodb/mongod.log --fork

打开 /var/log/mongodb/mongod.log 文件看到以下信息,说明启动成功。

# tail -10f /var/log/mongodb/mongod.log
2020-07-09T12:20:17.391+0800 I  NETWORK  [listener] Listening on /tmp/mongodb-27017.sock
2020-07-09T12:20:17.392+0800 I  NETWORK  [listener] Listening on 127.0.0.1
2020-07-09T12:20:17.392+0800 I  NETWORK  [listener] waiting for connections on port 27017

MongoDB 后台管理 Shell

$ cd /usr/local/mongodb4/bin
$ ./mongo
MongoDB shell version v4.2.8
connecting to: mongodb://127.0.0.1:27017/?compressors=disabled&gssapiServiceName=mongodb
Implicit session: session { "id" : UUID("2cfdafc4-dd56-4cfc-933a-187b887119b3") }
MongoDB server version: 4.2.8
Welcome to the MongoDB shell.
……

由于它是一个JavaScript shell,您可以运行一些简单的算术运算:

> 2+2
4
> 3+6
9

现在让我们插入一些简单的数据,并对插入的数据进行检索:

> db.runoob.insert({x:10})
WriteResult({ "nInserted" : 1 })
> db.runoob.find()
{ "_id" : ObjectId("5f069bdb4e02f8baf90f1184"), "x" : 10 }
> 

第一个命令将数字 10 插入到 runoob 集合的 x 字段中。

如果要停止 mongodb 可以使用以下命令:

mongod --dbpath /var/lib/mongo --logpath /var/log/mongodb/mongod.log --shutdown

也可以在 mongo 的命令出口中实现:

> use admin
switched to db admin
> db.shutdownServer()

更多安装方法可以参考官网:

​ https://docs.mongodb.com/manual/administration/install-on-linux/

Mac OSX 平台安装 MongoDB

MongoDB 提供了 OSX 平台上 64 位的安装包,你可以在官网下载安装包。

下载地址:https://www.mongodb.com/try/download/community

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3R65UcqK-1637594662754)(images/微信截图_20200915203221.png)]

https://fastdl.mongodb.org/osx/mongodb-macos-x86_64-4.4.1.tgz

从 MongoDB 3.0 版本开始只支持 OS X 10.7 (Lion) 版本及更新版本的系统。

接下来我们使用 curl 命令来下载安装:

# 进入 /usr/local
cd /usr/local

# 下载
sudo curl -O https://fastdl.mongodb.org/osx/mongodb-osx-ssl-x86_64-4.0.9.tgz

# 解压
sudo tar -zxvf mongodb-osx-ssl-x86_64-4.0.9.tgz

# 重命名为 mongodb 目录
sudo mv mongodb-osx-x86_64-4.0.9/ mongodb

安装完成后,我们可以把 MongoDB 的二进制命令文件目录(安装目录/bin)添加到 PATH 路径中:

export PATH=/usr/local/mongodb/bin:$PATH

创建日志及数据存放的目录:

  • 数据存放路径:

    sudo mkdir -p /usr/local/var/mongodb
    
  • 日志文件路径:

    sudo mkdir -p /usr/local/var/log/mongodb
    

接下来要确保当前用户对以上两个目录有读写的权限:

sudo chown runoob /usr/local/var/mongodb
sudo chown runoob /usr/local/var/log/mongodb

以上 runoob 是我电脑上对用户,你这边需要根据你当前对用户名来修改。

接下来我们使用以下命令在后台启动 mongodb:

mongod --dbpath /usr/local/var/mongodb --logpath /usr/local/var/log/mongodb/mongo.log --fork
  • –dbpath 设置数据存放目录
  • –logpath 设置日志存放目录
  • –fork 在后台运行

如果不想在后端运行,而是在控制台上查看运行过程可以直接设置配置文件启动:

mongod --config /usr/local/etc/mongod.conf

查看 mongod 服务是否启动:

ps aux | grep -v grep | grep mongod

使用以上命令如果看到有 mongod 的记录表示运行成功。

启动后我们可以使用 mongo 命令打开一个终端:

$ cd /usr/local/mongodb/bin 
$ ./mongo
MongoDB shell version v4.0.9
connecting to: mongodb://127.0.0.1:27017/?gssapiServiceName=mongodb
Implicit session: session { "id" : UUID("3c12bf4f-695c-48b2-b160-8420110ccdcf") }
MongoDB server version: 4.0.9
……
> 1 + 1
2
> 

使用 brew 安装

此外你还可以使用 OSX 的 brew 来安装 mongodb:

brew tap mongodb/brew
brew install [email protected]

@ 符号后面的 4.4 是最新版本号。

安装信息:

  • 配置文件:/usr/local/etc/mongod.conf
  • 日志文件路径:/usr/local/var/log/mongodb
  • 数据存放路径:/usr/local/var/mongodb

运行 MongoDB

我们可以使用 brew 命令或 mongod 命令来启动服务。

brew 启动:

brew services start [email protected]

brew 停止:

brew services stop [email protected]

mongod 命令后台进程方式:

mongod --config /usr/local/etc/mongod.conf --fork

这种方式启动要关闭可以进入 mongo shell 控制台来实现:

> db.adminCommand({ "shutdown" : 1 })

MongoDB 概念解析

不管我们学习什么数据库都应该学习其中的基础概念,在mongodb中基本的概念是文档、集合、数据库,下面我们挨个介绍。

下表将帮助您更容易理解Mongo中的一些概念:

SQL术语/概念 MongoDB术语/概念 解释/说明
database database 数据库
table collection 数据库表/集合
row document 数据记录行/文档
column field 数据字段/域
index index 索引
table joins 表连接,MongoDB不支持
primary key primary key 主键,MongoDB自动将_id字段设置为主键

通过下图实例,我们也可以更直观的了解Mongo中的一些概念:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-76PPacyU-1637594662757)(images/微信截图_20200915204757.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JjWy8VcU-1637594662758)(images/微信截图_20200916163055.png)]

数据库(Database)

一个mongodb中可以建立多个数据库。

MongoDB的默认数据库为"test",该数据库存储在data目录中。

MongoDB的单个实例可以容纳多个独立的数据库,每一个都有自己的集合和权限,不同的数据库也放置在不同的文件中。

"show dbs" 命令可以显示所有数据库的列表。

$ mongo
MongoDB shell version v3.6.20
connecting to: mongodb://127.0.0.1:27017/?gssapiServiceName=mongodb
Implicit session: session { "id" : UUID("1ae793b3-e9e5-44f8-8a99-2d0183dd343f") }
MongoDB server version: 3.6.20

> show dbs
admin   0.000GB
config  0.000GB
local   0.000GB

> show databases
admin   0.000GB
config  0.000GB
local   0.000GB

执行 “db” 命令可以显示当前数据库对象。

# 登录进来客户端之后,默认就是在test数据库下面,但是我们show db是的时候没有显示test,因为test里面没有数据,所以不显示
> db
test
> 

运行"use"命令,可以连接到一个指定的数据库。

# use 数据库名,连接到一个指定的数据库。如果没有该库,自动创建
> use local
switched to db local
> db
local
> 

以上实例命令中,“local” 是你要链接的数据库。

在下一个章节我们将详细讲解MongoDB中命令的使用。

数据库也通过名字来标识。数据库名可以是满足以下条件的任意UTF-8字符串。

  • 不能是空字符串("")。
  • 不得含有’ '(空格)、.、$、/、\和\0 (空字符)。
  • 应全部小写。
  • 最多64字节。

有一些数据库名是保留的,可以直接访问这些有特殊作用的数据库。

  • admin: 从权限的角度来看,这是"root"数据库。要是将一个用户添加到这个数据库,这个用户自动继承所有数据库的权限。一些特定的服务器端命令也只能从这个数据库运行,比如列出所有的数据库或者关闭服务器。
  • local: 这个数据永远不会被复制,可以用来存储限于本地单台服务器的任意集合
  • config: 当Mongo用于分片设置时,config数据库在内部使用,用于保存分片的相关信息。

文档(Document)

文档是一组键值(key-value)对(即 BSON)。MongoDB 的文档不需要设置相同的字段,并且相同的字段不需要相同的数据类型,这与关系型数据库有很大的区别,也是 MongoDB 非常突出的特点。

一个简单的文档例子如下:

{"name":"孙悟空", "age":523}

下表列出了 RDBMS 与 MongoDB 对应的术语:

RDBMS MongoDB
数据库 数据库
表格 集合
文档
字段
表联合 嵌入文档
主键 主键 (MongoDB 提供了 key 为 _id )
数据库服务和客户端 数据库服务和客户端
Mysqld/Oracle mongod
mysql/sqlplus mongo

需要注意的是:

  1. 文档中的键/值对是有序的。
  2. 文档中的值不仅可以是在双引号里面的字符串,还可以是其他几种数据类型(甚至可以是整个嵌入的文档)。
  3. MongoDB区分类型和大小写。
  4. MongoDB的文档不能有重复的键。
  5. 文档的键是字符串。除了少数例外情况,键可以使用任意UTF-8字符。

文档键命名规范:

  • 键不能含有\0 (空字符)。这个字符用来表示键的结尾。
  • .和$有特别的意义,只有在特定环境下才能使用。
  • 以下划线"_"开头的键是保留的(不是严格要求的)。

集合(Collection)

集合就是 MongoDB 文档组,类似于 RDBMS (关系数据库管理系统:Relational Database Management System)中的表格。

集合存在于数据库中,集合没有固定的结构,这意味着你在对集合可以插入不同格式和类型的数据,但通常情况下我们插入集合的数据都会有一定的关联性。

比如,我们可以将以下不同数据结构的文档插入到集合中:

{"name":"孙悟空"}
{"name":"猪八戒","age":523}
{"name":"沙和尚","age":512,"address":"流沙河"}

当第一个文档插入时,集合就会被创建。

合法的集合名

  • 集合名不能是空字符串""。
  • 集合名不能含有\0字符(空字符),这个字符表示集合名的结尾。
  • 集合名不能以"system."开头,这是为系统集合保留的前缀。
  • 用户创建的集合名字不能含有保留字符。有些驱动程序的确支持在集合名里面包含,这是因为某些系统生成的集合中包含该字符。除非你要访问这种系统创建的集合,否则千万不要在名字里出现$。

如下实例:

db.<collection>.findOne()

capped collections

Capped collections 就是固定大小的collection。

它有很高的性能以及队列过期的特性(过期按照插入的顺序). 有点和 “RRD” 概念类似。

Capped collections 是高性能自动的维护对象的插入顺序。它非常适合类似记录日志的功能和标准的 collection 不同,你必须要显式的创建一个capped collection,指定一个 collection 的大小,单位是字节。collection 的数据存储空间值提前分配的。

Capped collections 可以按照文档的插入顺序保存到集合中,而且这些文档在磁盘上存放位置也是按照插入顺序来保存的,所以当我们更新Capped collections 中文档的时候,更新后的文档不可以超过之前文档的大小,这样话就可以确保所有文档在磁盘上的位置一直保持不变。

由于 Capped collection 是按照文档的插入顺序而不是使用索引确定插入位置,这样的话可以提高增添数据的效率。MongoDB 的操作日志文件 oplog.rs 就是利用 Capped Collection 来实现的。

要注意的是指定的存储大小包含了数据库的头信息。

db.createCollection("mycoll", {capped:true, size:100000})
  • 在 capped collection 中,你能添加新的对象。
  • 能进行更新,然而,对象不会增加存储空间。如果增加,更新就会失败 。
  • 使用 Capped Collection 不能删除一个文档,可以使用 drop() 方法删除 collection 所有的行。
  • 删除之后,你必须显式的重新创建这个 collection。
  • 在32bit机器中,capped collection 最大存储为 1e9( 1X109)个字节。

元数据

数据库的信息是存储在集合中。它们使用了系统的命名空间:

dbname.system.*

在MongoDB数据库中名字空间 .system.* 是包含多种系统信息的特殊集合(Collection),如下:

集合命名空间 描述
dbname.system.namespaces 列出所有名字空间。
dbname.system.indexes 列出所有索引。
dbname.system.profile 包含数据库概要(profile)信息。
dbname.system.users 列出所有可访问数据库的用户。
dbname.local.sources 包含复制对端(slave)的服务器信息和状态。

对于修改系统集合中的对象有如下限制。

在{{system.indexes}}插入数据,可以创建索引。但除此之外该表信息是不可变的(特殊的drop index命令将自动更新相关信息)。

{{system.users}}是可修改的。 {{system.profile}}是可删除的。

MongoDB 数据类型

下表为MongoDB中常用的几种数据类型。

数据类型 描述
String 字符串。存储数据常用的数据类型。在 MongoDB 中,UTF-8 编码的字符串才是合法的。
Integer 整型数值。用于存储数值。根据你所采用的服务器,可分为 32 位或 64 位。
Boolean 布尔值。用于存储布尔值(真/假)。
Double 双精度浮点值。用于存储浮点值。
Min/Max keys 将一个值与 BSON(二进制的 JSON)元素的最低值和最高值相对比。
Array 用于将数组或列表或多个值存储为一个键。
Timestamp 时间戳。记录文档修改或添加的具体时间。
Object 用于内嵌文档。
Null 用于创建空值。
Symbol 符号。该数据类型基本上等同于字符串类型,但不同的是,它一般用于采用特殊符号类型的语言。
Date 日期时间。用 UNIX 时间格式来存储当前日期或时间。你可以指定自己的日期时间:创建 Date 对象,传入年月日信息。
Object ID 对象 ID。用于创建文档的 ID。
Binary Data 二进制数据。用于存储二进制数据。
Code 代码类型。用于在文档中存储 JavaScript 代码。
Regular expression 正则表达式类型。用于存储正则表达式。

下面说明下几种重要的数据类型。

ObjectId

ObjectId 类似唯一主键,可以很快的去生成和排序,包含 12 bytes,含义是:

  • 前 4 个字节表示创建 unix 时间戳,格林尼治时间 UTC 时间,比北京时间晚了 8 个小时
  • 接下来的 3 个字节是机器标识码
  • 紧接的两个字节由进程 id 组成 PID
  • 最后三个字节是随机数

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Sy4xmoXw-1637594662763)(images/微信截图_20200915210648.png)]

MongoDB 中存储的文档必须有一个 _id 键。这个键的值可以是任何类型的,默认是个 ObjectId 对象

由于 ObjectId 中保存了创建的时间戳,所以你不需要为你的文档保存时间戳字段,你可以通过 getTimestamp 函数来获取文档的创建时间:

> var newObject = ObjectId()
> newObject.getTimestamp()
ISODate("2017-11-25T07:21:10Z")

ObjectId 转为字符串

> newObject.str
5a1919e63df83ce79df8b38f

字符串

BSON 字符串都是 UTF-8 编码。

时间戳

BSON 有一个特殊的时间戳类型用于 MongoDB 内部使用,与普通的 日期 类型不相关。 时间戳值是一个 64 位的值。其中:

  • 前32位是一个 time_t 值(与Unix新纪元相差的秒数)
  • 后32位是在某秒中操作的一个递增的序数

在单个 mongod 实例中,时间戳值通常是唯一的。

在复制集中, oplog 有一个 ts 字段。这个字段中的值使用BSON时间戳表示了操作时间。

BSON 时间戳类型主要用于 MongoDB 内部使用。在大多数情况下的应用开发中,你可以使用 BSON 日期类型。

日期

表示当前距离 Unix新纪元(1970年1月1日)的毫秒数。日期类型是有符号的, 负数表示 1970 年之前的日期。

> var mydate1 = new Date()     //格林尼治时间
> mydate1
ISODate("2018-03-04T14:58:51.233Z")
> typeof mydate1
object
> var mydate2 = ISODate() //格林尼治时间
> mydate2
ISODate("2018-03-04T15:00:45.479Z")
> typeof mydate2
object

这样创建的时间是日期类型,可以使用 JS 中的 Date 类型的方法。

返回一个时间类型的字符串:

> var mydate1str = mydate1.toString()
> mydate1str
Sun Mar 04 2018 14:58:51 GMT+0000 (UTC) 
> typeof mydate1str
string

或者

> Date()
Sun Mar 04 2018 15:02:59 GMT+0000 (UTC) 

Mongo Shell

登录mongo shell

mongo

基本指令

  • help 语法帮助
  • use 更改当前操作的数据库
  • show 根据参数显示列表
    • dbs/databases 显示数据库列表
    • collections 显示当前数据库的集合
    • profile 显示时间超过1毫秒的system.profile条目
    • log[name] 显示登录记忆的最后一段
  • exit 退出数据库
  • db 当前所处的数据库

MongoDB管理工具

mongodbmanager:

​ https://www.mongodbmanager.com/download

​ Next安装即可

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uEsqVHW4-1637594662769)(images/微信截图_20200916152116.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CHVX7Tlz-1637594662775)(images/微信截图_20200916152142.png)]

studio3t:

​ https://studio3t.com/download/

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3yoUQLXT-1637594662777)(images/微信截图_20200915232046.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yhSSQ2S0-1637594662779)(images/微信截图_20200915232120.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6d25zmXX-1637594662780)(images/微信截图_20200915232134.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Aur2Xw0H-1637594662784)(images/微信截图_20200915232209.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RKnyTTzg-1637594662785)(images/微信截图_20200915232239.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nYyYGfd5-1637594662787)(images/微信截图_20200915232300.png)]

02_MongoDB CRUD数据库操作命令

数据库操作

创建数据库

语法

MongoDB 创建数据库的语法格式如下:

use DATABASE_NAME

如果数据库不存在,则创建数据库,否则切换到指定数据库。

实例

以下实例我们创建了数据库 newcapec:

> use newcapec
switched to db newcapec
> db
newcapec
> 

如果你想查看所有数据库,可以使用 show dbs 命令:

> show dbs
admin   0.000GB
config  0.000GB
local   0.000GB
> 

可以看到,我们刚创建的数据库 newcapec 并不在数据库的列表中, 要显示它,我们需要向 newcapec 数据库插入一些数据。

# db 代表的是当前数据库,stus是集合名,inset()是向集合中插入文档,文档是BSON格式
> db.stus.insert({"name":"张三丰"})
WriteResult({ "nInserted" : 1 })
> show dbs
admin     0.000GB
config    0.000GB
local     0.000GB
newcapec  0.000GB

MongoDB 中默认的数据库为 test,如果你没有创建新的数据库,集合将存放在 test 数据库中。

注意: 在 MongoDB 中,集合只有在内容插入后才会创建! 就是说,创建集合(数据表)后要再插入一个文档(记录),集合才会真正创建。

删除数据库

语法

MongoDB 删除数据库的语法格式如下:

db.dropDatabase()

删除当前数据库,默认为 test,你可以使用 db 命令查看当前数据库名。

实例

以下实例我们删除了数据库 newcapec。

首先,查看所有数据库:

> show dbs
admin     0.000GB
config    0.000GB
local     0.000GB
newcapec  0.000GB

接下来我们切换到数据库 newcapec:

> use newcapec
switched to db newcapec
> 

执行删除命令:

> db.dropDatabase()
{ "dropped" : "newcapec", "ok" : 1 }

最后,我们再通过 show dbs 命令数据库是否删除成功:

> show dbs
admin   0.000GB
config  0.000GB
local   0.000GB

删除集合

集合删除语法格式如下:

db.collection.drop()

以下实例删除了 newcapec 数据库中的集合 site:

> use newcapec
switched to db newcapec
# 先创建集合,类似数据库中的表
> db.createCollection("stus")     

# show collections 命令会更加准确点
> show tables             
stus

# 删除stus集合
> db.stus.drop()
true

> show tables
> 

集合操作

创建集合

语法

MongoDB 中使用 createCollection() 方法来创建集合。

语法格式:

db.createCollection(name, [options])

参数说明:

  • name: 要创建的集合名称
  • options: 可选参数, 指定有关内存大小及索引的选项

options 可以是如下参数:

字段 类型 描述
capped 布尔 (可选)如果为 true,则创建固定集合。固定集合是指有着固定大小的集合,当达到最大值时,它会自动覆盖最早的文档。 当该值为 true 时,必须指定 size 参数。
autoIndexId 布尔 3.2 之后不再支持该参数。(可选)如为 true,自动在 _id 字段创建索引。默认为 false。
size 数值 (可选)为固定集合指定一个最大值,即字节数。 如果 capped 为 true,也需要指定该字段。
max 数值 (可选)指定固定集合中包含文档的最大数量。

在插入文档时,MongoDB 首先检查固定集合的 size 字段,然后检查 max 字段。

实例

在 newcapec数据库中创建 stus集合:

> use newcapec
switched to db newcapec
> db.createCollection("stus")
{ "ok" : 1 }
>

如果要查看已有集合,可以使用 show collections 或 show tables 命令:

> show collections
stus

下面是带有几个关键参数的 createCollection() 的用法:

创建固定集合 mycol,整个集合空间大小 6142800 KB, 文档最大个数为 10000 个。

> db.createCollection("mycol", { capped : true, autoIndexId : true, size : 
   6142800, max : 10000 } )
{ "ok" : 1 }
>

在 MongoDB 中,你不需要创建集合。当你插入一些文档时,MongoDB 会自动创建集合。

> db.mycol2.insert({"name" : "小刘"})
WriteResult({ "nInserted" : 1 })

> show collections
mycol
mycol2
stus
...

删除集合

语法

MongoDB 中使用 drop() 方法来删除集合。

语法格式:

db.collection.drop()

参数说明:

返回值

如果成功删除选定集合,则 drop() 方法返回 true,否则返回 false。

实例

在数据库 newcapec中,我们可以先通过 show collections 命令查看已存在的集合:

>use newcapec
switched to db newcapec
>show collections
mycol
mycol2
stus
>

接着删除集合 mycol2 :

>db.mycol2.drop()
true
>

通过 show collections 再次查看数据库 mydb 中的集合:

>show collections
mycol
stus
>

从结果中可以看出 mycol2 集合已被删除。

文档操作

插入文档

文档的数据结构和 JSON 基本一样。

所有存储在集合中的数据都是 BSON 格式。

BSON 是一种类似 JSON 的二进制形式的存储格式,是 Binary JSON 的简称。

语法

MongoDB 使用 insert() 或 save() 方法向集合中插入文档,语法如下:

# db..insert()
#	向集合中插入一个或多个文档
#	当我们向集合中插入文档时,如果没有给文档指定_id属性,则数据库会自动为文档添加_id该属性用来作为文档的唯一标识
#	_id我们可以自己指定,如果我们指定了数据库就不会在添加了,如果自己指定_id 也必须确保它的唯一性
db.COLLECTION_NAME.insert(document)
或
db.COLLECTION_NAME.save(document)
  • save():如果 _id 主键存在则更新数据,如果不存在就插入数据。该方法新版本中已废弃,可以使用 db.collection.insertOne()db.collection.replaceOne() 来代替。
  • insert(): 若插入的数据主键已经存在,则会抛 org.springframework.dao.DuplicateKeyException 异常,提示主键重复,不保存当前数据。

3.2 版本之后新增了 db.collection.insertOne() 和 db.collection.insertMany()。

db.collection.insertOne() 用于向集合插入一个新文档,语法格式如下:

db.collection.insertOne(
   <document>,
   {
      writeConcern: <document>
   }
)

db.collection.insertMany() 用于向集合插入一个多个文档,语法格式如下:

db.collection.insertMany(
   [ <document 1> , <document 2>, ... ],
   {
      writeConcern: <document>,
      ordered: <boolean>
   }
)

参数说明:

  • document:要写入的文档。
  • writeConcern:写入策略,默认为 1,即要求确认写操作,0 是不要求。
  • ordered:指定是否按顺序写入,默认 true,按顺序写入。

实例

以下文档可以存储在 MongoDB 的 newcapec数据库 的 stus集合中:

> db.stus.insert({
	name: '孙悟空', 
    age: 28,
    address: '花果山',
    hobby: ['蟠桃', '拔毛', '打妖怪'],
    weapon: '如意金箍棒'
})

WriteResult({ "nInserted" : 1 })

以上实例中 stus是我们的集合名,如果该集合不在该数据库中, MongoDB 会自动创建该集合并插入文档。

查看已插入文档:

> db.stus.find()
{ "_id" : ObjectId("5f61da75c1f46921b9662b2e"), "name" : "孙悟空", "age" : 28, "address" : "花果山", "hobby" : [ "蟠桃", "拔毛", "打妖怪" ],"weapon" : "如意金箍棒" }
> 

我们也可以将数据定义为一个变量,如下所示:

> document=({
	name: '猪八戒', 
    age: 26,
    address: '高老庄',
    hobby: ['包子', '睡觉', '看美女'],
    weapon: '九齿钉耙'
});

执行后显示结果如下:

{ 
    "name" : "猪八戒", 
    "age" : 26.0, 
    "address" : "高老庄", 
    "hobby" : [
        "包子", 
        "睡觉", 
        "看美女"
    ], 
    "weapon" : "九齿钉耙"
}

执行插入操作:

> db.stus.insert(document)
WriteResult({ "nInserted" : 1 })
> 

插入文档你也可以使用 db.col.save(document) 命令。如果不指定 _id 字段 save() 方法类似于 insert() 方法。如果指定 _id 字段,则会更新该 _id 的数据。

使用insert()插入多个文档:

> db.stus.insert([
    {
    	name: '沙僧', 
        age: 20,
        address: '流沙河',
        hobby: ['刮痧', '呼救'],
        weapon: '降妖宝杖'
    },
    {
        name: '唐僧', 
        age: 30,
        address: '东土大唐',
        hobby: ['念经'],
        weapon: '禅杖'
    }
])

BulkWriteResult({
	"writeErrors" : [ ],
	"writeConcernErrors" : [ ],
	"nInserted" : 2,
	"nUpserted" : 0,
	"nMatched" : 0,
	"nModified" : 0,
	"nRemoved" : 0,
	"upserted" : [ ]
})

注:db.collection.insertOne() 和 db.collection.insertMany()其实就是insert()拆分开来使用,更具有语义而已;

查询文档

语法

MongoDB 查询文档使用 find() 方法。

find() 方法以非结构化的方式来显示所有文档。

MongoDB 查询数据的语法格式如下:

db.collection.find(query, projection)
	find()用来查询集合中所有符合条件的文档
 	find()可以接收一个对象作为条件参数
		{} 表示查询集合中所有的文档
		{属性:值} 查询属性是指定值的文档
	find()返回的是一个数组
  • query :可选,使用查询操作符指定查询条件
  • projection :可选,使用投影操作符指定返回的键。查询时返回文档中所有键值, 只需省略该参数即可(默认省略)。

如果你需要以易读的方式来读取数据,可以使用 pretty() 方法,语法格式如下:

>db.collection.find().pretty()

pretty() 方法以格式化的方式来显示所有文档。

无条件查询

实例

以下实例我们查询了集合 stus 中的数据:

> db.stus.find().pretty()

除了 find() 方法之外,还有一个 findOne() 方法,它只返回一个文档。

db.collection.findOne()
	用来查询集合中符合条件的第一个文档  
	findOne()返回的是一个文档对象 

条件查询

为了方便演示,插入如下文档;

db.stus.insert([
    {
        name: '铁扇公主', 
        age: 18,
        address: '芭蕉洞',
        hobby: ['煽风点火','喝酒'],
        weapon: '芭蕉扇'
    },
    {
        name: '牛魔王', 
        age: 36,
        address: '芭蕉洞',
        hobby: ['打呼噜','喝酒'],
        weapon: '混铁棍'
    },
    {
        name: '红孩儿', 
        age: 6,
        address: '芭蕉洞',
        hobby: ['玩火','尿床'],
        weapon: '火尖枪'
    },
    {
        name: '金角大王', 
        age: 45,
        address: '莲花洞',
        hobby: ['炼丹','喝酒'],
        weapon: '紫金红葫芦'
    },
    {
        name: '银角大王', 
        age: 42,
        address: '莲花洞',
        hobby: ['炼丹','喝酒'],
        weapon: '羊脂玉净瓶'
    }
])

比较条件查询

如果你熟悉常规的 SQL 数据,通过下表可以更好的理解 MongoDB 的条件语句查询:

操作 格式 范例 RDBMS中的类似语句
等于 {:} db.stus.find({"name":"孙悟空"}).pretty() where name = '孙悟空'
小于 {:{$lt:}} db.stus.find({"age":{$lt:50}}).pretty() where age < 50
小于或等于 {:{$lte:}} db.stus.find({"age":{$lte:50}}).pretty() where age <= 50
大于 {:{$gt:}} db.stus.find({"age":{$gt:50}}).pretty() where age > 50
大于或等于 {:{$gte:}} db.stus.find({"age":{$gte:50}}).pretty() where age >= 50
不等于 {:{$ne:}} db.stus.find({"age":{$ne:50}}).pretty() where age != 50
实例
# 查询stus集合中姓名为“孙悟空”的文档 (等于)
> db.stus.find({name:"孙悟空"})

# 查询stus集合中年龄大于等于25的文档  (大于等于)
> db.stus.find({age:{$gte:25}})

# 查询stus集合中年龄小于等于24的文档  (小于等于)
> db.stus.find({age:{$lte:24}})

# 查询stus集合中年龄不等于28的文档 (不等于)
> db.stus.find({age:{$ne:28}})

逻辑条件查询

AND 条件实例

MongoDB 的 find() 方法可以传入多个键(key),每个键(key)以逗号隔开,即常规 SQL 的 AND 条件。

语法格式如下:

>db.col.find({key1:value1, key2:value2}).pretty()

以下实例通过 addressage 键来查询数据

# 查询stus集合中address为“芭蕉洞”并且age大于等于18的文档
> db.stus.find({"address":"芭蕉洞", "age":{$gte:18}}).pretty()
{ 
    "_id" : ObjectId("5f6208fcfec6e0ff092e2497"), 
    "name" : "铁扇公主", 
    "age" : 18.0, 
    "address" : "芭蕉洞", 
    "hobby" : [
        "煽风点火", 
        "喝酒"
    ], 
    "weapon" : "芭蕉扇"
}
{ 
    "_id" : ObjectId("5f6208fcfec6e0ff092e2498"), 
    "name" : "牛魔王", 
    "age" : 36.0, 
    "address" : "芭蕉洞", 
    "hobby" : [
        "打呼噜", 
        "喝酒"
    ], 
    "weapon" : "混铁棍"
}

以上实例中类似于 WHERE 语句:WHERE address=‘芭蕉洞’ AND age=18

如果你想获取"stus"集合中 “age” 大于18,小于 40 的数据,你可以使用以下命令:

db.stus.find({age : {$lt :40, $gt : 18}})
OR 条件实例

MongoDB OR 条件语句使用了关键字 $or,语法格式如下:

>db.col.find(
   {
      $or: [
         {key1: value1}, {key2:value2}
      ]
   }
).pretty()

以下实例中,我们演示了查询键 address 值为 芭蕉洞 或键 age 值为 28 的文档。

>db.stus.find(
   {
      $or: [
         {address: "芭蕉洞"}, {age:28}
      ]
   }
).pretty()

{ 
    "_id" : ObjectId("5f61da75c1f46921b9662b2e"), 
    "name" : "孙悟空", 
    "age" : 28.0, 
    "address" : "花果山", 
    "hobby" : [
        "蟠桃", 
        "拔毛", 
        "打妖怪"
    ], 
    "weapon" : "如意金箍棒"
}
{ 
    "_id" : ObjectId("5f6208fcfec6e0ff092e2497"), 
    "name" : "铁扇公主", 
    "age" : 18.0, 
    "address" : "芭蕉洞", 
    "hobby" : [
        "煽风点火", 
        "喝酒"
    ], 
    "weapon" : "芭蕉扇"
}
{ 
    "_id" : ObjectId("5f6208fcfec6e0ff092e2498"), 
    "name" : "牛魔王", 
    "age" : 36.0, 
    "address" : "芭蕉洞", 
    "hobby" : [
        "打呼噜", 
        "喝酒"
    ], 
    "weapon" : "混铁棍"
}
{ 
    "_id" : ObjectId("5f6208fcfec6e0ff092e2499"), 
    "name" : "红孩儿", 
    "age" : 6.0, 
    "address" : "芭蕉洞", 
    "hobby" : [
        "玩火", 
        "尿床"
    ], 
    "weapon" : "火尖枪"
}
AND 和 OR 联合使用实例

以下实例演示了 AND 和 OR 联合使用

> db.stus.find({
    "age": {$gt:20}, 
    $or: [
        {"address": "芭蕉洞"},
        {"weapon": "如意金箍棒"}
    ]
})

{ 
    "_id" : ObjectId("5f61da75c1f46921b9662b2e"), 
    "name" : "孙悟空", 
    "age" : 28.0, 
    "address" : "花果山", 
    "hobby" : [
        "蟠桃", 
        "拔毛", 
        "打妖怪"
    ], 
    "weapon" : "如意金箍棒"
}
{ 
    "_id" : ObjectId("5f6208fcfec6e0ff092e2498"), 
    "name" : "牛魔王", 
    "age" : 36.0, 
    "address" : "芭蕉洞", 
    "hobby" : [
        "打呼噜", 
        "喝酒"
    ], 
    "weapon" : "混铁棍"
}

查询总数量

> db.stus.find({}).count();

$type 操作符

$type操作符是基于BSON类型来检索集合中匹配的数据类型,并返回结果。

MongoDB 中可以使用的类型如下表所示:

类型 数字 备注
Double 1
String 2
Object 3
Array 4
Binary data 5
Undefined 6 已废弃。
Object id 7
Boolean 8
Date 9
Null 10
Regular Expression 11
JavaScript 13
Symbol 14
JavaScript (with scope) 15
32-bit integer 16
Timestamp 17
64-bit integer 18
Min key 255 Query with -1.
Max key 127

如果想获取 “stus” 集合中 name为 String 的数据,你可以使用以下命令:

db.stus.find({"name" : {$type : 2}})
或
db.stus.find({"name" : {$type : 'string'}})

Limit与Skip方法

Limit() 方法

如果你需要在MongoDB中读取指定数量的数据记录,可以使用MongoDB的Limit方法,limit()方法接受一个数字参数,该参数指定从MongoDB中读取的记录条数。

limit()方法基本语法如下所示:

>db.COLLECTION_NAME.find().limit(NUMBER)

以下实例为显示查询文档中的两条记录:

db.stus.find().limit(2)

注:如果你们没有指定limit()方法中的参数则显示集合中的所有数据。

Skip() 方法

我们除了可以使用limit()方法来读取指定数量的数据外,还可以使用skip()方法来跳过指定数量的数据,skip方法同样接受一个数字参数作为跳过的记录条数。

skip() 方法脚本语法格式如下:

>db.COLLECTION_NAME.find().limit(NUMBER).skip(NUMBER)

以下实例只会显示第二条文档数据

>db.col.find().limit(1).skip(1)

 分页 每页显示10条
	1-10     0
	11-20    10
	21-30    20
	...
skip((页码-1) * 每页显示的条数).limit(每页显示的条数);
	skip()用于跳过指定数量的数据    
 	MongoDB会自动调整skip和limit的位置

sort() 方法

在 MongoDB 中使用 sort() 方法对数据进行排序,sort() 方法可以通过参数指定排序的字段,并使用 1 和 -1 来指定排序的方式,其中 1 为升序排列,而 -1 是用于降序排列。

sort()方法基本语法如下所示:

>db.COLLECTION_NAME.find().sort({KEY:1})

以下实例演示了 stus 集合中的数据按字段 age 的降序排列:

> db.stus.find().sort({"age":-1})

投影

在查询时,可以在第二个参数的位置来设置查询结果的 投影

# 对于想显示的字段,可以设置为1,_id默认必显示,我们可以设置为0隐藏
db.emp.find({},{ename:1 , _id:0 , sal:1});

更新文档

MongoDB 使用 update()save() 方法来更新集合中的文档。接下来让我们详细来看下两个函数的应用及其区别。

update 方法

语法

update() 方法用于更新已存在的文档。语法格式如下:

db.collection.update(
   <query>,
   <update>,
   {
     upsert: <boolean>,
     multi: <boolean>,
     writeConcern: <document>
   }
)

参数说明:

  • query : update的查询条件,类似sql update查询内where后面的。
  • update : update的对象和一些更新的操作符(如 , , ,inc…)等,也可以理解为sql update查询内set后面的
  • upsert : 可选,这个参数的意思是,如果不存在update的记录,是否插入objNew,true为插入,默认是false,不插入。
  • multi : 可选,mongodb 默认是false,只更新找到的第一条记录,如果这个参数为true,就把按条件查出来多条记录全部更新。
  • writeConcern :可选,抛出异常的级别。

实例

我们通过 update() 方法来更新姓名(name):

# 更新孙悟空的年龄为555
db.stus.update({name:"孙悟空"},{age:555});
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })

# 再去查看孙悟空文档
{ 
    "_id" : ObjectId("5f61da75c1f46921b9662b2e"), 
    "age" : 555.0
}

# update()默认情况下会使用新对象来替换旧的对象

如果需要修改指定的属性,而不是替换需要使用“修改操作符”来完成修改

​ $set 可以用来修改文档中的指定属性 (修改指定属性的值;如果属性不存在则新增该属性)

​ $unset 可以用来删除文档的指定属性

​ update()默认只会修改一个

i n c 、 inc、 incpush、$addToSet 我们在后续的练习讲解

# 根据id修改沙和尚的地址和新增一个gender字段
> db.stus.update(
    {"_id" : ObjectId("5f61df3ec1f46921b9662b30")},
    {$set:{
        gender:"男",
        address:"流沙河河底"
    }}    
)
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })

# 根据id删除猪八戒的hobby字段
db.stus.update(
    {"_id" : ObjectId("5f61dd19c1f46921b9662b2f")},
    {$unset:{
        hobby:1
    }}    
)
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })

# 把年龄超过40的文档添加old:true键值
db.stus.updateMany(
    {"age": {$gt:40}},
    {
        $set:{
            old:true
        }
    }    
)
    
db.stus.update(
    {"age": {$gt:40}},
    {
        $set:{
            old:true
        }
    },
    {
        multi:true
    }    
)

其他方法

db.collection.updateMany()	- 同时修改多个符合条件的文档
   
db.collection.updateOne()	- 修改一个符合条件的文档    
        
db.collection.replaceOne()	- 替换一个文档

save() 方法

语法

save() 方法通过传入的文档来替换已有文档,_id 主键存在就更新,不存在就插入。语法格式如下:

db.collection.save(
   <document>,
   {
     writeConcern: <document>
   }
)

参数说明:

  • document : 文档数据。
  • writeConcern :可选,抛出异常的级别。

实例

以下实例中我们替换了 _id 为 5f61da75c1f46921b9662b2e的文档数据:

> db.stus.save({
	_id: ObjectId("5f61da75c1f46921b9662b2e"),
	name: '孙悟空', 
    age: 28,
    address: '花果山',
    hobby: ['蟠桃', '拔毛', '打妖怪'],
    weapon: '如意金箍棒'
})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })

更多实例

只更新第一条记录:

db.col.update( { "count" : { $gt : 1 } } , { $set : { "test2" : "OK"} } );

全部更新:

db.col.update( { "count" : { $gt : 3 } } , { $set : { "test2" : "OK"} },false,true );

只添加第一条:

db.col.update( { "count" : { $gt : 4 } } , { $set : { "test5" : "OK"} },true,false );

全部添加进去:

db.col.update( { "count" : { $gt : 5 } } , { $set : { "test5" : "OK"} },true,true );

全部更新:

db.col.update( { "count" : { $gt : 15 } } , { $inc : { "count" : 1} },false,true );

只更新第一条记录:

db.col.update( { "count" : { $gt : 10 } } , { $inc : { "count" : 1} },false,false );

删除文档

MongoDB remove()函数是用来移除集合中的数据。

MongoDB数据更新可以使用update()函数。在执行remove()函数前先执行find()命令来判断执行的条件是否正确,这是一个比较好的习惯。

语法

remove() 方法的基本语法格式如下所示:

db.collection.remove(
   <query>,
   <justOne>
)

如果你的 MongoDB 是 2.6 版本以后的,语法格式如下:

db.collection.remove(
   <query>,
   {
     justOne: <boolean>,
     writeConcern: <document>
   }
)

参数说明:

  • query :(可选)删除的文档的条件。
  • justOne : (可选)如果设为 true 或 1,则只删除一个文档,如果不设置该参数,或使用默认值 false,则删除所有匹配条件的文档。
  • writeConcern :(可选)抛出异常的级别。

实例

接下来我们移除 age 大于 28 的文档

> db.stus.remove({age:{$gt:28}})
WriteResult({ "nRemoved" : 4 })

如果你只想删除第一条找到的记录可以设置 justOne 为 1,如下所示:

>db.COLLECTION_NAME.remove(DELETION_CRITERIA,1)

如果你想删除所有数据,可以使用以下方式(类似常规 SQL 的 truncate 命令):

# 慎重
db.col.remove({})
# 或直接删除集合
db.collection.drop() 

其他方法

db.collection.deleteOne()	只删除一个文档

db.collection.deleteMany()	删除多个文档

非删除

在实际应用中,数据一般不做删除,而是定义一个字段来标识文档的状态

> db.stus.insert([
	{
        name: '青毛狮王', 
        age: 34,
        address: '狮驼岭',
        isDel: 0
    },
    {
        name: '黄牙象王', 
        age: 32,
        address: '狮驼岭',
        isDel: 0
    },
    {
        name: '云程万里鹏', 
        age: 30,
        address: '狮驼岭',
        isDel: 0
    }
]);
BulkWriteResult({
	"writeErrors" : [ ],
	"writeConcernErrors" : [ ],
	"nInserted" : 3,
	"nUpserted" : 0,
	"nMatched" : 0,
	"nModified" : 0,
	"nRemoved" : 0,
	"upserted" : [ ]
})

# 标识name: '云程万里鹏'为删除状态
db.stus.updateOne({name:"云程万里鹏"},{$set:{isDel:1}});
{ 
    "acknowledged" : true, 
    "matchedCount" : 1.0, 
    "modifiedCount" : 1.0
}
    
db.stus.find({isDel:0})   

索引

索引通常能够极大的提高查询的效率,如果没有索引,MongoDB在读取数据时必须扫描集合中的每个文件并选取那些符合查询条件的记录。

这种扫描全集合的查询效率是非常低的,特别在处理大量的数据时,查询可以要花费几十秒甚至几分钟,这对网站的性能是非常致命的。

索引是特殊的数据结构,索引存储在一个易于遍历读取的数据集合中,索引是对数据库表中一列或多列的值进行排序的一种结构


createIndex() 方法

MongoDB使用 createIndex() 方法来创建索引。

注意在 3.0.0 版本前创建索引方法为 db.collection.ensureIndex(),之后的版本使用了 db.collection.createIndex() 方法,ensureIndex() 还能用,但只是 createIndex() 的别名。

语法

createIndex()方法基本语法格式如下所示:

>db.collection.createIndex(keys, options)

语法中 Key 值为你要创建的索引字段,1 为指定按升序创建索引,如果你想按降序来创建索引指定为 -1 即可。

实例

>db.stus.createIndex({"name":1})
>

createIndex() 方法中你也可以设置使用多个字段创建索引(关系型数据库中称作复合索引)。

>db.stus.createIndex({"name":1,"age":-1})
>

createIndex() 接收可选参数,可选参数列表如下:

Parameter Type Description
background Boolean 建索引过程会阻塞其它数据库操作,background可指定以后台方式创建索引,即增加 “background” 可选参数。 “background” 默认值为false
unique Boolean 建立的索引是否唯一。指定为true创建唯一索引。默认值为false.
name string 索引的名称。如果未指定,MongoDB的通过连接索引的字段名和排序顺序生成一个索引名称。
dropDups Boolean 3.0+版本已废弃。在建立唯一索引时是否删除重复记录,指定 true 创建唯一索引。默认值为 false.
sparse Boolean 对文档中不存在的字段数据不启用索引;这个参数需要特别注意,如果设置为true的话,在索引字段中不会查询出不包含对应字段的文档.。默认值为 false.
expireAfterSeconds integer 指定一个以秒为单位的数值,完成 TTL设定,设定集合的生存时间。
v index version 索引的版本号。默认的索引版本取决于mongod创建索引时运行的版本。
weights document 索引权重值,数值在 1 到 99,999 之间,表示该索引相对于其他索引字段的得分权重。
default_language string 对于文本索引,该参数决定了停用词及词干和词器的规则的列表。 默认为英语
language_override string 对于文本索引,该参数指定了包含在文档中的字段名,语言覆盖默认的language,默认值为 language.

在后台创建索引:

db.values.createIndex({open: 1, close: 1}, {background: true})

文档关系

文档之间的关系:

​ 一对一(one to one)

​ 夫妻 (一个丈夫 对应 一个妻子)

​ 在MongoDB,可以通过内嵌文档的形式来体现出一对一的关系

db.wifeAndHusband.insert([
    {
        name:"黄蓉",
        husband:{
            name:"郭靖"
        }
    },{
        name:"小龙女",
        husband:{
            name:"杨过"
        }
    }

]);

db.wifeAndHusband.find();

​ 一对多(one to many)/多对一(many to one)

​ 父母 - 孩子

​ 用户 - 订单

​ 文章 - 评论

​ 在MongoDB,也可以通过内嵌文档来映射一对多的关系

db.users.insert([
	{
    	username:"swk"
    },
    {
    	username:"zbj"
	}
]);

db.order.insert({
    list:["牛肉","漫画"],
    user_id: ObjectId("59c47e35241d8d36a1d50de0")
});

db.users.find()
db.order.find()

//查找用户zbj的订单
var user_id = db.users.findOne({username:"zbj"})._id;
db.order.find({user_id:user_id});

​ 多对多(many to many)

​ 分类 - 商品

​ 老师 - 学生

db.teachers.insert([
    {name:"洪七公"},
    {name:"黄药师"},
    {name:"龟仙人"}
]);

db.stus.insert([
    {
        name:"郭靖",
        tech_ids:[
            ObjectId("59c4806d241d8d36a1d50de4"),
            ObjectId("59c4806d241d8d36a1d50de5")
        ]
    },{
        name:"孙悟空",
        tech_ids:[
            ObjectId("59c4806d241d8d36a1d50de4"),
            ObjectId("59c4806d241d8d36a1d50de5"),
            ObjectId("59c4806d241d8d36a1d50de6")
        ]
    }
])

db.teachers.find()

db.stus.find()

练习

//1.进入my_test数据库
use my_test

//2.向数据库的user集合中插入一个文档  
db.users.insert({
    username:"sunwukong"
});

//3.查询user集合中的文档
db.users.find();

//4.向数据库的user集合中插入一个文档   
db.users.insert({
    username:"zhubajie"
});
   
//5.查询数据库user集合中的文档
db.users.find();

//6.统计数据库user集合中的文档数量
db.users.find().count();

//7.查询数据库user集合中username为sunwukong的文档
db.users.find({username:"sunwukong"});

//8.向数据库user集合中的username为sunwukong的文档,添加一个address属性,属性值为huaguoshan
db.users.update({username:"sunwukong"},{$set:{address:"huaguoshan"}});


//9.使用{username:"tangseng"} 替换 username 为 zhubajie的文档
db.users.replaceOne({username:"zhubajie"},{username:"tangseng"});    
    
//10.删除username为sunwukong的文档的address属性
db.users.update({username:"sunwukong"},{$unset:{address:1}});


//11.向username为sunwukong的文档中,添加一个hobby:{cities:["beijing","shanghai","shenzhen"] , movies:["sanguo","hero"]}
//MongoDB的文档的属性值也可以是一个文档,当一个文档的属性值是一个文档时,我们称这个文档叫做 内嵌文档
db.users.update({username:"sunwukong"},{$set:{hobby:{cities:["beijing","shanghai","shenzhen"] , movies:["sanguo","hero"]}}});
db.users.find();

//12.向username为tangseng的文档中,添加一个hobby:{movies:["A Chinese Odyssey","King of comedy"]}
db.users.update({username:"tangseng"},{$set:{hobby:{movies:["A Chinese Odyssey","King of comedy"]}}})

//13.查询喜欢电影hero的文档
//MongoDB支持直接通过内嵌文档的属性进行查询,如果要查询内嵌文档则可以通过.的形式来匹配
//如果要通过内嵌文档来对文档进行查询,此时属性名必须使用引号 
db.users.find({'hobby.movies':"hero"});

//14.向tangseng中添加一个新的电影Interstellar
//$push 用于向数组中添加一个新的元素
//$addToSet 向数组中添加一个新元素 , 如果数组中已经存在了该元素,则不会添加
db.users.update({username:"tangseng"},{$push:{"hobby.movies":"Interstellar"}});
db.users.update({username:"tangseng"},{$addToSet:{"hobby.movies":"Interstellar"}});
db.users.find();

//15.删除喜欢beijing的用户
db.users.remove({"hobby.cities":"beijing"});

//16.删除user集合
db.users.remove({});
db.users.drop();
show dbs;

//17.向numbers中插入20000条数据 (很久)
for(var i=1 ; i<=20000 ; i++){
    db.numbers.insert({num:i});
}
db.numbers.find()
db.numbers.remove({});

//0.4s
var arr = [];
for(var i=1 ; i<=20000 ; i++){
    arr.push({num:i});
}
db.numbers.insert(arr);

for(var i=1 ; i<=20000 ; i++){
    db.numbers.insert({num:i});
}
db.numbers.find()

//18.查询numbers中num为500的文档
db.numbers.find({num:500})

//19.查询numbers中num大于5000的文档
db.numbers.find({num:{$gt:500}});
db.numbers.find({num:{$eq:500}});

//20.查询numbers中num小于30的文档
db.numbers.find({num:{$lt:30}});

//21.查询numbers中num大于40小于50的文档
db.numbers.find({num:{$gt:40 , $lt:50}});

//22.查询numbers中num大于19996的文档
db.numbers.find({num:{$gt:19996}});

//23.查看numbers集合中的前10条数据
db.numbers.find({num:{$lte:10}});

//limit()设置显示数据的上限
db.numbers.find().limit(10);
//在开发时,我们绝对不会执行不带条件的查询
db.numbers.find();

//24.查看numbers集合中的第11条到20条数据
/*
    分页 每页显示10条
        1-10     0
        11-20    10
        21-30    20
        。。。
        
        skip((页码-1) * 每页显示的条数).limit(每页显示的条数);
        
    skip()用于跳过指定数量的数据    
    
    MongoDB会自动调整skip和limit的位置
*/
db.numbers.find().skip(10).limit(10);

//25.查看numbers集合中的第21条到30条数据
db.numbers.find().skip(20).limit(10);
db.numbers.find().limit(10).skip(10);

//26.将dept和emp集合导入到数据库中

//26.将dept和emp集合导入到数据库中
db.dept.find()
db.emp.find()

//27.查询工资小于2000的员工
db.emp.find({sal:{$lt:2000}});

//28.查询工资在1000-2000之间的员工
db.emp.find({sal:{$lt:2000 , $gt:1000}});

//29.查询工资小于1000或大于2500的员工
db.emp.find({$or:[{sal:{$lt:1000}} , {sal:{$gt:2500}}]});

//30.查询财务部的所有员工
//(depno)
var depno = db.dept.findOne({dname:"财务部"}).deptno;
db.emp.find({depno:depno});

//31.查询销售部的所有员工
var depno = db.dept.findOne({dname:"销售部"}).deptno;
db.emp.find({depno:depno});

//32.查询所有mgr为7698的所有员工
db.emp.find({mgr:7698})

//33.为所有薪资低于1000的员工增加工资400元
db.emp.updateMany({sal:{$lte:1000}} , {$inc:{sal:400}});
db.emp.find()

//34.查询所有员工,按照工资升序排列,如果工资相同,则按照员工号降序排列
//查询文档时,默认情况是按照_id的值进行排列(升序)
//sort()可以用来指定文档的排序的规则,sort()需要传递一个对象来指定排序规则 1表示升序 -1表示降序
//limit skip sort 可以以任意的顺序进行调用
db.emp.find({}).sort({sal:1,empno:-1});

03_MongoDB 结合Node

Node.js 连接 MongoDB

MongoDB是一种文档导向数据库管理系统,由C++撰写而成。

安装驱动

$ npm install mongodb
或
$ cnpm install mongodb

接下来我们来实现增删改查功能。

创建数据库

var MongoClient = require('mongodb').MongoClient;
var url = "mongodb://127.0.0.1:27017/newcapec";

// 通过设置第二个参数,避免“当前URL字符串解析器已被弃用”警告
MongoClient.connect(url, {
    useNewUrlParser: true,
    useUnifiedTopology: true
}, function (err, db) {
    if (err) {
        throw err
    } else {
        console.log("数据库已创建!");
        db.close();
    }
});

创建集合

我们可以使用 createCollection() 方法来创建集合:

var MongoClient = require('mongodb').MongoClient;
var url = 'mongodb://127.0.0.1:27017/newcapec';

MongoClient.connect(url, {
    useNewUrlParser: true,
    useUnifiedTopology: true
}, function (err, db) {
    if (err) {
        throw err
    } else {
        console.log('数据库已创建');
        // 使用newcapec数据库
        var dbase = db.db("newcapec");
        // 创建集合
        dbase.createCollection('users', function (err, res) {
            if (err) throw err;
            console.log("创建集合!");
            db.close();
        });
    }

});

数据库操作( CURD )

与 MySQL 不同的是 MongoDB 会自动创建数据库和集合,所以使用前我们不需要手动去创建。

插入数据

以下实例我们连接数据库 newcapec的 site 表,并插入一条数据条数据,使用 insertOne():

var MongoClient = require('mongodb').MongoClient;
var url = "mongodb://127.0.0.1:27017/";

MongoClient.connect(url, {
    useNewUrlParser: true,
    useUnifiedTopology: true
}, function (err, db) {
    if (err) throw err;
    var dbo = db.db("newcapec");
    var myobj = {
        name: '孙悟空',
        age: 28,
        address: '花果山',
        hobby: ['蟠桃', '拔毛', '打妖怪'],
        weapon: '如意金箍棒'
    };
    dbo.collection("users").insertOne(myobj, function (err, res) {
        if (err) throw err;
        // 通过res.insertedCount可以获取插入的数量
        console.log("文档插入成功", res);
        db.close();
    });
});

{
	name: '猪八戒', 
    age: 26,
    address: '高老庄',
    hobby: ['包子', '睡觉', '看美女'],
    weapon: '九齿钉耙'
}

如果要插入多条数据可以使用 insertMany():

var MongoClient = require('mongodb').MongoClient;
var url = "mongodb://127.0.0.1:27017/";

MongoClient.connect(url, {
    useNewUrlParser: true,
    useUnifiedTopology: true
}, function (err, db) {
    if (err) throw err;
    var dbo = db.db("newcapec");
    var myobj = [{
            name: '沙僧',
            age: 20,
            address: '流沙河',
            hobby: ['刮痧', '呼救'],
            weapon: '降妖宝杖'
        },
        {
            name: '唐僧',
            age: 30,
            address: '东土大唐',
            hobby: ['念经'],
            weapon: '禅杖'
        },
        {
            name: '铁扇公主',
            age: 18,
            address: '芭蕉洞',
            hobby: ['煽风点火', '喝酒'],
            weapon: '芭蕉扇'
        },
        {
            name: '牛魔王',
            age: 36,
            address: '芭蕉洞',
            hobby: ['打呼噜', '喝酒'],
            weapon: '混铁棍'
        },
        {
            name: '红孩儿',
            age: 6,
            address: '芭蕉洞',
            hobby: ['玩火', '尿床'],
            weapon: '火尖枪'
        },
        {
            name: '金角大王',
            age: 45,
            address: '莲花洞',
            hobby: ['炼丹', '喝酒'],
            weapon: '紫金红葫芦'
        },
        {
            name: '银角大王',
            age: 42,
            address: '莲花洞',
            hobby: ['炼丹', '喝酒'],
            weapon: '羊脂玉净瓶'
        }
    ];
    dbo.collection("users").insertMany(myobj, function (err, res) {
        if (err) throw err;
        console.log("插入的文档数量为: " + res.insertedCount);
        db.close();
    });
});

查询数据

可以使用 find() 来查找数据, find() 可以返回匹配条件的所有数据。 如果未指定条件,find() 返回集合中的所有数据。

var MongoClient = require('mongodb').MongoClient;
var url = "mongodb://127.0.0.1:27017/";

MongoClient.connect(url, {
    useNewUrlParser: true,
    useUnifiedTopology: true
}, function (err, db) {
    if (err) throw err;
    var dbo = db.db("newcapec");
    // 返回集合中所有数据
    dbo.collection("users").find({}).toArray(function (err, result) {
        if (err) throw err;
        console.log(result);
        db.close();
    });
});

以下实例检索 name 为 “孙悟空” 的实例:

var MongoClient = require('mongodb').MongoClient;
var url = "mongodb://127.0.0.1:27017/";

MongoClient.connect(url, {
    useNewUrlParser: true,
    useUnifiedTopology: true
}, function (err, db) {
    if (err) throw err;
    var dbo = db.db("newcapec");
    // 查询条件
    var whereStr = {
        "name": '孙悟空'
    };
    dbo.collection("users").find(whereStr).toArray(function (err, result) {
        if (err) throw err;
        console.log(result);
        db.close();
    });
});

以下实例检索 _id 为 “5f635b381175012f20bd9518” 的实例:

const {
    ObjectId
} = require('mongodb');

var MongoClient = require('mongodb').MongoClient;
var url = "mongodb://127.0.0.1:27017/";

MongoClient.connect(url, {
    useNewUrlParser: true,
    useUnifiedTopology: true
}, function (err, db) {
    if (err) throw err;
    var dbo = db.db("newcapec");
    // 查询条件
    var whereStr = {
        "_id": ObjectId('5f635b381175012f20bd9518')
    };
    dbo.collection("users").find(whereStr).toArray(function (err, result) {
        if (err) throw err;
        console.log(result);
        db.close();
    });
});

以下实例检索 hobby包含 为 “喝酒” 的实例:

const {
    ObjectId
} = require('mongodb');

var MongoClient = require('mongodb').MongoClient;
var url = "mongodb://127.0.0.1:27017/";

MongoClient.connect(url, {
    useNewUrlParser: true,
    useUnifiedTopology: true
}, function (err, db) {
    if (err) throw err;
    var dbo = db.db("newcapec");
    // 查询条件
    var whereStr = {
        hobby: '喝酒'
    };
    dbo.collection("users").find(whereStr).toArray(function (err, result) {
        if (err) throw err;
        console.log(result);
        db.close();
    });
});

更新数据

我们也可以对数据库的数据进行修改,以下实例将 name 为 “孙悟空” 的 address 改为 西天灵山:

var MongoClient = require('mongodb').MongoClient;
var url = "mongodb://127.0.0.1:27017/";

MongoClient.connect(url, {
    useNewUrlParser: true,
    useUnifiedTopology: true
}, function (err, db) {
    if (err) throw err;
    var dbo = db.db("newcapec");
    // 查询条件
    var whereStr = {
        "name": '孙悟空'
    };
    var updateStr = {
        $set: {
            "address": "芭蕉洞"
        }
    };
    dbo.collection("users").updateOne(whereStr, updateStr, function (err, res) {
        if (err) throw err;
        console.log("文档更新成功", res.result.nModified);
        db.close();
    });
});

如果要更新所有符合条的文档数据可以使用 updateMany():

var MongoClient = require('mongodb').MongoClient;
var url = "mongodb://127.0.0.1:27017/";

MongoClient.connect(url, {
    useNewUrlParser: true,
    useUnifiedTopology: true
}, function (err, db) {
    if (err) throw err;
    var dbo = db.db("newcapec");
    var whereStr = {
        "address": '芭蕉洞'
    }; // 查询条件
    var updateStr = {
        $set: {
            "type": 1,
            "group": "一家人"
        }
    };
    dbo.collection("users").updateMany(whereStr, updateStr, function (err, res) {
        if (err) throw err;
        console.log(res.result.nModified + " 条文档被更新");
        db.close();
    });
});

result.nModified 为更新的条数。

删除数据

以下实例将 name 为 “孙悟空” 的数据删除 :

var MongoClient = require('mongodb').MongoClient;
var url = "mongodb://127.0.0.1:27017/";

MongoClient.connect(url, {
    useNewUrlParser: true,
    useUnifiedTopology: true
}, function (err, db) {
    if (err) throw err;
    var dbo = db.db("newcapec");
    // 查询条件
    var whereStr = {
        "name": '孙悟空'
    };
    dbo.collection("users").deleteOne(whereStr, function (err, res) {
        if (err) throw err;
        console.log("文档删除成功", res.result.n);
        db.close();
    });
});

如果要删除多条语句可以使用 deleteMany() 方法

以下实例将 type 为 1 的所有数据删除 :

var MongoClient = require('mongodb').MongoClient;
var url = "mongodb://127.0.0.1:27017/";

MongoClient.connect(url, {
    useNewUrlParser: true,
    useUnifiedTopology: true
}, function (err, db) {
    if (err) throw err;
    var dbo = db.db("newcapec");
    // 查询条件
    var whereStr = {
        type: 1
    };
    dbo.collection("users").deleteMany(whereStr, function (err, res) {
        if (err) throw err;
        console.log(res.result.n + " 条文档被删除");
        db.close();
    });
});

排序

排序 使用 sort() 方法,该方法接受一个参数,规定是升序(1)还是降序(-1)。

例如:

{ type: 1 }  // 按 type 字段升序
{ type: -1 } // 按 type 字段降序

按 type 升序排列:

var MongoClient = require('mongodb').MongoClient;
var url = "mongodb://127.0.0.1:27017/";

MongoClient.connect(url, {
    useNewUrlParser: true,
    useUnifiedTopology: true
}, function (err, db) {
    if (err) throw err;
    var dbo = db.db("newcapec");
    var mysort = {
        age: 1
    };
    dbo.collection("users").find().sort(mysort).toArray(function (err, result) {
        if (err) throw err;
        console.log(result);
        db.close();
    });
});

查询分页

如果要设置指定的返回条数可以使用 limit() 方法,该方法只接受一个参数,指定了返回的条数。

var MongoClient = require('mongodb').MongoClient;
var url = "mongodb://localhost:27017/";

MongoClient.connect(url, {
    useNewUrlParser: true,
    useUnifiedTopology: true
}, function (err, db) {
    if (err) throw err;
    var dbo = db.db("newcapec");
    dbo.collection("users").find().limit(2).toArray(function (err, result) {
        if (err) throw err;
        console.log(result);
        db.close();
    });
});

如果要指定跳过的条数,可以使用 skip() 方法。

var MongoClient = require('mongodb').MongoClient;
var url = "mongodb://localhost:27017/";

MongoClient.connect(url, {
    useNewUrlParser: true,
    useUnifiedTopology: true
}, function (err, db) {
    if (err) throw err;
    var dbo = db.db("newcapec");
    dbo.collection("users").find().skip(2).limit(2).toArray(function (err, result) {
        if (err) throw err;
        console.log(result);
        db.close();
    });
});

删除集合

我们可以使用 drop() 方法来删除集合:

var MongoClient = require('mongodb').MongoClient;
var url = "mongodb://localhost:27017/";

MongoClient.connect(url, {
    useNewUrlParser: true,
    useUnifiedTopology: true
}, function (err, db) {
    if (err) throw err;
    var dbo = db.db("newcapec");
    // 删除 test 集合
    dbo.collection("users").drop(function (err, delOK) { // 执行成功 delOK 返回 true,否则返回 false
        if (err) throw err;
        if (delOK) console.log("集合已删除");
        db.close();
    });
});

论坛案例

数据

use newcapec

db.users.find()

db.classesMods.insert([
    {
		"_id" : ObjectId("5f6453b42e454d7dde560660"), 
        moduleName: "UI39班"
    },
    {
		"_id" : ObjectId("5f6453b42e454d7dde560661"), 
        moduleName: "JAVA40班"
    },
    {
		"_id" : ObjectId("5f6453b42e454d7dde560662"), 
        moduleName: "前端41班"
    },
    {
		"_id" : ObjectId("5f6453b42e454d7dde560663"), 
        moduleName: "运维42班"
    }
])


db.populars.insert([
    { 
    "_id" : ObjectId("5f64530c2e454d7dde560654"), 
    "topicName" : "网站广告图", 
    "topicDes" : "提供宣传广告图专业设计", 
    "topicContent" : "....", 
	"count":0,
    "classesId" : "5f6453b42e454d7dde560660"
},
{ 
    "_id" : ObjectId("5f64530c2e454d7dde560655"), 
    "topicName" : "LOGO设计", 
    "topicDes" : "为企业定制专属标志形象", 
    "topicContent" : "....", 
	"count":0,
    "classesId" : "5f6453b42e454d7dde560660"
},
{ 
    "_id" : ObjectId("5f64530c2e454d7dde560656"), 
    "topicName" : "企业宣传册", 
    "topicDes" : "手册、三折页、统一风格设计", 
    "topicContent" : "....", 
	"count":0,
    "classesId" : "5f6453b42e454d7dde560660"
},
{ 
    "_id" : ObjectId("5f64530c2e454d7dde560657"), 
    "topicName" : "营销PPT", 
    "topicDes" : "招商、路演、品牌宣传PPT", 
    "topicContent" : "....", 
	"count":0,
    "classesId" : "5f6453b42e454d7dde560660"
},
{ 
    "_id" : ObjectId("5f64530c2e454d7dde560658"), 
    "topicName" : "广告策划", 
    "topicDes" : "Good Ideas", 
    "topicContent" : "....", 
	"count":0,
    "classesId" : "5f6453b42e454d7dde560660"
},
{ 
    "_id" : ObjectId("5f64530c2e454d7dde560659"), 
    "topicName" : "闪亮创意", 
    "topicDes" : "Good 好主意", 
    "topicContent" : "....", 
	"count":0,
    "classesId" : "5f6453b42e454d7dde560660"
}
])


db.populars.insert([
    {
        topicName: "JVM底层原理解析",
        topicDes: "深入全面理解JVM",
        topicContent: "....",
		"count":0,
        classesId:"5f6453b42e454d7dde560661"
    },
    {
        topicName: "数据库底层解析",
        topicDes: "理解BTree,B+Tree,红黑树",
        topicContent: "....",
		"count":0,
        classesId:"5f6453b42e454d7dde560661"
    },
    {
        topicName: "Tomcat调优",
        topicDes: "Tomcat性能调优的一些见解",
        topicContent: "....",
		"count":0,
        classesId:"5f6453b42e454d7dde560661"
    },
    {
        topicName: "SpringCloud微服务",
        topicDes: "快速搭建微服务应用",
        topicContent: "....",
		"count":0,
        classesId:"5f6453b42e454d7dde560661"
    },
    {
        topicName: "Web 服务器底层",
        topicDes: "详解Web服务器底层",
        topicContent: "....",
		"count":0,
        classesId:"5f6453b42e454d7dde560661"
    }
])

db.populars.insert([
    {
        topicName: "IE兼容性问题全解",
        topicDes: "全面解析IE兼容性问题",
        topicContent: "....",
		"count":0,
        classesId:"5f6453b42e454d7dde560662"
    },
    {
        topicName: "Git全教程",
        topicDes: "最前面的Git的相关介绍",
        topicContent: "....",
		"count":0,
        classesId:"5f6453b42e454d7dde560662"
    },
    {
        topicName: "前端全栈实现",
        topicDes: "使用NodeJS搭建前端全栈",
        topicContent: "....",
        classesId:"5f6453b42e454d7dde560662"
    },
    {
        topicName: "Vue全家桶",
        topicDes: "最全面的Vue教程",
        topicContent: "....",
		"count":0,
        classesId:"5f6453b42e454d7dde560662"
    }

])

db.populars.insert([
    {
        topicName: "运维问题排查思路",
        topicDes: "如果更有效的解决问题",
        topicContent: "....",
		"count":0,
        classesId:"5f6453b42e454d7dde560663"
    },
    {
        topicName: "IT运维人员必备软件",
        topicDes: "最全面的软件介绍及使用方法",
        topicContent: "....",
		"count":0,
        classesId:"5f6453b42e454d7dde560663"
    },
    {
        topicName: "运维面试问题",
        topicDes: "2020年最新运维面试题",
        topicContent: "....",
		"count":0,
        classesId:"5f6453b42e454d7dde560663"
    },
    {
        topicName: "项目运维问题总结",
        topicDes: "实战过程中碰到的问题解析",
        topicContent: "....",
		"count":0,
        classesId:"5f6453b42e454d7dde560663"
    }
])

// 5f6453b42e454d7dde560662 

db.classesMods.find()

db.populars.find({classesId:"5f6453b42e454d7dde560660"})

获取MongoClient封装

// 引入mongodb模块
const MongoClient = require('mongodb').MongoClient;

module.exports.getMongoClient = function (extname) {
    return new Promise(function (resolve, reject) {
        // 连接数据库
        MongoClient.connect("mongodb://127.0.0.1:27017/", {
            useNewUrlParser: true,
            useUnifiedTopology: true
        }, function (err, client) {
            if (err) throw err;
            // client是一个MongoClient客户端对象
            resolve(client);
        })
    })

}

登录

发送请求

//点击登录
login.click(function () {
    console.log("登录");
    if (phoneFlag && pwdFlag) {
        // 发送异步ajax
        $.ajax({
            type: "post",
            url: "http://127.0.0.1:3000/user/login",
            data: {
                phone: phoneLog.val(),
                pwd: pwdLog.val()
            },
            success: function (data) {
                if (data.msg === '1') {
                    location.href = "http://127.0.0.1:3000/"
                } else {
                    layer.alert('手机号或密码错误,请重试!', {
                        icon: 6
                    });
                }
            }
        })
    } else {
        layer.alert('手机号或密码格式错误', {
            icon: 6
        });
    }

})

路由

// 引入common模块
const common = require("../modules/common");

// 定义用户登录
router.post("/login", function (req, res) {
    console.log(req.body);
    // 根据手机号和密码去数据库里面查询,查询得到表示登录成功;没有查到表示登录失败
    common.getMongoClient().then((client) => {
        var dbo = client.db("newcapec"); // dbo就是指定的数据库对象

        dbo.collection("users").find(req.body).toArray(function (err, result) {
            if (err) throw err;
            if (result.length > 0) {
                res.json({
                    code: 3,
                    msg: "1"
                });
            } else {
                res.json({
                    code: 3,
                    msg: "0"
                });
            }
        })
    })
})

注册

手机号唯一校验

//注册验证
//手机号码验证
$('#regphone').blur(function () {
    let phone = $('#regphone').val()
    let obj = $('.check_reg_phone')
    phoneFlag = checkPhone({
        phone,
        obj,
    })
    // phoneFlag 正则校验格式,发送异步ajax请求去服务器,判断手机号是否存在
    // obj.html('手机号码格式错误')
    if (phoneFlag) {
        $.ajax({
            type: "post",
            url: "http://127.0.0.1:3000/user/checkPhoneUnique",
            data: {
                phone: phone
            },
            success: function (res) {
                if (res.msg === '1') {
                    obj.html('手机号码已存在')
                    phoneFlag = false;
                } else {
                    obj.html('')
                }
            }
        })
    }
})

发送请求

//点击注册
regist.click(function () {
    console.log("注册");
    let check = phoneFlag && checkpwd1 && checkpwd2

    if (check) {
        // 发送异步ajax
        $.ajax({
            type: "post",
            url: "http://127.0.0.1:3000/user/reg",
            data: {
                phone: $('#regphone').val(),
                pwd: $('#regpwd').val()
            },
            success: function (data) {
                if (data.msg === 'regOk') {
                    location.href = "http://127.0.0.1:3000/login.html"
                } else {
                    layer.alert('注册失败!', {
                        icon: 6
                    });
                }
            }
        })
    } else {
        layer.alert('手机号或密码格式错误', {
            icon: 6
        });
    }
})

路由

// 定义判断用户名唯一
router.post("/checkPhoneUnique", function (req, res) {
    console.log(req.body);
    // 根据手机号去数据库里面查询
    common.getMongoClient().then((client) => {
        // 通过client对象链接到指定的数据库
        var dbo = client.db("newcapec"); // dbo就是指定的数据库对象

        dbo.collection("users").find(req.body).toArray(function (err, result) {
            if (err) throw err;
            if (result.length > 0) {
                res.json({
                    code: 2,
                    msg: "1"
                });
            } else {
                res.json({
                    code: 2,
                    msg: "0"
                });
            }
        })
    })

})

// 定义用户注册
router.post("/reg", function (req, res) {
    console.log(req.body);
    // 把手机号和密码存储到数据库里面去
    common.getMongoClient().then((client) => {
        // 通过client对象链接到指定的数据库
        var dbo = client.db("newcapec"); // dbo就是指定的数据库对象

        dbo.collection("users").insertOne(req.body, function (err, dbRes) {
            if (err) throw err;
            // dbRes是数据库给予我们的响应,dbRes对象的insertedCount属性表示插入了几条数据

            if (dbRes.insertedCount > 0) {
                res.json({
                    code: 1,
                    msg: "regOk"
                });
            } else {
                res.json({
                    code: 1,
                    msg: "regError"
                });
            }

            // 关闭客户端
            client.close();

        });
    })

})

首页

模版

DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>论坛-首页title>
    <link href="/css/index.css" rel="stylesheet" type="text/css" />
head>

<body>
    <div class="login-top" style="background: #fff;">
        <div class="head">
            <a href="http://edu.newcapec.com.cn/"><img src="./images/logo.png">a>
            <span>
                · 论坛
            span>
        div>
    div>
    <div class="index_bg">

    div>
    <div class="case-container">
        <div class="case-index-title">
            <h2>班级模块h2>
        div>
        <div class="case-index-content">
            <ul class="service-list">
                {{each classesModules as module index}}
                <li class="service-list-item service-bj{{index+1}}">
                    <div class="service-title">
                        <h3>{{module.moduleName}}h3>
                    div>
                    <div class="service-content">
                        <ul class="service-content-list">
                            {{each module.populars as topic}}
                            <li>
                                <a href="http://127.0.0.1:3000/topic/queryOne/{{topic._id}}">
                                    <h4>{{topic.topicName}}h4>
                                    <p>{{topic.topicDes}}p>
                                a>
                            li>
                            {{/each}}
                        ul>
                        <a href="javascript:;" class="service-list-more">了解更多a>
                    div>
                li>
                {{/each}}
            ul>

        div>
    div>

body>

html>

路由

var express = require('express')
var router = express.Router()
// 引入common模块
const common = require("../modules/common");


function getPopDataById(dbo, modId) {
    return new Promise(function (resolve, reject) {
        dbo.collection("populars").find({
            classesId: modId
        }).sort({
            count: -1
        }).limit(4).toArray(function (err, popResult) {
            resolve(popResult);
        })
    })
}


// 定义首页路由
router.get("/", function (req, res) {
    // 当我们去渲染index.art的时候,这里面的data数据其实就是从数据库里面查询得到的数据
    var classesModules = [];
    common.getMongoClient().then((client) => {
        var dbo = client.db("newcapec"); // dbo就是指定的数据库对象
        // 先查询所有的模块
        dbo.collection("classesMods").find({}).toArray(async function (err, result) {
            var classesModsArr = result;
            // 根据每个模块的_id去populars中查询对应的文档
            for (var i = 0; i < classesModsArr.length; i++) {
                var modId = classesModsArr[i]._id.toString();

                // await 等待,等待异步执行完成
                var popData = await getPopDataById(dbo, modId); // popData是数组
                var obj = {
                    moduleName: classesModsArr[i].moduleName,
                    populars: popData
                }
                classesModules.push(obj);
            }
            res.render('index.art', {
                classesModules: classesModules
            });

            client.close();
        })
    })

})

module.exports = router

帖子热度

var express = require('express');
const {
    ObjectId
} = require('mongodb');
var router = express.Router()
// 引入common模块
const common = require("../modules/common");

// 定义获取帖子详情的路由
router.get("/queryOne/:topicId", function (req, res) {
    var topicId = req.params.topicId;
    topicId = topicId.substr(1, 24)
    console.log("帖子Id:", topicId);
    // 每访问一次,就记录一次
    common.getMongoClient().then((client) => {
        var dbo = client.db("newcapec"); // dbo就是指定的数据库对象

        dbo.collection("populars").updateOne({
            _id: ObjectId(topicId)
        }, {
            $inc: {
                count: 1
            }
        }, function (err, dbRes) {
            if (err) throw err;
            console.log("数据更新成功!", dbRes.result.nModified);
            client.close();
        })
    })

    common.getMongoClient().then((client) => {
        var dbo = client.db("newcapec"); // dbo就是指定的数据库对象

        dbo.collection("populars").find({
            _id: ObjectId(topicId)
        }).toArray(function (err, result) {
            res.json(result);
        })
    })

})

module.exports = router

你可能感兴趣的:(前端开发知识点,javascript,mongodb,数据库)