第一章 MongoDB开篇

1.1 什么是MongoDB?
MongoDB和MySQL一样都是数据库, 都是存储数据的仓库,
不同的是MySQL是关系型数据库, 而MongoDB是非关系型数据库,MongoDB中的存储的数据是类JSON。
1.2 什么是非关系型数据库?
1.在'关系型数据库'中, 数据都是存储在表中的, 对存储的内容有严格的要求,因为在创建表的时候我们就已经规定了表中有多少个字段,已经规定了每个字段将来要存储什么类型数据,已经规定了每个字段将来是否可以为空,是否必须唯一等等
2.在'非关系型数据库'中, 没有表概念, 所以存储数据更加灵活因为不需要创建表,所以也没有规定有哪些字段,也没有规定每个字段数据类型,也没有规定每个字段将来是否可以为空,是否必须唯一等等
3.'关系型数据库'由于操作的都是结构化的数据, 所以我们需要使用结构化语言SQL来操作
4.'非关系型数据库'由于数据没有严格的结构要求, 所以无需使用SQL来操作
1.3 什么是MongoDB?
1.存储文档(BSON)的非关系型数据库1)例如在MySQL中:|--------------------------------------------------------||   name(varchar(255) not null)   |    age(int unique)   ||--------------------------------------------------------|我们可以把            'zs', 33        保存到表中但是我们不能将         33, 'zs'       保存到表中但我们不能能将         null, 33       保存到表中但是我们不能将         'zs', 33,  '男' 保存到表中但是我们不能再次将     'zs', 33        保存到表中
2)例如在MongoDB中:我们可以把         {name: 'zs', age: 33};              保存到集合中我们也可以把       {name: 33, age: 'zs'};              保存到集合中我们也可以把       {name: null, age: 33};              保存到集合中我们也可以把       {name: 'zs', age: 33, gender:'男'}; 保存到集合中但是我们可以再次将 {name: 'zs', age: 33};              保存到集合中
2.'非关系型数据库'可以看做是'关系型数据库'的功能阉割版本,通过减少用不到或很少用的功能,从而提升数据库的性能
1.4 MongoDB是如何存储文档的?
1.MySQL中所有的数据都是存储在表中的, 而MongoDB中所有的数据都是存储在集合中的1)MySQL|--行1|--表1-- |--行2数据库--|         |--行3|--表2|--... ...2)MongoDB|--文档1|--集合1--|--文档2数据库--|          |--文档3|--集合2|--... ...
1.5 企业开发如何选择?
1.关系型数据库和非关系型数据库之间并不是替代关系, 而是互补关系
所以在企业开发中大部分情况是结合在一起使用.
2.对于数据模型比较简单、数据性能要求较高、数据灵活性较强的数据, 我们存储到非关系型数据库中
相反则存储到关系型数据库中
3.具体使用: 会在项目中实现比较重要的数据,存储在mysql中,不是那么重要的数据,存储在MongoDB中。

第二章 MongoDB安装

2.1 安装MongoDB
1.https://www.mongodb.org/dl/win32
2.MongoDB版本号: 偶数为稳定版(推荐), 基数为开发版
3.MongoDB对32位操作系统支持不佳, 不推荐在32位系统上使用, 并且官方已经停更32位安装包

2.2 安装步骤
1.全程下一步,修改安装路径
2.配置环境变量, 将MongoDB的bin目录配置到系统变量Path中
3.可以去服务列表中打开服务,相当于开始服务器。
2.3 连接服务器
1.mongo

第三章 快速上手MongoDB

3.1 连接MongoDB服务器
1.通过mongo连接MongoDB服务器
3.2 查看数据库
1.show dbs
2.#和MySQL中的 show databases; 指令一样
3.3 创建数据库
1.use 数据库名称
2.#和MySQL中的 use 指令一样, 只不过MongoDB中的use数据库不存在会自动创建user person;  创建数据库  数据库名是person;如果没有向person数据库中插入集合,此时,这个数据库还是不存在的。
3.4 查看数据库中有哪些集合
1.show collections
2.#和MySQL中的 show tables; 指令一样
3.5 创建集合
前面,使用use person,此时,db就表示person。
1.db.createCollection('集合名称');db.createCollection('users')只要向person数据库中插入了集合,数据就才正式创建。
2.#和MySQL中的 create table xxx(); 指令一样
3.6 插入数据
1.db.集合名称.insert(文档对象);db.users.insert({name:"wc",age:100})插入时,它会自动给每一个文档,添加一个_id,_id叫主键。
2.#和MySQL中的 insert into xxx values () 指令一样
3.7 查询数据
1.db.集合名称.find();db.users.find();
2.#和MySQL中的 select * from xxx; 指令一样
3.8 删除集合
1.db.集合名称.drop()db.users.drop();如果数据库中没有一个集合,那么数据库也不存在了
2.#和MySQL中的 drop table xxx; 指令一样
3.9 删除数据库
1.db.dropDatabase()2.#在哪个数据库中就会删除哪个数据库
3.#和MySQL中的 drop database xxx; 指令一样
3.10 和MySQL的不同
1.没有MySQL中表的概念, 取而代之的是集合
2.创建集合时不用指定集合中有哪些字段
3.只要是一个合法的文档对象都可以往里面存储... ...
3.11 主键
1.MongoDB的主键和MySQL一样, 也是用于保证每一条数据唯一性的
2.和MySQL不同的是, MongoDB中的主键无需明确指定1)每一个文档被添加到集合之后, MongoDB都会自动添加主键2)MongoDB中文档主键的名称叫做 _id3.默认情况下文档主键是一个ObjectId类型的数据1)ObjectId类型是一个12个字节字符串(5e8c5ae9-c9d35e-759b-d6847d)a)4字节是存储这条数据的时间戳b)3字节的存储这条数据的那台电脑的标识符c)2字节的存储这条数据的MongoDB进程idd)3字节是计数器
3.12 为什么要使用ObjectId类型数据作为主键?
1.因为MongoDB是支持'横向扩展'的数据库1)横向扩展是指'增加数据库服务器的台数'2)纵向扩展是指'增加数据库库服务器的配置'3)过去一个数据库只能安装在一台电脑上, 但是每台电脑的性能是有峰值的一旦达到峰值就会导致服务器卡顿、宕机、重启等问题.所以过去为了防止如上问题的出现,我们只能不断的'纵向扩展'也就是不断的提升服务器的配置, 让服务器能处理更多的请求但是纵向扩展也是有峰值的, 一台电脑的配置不可能无限提升所以为了解决这个问题就有了分布式数据库4)分布式数据库是指可以在多台电脑上安装数据库, 然后把多台电脑组合成一个完整的数据库,在分布式数据库中,我们可以通过不断同步的方式, 让多台电脑都保存相同的内容当用户请求数据时, 我们可以把请求派发给不同的数据库服务器处理当某一台服务器宕机后, 我们还可以继续使用其它服务器处理请求从而有效的解决了单台电脑性能峰值和单台电脑宕机后服务器不能使用的问题
2.为什么要使用ObjectId类型数据作为主键?正是因为MongoDB是一个分布式数据库, 正是因为分布式数据库可以把请求派发给不同的服务器所以第一次插入数据时, 我们可能派发给了A服务器, 插入到了A服务器的数据库中但是第二次插入数据时, 我们又可能派发给了B服务器, 插入到了B服务器的数据库中但是B服务器此时并不知道A服务器当前的主键值是多少, 如果通过MySQL中简单的递增来保证数据的唯一性那么将来在多台服务器同步数据的时候就会出现重复的情况, 所以MongoDB的主键并没有使用简单的递增而是使用了ObjectId类型数据作为主键
3.13 是否支持其它类型数据作为主键?
1.在MongoDB中支持除了'数组类型'以外的其它类型数据作为主键
2.在MongoDB中甚至还支持将一个文档作为另一个文档的主键(复合主键)
3.  db.person.insert({name: 'wc', age: 33});db.person.insert({_id: 1, name: 'wc', age: 33});#db.person.insert({_id: 1, name: 'wc', age: 33}); #报错db.person.insert({_id: '1', name: 'wc', age: 33});db.person.insert({_id: {name:'xq', gender: '男'}, name: 'wc', age: 23});#db.person.insert({_id: {name:'xq', gender: '男'}, name: 'wc', age: 23}); #报错db.person.insert({_id: {gender: '男', name:'xq'}, name: 'wc', age: 23});
3.14 可视化工具
mongoDBManager下载地址:https://www.mongodbmanager.com/傻瓜式安装

第四章 MongoDB-创建文档

4.4 写入一个文档
1.   db.<collection>.insertOne(<document>,{writeConcern: <document>});document: 需要写入的文档writeConcern: 写入安全级别
2.安全级别1)用于判断数据是否写入成功,2)安全级别越高, 丢失数据风险越小, 但是性能消耗(操作延迟)也就越大3)默认情况下MongoDB会开启默认的安全些级别,先不用关心
3.注意点在使用insertXXX写入文档时, 如果调用insertOne的集合不存在会自动创建db.users.insertOne({name:'z3',age:18});db.users.find()// student集合并不存在db.student.insertOne({name:'zs', age:18}) #集合不存在会自动创建db.student.find()
4.其它方式db.<collection>.save(<document>,{writeConcern: <document>});// users集合现在是存在的db.users.save({name:'ls', age:19})db.person.find()// teacher集合不存在  不存在时,也会创建db.teacher.save({name:'ls', age:19}) #集合不存在会自动创建db.teacher.find()
5.insertOne和save不同主键冲突时insertOne会报错,而save会直接用新值覆盖旧值// users集合现在是存在的  自己指定主键db.users.insertOne({_id:1, name:'ww', age:22})db.users.find()db.users.insertOne({_id:1, name:'ww', age:22}) #报错// teacher集合存在db.teacher.save({_id:1, name:'wc', age:66}) #用新数据替换久数据db.teacher.find()
4.5 写入多个文档
1.   db.<collection>.insertMany([<document>, ...],{writeConcern: <document>,ordered: <boolean>});ordered: 是否按顺序写入ordered默认取值是true, 也就是会严格按照顺序写入如果ordered是false, 则不会按照顺序写入, 但写入效率更高(系统会自动优化)删除person数据库下面的users集合。向users集合中插入N个文档,没有这个集合,会自动创建:db.users.insertMany([{name:'zs', age:18},{name:'ls', age:19},{name:'ww', age:20}],{})db.person.find()
2.注意点1)如果ordered是true, 前面的文档出错, 后面的所有文档都不会被写入2)如果ordered是false, 前面的文档出错, 后面的所有文档也会被写入3)    db.users.insertMany([{_id:1, name:'zs', age:18},{_id:1, name:'ls', age:19},{_id:2, name:'ww', age:20}],{ ordered: true })// 使用insertMany插入多个文档时,主键冲突,会报错db.users.find()db.users.remove({})  // 移除集合中的所有文档db.users.insertMany([{_id:1, name:'zs', age:18},{_id:1, name:'ls', age:19},{_id:2, name:'ww', age:20}],{ ordered: false })db.users.find()
4.6 写入一个或多个文档
1.   db.<collection>.insert(<document> or ,[<document>, ...]{writeConcern: <document>,ordered: <boolean>});insertOne和insertMany结合体
2.注意点1)和insertOne/insertMany一样,  集合不存在会自动创建2)和insertOne/insertMany一样,  主键冲突会报错3)和insertMany一样, 默认都是按顺序插入, 前面的文档出错, 后续所有文档不会被插入

第五章 MongoDB读取文档(查询)

5.1 查询文档
1.   db.<collection>.find(<query>,<projection>)query: 查询条件, 相当于MySQL中的whereprojection: 投影条件, 规定了结果集中显示那些字段, 相当于MySQL中的 select 字段1, 字段2, .. from 表名;
5.2 查询所有文档
db.<collection>.find();
不传入条件, 默认就是查询所有
5.3 查询满足条件文档
1.db.users.insert([{name:'zs', age:17},{name:'ls', age:18},{name:'ww', age:19}])1)单个字段条件db.users.find() // 默认会返回指定集合中所有的数据和所以的字段db.users.find({name:'zs'}) // 我们可以通过第一个参数指定查询条件, find方法会把所有满足条件的数据返回给我们  类似于mysql中的where2)多个字段条件db.users.find({name:'zs', age:17}) // 默认是And关系, 也就是默认要求同时满足所有指定的条件, 才会返回对应的数据db.users.find({age:17, name:'zs'}) // 注意点: 没有顺序要求, 只要是同时满足多个条件即可3)文档中又是文档情况  先删除uers集合db.users.insert([{name:'zs', age:17, book:{name:'HTML', price:66}},{name:'ls', age:18, book:{name:'JavaScript', price:88}},{name:'ww', age:19, book:{name:'Vue', price:199}}])4)db.users.find({'book.name':'JavaScript'}) // 如果某一个文档的某一个字段的取值又是一个文档, 那么在判断的时候我们可以通过'字段.文档属性名称'的方式来判断
5.4 查询指定字段
1. 0表示不显示, 1表示显示
2. 除主键以外, 其它字段不能同时出现0和1(要么不写,写了就必须全是1或者全是0)db.users.find({},{_id:0}) // 如果不想查询某一个字段, 那么就可以指定这个字段的投影取值为0db.users.find({},{_id:0, name:1, age:1, book:1}) // 如果想查询某一个字段, 那么就可以指定这个字段的投影取值为1// 默认情况下如果不指定, 那么所有字段的投影取值都是1db.users.find({},{_id:0, name:1, age:1, book:0}) // 除了_id字段以外, 其它的字段不能同时出现0和1db.users.find({},{_id:0, book:0})
5.5 比较操作符
1.和MySQL一样, MongodDB中也支持很多比较操作符1)$eq: 等于 / $ne: 不等于2)$gt: 大于 / $gte: 大于等于3)$lt: 小于 / $lte: 小于等于
2.使用格式db.<collection>.find({<field>: {$<operator>: <value>}},<projection>)
3.示例db.users.insert([{name:'zs', age:17, gender:'男'},{name:'ls', age:18},{name:'ww', age:19}])查询名称叫做zs的人db.users.find({name:'zs'});db.users.find({name:{$eq:'zs'}});查询所有成年人db.users.find({age:{$gte:18}});查询所有未成年人db.users.find({age:{$lt:18}});查询所有不是18岁的人db.users.find({age:{$ne:18}});注意点: 没有指定字段也算作不等于  没有写也是不等于db.users.find({gender:{$ne: '女'}}) 注意点: 在做不等于判断的时候, 没有需要判断的字段, 也算作是不等于
5.6 其它比较操作符
1.$in: 匹配和任意指定值相等的文档$nin:匹配和任意指定值都不相等的文档
2.使用格式db.<collection>.find({<field>: {$<operator>: [<value1>, <value2>, ...]}},<projection>)
3.实例查询名称叫做zs或者ls的人db.users.find({name:{$in:['zs','ls']}});  // 匹配和任意指定值相等的文档查询名称不叫zs或者ls的人db.users.find({name:{$nin:['zs','ls']}});  // 匹配和任意指定值都不相等的文档查询性别不是男或女的人db.users.find({gender:{$nin:['男', '女']}});注意点: 和$ne一样, 如果没有需要判断的字段, 也算作满足条件注意点: 没有指定字段也算作不包含
5.7 逻辑操作符
1.   $not: 匹配条件不成立的文档{<field>: {$not: {<expression>}}}$and: 匹配条件全部成立的文档{<field>: {$and: [{<expression1>}, {<expression2>}, ...}]}$or : 匹配至少一个条件成立的文档{<field>: {$or: [{<expression1>}, {<expression2>}, ...}]}$nor: 匹配多个条件全部不成立的文档{<field>: {$nor: [{<expression1>}, {<expression2>}, ...}]}
2.示例:1)$not//查询所有年龄不等于18岁的人db.users.find({age:{$ne:18}})db.users.find({age:{$not:{$eq:18}}})//查询不是男人的人db.users.find({gender:{$eq:'男'}})  // 查询性别是男的人db.users.find({gender:{$not:{$eq:'男'}}});  // 查询不是男人的人注意点: $not运算符和$ne/$nin一样, 如果需要查询的字段不存在, 也会算作条件成立2)$and  就是同时满足多个条件//查询所有名称叫做zs的未成年人// 和下面的写法是一样的db.users.find({$and:[{name:{$eq:'zs'}},{age:{$lt:18}}]})// $and后面可以写一个数组,数组中放N个条件db.users.find({$and:[{name:'zs'},{age:{$lt:18}}]})// find第一个参数表示查询条件,里面可以指定多个字段db.users.find({name:'zs', age:{$lt:18}})3)$or//查询所有名称叫做zs或者ls的人db.users.find({name:{$in:['zs','ls']}})// $or后面写一个数组,数组中放多个条件,这些条件是或的关系db.users.find({$or:[{name:{$eq:'zs'}},{name:{$eq:'ls'}}]})db.users.find({$or:[{name:'zs'},{name:'ls'}]})4)$nor//查询所有名称不叫zs或者ls的人db.users.find({name:{$nin:['zs','ls']}})db.users.find({$nor:[{name:'zs'},{name:'ls'}]})//查询所有名称不叫zs或者性别不是男的人db.users.find({$nor:[{name:'zs'},{gender:'男'}]})注意点: $nor运算符和$ne/$nin/$not一样, 如果需要查询的字段不存在, 也会算作条件成立
5.8 字段操作符
1.   $exists: 查询包含某个字段的文档{<field>: {$exists: <boolean>}}$type:   查询指定字段包含指定类型的文档{<field>: {$type: <BSON> or [<BSON1>, <BSON2>]}}
2.查询包含字段gender的人db.users.insert([{name:'zs', age:17, gender:'男'},{name:'ls', age:18},{name:'ww', age:19},{name:'wc', age:20, gender:'女'}])// 需求: 要求查询出所有拥有gender属性的文档db.users.find({gender:{$exists: true}})3.应用场景:// 应用场景: 配合$ne/$nin/$nor/$not来清理数据db.users.find({gender:{$ne:'男'}})db.users.find({gender:{$ne:'男', $exists:true}})4.查询所有年龄是字符串类型的文档db.users.insert([{name:'wc', age:'666'},{name:'xq', age:'888'},])// 需求: 要求查询出所有age属性的取值是字符串类型的文档db.users.find({age:{$type:'string'}})
5.9 数组操作符
1.   $all: 匹配数组中包含所有指定查询值的文档{<field>: {$all: [<value1>, <value2>, ...]}}$elemMatch: 匹配数组中至少有一个能完全匹配所有的查询条件的文档{<field>: {$elemMatch: {<query1>, <query2>, ...}}}2.示例1)查询tags中同时拥有html和js的文档db.users.insert([{name: 'zs', tags:['html', 'js', 'vue']},{name: 'ls', tags:['html', 'react', 'vue']},{name: 'ww', tags:['html', 'node', 'js']},])db.users.find({tags:{$all:['html', 'js']}})2)查询所有名称叫做zs,年龄是18岁的文档db.school.insert([{class: 'one',studnets: [{name:'zs', age: 18},{name:'ls', age: 19},{name:'ww', age: 20},]},{class: 'two',studnets: [{name:'zs', age: 20},{name:'ls', age: 19},{name:'ww', age: 18},]},])// 不OK  db.school.find({'studnets.name':'z2', 'studnets.age':18})// OKdb.school.find({studnets:{$elemMatch:{name:'zs',age:18}}})
5.10 运算操作符
1.运算操作符{ <field>: { $regex: /pattern/, $options: '<options>' } }{ <field>: { $regex: /pattern/<options> } }查询满足正则的文档
2.示例db.users.insert([{name:'zs', age:18},{name:'ls', age:19},{name:'ww', age:17},{name:'Zsf', age:18},{name:'xq', age:19},{name:'Wz', age:17}])// 需求: 要求查询出所有姓z的人(文档)   $regex表示后面写正则  两个//表示正则// ^ 以xxx开头     $options表示选项  i表示忽略大小写db.person.find({name:{$regex:/^z/, $options: 'i'}})// 需求: 要求查询出所有姓是z或者x的人(文档)   /^z/i 也是正则  i表示忽略大小写db.person.find({name:{$in:[/^z/i, /^x/i]}})
5.11 文档游标(指针)
1.为什么学习前端都要学习MongoDB?因为MongoDB原生就支持JavaScript, 也就是我们可以直接在MongoDB中混入JS代码MongoDB对前端非常友好
2.什么是文档游标我们执行find方法后, find方法其实是有返回值的, find方法会返回一个文档游标(相当于C语言指针)find方法就返回一个游标,游标是一个对象。
3.文档游标常用方法1)hasNext(): 是否还有下一个文档,如果为true表示还有下一个文档2)next():    取出下一个文档3)forEach(): 依次取出所有文档,遍历所有的文档
4.文档游标注意点1)默认情况下通过文档游标遍历完所有文档后, 系统会在10分钟后自动关闭当前游标2)如果不想自动关闭, 我们可以通过noCursorTimeout函数来保持游标一直有效3)如果想手动关闭游标, 我们也可以通过close函数来手动关闭游标
5.示例// 需求: 往users集合中插入100个文档var arr =[];for(var i = 0; i < 100; i++){arr.push({name:'wc'+i, age:18+i});}db.users.insertMany(arr)// cursor叫游标var cursor = db.users.find().noCursorTimeout()//cursor[0]//cursor[1]// cursor.hasNext()表示判断是否还有下一个文档,如果有返回true// cursor.next()得到文档while(cursor.hasNext()){printjson(cursor.next())}cursor.forEach(printjson)cursor.close()
5.12 分页方法
1.   cursor.limit(<number>): 取多少个文档cursor.skip(<offset>) : 跳过多少个文档2.示例var cursor = db.users.find()// 需求: 要求取出前5个文档cursor.limit(5)// 需求: 要求跳过前面的5个文档, 取出剩余的所有cursor.skip(5)// 注意点: 我们可以直接在find方法后面调用limit方法或者skip方法db.users.find().limit(5)db.users.find().skip(5)3.分页函数注意点// 注意点: MongoDB是支持链式调用的// 需求: 跳过前面5个文档, 取出后面的5个文档db.users.find().skip(5).limit(5)// 注意点:在链式调用的时候, 无论skip写在前面还是后面, 都会在limit之前执行db.users.find().limit(5).skip(10)
5.13 排序函数
1.排序函数1)cursor.sort({field: ordering, ...}): 按照指定规则排序2)ordering为1表示升序排序3)ordering为-1表示降序排序
2.示例准备数据:db.users.insert({name:'wc', age:15})db.users.insert({name:'xq', age:18})db.users.insert({name:'zs', age:9})排序: db.users.find().sort({age:1})db.users.find().sort({age:-1})
3.注意点1)find方法默认会取出所有文档2)如果有sort,会先执行sort,然后,再分页db.users.find().skip(1).limit(1)db.users.find().skip(1).limit(1).sort({age:-1})
5.14 统计函数
1.统计函数cursor.count(<applySkipLimit>): 统计集合中文档的个数applySkipLimit默认为false, 表示忽略skip和limit
2.示例db.users.find().count()// 注意点: count函数可以接收一个applySkipLimit参数, 通过这个参数可以告诉MongoDB在统计的时候是否需要忽略Skip和Limit//      默认情况下applySkipLimit的取值是false, 表示忽略Skip和Limitdb.person.find().skip(6).count()db.person.find().limit(5).count()db.person.find().skip(6).count({applySkipLimit:true})db.person.find().limit(5).count({applySkipLimit:true})
2.统计函数注意点1)在find函数不提供筛选条件时, count函数会从集合的元数据中取得结果2)在单台电脑上是这个结果是准确的,但是如果数据库为分布式结构(多台电脑)时,如果不给find函数提供筛选条件, 那么count函数返回的结果并不一定准确

第六章 MongoDB更新文档

6.1 更新文档
MongoDB中有三个常用的更新方法: save()/update()/findAndmodify()
6.2 save方法
1.save用于往集合里添加一个新文档或者覆盖文档
2.当没有指定文档_id的时候就是新增
3.当指定了集合中已经存在的_id的时候就是覆盖
4.  db.users.insert([{name:'zs', age:18},{name:'ls', age:19},{name:'ww', age:20},{name:'zs', age:21},])db.users.find()db.users.save({name:"xq",age:666})  // 表示新增// ObjectId("3243678fasfdd")表示把字符串转成ObjectId类型// 使用新的数据,把老的数据覆盖掉,老的数据没有了db.users.save({_id:ObjectId("60e2ad7432ce4fadfb5cce9e"),score:80})
6.3 update方法

1)默认情况下,update是使用新的文档,把旧的文档覆盖掉

2)匹配时,默认只会匹配第1个文档

1.   db.collection.update(<filter>, <update>, <options>)1)<filter>: 筛选条件2)<update>: 新的内容3)<options>: 额外配置2.通过update覆盖满足条件数据默认情况下如果<update>没有使用更新操作符, 那么就会使用指定的内容覆盖符合条件的内容3.示例:// {name:'zs'}是条件    {name:'zs123'}是新内容db.users.update({name:'zs'}, {name:'zs123'})4.注意点:// 注意点: update方法默认情况下就是覆盖// 如果不想覆盖, 而是想单纯的更新, 那么就必须在第二个参数中使用'更新操作符'// 之前ww文档中是没有score,更新时,会把score把name和age覆盖掉db.users.update({name:'ww'},{score: 99.9},{})// 注意点: update方法默认只会更新满足条件的第一个文档// 如果想更新所有满足条件的文档, 那么就必须指定第三个参数db.users.update({name:'zs'}, {name:'zs', age:55}, {})// 注意点: 如果在使用update方法的时候, 在第二个参数中指定了_id, 那么就必须保证指定的_id和被更新的文档的_id的取值一致// 否则就无法更新, 否则就会报错// 开发技巧: 在企业开发中如果需要使用update方法, 那么就不要指定_iddb.users.update({name:'zs'}, {_id:1, name:'zs', age:55}, {})db.users.update({name:'zs'}, {_id:ObjectId("5e9007350718cb6e37ab4515"), name:'zs', age:88}, {})// 注意点: 如果想更新所有满足条件的文档, 我们可以指定第三个参数的取值multi:true// 注意点: 如果指定了multi:true, 那么就必须在第二个参数中使用'更新操作符'db.users.update({name:'zs'}, {name:'zs', age:55}, {multi:true})
6.4 更新操作符
默认情况下update会使用新文档覆盖旧文档,如果不想覆盖而是仅仅想更新其中的某些字段,那么我们就需要使用update的更新操作符
6.5 $set更新操作符
1.$set更新操作符$set: 更新或者新增字段, 字段存在就是更新, 字段不存在就是新增格式: {$set:<value1>, ...}
2.示例:// {name:'zs'} 表示更新的条件   $set叫更新操作符  不再是使用新文档覆盖旧文档// $set就是用来设置某个文档中的某个字段的值db.users.update({name:'zs'}, {$set:{name:'zs123'}})db.users.update({name:'ls'}, {$set:{age:'888'}})3.更新内嵌文档和数组db.users.insert([{name:'zs', age:18},{name:'ls', age:19},{name:'ww', age:20},{name:'zs', age:21},])db.users.update({name:'ww'}, {age:55})// 更新普通字段db.users.update({name:'ls'}, {$set:{age:55}})// {multi:true}批量更新db.users.update({name:'zs'}, {$set:{age:88}}, {multi:true})db.users.insert({name:'ww',age:18,book:{name:'精通mongodb', price:198},tags:['html', 'JavaScript']})// 更新文档字段db.users.update({name:'ww'}, {$set:{'book.name': '精通express'}})// 更新数组字段db.users.update({name:'ww'}, {$set: {'tags.0': 'vue'}})4.注意点:// 注意点: 如果操作的字段存在, 那么就是更新, 如果操作的字段不存在, 那么就是新增db.users.update({name:'ls'}, {$set:{score: 59.5}})// 注意点: 如果操作的是数组字段, 如果操作索引不存在, 那么也会自动新增// 如果被操作的索引前面没有数据, 那么会自动用null来填充db.users.update({name:'ww'}, {$set: {'tags.2': 'react'}})db.users.update({name:'ww'}, {$set: {'tags.5': 'node'}})
6.6 $unset更新操作符
1.   $unset更新操作符$unset: 删除字段格式  :{$unset:{<field>:'', ...}}
2.示例:// 删除普通字段db.users.update({name:'ls'}, {$unset:{age:''}})// 注意点: 如果使用$unset删除某一个字段, 那么后面赋值为任何的内容都不重要,都无所谓db.users.update({name:'ls'}, {$unset:{age:'www.baidu.com'}})// 删除文档字段中的字段db.users.update({name:'ww'}, {$unset:{'book.price': ''}})// 删除数组字段中的元素// 注意点: 如果删除的是数组字段中的元素, 那么并不会修改数组的长度, 而是用null来填充删除的内容db.users.update({name:'ww'}, {$unset:{'tags.1': ''}})
3.注意点:1)删除数组元素并不会影响数组的长度, 而是设置为Null2)如果删除的字段不存在, 不会做任何操作
6.7 $rename更新操作符
1.$rename更新操作符$rename: 重命名字段格式  :{$rename:{<field>:<newName>, ...}}
2.示例db.users.update({name:'zs'}, {$rename:{name:'MyName'}})// 注意点: 如果修改的是文档字段中的字段, 那么取值必须写上层级关系db.users.update({name:'ww'}, {$rename:{'book.name':'book.BookName'}})// 注意点: 如果要操作的字段不存在, 那么不会做任何的操作db.users.update({name:'ls'}, {$rename:{age:'MyAge'}})// 注意点: 如果重命名之后的名称已经存在了, 那么已经存在的字段就会被删除// 底层的本质: 先调用了$unset删除了原有的book字段, 然后再调用$set修改字段的名称db.users.update({name:'ww123'}, {$rename:{name:'book'}})// 注意点: 不能通过$rename更新操作符来操作数组db.users.insert({name:'z6',age:888,book:{name:'mongodb', price:999},tags:[{name:'html', price:'123'}, {name:'js', price:456}]})db.users.update({name:'z6'}, {$rename:{'tags.0.name':'tags.0.TagName'}})4.乾坤大挪移// 可以将外层的字段转移到内层的文档中db.users.update({name:'z6'}, {$rename:{age:'book.age'}})db.users.find()// 可以将内层文档中的字段, 转移到外层文档中db.users.update({name:'z6'}, {$rename:{'book.age':'age'}})
6.8 i n c 和 inc和 inc和mul更新操作符
1.$inc和$mul更新操作符$inc:更新字段值(增加或者减少字段保存的值)格式: {$inc:{<field>: <number>}}$mul:更新字段值(乘以或者除以字段保存的值)格式: {$mul:{<field>: <number>}}
2.示例db.users.update({name:'zs'}, {$inc:{age:2}})db.users.update({name:'zs'}, {$inc:{age:-5}})db.users.update({name:'zs'}, {$mul:{age:0.5}})db.users.update({name:'zs'}, {$mul:{age:2}})
3.注意点:1)只能操作数字类型字段2)如果操作的字段不存在, 会自动新增这个字段不同的是$inc会把操作的值赋值给新增的字段, 而$mul会自动赋值为0db.users.update({name:'zs'}, {$inc:{weight:2}})db.users.update({name:'zs'}, {$mul:{height:2}})
6.9 m i n 和 min和 min和max更新操作符
1.$min和$max更新操作符$min:比较保留更小字段值格式: {$min:{<field>: <value>}}$max:比较保留更大字段值格式: {$max:{<field>: <value>}}2.示例db.users.insert({name:'z3', age:33})db.users.update({name:'z3'}, {$min:{age:50}})db.users.update({name:'z3'}, {$min:{age:18}})db.users.update({name:'z3'}, {$max:{age:5}})db.users.update({name:'z3'}, {$max:{age:55}})
3.注意点:// 注意点: 如果操作的字段不存在, 那么会自动增加, 并且会将操作的值赋值给新增的字段db.users.update({name:'z3'}, {$min:{weight:120}})db.users.update({name:'z3'}, {$max:{height:175}})// 注意点: 和$inc/$mul不同, $min/$max不仅仅能操作数值类型的字段, 只要是可以比较的字段都可以操作db.users.insert({name:'def', age:666})  db.users.update({name:'def'}, {$min:{name:'efg'}})db.users.update({name:'def'}, {$min:{name:'cde'}})// 注意点: 不是相同的数据类型也可以进行比较db.users.update({name:'z3'}, {$min:{age:null}})
6.10 $addToSet数组更新操作符
1.$addToSet数组更新操作符1)$addToSet: 向数组字段中添加元素2)格式: {$addToSet: {<field>:<values>, ...}}
2.示例db.users.insert([{name:'zs', books:[{name:'html', price:66}, {name:'js', price:88}], tags:['html', 'js']},{name:'ls', books:[{name:'vue', price:99}, {name:'node', price:199}], tags:['vue', 'node']}])db.users.update({name:'zs'}, {$addToSet:{tags:'react'}})
3.注意点// 注意点:如果操作的元素不存在, 那么会自动新增, 并且将操作的值赋值给新增的数组字段db.users.update({name:'zs'}, {$addToSet:{other:'123'}})// Set 里面不能包含重复元素// 注意点: $addToSet会自动去重, 如果添加的元素已经存在了, 那么就不会添加了db.users.update({name:'zs'}, {$addToSet:{other:'123'}})// 注意点: 如果往数组字段中添加的是文档类型, 那么必须一模一样才会去重// 可以添加db.users.update({name:'zs'}, {$addToSet:{books:{name:'css', price:88}}}) // 不能添加  原因是人家本身内部就有一个一模一样的文档db.users.update({name:'zs'}, {$addToSet:{books:{name:'html', price:66}}})// 可以添加  因为文档中的顺序不一样db.users.update({name:'zs'}, {$addToSet:{books:{price:66, name:'html'}}})// 注意点: 如果往数组字段中添加的是数组, 那么也必须一模一样才会去重// 向数组中添加一个新的数组db.users.update({name:'ls'}, {$addToSet:{tags:['1', '2']}})// 添加失败,因为set中不能添加重复元素db.users.update({name:'ls'}, {$addToSet:{tags:['1', '2']}})// 可以添加成功db.users.update({name:'ls'}, {$addToSet:{tags:['2', '1']}})// 注意点: 如果往往数组字段中添加的是数组, 那么默认情况下会将整个数组作为一个元素添加进去//      如果不想诶一个整体添加进去,那么必须使用$each来添加db.users.update({name:'ls'}, {$addToSet:{tags:{$each: ['1', '2', '3']}}})
6.11 $push数组更新操作符
1.$push: 向数组字段中添加元素(不去重)
2.格式: {$push: {<field>:<value1>, ...}}// 可以添加重复元素
db.users.update({name:'z3'},{$push:{tags:'react'}})
db.users.update({name:'ls'}, {$push:{tags:"3"}})
6.12 $pop数组更新操作符
1.$pop数组更新操作符$pop: 从数组字段中删除元素   删除第1个或者最的1个格式: {$pop: {<field>:<1|-1>, ...}}
2.示例db.users.update({name:'zs'}, {$pop:{tags:1}}) #删除最后一个db.users.update({name:'zs'}, {$pop:{tags:-1}})#删除第一个
3.注意点数组中的元素都被删除以后, 仍然会保留空的数组
6.13 $pull数组更新操作符
1.$pull数组更新操作符$pull: 从数组字段中删除特定元素格式: {$pull: {<field>:<value|condition>, ...}}
2.示例db.users.insert([{name:'zs', books:[{name:'html', price:66}, {name:'js', price:88}], tags:['html', 'js', ['1', '2']]},{name:'ls', books:[{name:'vue', price:99}, {name:'node', price:199}], tags:['a', 'b', 'ab', 'c', 'ac']}])删除特定元素根据条件删除元素db.users.update({name:'zs'}, {$pull:{tags:'js'}})// /^a/  // 表示正则  ^a表示以a开头  db.users.update({name:'ls'}, {$pull:{tags:/^a/}})
3.注意点// 注意点: 如果要删除的元素是一个数组, 那么必须一模一样才能删除db.users.update({name:'zs'}, {$pull:{tags:['2', '1']}})db.users.update({name:'zs'}, {$pull:{tags:['1', '2']}})// 注意点: 如果要删除的元素是一个文档, 那么不用一模一样也可以删除db.users.update({name:'zs'}, {$pull:{books:{price:66, name:'html'}}})db.users.update({name:'zs'}, {$pull:{books:{name:'js'}}})
6.14 $pullAll数组更新操作符
1.$pullAll数组更新操作符$pullAll: 从数组字段中批量删除特定元素格式: {$pullAll: {<field>: [<value1>, <value2>, ...], ...}}
2.示例db.users.insert([{name:'zs', books:[{name:'html', price:66}, {name:'js', price:88}], tags:['html', 'js', ['1', '2']]},{name:'ls', books:[{name:'vue', price:99}, {name:'node', price:199}], tags:['a', 'b', 'ab', 'c', 'ac']}])db.users.update({name:'zs'}, {$pullAll:{tags:['html', 'js']}})
3.注意点// 注意点: 和$pull一样, 如果删除的是数字字段中的数组元素, 那么必须一模一样才能删除db.users.update({name:'zs'}, {$pullAll:{tags:[['2','1']]}})db.users.update({name:'zs'}, {$pullAll:{tags:[['1','2']]}})// 注意点: 和$pull不一样, 如果删除的是数组字段中的文档元素, 那么也必须一模一样才能删除db.users.update({name:'zs'}, {$pullAll:{books:[{price:66,name:'html'}]}})db.users.update({name:'zs'}, {$pullAll:{books:[{name:'html',price:66}]}})
6.15 和 和 和[]数组更新操作符
1.   $和$[]数组更新操作符$: 更新数组中满足条件的特定元素格式:db.<collection>.update({ <array field>:<query selector> }{ <update operator>: {'<array field>.$':value}})$[]: 更新数组中所有元素db.<collection>.update({ <update operator>: {'<array field>.$[]':value}})
2.示例db.users.insert([{name:'zs', books:[{name:'html', price:66}, {name:'js', price:88}], tags:['html', 'js', ['1', '2']]},{name:'ls', books:[{name:'vue', price:99}, {name:'node', price:199}], tags:['a', 'b', 'ab', 'c', 'ac']}])db.users.find()db.users.update({name:'zs', tags:'js'},{$set:{'tags.$':'JavaScript'}})db.users.update({name:'zs'},{$set:{'tags.$[]': 'wc'}})

第七章 MongoDB删除文档

7.1 删除文档
db.<collection>.remove(<query>, <options>)
<query>: 删除筛选条件
<options>: 删除额外配置

准备数据:

db.users.insert([{name:'zs', age:18},{name:'zs', age:19},{name:'ls', age:20},{name:'ls', age:21},{name:'ww', age:22},{name:'zl', age:23},
])

删除操作:

//删除所有满足条件
// 注意点: 和update方法不同, remove方法默认就会删除所有满足条件的数据
db.users.remove({name:'zs'})//删除第一个满足条件
db.users.remove({name:'ls'},{justOne:true})//删除所有文档
db.users.remove({})

第八章 MongoDB聚合

8.1 什么是聚合操作?
1.聚合操作就是通过一个方法完成一系列的操作
2.在聚合操作中, 每一个操作我们称之为一个阶段,聚合操作会将上一个阶段处理结果传给下一个阶段继续处理,所有阶段都处理完毕会返回一个新的结果集给我们
8.2 聚合操作格式
db.<collection>.aggregate(<pipeline>, <options>)
<pipeline>: 定义每个阶段操作
<options> : 聚合操作额外配置
8.3 聚管道阶段 $project
1.   $project: 对输入文档进行再次投影作用: 按照我们需要的格式生成结果集格式: {$project:{<field>:<value>}}
2.示例db.users.insert([{name:{firstName:'w', lastName:'c'}, age:18, book:{name:'玩转HTML', price: 88}},{name:{firstName:'x', lastName:'q'}, age:17, book:{name:'玩转JavaScript', price: 99}}])db.users.aggregate([{$project:{_id:0,clientName: '$name.firstName',clientAge: '$age'}}])
3.聚合表达式1)字段路径表达式$<filed>: 使用$来指示字段路径$<filed>.<sub-field>: 使用$和.来指示内嵌文档字段路径2)字段路径表达式示例$name$book.name
4.注意点:// 注意点: $project修改的是结果集而不是原有的集合db.users.find()// 注意点: 如果字段表达式使用了不存在的字段, 那么会自动用Null填充db.users.aggregate([{$project:{_id:0,fullName: ['$name.firstName', '$name.middleName','$name.lastName'],clientAge: '$age'}}])
8.4 聚合管道阶段 $match
1.聚合管道阶段$match: 和find方法中的第一个参数一样, 用于筛选符合条件的文档格式  : {$match:{<query>}}
2.示例db.users.aggregate([{$match:{  // 引用之前结果集中的数据时,如果是值,使用使用$name  如果是键,不需要使用$'name.firstName':'w'}}])db.users.aggregate([{$match:{'name.firstName':'w'}},{$project:{_id:0,clientName: '$name.firstName',clientAge: '$age'}}])
3.使用技巧:应该在聚合操作的最前面使用$match, 这样可以有效减少处理文档的数量, 大大提升处理的效率
8.5 聚合管道阶段 $limit
1.聚合管道阶段1)$limit: 和游标的limit方法一样, 用于指定获取几个文档格式  : {$limit:<number>}2)$skip : 和游标的skip方法一样, 用于指定跳过几个文档格式  : {$skip:<number>}
2.示例db.users.aggregate([{$skip:1},{$limit:1},{$project:{_id:0,clientName: '$name.firstName',clientAge: '$age'}}])
8.6 聚合管道阶段 $unwind
1.聚合管道阶段$unwind: 展开数组字段格式   : {$unwind:{path:<field>}}
2.示例:db.users.update({'name.firstName':'w'}, {$set:{tags:['html', 'js']}})db.users.update({'name.firstName':'x'}, {$set:{tags:'vue'}})db.users.aggregate([{$unwind:{path:'$tags'}}])
3.注意点:1)$unwind会为数组中的每个元素创建一个新的文档2)可以通过includeArrayIndex属性添加展开之后的元素在原数组中的位置db.users.aggregate([{$unwind:{path:'$tags',includeArrayIndex: 'index'}}])3)如果需要展开的字段不存在, 或者数组中没有元素, 或者为null, 会被unwind剔除db.users.insert([{name:{firstName:'san', lastName:'zhang'}, age:20},{name:{firstName:'si', lastName:'li'}, age:21, tags:[]},{name:{firstName:'wu', lastName:'wang'}, age:22, tags:null}])4)如果想让unwind不剔除不存在/没有元素/为Null的文档, 那么可以添加preserveNullAndEmptyArrays属性db.person.aggregate([{$unwind:{path:'$tags',includeArrayIndex: 'index',preserveNullAndEmptyArrays: true}}])
8.7 聚合管道阶段 $sort
1.聚合管道阶段$sort: 和文档游标sort方法一样, 对文档进行排序格式   : {$sort:{<field>>:1|-1}}
2.示例db.person.aggregate([{$sort:{age: 1}}])
8.8 聚合管道阶段 $lookup
1.聚合管道阶段1)$lookup: 用来做关联查询2)格式:{$lookup:{from: 关联集合名称,localField: 当前集合中的字段名称,foreignField:关联集合中的字段名称,as: 输出字段的名称}}
2.示例:db.users.insert([{name:{firstName:'Jonathan', lastName:'Lee'}, age:18, books:['html', 'js']},{name:{firstName:'Amelie', lastName:'Jiang'}, age:19, books:['vue']},{name:{firstName:'si', lastName:'Li'}, age:20, books:[]}])db.books.insert([{name:'html', price:88},{name:'js', price:99},{name:'vue', price:110},])db.users.aggregate([{$lookup:{from: 'books',localField: 'books',foreignField: 'name',as: 'booksData'}};])
3.和unwind阶段结合使用1)可以有效的过滤掉无效数据2)可以给每个匹配的结果生成一个新的文档db.users.aggregate([{$unwind:{path:'$books'}},{$lookup:{from: 'books',localField: 'books',foreignField: 'name',as: 'booksData'}}])
8.9 聚合管道阶段 $lookup
1.聚合管道阶段$lookup: 用来做关联查询格式   :{$lookup:{from: 关联集合名称,let: {定义给关联集合的聚合操作使用的当前集合的常量},pipeline: [关联集合的聚合操作]as: 输出字段的名称}}
2.示例:不相关查询db.users.aggregate([{$lookup:{from: 'books',pipeline: [{$match:{price:{$gte:100}}}],as: 'booksData'}}])相关查询db.users.aggregate([{$lookup:{from: 'books',let: { bks: '$books'},pipeline: [{$match:{$expr:{$and:[{$gte: ['$price', 100]},{$in: ['$name', '$$bks']}]}//price:{$gte:100}}}],as: 'booksData'}}])
3.系统变量表达式$$<variable>: 使用$$来指示系统变量
8.10 聚合管道阶段 $group
1.聚合管道阶段1)$group: 对文档进行分组2)格式:{$group:{_id:<expression>,<field1>: {<accumulator1>: <expression1>}... ...}}3)_id: 定义分组规则4)<field>: 定义新字段
2.示例db.users.insert([{name:'zs', age:10, city:'北京'},{name:'ls', age:20, city:'上海'},{name:'ww', age:30, city:'北京'},{name:'zl', age:40, city:'上海'},{name:'xq', age:50, city:'北京'},{name:'jjj', age:60, city:'广州'},])db.users.aggregate([{$group:{_id:'$city',totalAge:{$sum:'$age'},avgAge:{$avg:'$age'},minAge:{$min:'$age'},maxAge:{$max:'$age'},totalName:{$push:'$name'}}}])
8.11 聚合管道阶段 $out
1.聚合管道阶段1)$out: 前面阶段处理完的文档写入一个新的集合2)格式: {$out: <new collection name>}
2.示例:db.users.aggregate([{$group:{_id: '$city',totalAge: {$sum:'$age'},avgAge: {$avg: '$age'},minAge: {$min: '$age'},maxAge: {$max: '$age'},totalAges: {$push: '$age'}}},{$out:'newPerson'}])db.newPerson.find()
3.注意点:如果利用$out写入一个已经存在的集合, 那么集合中的原有数据会被覆盖
8.12 聚合操作额外配置
1.聚合操作额外配置1)db.<collection>.aggregate(<pipeline>, <options>)2)格式: {allowDiskUse: <boolean>}allowDiskUse默认取值是false, 默认情况下管道阶段占用的内存不能超过100M,如果超出100M就会报错3)如果需要处理的数据比较多, 聚合操作使用的内存可能超过100M, 那么我们可以将allowDiskUse设置为true4)如果allowDiskUse设置为true, 那么一旦超出100M就会将操作的数据写入到临时文件中, 然后再继续操作
8.13 字段路径表达式
1.字段路径表达式1)$<filed>: 使用$来指示字段路径2)$<filed>.<sub-field>: 使用$和.来指示内嵌文档字段路径
2.示例1)$name2)$book.name
3.系统变量表达式$$CURRENT: 表示当前操作的文档
4.示例$$CURRENT.name  等价于 $name
5.常量表达式$literal:<value> : 表示常量<value>
6.示例$literal:'$name' : 表示常量字符串$namedb.person.insert([{name:{firstName:'Jonathan', lastName:'Lee'}, age:18},{name:{firstName:'Amelie', lastName:'Wang'}, age:19}])db.person.find()db.person.aggregate([{$project:{_id:0,//myName:'$name.firstName', // 字段路径表达式//myAge:'$age' // 字段路径表达式//myName:'$$CURRENT.name.firstName', //系统变量表达式//myAge:'$$CURRENT.age' // 系统变量表达式myName:'$name.firstName',myAge:{$literal:'$age'} // 常量表达式}}])
8.14 数据类型转换操作符
1.数据类型转换操作符1)MongoDB对于文档的格式并没有强制性的要求, 同一个集合中存储的文档, 字段的个数和数据类型都可以不同对与文档的格式没有强制性的要求是MongoDB的一大优势, 但是同时也增加了数据消费端的使用难度因为我们在使用数据的时候, 有可能同一个字段取出来的数据类型是不同的, 这样非常不利于我们后续操作所以也正是因为如此, MongoDB在4.0中推出了$convert数据类型转换操作符2)通过$convert数据类型转换操作符, 我们可以将不同的数据类型转换成相同的数据类型,以便于后续我们在使用数据的过程中能够统一对数据进行处理
2.$convert格式{$convert:{input: '需要转换的字段',to: '转换之后的数据类型',onError: '不支持的转换类型',onNull: '没有需要转换的数据'}}
3.示例db.person.insert([{name:'zs', timestamp:ISODate('2020-08-09T11:23:34.733Z')},{name:'ls', timestamp:'2021-02-14 12:00:06 +0800  '},{name:'ww', timestamp:'  2023-04-01T12:00:00Z'},{name:'zl', timestamp:'1587009270000'},{name:'xq', timestamp:'Sunday'},{name:'xx'},])db.person.aggregate([{$project:{_id:0,timestamp:{$convert:{input:'$timestamp',to:'date',onError: '不支持的转换类型',onNull: '没有需要转换的数据'}}}}])

第九章 数据模型

9.1 文档之间关系
1.MongoDB对于文档的格式并没有强制性的要求, 但不等于我们不能在文档中表达数据的关系在MongoDB中我们可以通过'内嵌式结构'和'规范式结构'来表达文档之间的关系2.内嵌式结构
在一个文档中又包含了另一个文档, 我们就称之为内嵌式结构
例如:{name:'zs',age:'18',card:{num:'420626200002023556',date: 88}}
3.规范式结构
将文档存储在不同的集合中, 然后通过某一个字段来建立文档之间的关系, 我们就称之为规范式{_id: 1,num:'420626200002023556',date: 88}{name:'zs',age:'18',cardId: 1}
9.2 文档一对一关系
一个人有一张身份证
1.内嵌式结构db.person.insert({name:'zs',age:'18',card:{num:'420626200002023556',date: 88}})db.person.find({name:'zs'})优势: 一次查询就能得到所有数据劣势: 如果数据比较复杂, 不方便管理和更新应用场景: 数据不复杂/查询频率较高数据
2.规范式结构db.card.insert({_id: 123,num:'420626200002023556',date: '2022-12-08',userId: 456})db.person.insert({_id: 456,name:'zs',age:'18',cardId: 123})db.person.aggregate([{$lookup:{from: 'card',localField: 'cardId',foreignField: '_id',as: 'card'}}])优势: 如果数据比较复杂, 也方便管理和更新劣势: 查询数据相对内嵌结果稍微有点复杂应用场景: 数据比较复杂/更新频率较高数据
9.3 文档一对多关系
一个人有多本书
1.内嵌式结构db.person.insert({name:'zs',age:'18',books:[{name:'玩转HTML',price: 88},{name:'玩转CSS',price: 88}]})db.person.find({name:'zs'})优势: 一次查询就能得到所有数据劣势: 冗余数据较多, 不方便管理和更新应用场景: 数据不复杂/查询频率较高数据
2.规范式结构db.books.insert([{_id: 1,name:'玩转HTML',price: 88,userId:123},{_id: 2,name:'玩转CSS',price: 88,userId:123}])db.person.insert({_id: 123,name:'ls',age:'20',booksId:[1, 2]})db.person.aggregate([{$lookup:{from: 'books',localField: 'booksId',foreignField: '_id',as: 'books'}}])优势: 冗余数据较少, 更新较为方便劣势: 查询数据相对内嵌结果稍微有点复杂应用场景: 数据比较复杂/更新频率较高数据
9.4 文档多对多关系
一个学生有多个老师
一个老师有多个学生
1.内嵌式结构db.students.insert([{name:'zs', teachers:[{name:'taobao'}, {name:'baidu'}]},{name:'ls', teachers:[{name:'taobao'}, {name:'baidu'}]}])db.teachers.insert([{name:'taobao', students:[{name:'zs'}, {name:'ls'}]},{name:'baidu', students:[{name:'zs'}, {name:'ls'}]}])db.students.find({name:'zs'})db.teachers.find({name:'baidu'})优势: 一次查询就能得到所有数据劣势: 冗余数据较多, 更新和管理较为复杂应用场景: 数据比较简单/查询频率较高数据
2.规范式结构db.students.insert([{_id:1, name:'zs'},{_id:2, name:'ls'}])db.teachers.insert([{_id:3, name:'taobao'},{_id:4, name:'baidu'}])db.relation.insert([{stuId:1, teacherId:3},{stuId:1, teacherId:4},{stuId:2, teacherId:3},{stuId:2, teacherId:4}])db.students.aggregate([{$lookup:{from: 'relation',localField: '_id',foreignField:'stuId',as: 'relation'}},{$lookup:{from: 'teachers',localField: 'relation.teacherId',foreignField:'_id',as: 'teachers'}},{$project:{_id:0, name:1, teachers:1}}])优势: 冗余数据较少, 更新较为方便劣势: 查询数据相对内嵌结果稍微有点复杂应用场景: 数据比较复杂/更新频率较高数据

第十章 mongoose

对于原生node操作mongodb的使用,直接百度,用的时候,直接查,不需要记:

但是一般情况下,我们写项目时,会直接使用mongoose

10.1 mongoose使用
# 什么是Mongoose?
- 像操作JS对象一个,去操作集合
http://www.mongoosejs.net/
https://mongoosejs.com/# Mongoose和MongoDB映射关系
在Mongoose中JS中的一个模型就对应数据库中的一个集合
在Mongoose中JS中的一个对象就对应集合中的一个文档
在Mongoose中JS中的一个对象的属性就对应文档的一个字段# 使用步骤
// 1.定义集合规则
cosnt 规则名称 = new Schema({name: String,age: Number
});// 2.利用规则创建一个集合
let User = mongoose.model('User', userSchema);// 3.创建一个文档
let u = new User({name:'zs',age:18
});// 4.操作集合和文档
只要是通过Mongoose定义的模型, 那么Mongoose就会自动给这个模型添加很多操作集合和文档的方法
以后我们就可以直接通过模型操作集合, 通过模型创建出来的对象操作数据

代码如下:

// 1.导入mongoose
const mongoose = require('mongoose');/*
mongodb://MongoDB服务器IP地址:MongoDB服务器端口号/需要打开的数据库名称
* */
// 2.利用mongoose链接MongoDB服务器
mongoose.connect('mongodb://127.0.0.1:27017/demo');// 3.监听链接成功还是失败
let db = mongoose.connection;
db.on('error', (err)=>{console.log(err, '连接失败');
});
db.once('open', function() {console.log('连接成功');
});
db.once('close', function() {console.log('断开连接');
});// 1.定义集合中存储数据规则
let userSchema = new mongoose.Schema({name: String,age: Number
});
// 2.利用规则创建集合
// 注意点: 只要创建好了模型, 那么以后就可以使用模型来操作这个集合
// 注意点: mongoose会自动将我们指定的集合名称变成复数
let User = mongoose.model('User', userSchema);// 3.利用集合创建文档
// 注意点: 只要创建好了对象, 那么以后就可以使用对象来操作文档
let u = new User({ name: 'zs', age:18 });// 4.操作文档
u.save((err, product) => {if (!err){console.log('文档保存成功');console.log(product);}
});
10.2 CRUD
// 1.导入mongoose
const mongoose = require('mongoose');// 2.利用mongoose链接MongoDB服务器
mongoose.connect('mongodb://127.0.0.1:27017/blog');// 3.监听链接成功还是失败
let db = mongoose.connection;
db.on('error', (err)=>{console.log(err, '连接失败');
});
db.once('open', function() {console.log('连接成功');
});
db.once('close', function() {console.log('断开连接');
});// 1.定义集合中存储数据规则
let userSchema = new mongoose.Schema({name: String,age: Number
});// 2.利用规则创建集合
let User = mongoose.model('User', userSchema);// 增加
/*
User.create({name:'zs', age:666}, (err, result)=>{if(!err){console.log('插入成功');console.log(result);}
});
*/
/*
User.create([{name:'ls', age:18},{name:'ls', age:22},{name:'ww', age:21},{name:'zl', age:23},{name:'xq', age:33},],(err, result)=>{if(!err){console.log('插入成功');console.log(result);}
});*/
/*
(async ()=>{let result = await User.create([{name:'ls', age:18},{name:'ls', age:22},{name:'ww', age:21},{name:'zl', age:23},{name:'xq', age:33},]);console.log(result);
})();*/// 查询
/*
User.find({},{},(err, docs)=>{if(!err){console.log(docs);}
});*/
/*
User.find({},{_id:0, name:1, age:1},(err, docs)=>{if(!err){console.log(docs);}
});*/
/*
User.find({name:'ls'},{_id:0, name:1, age:1},(err, docs)=>{if(!err){console.log(docs);}
});*/
/*
User.find({},{_id:0, name:1, age:1},{ skip: 5, limit: 5},(err, docs)=>{if(!err){console.log(docs);}
});*/
/*
(async ()=>{let result = await User.find({},{_id:0, name:1, age:1},{ skip: 5, limit: 5});console.log(result);
})();
*/// 修改
/*
User.update({name:'ls'},{$set:{age:888}},(err, docs)=>{if(!err){console.log('更新成功');console.log(docs);}
});*/
/*
User.update({name:'ls'},{$set:{age:888}}, {multi: true},(err, docs)=>{if(!err){console.log('更新成功');console.log(docs);}
});*/
/*
(async ()=>{let result = await User.update({name:'ls'},{$set:{age:123}}, {multi: true});console.log(result);
})();*/// 删除
/*
User.remove({name:'ww'}, {}, (err, docs)=>{if(!err){console.log('删除成功');console.log(docs);}
});*/
/*
User.deleteOne({name:'xq'}, (err, docs)=>{if(!err){console.log('删除成功');console.log(docs);}
});*/
(async ()=>{let result = await User.deleteOne({name:'xq'});console.log(result);
})();

mongodb(可以当查询手册使用)相关推荐

  1. MongoDB 进阶-关联查询

    [苏州需要工作的加我QQ,内推介绍费平分] MongoDB 进阶 1.数据库命令 a.命令的工作原理 drop命令,在shell中删除一个集合,执行db.refactor.drop().其实这个函数实 ...

  2. MongoDB自定义条件查询案例

    MongoDB自定义条件查询案例

  3. MongoDB数据库(5.mongodb的聚合操作以及mongodb的高级查询2)

    聚合aggregate 聚合(aggregate) 是基于数据处理的聚合管道,每个文档通过一个由多个阶段(stage) 组成的管道,可以对每个阶段的  管道进行分组.过滤等功能,然后经过一系列的处理, ...

  4. php mongodb 子查询,MongoDB数组子查询elemMatch

    MongoDB数组子查询elemMatch 在 MongoDB数组子查询elemMatch详解 语法 db_name.collection_name.find({field:{$elemMatch:{ ...

  5. SQL Server 语句查询手册

    建表: CREATE TABLE  [DB.dbo].tableName (Stud_id int CONSTRAINT  constraintName1  not null primary key, ...

  6. MongoDB文档查询操作(三)

    关于MongoDB中的查询,我们已经连着介绍了两篇文章了,本文我们来介绍另外一个查询概念游标. 本文是MongoDB系列的第七篇文章,了解前面的文章有助于更好的理解本文: 1.Linux上安装Mong ...

  7. python数据库-mongoDB的高级查询操作(55)

    一.MongoDB索引 为什么使用索引? 假设有一本书,你想看第六章第六节讲的是什么,你会怎么做,一般人肯定去看目录,找到这一节对应的页数,然后翻到这一页.这就是目录索引,帮助读者快速找到想要的章节. ...

  8. Qt开发技术:Qt富文本(三)Qt支持的HTML子集(查询手册)以及涉及的类

    若该文为原创文章,未经允许不得转载 原博主博客地址:https://blog.csdn.net/qq21497936 原博主博客导航:https://blog.csdn.net/qq21497936/ ...

  9. mongodb 之 模糊查询

    mongodb的模糊查询需要熟练掌握 正则匹配($regex),和全文检索($text),或者直接使用正则表达式. 一.mongodb中正则($regex)的用法: 1.{field:{$regex: ...

  10. MongoDB 地理空间查询

    本章介绍以下几点: 地理空间数据 地理空间索引 地理空间查询 地理空间模型 例子 MongoDB支持对地理空间数据的查询操作.本节介绍MongoDB的地理空间特性. 1.地理空间数据 在MongoDB ...

最新文章

  1. 用gulp构建你的前端项目
  2. JMeter断言介绍
  3. linux文本工具总结,Linux 文本工具
  4. java dll 乱码_java调用c++ dll出现中文乱码 | 学步园
  5. python 赋值方法_基于Python List的赋值方法
  6. mac下加速下载百度云资源
  7. 把栏杆拍遍--辛弃疾
  8. js html title属性,HTML DOM title 属性
  9. CSAPP-Lab02 Bomb Lab 详细解析
  10. 一段很有意思的代码!!
  11. 使用AI技术获取图片文字与识别图像内容
  12. NO2.高可用搭建-mysql安装和双主配置
  13. java线程池是如何复用线程_线程池如何复用一个线程-- ThreadPoolExecutor的实现(未完)...
  14. 高仿it之家新闻客户端源码
  15. sql语句查询 近7天,三十天数据
  16. rj45接口定义/rj45针脚定义/rj45引脚定义
  17. 基础地理信息术语(a-b-c-d-e-f-g-h-i-k-l-m-n-o-t-u-v-w-x-y-z)
  18. [转载]设置、修改、重设mysql root密码
  19. 成功代练者必备的技能与装备
  20. PHP项目:基于php+thinkphp的网上书店管理系统

热门文章

  1. clearInterval()仍然执行剩余代码的解决方案
  2. Dropbox推广链接
  3. qduoj 140 题目6 142 ycb的ACM进阶之路
  4. 将iPhone打造成数码单反相机
  5. 段永平连答53问:不做不对的事,比做对的事更重要
  6. java比较字符串大小写_java 字符串中判断字母大小写方法
  7. 怎样才能选好贵金属交易平台
  8. 深度学习-多任务学习总结
  9. (uni-app)微信小程序之腾讯地图(定位当前位置,地图标点及导航)
  10. 【译】Databricks使用Spark Streaming和Delta Lake对流式数据进行数据质量监控介绍