一、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 操作符
六、注意项
RDBMS(关键字/解释) | MongoDB(关键字/解释) |
---|---|
数据库(database) | 数据库(database) |
表格(table) | 集合(collection) |
行(row) | 文档(document) |
列(column) | 字段(field) |
表联合(table joins) | 嵌入文档() |
索引(index) | 索引(index) |
主键 | 主键(MongoDB提供了key为 _id) |
Mysqld(服务端入口文件) | mongod(服务端入口文件) |
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)
集合命名空间 | 描述 |
---|---|
dbname.system.namespaces | 命名空间列表 |
dbname.system.indexs | 索引列表 |
dbname.system.profile | 包含数据库概要信息 |
dbname.system.users | 可访问数据库的用户列表 |
dbname.local.sources | 包含复制对端(slave)的服务器信息和状态 |
常用类型 | 描述 |
---|---|
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) show databases; // 显示所有的数据非空的数据库,简写: show dbs;
2) use test; // 切换进入指定的数据库,如test库;若test数据库不存在,则自动创建,但必须向test插入数据才能让test显示在show dbs的结果中
3) db; // 显示当前所在的数据库名
4) show collections; // 查看当前db的所有集合,也可使用 show tables;
注意事项:
1) 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 中, 修改已有的文档中一个不存在的字段,会在原有文档记录中追加这个键值对
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就可以实现字段去重数组)
字段 | 类型 | 描述 |
---|---|---|
capped | 布尔 | (可选)如果为 true,则创建固定集合。固定集合是指有着固定大小的集合,当达到最大值时,它会自动覆盖最早的文档。 当该值为 true 时,必须指定 size 参数 |
autoIndexed | 布尔 | 可选)如为 true,自动在 _id 字段创建索引。默认为 false |
size | 数值 | (可选)为固定集合指定一个最大值,以千字节计(KB)。 如果 capped 为 true,也需要指定该字段 |
max | 数值 | (可选)指定固定集合中包含文档的最大数量 |
操作 | 格式 | 实例 | 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 |
操作符 | 英文全称 | 含义 | 意义 |
---|---|---|---|
$gt | greater | > | 大于 |
$gte | gt equal | >= | 大于或等于 |
$lt | less than | < | 小于 |
$lte | lt equal | <= | 小于或等于 |
$ne | not equal | != | 不等于 |
$eq | equal | = | 等于 |
类型 | 数字 | 备注 |
---|---|---|
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