MongoDB文档操作
一、插入并保存文档
1.1 insert()方法
db.COLLECTION_NAME.insert(document)
insert()方法是向文档中插入数据最基本的方法,该方法参数接受一个文档,将文档加入到目标集合中。
如将文档{name:"xxx",age:25}插入集合foo中,只需要如下操作:
db.foo.insert({name:"xxx",age:25})
就可在集合中插入该文档。
需要注意的是,因为我们插入的文档中没有"_id"键,所以这个操作会给文档增加一个自动生成的"_id"键,然后再将其保存在数据库中。这个键并不是地址,也不是简单递增的,而是通过"时间戳+机器编号+进程编号+序列号"生成,所以每个"_id"都是唯一的。如下图所示"_id"为:5bbd4dd38f06ac759d5e8a00,就可分解为时间戳(前四字节):5bbd4dd3 + 机器号(三字节):8f06ac + 进程编号(两字节):759d + 序列号(三字节):5e8a00 。
1.2 save()方法
save()是一个shell函数,它不仅仅支持插入操作,还支持更新操作,它只有一个参数:文档,说的更详细些的话,则是该文档的"_id"键的值,如果已有一个文档具有相同的"_id"键值,则执行更新操作,具体则是调用upsert()函数,否则则调用insert()执行更新操作。
-
db.collection.save(
-
<document>,
-
{
-
writeConcern: <document>
-
}
-
)
1.3 批量插入
除了insert()方法外,还可使用batchInsert()方法实现批量插入,该函数与insert()方法不同的地方在于,它接受一个文档数组作为参数。
例子:在集合foo中插入{name:"aaa",age:20}, {name:"bbb",age:25}, {name:"ccc",age:30}三条文档:
-
db.foo.batchInsert([{name:"aaa",age:20}, {name:"bbb",age:25}, {name:"ccc",age:30}])
1.4 插入原理与方法
当执行数据插入的时候,驱动程序会把数据转化成BSON格式,然后将数据导入数据库。数据库会解析BSON,并对其进行最基本的检查:检查文档的基本结构。如文档大小(应小于16MB),"_id"字段等。因此非法数据是无法被甄别的,所以需要对数据源的数据进行判断,或者在数据插入数据库之前做数据校验。
二、删除文档
2.1 remove()方法
-
db.collection.remove(
-
<query>,
-
<justOne>
-
)
MongoDB v2.6版本对remove()函数进行了更新,修改了参数。
-
db.collection.remove(
-
<query>,
-
{
-
justOne: <boolean>,
-
writeConcern: <document>
-
}
-
)
该函数的所有参数都为可选参数,如果全为空,则代表删除集合里的所有文档。
- query :(可选)删除的文档的条件。
- justOne : (可选)如果设为 true 或 1,则只删除一个文档。
-
writeConcern :(可选)抛出异常的级别。
2.2 deleteOne()以及deleteMany()
deleteOne()以及deleteMany()可以认为是remove()方法的拆解,deleteOne()删除满足条件的一个文档,deleteMany()删除满足条件的所有文档
-
db.collection.deleteOne(
-
<filter>,
-
{
-
writeConcern: <document>,
-
collation: <document>
-
}
-
)
-
-
db.collection.deleteMany(
-
<filter>,
-
{
-
writeConcern: <document>,
-
collation: <document>
-
}
-
)
2.3 drop()
drop()方法并不对文档进行操作,而是直接删除集合,该函数适用于上文remove()无参数的情况,该函数直接删除集合比删除所有的文档效率更高。
-
db.collection.drop( { writeConcern: <document> } )
三、文档更新
3.1 原子性
在MongoDB中,更新单个的文档操作是原子性的。默认情况下,如果一个update()更新多个文档,那么对每个文档的更新是原子性的,但是对整个update而言则不是原子性的。有可能存在前一个文档更新成功,后面的文档更新失败的情况。由于单个文档的更新是原子性的,如果两个更新同时发生,就会出现阻塞,先到的先执行,所以文档最终结果由靠后的操作决定。
3.2 update()方法
-
db.collection.update(
-
<query>,
-
<update>,
-
{
-
upsert: <boolean>,
-
multi: <boolean>,
-
writeConcern: <document>,
-
collation: <document>,
-
arrayFilters: [ <filterdocument1>, ... ]
-
}
-
)
update()方法有两个参数,一个是查询文档,用于定位需要更新的目标文档;另一个是修改器,用于说明要对找到的文档进行哪些修改。
3.3 文档替换
替换操作是用一个新的文档替换旧的文档,这一般用于修改比较大的情况。该操作需要一个中间变量来存储新值,最后再将旧值覆盖。
下图使用该操作完成了文档值的更新操作:将"xi"的信息替换为"xixi",当然,这存在着更好的方法。
另外,使用update()更新时最好指定一个唯一的键,如"_id",以防匹配到相同的字段导致跟新失败。
3.4 修改器
3.4.1 $set
"$set"可以完成特定字符的修改,如用来指定一个字段的值。如果该字段不存在,则创建它。
下图对"name":"ahn"文档中的"status"值进行修改,将原来的"A"改为"D"。
"$set"还可用来修改键的类型以及内嵌文档,使用"$unset"还可将键删除。
3.4.2 $inc
"$inc"可以对数据进行增加或减少,这个操作只针对数字类型,如整形、长整形与双精度浮点型。如果操作的该键不存在,则创建一个。
如将info集合中name: "wangsan"文档对应的age减2:
3.5 数组修改器
"$push"将数据插入数组末尾,如果没有该数组,则创建一个。
如在info集合中数组元素的追加,对name为'bob'的文档添加一个数组元素。
"each"与"$push"配合使用,可以一次性push多个值。
-
db.info.update({ "name" : "bob"},{$push: {"detail": {$each: [{"city" : "Beijing"},{ "city" : "Shanghai"}]}}})
上诉操作可在"detail" 中插入两个值。
"$slice"+"$push"可规定数组长度,"$slice"的值必须为负整数。
"$sort"则可根据某字段的值对所有对象进行排序。
3.6 数组作为数据集
"$addToSet"向数组中添加元素,如果元素已经存在就不添加。
"$addToSet"+"$set":添加多个不重复的值。
3.7 删除数组元素
"$pop"将数组看成队列,每次pop都删除一个元素。{"$pop":{"key":1}}从数组末尾删除一个元素,"key":-1则从头部删除一个元素。
"$pull"可根据条件来删除元素。如db.foo.update({},{"$pull":{"class":"English"}})可将class数组中的"English"元素删除。
3.8 基于位置的数组修改器
3.8.1 通过位置
数组下标可直接作为键来选择元素,且以0开头。
如过我们要将上图文档中detail数组里第一个元素的"class"元素改为"0701",则可用下面的方法:
-
db.info.update({ "name" : "bob"},{$set: {"detail.0.class":"0701"}})
3.8.2 定位操作符
但很多时候,我们并不知道需要修改的数组的下标,所以我们就需要使用定位操作符"$",用来定位查询文档已经匹配的数组元素,并进行更新。
如上面的修改"class",使用定位符如下所示:
另外,定位符只更新匹配到的第一个元素。
3.9 修改器速度
最初将文档插入到MongoDB时,一次插入的文档在磁盘上的位置是相邻的,且文档之间没有多余的空间。因此当一个文档变大时,原有的位置就无法放下该文档了,所以这个文档就会被移动位置。
文档创建之初:(该示例源于《MongoDB权威指南》p.43)
当对中间的文档的数据进行增加时,这个文档就会被移动到文档尾部:
当MongoDB不得不移动文档时,它会修改集合的填充因子——为每个新文档预留的增长空间。初始化时,填充因子为1,即完全没有多余的空间。在执行完上面的操作后,填充因子扩大为1.5,即每个新加入的文档在之后预留自身1/2大小的空间。如果文档更新操作频繁,移动次数多,填充因子就会增大。反之,不再有文档移动,填充因子就会缓慢减小。
这也是有时push成为系统瓶颈的原因,文档更新造成文档磁盘结构的变化,导致大量的硬盘读写操作。
3.10 多个文档的更新
默认的update操作只会更新第一个匹配,要对数据进行批量更新,则需要使用update的第四个参数,将其设为true,表示是否更新全部查到的文档。而有第四个就需要第三个,第三个参数也接收bool类型,表示是否要将我们更新的文档作文新文档插入,默认为false。
例子:将info集合中将所有status为"B"对应的文档值的status全部改成"B+":
参考文献:
《MongoDB权威指南》 人民邮电出版社
《NoSQL数据库原理与应用》 学校编著