Appearance
mongo基础学习
主键
MongoDB的主键和MySQL一样,也是用于保证每一条数据的唯一性
和MySQL不同的是,MongoDB中的主键无需明确指定
- 灭一个文档被添加到集合之后,MongoDB都会自动添加主键
- MongoDB中文档主键的名称叫做 _id
默认情况下文档主键是一个ObjectId类型数据
- ObjectId类型是一个12个字节字符串
- 4字节存储这条数据的时间戳
- 3字节存储这条数据的是属于哪台电脑的标识符
- 2字节存储这条数据的MongoDB进程id
- 3字节是计数器
- ObjectId类型是一个12个字节字符串
为什么要使用ObjectId类型数据作为主键?
因为MongoDB是支持'横向扩展'的数据库
横向扩展是指'增加数据库服务器的台数'
纵向扩展是指'增加数据库服务器的配置'
是否可以指定其他类型作为主键
- 除了数组类型以外的其他类型都可以作为主键
- 在使用文档类型作为主键的时候,一定要保证文档类型的一致性
写入文档
db.集合名称.insertOne(
文档,
{
writeConcern:安全级别
}
)
- 安全级别
用于判断数据是否写入成功
安全级别越高,丢失数据风险越小,但是性能的消耗就越大
默认情况下MongoDB会开启默认的安全级别,先不用关心
如果插入的集合不存在就会自动创建一个集合
其他写入数据的方式
db.集合名称.save(
文档,
{
writeConcerb:文档安全级别
}
)
- insertOne和save的区别 insertOne如果插入的主键重复会报错,而save会直接覆盖掉原来的主键数据
写入多个文档
db.文档名称.insertMany(
[文档1,文档2],
{
writeConcerb:文档安全级别,
ordered:是否按顺序写入(默认是true)
}
)
注意点:
如果文档内存在重复的主键,按照顺序(ordered:true)去写入数据的时候,后面的文档数据就不会再写入
不按照顺序写入(ordered:false),后面的文档会被写入
查询
基本查询
db.集合名称.find(
查询条件,
投影条件
)
查询条件:相当于Mysql中的where
投影条件:相当于Mysql中按字段查询
查询条件同时指定多个的情况下默认是and关系:
db.stu.find(
{name:'ls',age:17}
)
# 同时要满足name为ls并且age是17岁的才可以
# 如果文档下面还有一个文档
{
name:'ls',
age:99,
book:{
name:'html'
}
}
db.stu.find(
{'book.name':'html'}
}
)
投影条件:
# 不显示需要通过0和1区分
db.stu.find({},{_id:0}) # 不显示_id
默认情况下所有字段都为1
除了_id以外,其他的字段不能同时出现0和1
db.stu.find({},{_id:0,age:1,name:0}) # 错误的指定方式
比较操作符
和MySQL一样MongoDB中也支持比较操作符
$eq:等于 / $ne:不等于
$gt:大于 / $gte:大于等于
$lt:小于 / $lte:小于等于
默认情况下等于可以不需要写
db.集合名称.find(
{字段名:{比较操作符:值}},
投影条件
)
注意点
$ne 在做不等于字段中,如果某个字段不存在也算不等于
其他运算符
$in:匹配和任意指定值相等的文档
$nin:匹配和任意指定值不相等的文档
db.集合名称.find(
{
字段名称:{$in/$nin:[需要匹配的数组]}
}
)
注意点
和$ne一样如果没有需要判断的字段,也算作满足条件
逻辑操作符
$not: 匹配条件不成立的文档
{<field>:{$not:{条件}}}
$and: 匹配条件同时成立的文档
{$and:[{条件1},{条件2}]}
$or: 匹配条件只满足其中一个的文档
{$or:[{条件1},{条件2}]}
$nor: 匹配条件不在两个条件之间的数据
{$nor:[{条件1},{条件2}]}
字段操作符
$exists: 查询包含某个字段的文档
{字段名:{$exists:true}} #一般配合前面的运算符使用
db.stu.find(
{
gender:{$ne:'男',$exists:true}
}
)
- 一般配合
$ne/$nin/$nor/$not来清理数据
$type: 查询某个字段类型的文档
{字段名名:{$type:'string'}}
数组操作符
$all: 匹配数组中包含所有指定查询值的文档
{字段名:{$all:['数据1','数据2']}}
$elemMatch : 匹配数组中至少有一个能完全匹配所有的查询条件
{class:'one',students:[{name:'ww',age:19},{name:'ls',age:18}]}
{class:'two',students:[{name:'ww',age:18},{name:'ls',age:19}]}
// 查询出 有名称叫ww,并且年龄是18岁的这个学生的班级
db.stu.find({students:{$elemMatch:{name:'ww',age:18}}})
运算操作符
{字段名名称:{$regex:/pattern/,$options:'<options>'}}
{字段名名称:{$regex:/pattern/}}
{name:'zz'}
{name:'Z12'}
{name:'L12'}
{name:'l2'}
# 查询出所有姓z的人
db.stu.find(
{name:{$regex:/^z/,$options:'i'}}
)
# 查询出所有是z或者l的人
db.stu.find({
name:{$in:[/^z/i,/^l/i]}
})
文档游标
为什么学习前端要学习MongoDB
- MongoDB中原生就支持JavaScript,也就是我们可以直接在MongoDB中混入JS代码
var arr = [];
for(var i = 0;i<100;i++){
arr.push({name:'sasa'+i})
}
db.stu.insertMany(arr);
什么是文档游标
我们在执行find方法后,find方法是有返回值的,find方法会返回一个文档游标(相当于C语言的指针)
文档游标的常用方法
hasNext():是否还有下一个文档
next(): 取出下一个文档
forEach(): 依次取出所有文档
var data = db.stu.find()
data.forEach(printjson)
注意点:
默认情况下通过文档游标遍历完成所有文档后,系统会在10分钟后自动关闭文档游标, 如果不想自动关闭,我们可以通过noCursorTimeout()函数来保持游标一直有效
var cursor = db.stu.find().noCursorTimeout()
如果想手动关闭游标,我们也可以通过close函数手动关闭游标
cursor.close()
分页函数
cursor.limit(<number>):要取出多少个文档
cursor.skip(<number>): 跳过几个文档开始取
注意点:
我们可以直接在find方法后直接调用limit和skip方法
MongoDB是支持链式调用的
在链式操作中,无论skip在limit前面还是后面都会先执行skip
排序函数
cursor.sort({field:ordering,...}) 按照指定的规则排序
ordering 为1表示升序排序
ordering 为-1表示降序排序
注意点:
默认情况下find只会返回100个文档
sort函数如果和limit/skip一起使用的时候,无论sort函数写在前面还是后面,都会先执行sort函数
统计函数
cursor.count()
注意点:
count函数可以接收一个applySkipLimit参数,通过这个参数可以告诉MongoDB是否需要忽略Skip和Limit 默认情况下取值为false,表示忽略Skip和Limit
如果在使用find查询数据的时候,没有指定查询的条件,那么默认情况下在一台电脑上通过count函数统计的结果是准确的。
如果现在是分布式(在多台电脑上的),那么通过count函数统计的结果就不一定准确了
结论:只要需要使用count函数来统计文档个数,那么在查询的时候一定要加上条件,这样无论在什么样的环境下都是准确的了
更新
MongoDB中有三个常用的更新方法:save()/update()/findAndModify()
save方法
save用于往集合里面添加一个新的文档或者覆盖文档
当没有指定文档就创建一个文档, 指定了_id存在就将当前的文档覆盖掉
update方法
db.文档名称.update(
<filter>,# 筛选条件
<update>,# 新的内容
<options># 额外配置
)
- 通过update覆盖满足条件的数据
默认情况下如果<update>没有使用更新操作符, 那么就会使用指定的内容覆盖符合条件的内容, 如果只是想单纯的更新某个字段,需要使用更新操作符$set
- update方法,默认只更新满足条件的第一个文档
想要更新满足条件的文档 需要在<options>进行配置如下
{
// 批量更新
multi:true
}
注意点:
如果指定了multi,就必须在第二个参数中使用更新操作符
- 如果在使用update方法中,在第二个参数指定了_id,那么久必须保证和被更新文档的_id的取值一致 若不一致就会报错
- 开发技巧:在企业开发中需要使用update方法,那么就不要在第二个参数指定_id
更新操作符$set
默认情况下update会使用新的文档覆盖掉旧文档
如果我们不想覆盖而是仅仅想更新其中的某些字段
那么我们就要使用update的更新操作符
使用
$set:更新或者新增字段,字段存在就是更新,字段不存在就是新增
格式:{$set:<value1>,...}
db.stu.update(
{name:'zs'},{$set:{name:'ww'}}
)
更新内嵌文档或数组
{name:'ls',book:{name:'html',price:12}}
{name:'ww',infos:['html','js']}
db.stu.update({name:'ls'},{$set:{'book.name':'js'}})
db.stu.update({name:'ww'},{$set:{'infos.0':'vue'}}})
注意点:
如果操作的字段存在,就是更新,如果操作的字段不存在,那么就是更新
如果操作的是数组字段,如果操作的索引不存在,也会自动新增,如果被操作的索引前面没有数据,那么会自动用null来填充
更新操作符$unset
$unset: 删除字段
格式:{$unset:{字段名名称:'',...}}
- 删除普通字段
db.集合名称.update({name:'ls'},{$unset:{scores:''}})
# 如果使用unset删除某一个字段,那么后面赋值任何内容都不影响
- 删除文档字段中的字段
db.集合名称.update({name:'ls'},{$unset:{'book.name':''}})
- 删除数组中的字段
db.集合名称.update({name:'ls'},{$unset:{'infos.0':''}})
# 如果删除的是数组字段元素,并不会修改数组的长度,而是用null来填充删除内容
更新操作符$rename
$rename : 重命名字段
格式:{$rename:{字段名名称:新名称}}
# 普通文档修改
db.集合名称.update({name:'zs'},{$rename:{name:'myName'}})
# 文档下面的字段修改,取值必须写上层级关系
db.集合名称.update({name:'zs'},{$rename:{'book.name':'book.bookRename'}})
注意点:
如果要操作的字段不存在不会做任何操作
如果重命名之后的名称在表内已经存在了,那么已经存在的字段名称就会被删除:
- 底层的本质:先调用了$unset删除了原有的字段,再调用$set修改表字段名称
不能通过$rename更新操作符来操作数组
tags:[{name:'html','price':99},{name:'js',price:88}]
db.集合名称.update({name:'test',{$rename:{'tags.0.name':'tags.0.book'}}})
# 不允许这样操作的,会报错
- $rename使用技巧
- 可以将外层的字段转移到内层的文档中
db.集合名称.update({name:'test',{$rename:{age:'book.age'}}})
- 可以将内层的文档转移到外层的文档中
db.集合名称.update({name:'test',{$rename:{'book.age':'age'}}})
$inc和$mul更新操作符
$inc:更新字段值(增加或减少字段保存的值) 格式:{$inc:{<field>:正负整数}}
$mul:更新字段值(乘以或除以字段保存的值)
格式:{$mul:{<filed>:整数为乘以,小数相当于除以}}
注意点:
如果操作的字段不存在,那么会自动更新
如果$inc那么不仅仅会新增,还会将操作的赋值给新增的字段
如果$mul那么只会新增字段,不会讲操作的赋值给新增的字段,他会用来填充0来填充
$inc/$mul只能操作数值类型
$min和$max操作符
- $min:比较保留更小的字段值
格式:{$min:{<field>:条件}}
- $max:比较保留更大的字段值
格式:{$max:{<field>:条件}}
注意点
如果操作的字段不存在,那么会自动增加,并且会将操作的值赋值给新增的字段
和$inc/$mul不同,$min/$max不仅仅能操作数值类型的字段,只要是可以比较的字段都可以操作
不是相同的数据类型也可以进行比较,同样可以更新
内部顺序表(从小到大):
NULL,
Numbers(ints,longs,doubles,decimals),
Symbel String,
Object,
Array,
BinData,
ObjectId,
Boolean,
Date,
Timestamp,
Regular Expression
$addToSet数组更新操作符
给数组中新增元素:
{$addToSet:{<field>:值}}
注意点:
如果操作的元素不存在,那么会自动新增,并将操作的值赋值给新增的数组字段
$addToSet会自动去重,如果添加的元素已经存在了,那么就不会添加了
如果往数组字段中添加的是文档类型,必须一模一样才会去重,顺序不一样也不会去重
如果往数组字段中添加的是数组,那么一模一样才会去重
如果往数组字段中添加的是数组,那么默认情况下会将整个数组作为一个元素添加进去, 如果不想读一个整体添加进去,那么必须使用$each添加 {$addToSet:{tags:{$each:[1,2,3]}}}
$push数组更新操作符
$push:向数组字段中添加元素(不去重)
格式:{$push:{tags:值}}
$pop数组更新操作符
$pop : 从数组中删除元素
格式:{$pop:{<field>:<1|-1>,...}}
1为删除最后一个元素
-1为删除第一个元素
# 删除最后一个元素
db.person.update({name:'zs'},{$pop:{tags:1}})
# 删除第一个元素
db.person.update({name:'zs'},{$pop:{tags:-1}})
注意点:
通过$pop删除数组字段中的元素,哪怕数组已经删除空了,也会保留空的数组
$pull数组更新操作符
$pull:从数组字段中删除特定的元素
格式:{$pull:{$field:<value|condition>,...}}
{
name:'zs',
books:[{name:'html',price:66}],
tags:['1','2','html',['1','2']],
info:['a','b','ac','bc','abc']
}
# 可以给一个具体的值
db.集合名称.update({name:'zs'},{$pull:{tags:'html'}})
# 可以使用正则表达式删除
db.集合名称.update({name:'zs'},{$pull:{info:/^a/}})
注意点:
如果删除的元素是一个数组,那么必须一模一样才能删除
# 删除失败,数组的顺序与文档中的数组不一致
db.集合名称.update({name:'zs'},{$pull:{tags:['2','1']}})
如果删除的元素是一个文档,那么不需要一模一样就能删除
# 可以删除成功
db.集合名称.update({name:'zs'},{$pull:{books:{name:'html'}}})
- 文档删除成功的几项标准原则
- 只要包含文档中的一个元素即可删除掉文档
- 不需要保证百分百一致顺序可以颠倒
$pullAll数组更新操作符
$pullAll:从数组字段中批量删除特定元素
格式:{$pullAll:{<field>:[<value1>,<value2>,...]}}
db.集合名称.update({name:'zs'},{$pullAll:{tags:['1','2']}})
注意点:
和$pull一样,如果删除的是数组字段中的数组元素,必须一模一样才能删除
和$pull不一样,如果删除的是数组字段中的文档元素,也必须要一模一样才能删除
$和$[]数组更新操作符
$ : 更新数组中满足条件的特定元素
db.集合名称.update(
{
name:'zs',tags:'html'
},
{
$set:{'tag.$':'javaScript'}
}
)
$[]: 更新数组中所有的元素为指定的值
db.集合名称.update(
{name:'zs'},
{$set:{'tags.$[]':'info'}}
)
删除文档
db.文档名称.remove(
{name:'zs'},# 删除的筛选条件
{} # 删除的配置项
)
注意点:
和update不通,remove方法默认删除所有满足条件的数据
# 只删除第一个满足条件的文档
db.文档集合.remove({name:'zs'},{justOne:true})
# 删除集合中的所有文档
db.集合名称.remove({})