mongodb入门到应用(二)--使用篇

MongoDB的基础语法和使用

本篇内容简介:

​ 一、MongoDB和MySQL 的名词映射

​ 二、MongoDB的基本概念和系统库基本操作

​ 1. 基本概念+简介+系统库

​ 2. MongoDB数据库中几项 dbname.system.* 命令的基本含义

​ 3. MongoDB数据几种常用类型

​ 三、基本使用

​ 1. MongoDB的Shell命令行模式

​ 2. MongoDB跨集合多文档事务操作

​ 3. MongoDB常用的CURD

​ 4.MongoDB集合可选项

​ 5.MongoDB与RDBMS WHERE语句比较

​ 6. 部分操作符说明

​ 7. MongoDB $type 操作符

​ 六、注意项

一、MongoDB和Mysql 的名词映射

RDBMS(关键字/解释) MongoDB(关键字/解释)
数据库(database) 数据库(database)
表格(table) 集合(collection)
行(row) 文档(document)
列(column) 字段(field)
表联合(table joins) 嵌入文档()
索引(index) 索引(index)
主键 主键(MongoDB提供了key为 _id)
Mysqld(服务端入口文件) mongod(服务端入口文件)

二、MongoDB的基本概念和系统库基本操作

1.基本概念+简介+系统库
1. 名词:
	replica set (副本集)
	shard cluster (分片)
	Json 和 Bson 


2.MongoDB简介和注意事项:
	1) 数据库
	1.0) 注意事项:
    	A) MongoDB默认数据库为 "db",数据库存储在/data/mongodb/db (这个在启动mongodb时配置指定,可自选)
		B) MongoDB单个实例可容纳多个独立数据库,每个数据库都有自己的集合和权限
		C) 相关命令
			show dbs;	# 查看所有的数据非空的数据库列表,也可使用: show databases;若数据库test中数据为空,则列表中不显示test
			use local;	# 切换到数据库local
			db;			# 查看当前所在的数据库名
			
	1.1)数据库命名: 不能为空字符; 数据库名种不得含有 空格,.(点),$,\(正斜杠),/(反斜杠),空字符; 且必须全部为小写; 库名长度最多64字节 
	1.2) mongodb的系统保留数据库名(已存在,可直接访问):
		A) admin : 存储"root"权限的用户数据库;添加到这个库的用户自动继承所有数据库的权限;一些特定的服务器端命令只能从这个数据库运行(如:列出所有的数据库/关闭服务器等命令)
		B) local : 这个数据永远不会被复制,用来存储只在本地单台服务器的任意集合
		C) config : 当 Mongo 用于分片设置时, config 数据库在内部使用,用于保存分片的相关信息

	2) 集合(对应 RDB的表table)
	2.0) 集合: 是MongoDB的文档组,类似RDBMS(关系型数据库管理系统)的数据表,集合没有固定结构,即集合可以存入不同格式和类型的数据,但一般存入的数据都有一定的关联性
	2.1) 合法的集合名命名规则 
		A) 集合名不能为空字符
		B) 集合名不能含有 \0(空字符),这个字符表示集合名的结尾
		C) 集合名不能以"system."开头,这是为系统集合保留的前缀
		D) 集合名字不能含有系统保留字符; 特例: 有些驱动程序的确支持在集合名中包含保留字符,因为某些系统生成的集合中包含该字符; 千万不要在集合名中使用$ (除非要访问系统创建的集合)
		
	
	3) 文档(对应 RDB的行记录row)
	3.0) 文档: 是一组键值对(key-value); MongoDB特点(也是和RDB的区别): MongoDB的文档不需要设置相同的字段,并且相同的字段不需要相同的数据类型 ????理解???
	
	3.1) 文档的键值对是有序的
	3.2) 文档中的值不仅可以是在双引号中的字符串,还可以是其他的几种数据类型(甚至可以是整个嵌入的文档)
    3.3) MongoDB区分类型和大小写
	3.4) MongoDB的文档不能有重复的键
	3.5) 文档的键是字符串,键可使用任意UTF-8字符(除少数例外情况外)
	3.6) 文档键的命名规则:
		A) 键不能含有 \0(空字符),这个字符表示键的结尾
		B) . 和 $ 有特别的意义,只能在特定环境下使用,如: 点用于连接数据库和表 db.table, $all , $and 表示逻辑与, $or 表示逻辑或
		C) 以下划线(_)开头的键是保留的(不是严格要求,但强烈建议遵循),如: 主键 _id 
		
	4) capped collections
	4.0) capped collections 是固定大小的集合
	4.1) 特点: 高性能,队列过期特性(按照插入时间的顺序过期),类似RRD(时序数据库)概念
	4.2) 原理: 
		A) 显示创建capped collection,并指定collection大小,单位是字节,collection数据存储空间是提前分配好的;
		B) 文档按照插入顺序存储在collection对应的位置,当更新文档时,为了保证所有文档在磁盘的位置一直不变(从而保证高性能),故每个文档更新后的文档空间大小不能超过更新前的文档大小
		C) capped collection可提高增添数据的效率(因其文档插入顺序确定下一个文档的插入位置,而不是使用索引);例如: MongoDB的操作日志文件 oplog.rs 通过 Capped Collection实现

	4.3) 适合场景:记录日志; 例如: MongoDB的操作日志文件 oplog.rs 通过 Capped Collection实现
	4.4) capped collection相关操作:
		A) 在capped collection 中,可添加新的对象
    	B) 能进行更新,但对象不会存储空间,若增加空间,则更新会失败; 故更新后的内容空间不能超过更新前的内容空间,否则更新失败;
    	C) capped collection 不能删除一个稳定,只能使用drop()删除collection所有的行
    	D) 删除后,必须重新显示创建这个collection
    	F) 32bit机器中,capped collection 最大存储为1e9(即10^9)个字节
   	
   
   	5) 元数据
   	5.0) 数据库的信息存储在集合中,如: dbname.system.*,详情见下列表格
   	5.1) 对于上述系统集合中对象的限制:
   		A) 在{
    {system.indexs}}插入数据,可创建索引;但除索引外,该表信息不可变(drop index命令将自动更新相关信息)??? 更新表信息,还是数据信息????????????? 
   		B) {
    {system.users}}是可修改的
   		C) {
    {system.profile}}是可删除的
   	
   	6) MongoDB的数据类型
   	6.0) 见下列表格
   	6.1) 说明几种重要的数据类型
   		A) ObjectID: 类似唯一主键,可很快的生成和排序,包含12bytes
   			a) 前4个字节表示创建unix时间戳;(格林尼治时间UTC时间,北京时间是UTC+8,即北京时间比UTC晚8个小时)
   			b) 接下来三个字节是机器标识码
   			c) 紧接着两个字节由进程id组成PID
   			d) 最后三个字节是随机数
   		B) 字符串
   			a) BSON字符串都是UTF-8编码
   			
   		C) 时间戳
   			a) BSON 有一个特殊的时间戳类型用于MongoDB内部使用,与普通日期类型不相关,时间戳值是一个64位的值:
   				a.1) 前32位是一个time_t值(与Unix新纪元相差的秒数)
   				a.2) 后32位是在某秒中操作的一个递增的序数
   			b) 单个mongod实例中,时间戳值通常是唯一的; 在复制集中,oplog有一个ts字段,该字段值使用BSON时间戳表示操作时间
   			c) BSON时间戳类型主要用于MongoDB内部使用,大部分情况,可使用BSON日期类型
	   	D) 日期
	   		A) 表示当前距离Unix新纪元(1970年1月1日)的毫秒数;日期类型时有符号的,负数表示1970前的日期
	   		B) 创建时间相关命令
	   			a)类型为object的时间
                    > var mydate1 = new Date()     //格林尼治时间
                    > var mydate2 = ISODate()     //格林尼治时间
                    > mydate1
                    输出: ISODate("2018-03-04T14:58:51.233Z")
                    > typeof mydate1	// mydate1,mydate2输出的都是object类型的时间
                    输出: object
                    
	   			b)类型为string的时间
	   				> var mydateStr = ISODate().toString()
	   				> var mydateStr2 = new Date().toString()
	   				> Date()
	   				输出: Fri Jan 10 2020 20:27:03 GMT+0800 (CST)
	   				> typeof mydateStr	// mydateStr,mydateStr2,Date()输出的都是string类型的时间
	   				输出: string
   	
    **注意: (考虑到mysql的普及,强调一下和关系型数据管理系统RDBMS的对应关系), 格式: mongodb概念(对应mysql概念), db => collection => document 
    MongoDB数据库db(MySQL数据库database) 
    => MongoDB集合collection(Mysql数据表table) 
    => MongoDB文档document(Mysql行记录row)
    
    
2.MongoDB数据库中几项 dbname.system.* 命令的基本含义
集合命名空间 描述
dbname.system.namespaces 命名空间列表
dbname.system.indexs 索引列表
dbname.system.profile 包含数据库概要信息
dbname.system.users 可访问数据库的用户列表
dbname.local.sources 包含复制对端(slave)的服务器信息和状态
3.MongoDB数据几种常用类型
常用类型 描述
String 字符串;常用类型;MongoDB中,UTF-8编码的字符才是合法的
Integer 整型;存储数值; 根据你采用的服务器,分为32/64位
Boolean 布尔值;用于存储布尔值(true/false)
Double 双精度浮点型;存储浮点值
Min/Max keys 将一个值与BSON(二进制的JSON)元素的最低值和最高值相对比
Array 用于将数组/列表/多个值存储为一个键
Timestamp 时间戳;记录文档修改/添加的具体时间
Object 用于内嵌文档
Null 用于创建空值
Symbol 符号;类似String类型,但它一般用于采用特殊符号类型的语言
Date 日期时间;用UNIX格式存储当前日期/时间
Object ID 对象ID;用于创建文档的ID
Binary Data 二进制数据;存储二进制数据
Code 代码类型;用于在文档中存储JavaScript代码
Regular expression 正则表达式类型;用于存储正则表达式

三、基本使用

1.MongoDB的Shell命令行模式
1) show databases;	// 显示所有的数据非空的数据库,简写: show dbs;
2) use test;		// 切换进入指定的数据库,如test库;若test数据库不存在,则自动创建,但必须向test插入数据才能让test显示在show dbs的结果中
3) db;				// 显示当前所在的数据库名
4) show collections;	// 查看当前db的所有集合,也可使用 show tables;


注意事项:
	1) MongoDB中,只有当集合有内容插入后,才会创建集合; 即创建一个空的集合后,并且插入至少一条记录,集合才会真正被创建
	
2.MongoDB跨集合多文档事务操作

1. mongo shell命令行,事务demo:
	1) 命令行操作过程
    	A) session=db.getMongo().startSession();	// 显式开启一个会话)
		B) session.startTransaction();		// 显式开启事务
		C) u=session.getDatabase('testdb').tb_users;	// 获取表对象
		D) s=session.getDatabase('testdb').tb_students;
		E) u.insertOne({'not_test':false});	// 使用表对象的插入函数进行插入
		F) s.insertOne({'not_test':false});	
	
		H) session.commitTransaction();	// 提交事务
		I) session.abortTransaction();	// 回滚事务
		// 检查事务准确性
		J) db.tb_users.find()
		K) db.tb_students.find()
	2) 分析: 事务在commit提交后才会生效,若在commit之前任一地方执行回滚(即 abortTransaction),则事务操作失败(若回滚,则事务执行前后,数据不变)
	
	3) ** 注意: 
		A) MongoDB的事务,必须在副本集模式下才能使用,故必须先安装并运行副本集模式 (详见上述安装副本集)
		B) MongoDB事务中,操作的集合必须是非空集合,否则事务执行失败, 集合中的 _id 必须唯一
		C) MongoDB 中, 修改已有的文档中一个不存在的字段,会在原有文档记录中追加这个键值对


3.MongoDB常用CURD
1. MongoDB连接:
	1) 格式:
		mongodb://[username:password@]host1[:port1][,host2[:port2],...[,hostN[:portN]]][/[database][?options]]
	
	host:至少指定一个
	port: 默认端口,27017
	2) 例如: 
		A)mongo 127.0.0.1:27017,127.0.0.2:27017,127.0.0.3:27017/db_dev -u devUser -p 123456 //Linux系统mongos命令行模式登陆,指定连接db_dev
		B) mongodb://devUser:[email protected]:27017,127.0.0.2:27017,127.0.0.3:27017/db_dev	// 客户端连接MongoDB集群,指定连接db_dev
		C) mongodb://devUser:[email protected]:27017	// 默认连接admin库
		D) mongodb://devUser:[email protected]:27017/db_dev	// 客户端连接MongoDB单节点,指定连接db_dev
		

2. 数据库列表/增/删/重命名
	1) show dbs;	// 查看非空数据库列表
	2) db.demo2019.insert({"name":"demo","age":24})	// 向数据库demo2019中的集合demo2019插入一条文档记录
	3) use demo2019; 
 		db.dropDatabase();	// 切换到数据库demo2019,并删除demo2019库,成功返回true,否则返回false;返回值: { "dropped" : "demo2019", "ok" : 1 }

3. 集合(数据表)列表/增/删/重命名
	1) show collections;	// 查看集合列表,也可使用 show tables;
 	2) db.demo2019.drop()		// 删除集合demo2019,格式: db.collectionName.drop()
 	3) db.demo2019.insert({"name":"菜鸟教程"})		// MongoDB中会自动创建集合demo2019,不需要手动创建; 返回值: WriteResult({ "nInserted" : 1 })
  	5) db.demo2019.renameCollection("demo2020")		//重命名,将集合名由demo2019重命名为demo2020

	6) 先创建空集合,再插入文档
	db.createCollection(name, options)		// name: 集合; options: 可选参数,涉及内存分配,索引
	例如: 
		db.createCollection("myDemo",{capped:true, autoIndexId:true, size:6142800, max: 10000})	// 创建固定集合myDemo,整个集合空间大小为6142800KB,文档最大个数为10000个
	
 **注: 
 	A) MongoDB首先检查固定集合的size字段,然后检查max字段
 	B) 集合正确创建方法: MongoDB中不需要创建空集合,向一个不存在的集合中插入文档时,会自动创建该集合
 	C) 若只创建空集合,不插入文档,那么这个集合不会被MongoDB记录,正确创建文档的方式,参见操作上一条
	
 
4. 文档(数据表行记录)CURD,查询(单/多条)/增(单/多条)/删(单/多条)/改(单/多条);格式: db.collectionName.*,以下集合均以demo20191223为例

	1) 查询
    	A) db.demo20191223.find();		// 查看集合所有数据(列表)
		B) db.demo20191223.findOne();	// 查看集合第一条数据(第一条详情)
		C) db.demo20191223.find().pretty();		// 列表,pretty()使得数据显示更利于观看
		D) db.demo20191223.find({"title":"后端"})		//条件查询;查询匹配title=后端的所有文档记录;类似SQL: select * from demo20191223 WHERE title = '后端'
		E) 	// 条件分组查询排序; 类似SQL: select * from demo20191223 WHERE GROUP BY ORDER BY DESC
		
		F) 查询语句语法 
			A) db.demo20191223.find(query,projection);	// query可选,查询条件; projection可选,指定返回的键名;如: 
				db.demo20191223.find({"title":"后端"},{"title":1});	//类似MySQL: select title from demo20191223 WHERE title = '后端'; project中的{"title":1},1-返回title字段,0-不返回title字段
			B) MongoDB和RDBMS WHERE语句比较,参见下表
			C) MongoDB AND条件:
				a) db.demo20191223.find({k1:v1,k2:v2});	// 类似SQL: WHERE k1=v1 AND k2=v2;
			D)  MongoDB OR条件:
				a) db.demo20191223.find(
                    {
                        $or:[
                            {k1:v1},{k2:v2}
                        ]  
                    }
                    );	// 类似SQL: WHERE k1=v1 OR k2=v2
				b) 实例: db.demo20191223.find({$or:[{"name":"菜鸟"},{"age":23}]});	// 查询name="菜鸟" 或 age=23的文档; 类似SQL: where name="菜鸟" or age=23
			E) MongoDB AND和OR联合使用
				db.demo20191223.find({"age":{$gt:50},$or:[{name="菜鸟"},{"status":32}]);	// 类似SQL where age>50 AND (name="菜鸟" OR status=32)
			F) 类似SQL范围between AND查找
				db.demo20191223.find({age:{$lte:50},{$gte:30}});	// 类似SQL: WHERE age between 30 AND 50; 或 WHERE age>=30 AND age<=50;
			G) 模糊查询 (类似SQL中WHERE like用法)
				a) db.demo20191223.find({title:/菜/});	// 返回包含"菜"的文档列表; 类似SQL的WHERE name like "%菜%"用法
				b) db.demo20191223.find({title:/^菜/}); // 返回以"菜"为首字符的文档列表;类似SQL的WHERE name like "^菜"用法
				c) db.demo20191223.find({title:/菜$/}); // 返回以"菜"为尾字符的文档列表;类似SQL的WHERE name like "菜$"用法
				
	2) 添加,insert(),save()
		A) db.demo20191223.insert(jsonStr);			//添加单条;jsonStr为json格式的字符串,如:db.demo20191223.insert({"name":"胡汉三","age":24}); 结果包含三个字段,name,age,_id(系统唯一字符)
		B) db.demo20191223.insertOne(jsonStr);		// 添加单条
		C) db.demo20191223.insertMany(Array);		// 添加多条,Array为[jsonStr1,jsonStr2,...]
		D) db.demo20191223.insert(Array);			//	添加多条数据
		
	3) 更新,update(),save()
		A) db.demo20191223.update(
		,	//query: 更新的查询条件,类似mysql的where;
		,	// update:更新的对象和一些更新的操作符,类似mysql的update语句的set后面的内容;
		{
			upsert:,	// upsert: 可选,若更新的记录不存在,是否插入,true-插入,默认false-不插入;
			multi:,	// multi:可选,mongodb默认为false--表示只更新匹配的第一条记录,若true--表示更新匹配的所有记录
			writeConcern:	// 可选,抛出异常的级别
            });
		a) query: 更新的查询条件,类似mysql的where;
         b)update:更新的对象和一些更新的操作符,类似mysql的update语句的set后面的内容; c) upsert: 可选,若更新的记录不存在,是否插入,true-插入,默认false-不插入; d) multi:可选,mongodb默认为false--表示只更新匹配的第一条记录,若true--表示更新匹配的所有记录; e) 可选,抛出异常的级别;
         
		B) 实例:
        	db.demo20191223.update({'title':"JS"},{$set:{'title':'MongoDB'}});	// 查询title=JS的文档,并只修改第一条文档中的部分内容:title=MongoDB; 类似: UPDATE demo20191223 SET title = 'MongoDB' WHERE title='JS';
        	db.demo20191223.update({'title':"MongoDB"},{$set:{'title':'MongoDB教学练习2020'}},{multi:true,upsert:true});	// 查询title=MongoDB的文档记录,若文档存在,则修改匹配的每一条文档记录的部分内容:title=MongoDB教学练习2020;若不存在,则插入一条新的记录(新纪录内容为{'title':'MongoDB教学练习2020'}); 相关sql可自行查阅(根据主键/唯一键重复键判断(sql中需含主键/唯一键),存在则更新,否则插入)
	
	
	5) 特殊的写操作 db.collectionName.save(p1,p2)
		插入
		A) db.demo20191223.save(JsonStr);	//	添加单条数据,
		B) db.demo20191223.save(Array);		//	添加多条数据,
		注: 重复插入同一条值
		C) db.demo20191223.save(Array);		//	添加多条数据,
		
		更新
   		D) db.demo20191223.save(
   			,	// 更新后的文档数据(替换)
   			{
                writeConcern: 	// 可选,抛出异常的级别
   			}
   		);
   		E) 实例:
   			db.demo20191223.save({ "_id" : ObjectId("5e189ff1683f2830ceb8b671"), "title" : "MongoDB教学练习2009"});	//覆盖对应_id的数据,save内容时更新后的内容

			db.demo20191223.save({ "_id" : ObjectId("5e189858841f5283b06062ac"), "tag" : "脚本语言9933" });			//指定_id,覆盖原文档记录,若原文档内容: { "_id" : ObjectId("5e189858841f5283b06062ac"), "kind" : "服务端022", "tag" : "脚本语言9922" },更新后内容: { "_id" : ObjectId("5e189858841f5283b06062ac"), "tag" : "脚本语言9933" }

		错误示范: save()无法同时更新多条数据
   			db.demo20191223.save([{ "_id" : ObjectId("5e189ff1683f2830ceb8b671"), "title" : "MongoDB教学练习2009"},{ "_id" : ObjectId("5e189858841f5283b06062ac"), "language" : "shell", "kind" : "服务端002", "tag" : "脚本语言9900" }])


	6) 删除 
	(remove() 方法 并不会真正释放空间。需要继续手动回收磁盘空间;)
		A) 官方推荐deleteOne() 和 deleteMany(),实例:
			db.demo20191223.deleteMany({});	// 清空集合(数据表)并会回收磁盘空间; 类似SQL: TRUNCATE demo20191223;
			db.demo20191223.deleteMany({title:"后端"});	// 删除集合中所有匹配项;类似SQL: DELETE FROM demo20191223 WHERE title = "后端";
			db.demo20191223.deleteOne({title:"后端"});	// 删除集合中匹配的第一条;
	
	
        C) (remove() 方法 并不会真正释放空间。需要继续手动回收磁盘空间;)
        db.demo20191223.remove(
        ,	// (可选) 删除的条件
        {
            justOne: 	// (可选) 若设为true或1,则只删除一个文档,默认值为false--删除所有匹配项
            writeConcern: 	// (可选)抛出异常的级别
        }
        );

        D) 例如: 
        	db.demo20191223.remove({title:"后端"});	// 删除所有title="后端"的匹配项; 类似: delete from demo20191223 WHERE title = "后端"; 
        	db.demo20191223.remove({title:"后端"},1);	// 删除所有title="后端"的匹配项的第一条文件; 第二个参数值可以为:1 , true, {justOne:1},{justOne:true} 
        	db.demo20191223.remove({});		// 清空集合中所有文档记录;类似SQL: DELETE FROM demo20191223; ***注: db.demo20191223.remove(); 是错误写法,必须 remove({}) 

	
	7) 拓展:
		1) remove() 方法 并不会真正释放空间。需要继续手动回收磁盘空间: 执行 db.repairDatabase(); 或 db.runCommand({ repairDatabase: 1 });
		2) 删除集合文档时,错误写法:db.demo20191223.remove(); 正确写法:必须 db.demo20191223.remove({}) 
		3)多表关联查询:关键字$lookup,属于MongoDB的管道聚合操作,类似MySQL的聚合函数查询;
			A)语法:
                db.demoA.aggregate([$lookup:{
                        from:"tb_users",		// 被关联的集合
                        localField:"a_uid",	// demoA中的字段a_uid
                        foreignFeild:uid", // tb_users中的字段uid
                        as:"res"			 // 联表查询结果的别名
                    }]);	// 类似SQL: SELECT * FROM demoA a LEFT JOIN tb_users tu ON tu.uid = a.a_uid;
			B) 实例如: db.tb_flows.aggregate([
                {$match:{app_id:"1008611",flow_add_date:20200115}},
                {$lookup:{from:"tb_users", localField:"fb_uid", foreignField:"fb_uid", as: "res"}},				// 联表操作
                {$unwind:"$res"},							// 拆分字段res,因为$lookup所有结果存储在一个字段数组arr中,$unwind将arr拆成若干个单个子集(非数组)
                {$match:{res.add_date":20200114}},			// 类似SQL的having
                {$group:{_id:"$fb_uid",counts:{$sum:1}}}	// mongo根据group去重
			]);
			类似SQL: SELECT count(distinct fb_uid) as counts FROM tb_flows tf LEFT JOIN tb_users tu ON tu.fb_uid = tf.fb_uid WHERE tu.app_id = "1008611" AND tu.add_date = 20200114 AND tf.flow_add_date = 20200115;
			分析:这是管道聚合查询
				a) 首先,这里使用了2次$match,只有第一个$match会用到索引,其他的操作均在内存中完成计算,无法使用索引,分步匹配,尽可能得过滤掉无关的数据,减少内存操作数据量
				b) 其次,MongoDB自身无法去重函数(MySQL中可disinct 关键字去重),但是可以根据group实现去重的效果;
				
	    4) 管道聚合操作中,尽量第一个管道使用$match过滤无关的数据,因为只有第一个$match会用到索引,其他的操作均在内存中完成计算,无法使用索引,$match可分步匹配,尽可能得过滤掉无关的数据,减少内存操作数据量;类似SQL的 WHERE和HAVING的使用
        
        5) MongoDB自身无法去重函数(MySQL中可disinct 关键字去重),但是可以根据group实现去重的效果;****注意:Mongo Shell 工具用js封装了 distinct(),使用如: db.demo20191223.distinct("字段名"),返回会该字段去重后的字段值数组;但是处理数据量过大时,会出现异常,且性能比不上聚合操作,故建议使用聚合操作($group,$addToSet就可以实现字段去重数组)
				


4.MongoDB集合可选项
字段 类型 描述
capped 布尔 (可选)如果为 true,则创建固定集合。固定集合是指有着固定大小的集合,当达到最大值时,它会自动覆盖最早的文档。 当该值为 true 时,必须指定 size 参数
autoIndexed 布尔 可选)如为 true,自动在 _id 字段创建索引。默认为 false
size 数值 (可选)为固定集合指定一个最大值,以千字节计(KB)。 如果 capped 为 true,也需要指定该字段
max 数值 (可选)指定固定集合中包含文档的最大数量
5.MongoDB与RDBMS WHERE语句比较
操作 格式 实例 RDBMS的类似语句
等于 {:} db,collection.find(name:“菜鸟”}) where name = “菜鸟”
小于 {:$lt:} db.collection.find({“age”:{$lt:50}}) where age < 50
小于或等于 {:$lte:} db.collection.find({“age”:{$lte:50}}) where age <= 50
大于 {:$gt:} db.collection.find({“age”:{$gt:50}}) where age > 50
大于或等于 {:$gte:} db.collection.find({“age”:{$gte:50}}) where age >= 50
不等于 {:$ne:} db.collection.find({“age”:{$ne:50}}) where age != 50 或 where age <> 50
6. 部分操作符说明
操作符 英文全称 含义 意义
$gt greater > 大于
$gte gt equal >= 大于或等于
$lt less than < 小于
$lte lt equal <= 小于或等于
$ne not equal != 不等于
$eq equal = 等于
7. MongoDB $type 操作符
类型 数字 备注
Double 1
String 2
Object 3
Array 4
Binary data 5
Underfined 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

五、注意项

1. 聚合操作时$sum 操作作用同Mysql的count()和sum(),
	db.tb_users.aggregate([{count:$sum:1}]) // select count(*) from tb_users
	db.tb_users.aggregate([{count:$sum:"$age"}]) // select sum(age) from tb_users
	
	***注意: mongodb的集合tb_users的字段age必须是数字类型,若是字符串类型,则无法使用sum()的功能
	
2. MongoDB对字段类型严格,若设置字段age为int,则db.tb_users.find({age:{$ge:"22"}); 无法获取查询结果,正确写法: db.tb_users.find({age:{$ge:22}})

3. 查询集合文档(对应Mysql数据表行记录)和聚合查询(类似Mysql的聚合函数操作)优化方案
	1) 片键使用(查询是否命中片键,片键:mongo分片存储的规则,如hash)
 	2) 索引命中(match+project 都包含于 索引中,则从内存中读取,否则磁盘读取; 聚合操作中只有第一个match会用到索引; 通过project操作过滤掉不必要的字段,减少传输带宽过大造成的内存损耗)
 	
 4. MongoDB的集合(数据表)关系和设计原则参考
	1->1: 内嵌
	1->多:内嵌数组,或多 侧引用(引用另一个集合的_id等唯一键)
	多->多: 跟实际情况而定
	
5. MongoDB的集合(数据表)中,单个文档(行记录)最大不超过16MB

6. 两个操作内存上限:
	1) 单个文档存储最大不超过16MB,超过16MB会异常,可通过设置标记字段,标记当前文档为异常文档,详情参见(六-1-构建模式-异常模式)
	2)MongoDB的内存操作最大不超过100MB,否则异常,但可通过设置allowDiskUse=true实现大于100MB的操作;缺点是allowDiskUse=true会将数据写入磁盘中,会降低响应速度; 例如:管道聚合操作时,使用allowDiskUse后比使用前,响应更慢

7. MongoDB的事务支持: MongoDB-4.0以前只支持单文档事务,Mongo-4.0开始支持副本集事务(跨文档事务),MongoDB-4.2开始支持副本集(跨文档事务),分片事务(跨分片事务)

8. remove() 方法 并不会真正释放空间。需要继续手动回收磁盘空间: 执行 db.repairDatabase(); 或 db.runCommand({ repairDatabase: 1 });

9. 删除集合文档时,错误写法:db.demo20191223.remove(); 正确写法:必须 db.demo20191223.remove({}) 
10. 多表关联查询:关键字$lookup,属于MongoDB的管道聚合操作,类似MySQL的聚合函数查询;
			A)语法:
                db.demoA.aggregate([$lookup:{
                        from:"tb_users",		// 被关联的集合
                        localField:"a_uid",	// demoA中的字段a_uid
                        foreignFeild:uid", // tb_users中的字段uid
                        as:"res"			 // 联表查询结果的别名
                    }]);	// 类似SQL: SELECT * FROM demoA a LEFT JOIN tb_users tu ON tu.uid = a.a_uid;
			B) 实例如: db.tb_flows.aggregate([
                {$match:{app_id:"1008611",flow_add_date:20200115}},
                {$lookup:{from:"tb_users", localField:"fb_uid", foreignField:"fb_uid", as: "res"}},				// 联表操作
                {$unwind:"$res"},							// 将查询拆分
                {$match:{res.add_date":20200114}},			// 类似SQL的having
                {$group:{_id:"$fb_uid",counts:{$sum:1}}}	// mongo根据group去重
			]);
			类似SQL: SELECT count(distinct fb_uid) as counts FROM tb_flows tf LEFT JOIN tb_users tu ON tu.fb_uid = tf.fb_uid WHERE tu.app_id = "1008611" AND tu.add_date = 20200114 AND tf.flow_add_date = 20200115;
			分析:这是管道聚合查询
				a) 首先,这里使用了2次$match,只有第一个$match会用到索引,其他的操作均在内存中完成计算,无法使用索引,分步匹配,尽可能得过滤掉无关的数据,减少内存操作数据量
				b) 其次,MongoDB自身无法去重函数(MySQL中可disinct 关键字去重),但是可以根据group实现去重的效果;
				
11.  管道聚合操作中,尽量第一个管道使用$match过滤无关的数据,因为只有第一个$match会用到索引,其他的操作均在内存中完成计算,无法使用索引,$match可分步匹配,尽可能得过滤掉无关的数据,减少内存操作数据量;类似SQL的 WHERE和HAVING的使用
        
12. MongoDB自身无法去重函数(MySQL中可disinct 关键字去重),但是可以根据group实现去重的效果;****注意:Mongo Shell 工具用js封装了 distinct(),使用如: db.demo20191223.distinct("字段名"),返回会该字段去重后的字段值数组;但是处理数据量过大时,会出现异常,且性能比不上聚合操作,故建议使用聚合操作($group,$addToSet就可以实现字段去重数组)

13. mongo shell 命令行模式下,每个会话session,只允许登录一次(即需要切换账号密码时,需要先退出mongo,重新进入mongo命令行界面)



参考文献:

1) 菜鸟教程: https://www.runoob.com/mongodb/mongodb-databases-documents-collections.html
2)MongoDB基本语法和CURD-Demon:
	A) 语法: https://www.jianshu.com/p/b63e5cfa4ce5
	B) 实例: https://www.cnblogs.com/zcqkk/p/11234227.html


***【为避免篇幅过长造成不适,故分为三部分:

1) MongoDB安装
2) MongoDB基本语法和demo [本篇]
3) 设计应用和企业实例 [ Golang的MongoDB类库(mgo+mongo官方库)简介 ]
如有异议,欢迎交流讨论

你可能感兴趣的:(实践类,mongodb,nosql,经验分享,golang,数据库)