Skip to content

mongo基础学习

主键

  • MongoDB的主键和MySQL一样,也是用于保证每一条数据的唯一性

  • 和MySQL不同的是,MongoDB中的主键无需明确指定

    • 灭一个文档被添加到集合之后,MongoDB都会自动添加主键
    • MongoDB中文档主键的名称叫做 _id
  • 默认情况下文档主键是一个ObjectId类型数据

    • ObjectId类型是一个12个字节字符串
      • 4字节存储这条数据的时间戳
      • 3字节存储这条数据的是属于哪台电脑的标识符
      • 2字节存储这条数据的MongoDB进程id
      • 3字节是计数器
  • 为什么要使用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># 额外配置
)

  1. 通过update覆盖满足条件的数据

默认情况下如果<update>没有使用更新操作符, 那么就会使用指定的内容覆盖符合条件的内容, 如果只是想单纯的更新某个字段,需要使用更新操作符$set

  1. update方法,默认只更新满足条件的第一个文档

想要更新满足条件的文档 需要在<options>进行配置如下


{
    // 批量更新
    multi:true
}

注意点:

如果指定了multi,就必须在第二个参数中使用更新操作符

  1. 如果在使用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:{字段名名称:'',...}}

  1. 删除普通字段

db.集合名称.update({name:'ls'},{$unset:{scores:''}})

# 如果使用unset删除某一个字段,那么后面赋值任何内容都不影响

  1. 删除文档字段中的字段
db.集合名称.update({name:'ls'},{$unset:{'book.name':''}})

  1. 删除数组中的字段

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使用技巧
  1. 可以将外层的字段转移到内层的文档中

db.集合名称.update({name:'test',{$rename:{age:'book.age'}}})

  1. 可以将内层的文档转移到外层的文档中

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'}}})

  • 文档删除成功的几项标准原则
  1. 只要包含文档中的一个元素即可删除掉文档
  2. 不需要保证百分百一致顺序可以颠倒

$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({})

Released under the MIT License.