MongoDB增删改查
文档插入
插入是向MongoDB添加数据的基本方法,可使用 insert()
向目标集合插入一个文档,插入操作会给文档自动添加一个 _id
键。
db.doc.insert({bar:baz})
插入原理
当执行db.docname.insert()
操作时,使用的驱动程序会将数据转换成BSON格式,然后将其送入MongoDB。数据库解析BSON,检验是否包含_id
键,并且文档是否超过4MB,除此之外不做别的数据验证,只是简单地将文档原样存入数据库。此做法的缺陷是允许插入无效数据,优势是能让数据库更加安全原理注入式攻击。
主流语言的驱动在传送数据前会进行一些书有限性检查,例如文档是否超长,是否包含非UTF-8字符,是否使用未知类型等。要是对使用的驱动拿捏不准,可在启动数据库服务器的使用 --objcheck
选项,这样服务器会插入前先检查文档结构的有效性,但会牺牲些性能。
插入文档
db.docname.insert({})
db.persons.find({_id:'A01', name:'alice'});
插入校验
插入数据时MongoDB只对数据进行最基本的检查:检查文档基本结构,若无_id
字段则自动增加一个。
检查大小就是其中一项基本结构检查:所有文档必须小于16MB(人为设定,不同版本不同。)。作为这样的限制主要是为了防止不良的模式设计,并保持性能一致。
若要查看 doc 文档的 BSON 大小(单位为字节),可在 shell 中执行 Object.bsonsize(doc)
。
Object.bsonsize(db.user)
398
批量插入
db.users.insert([
{sn:'002', name:'ben'},
{sn:'003', name:'carl'}
])
在Shell中可使用 batchInsert()
实现批量插入。
db.foo.batchInsert([{_id:0}, {_id:1}, {_id:2}])
批量插入多个文档即传递一个由文档构成的数组给MongoDB。批量插入可使用 mongoimport
导入。不同前版本MongoDB消息长度不同。
若执行批量插入过程中有一个文档插入失败,那么在这个文档之前的所有文档都会成功插入到集合中,而这个文档以及之后的所有文档全部插入失败。
db.docname.insert([{},{}...])
此种方式是无法实现批量插入的,shell并不支持这种语法。若要完成批量插入可使用mongo的应用驱动或使用shell的for循环来完成。
在mongo3.4版本中已经支持此种批量插入的方式。
mongo --version
MongoDB shell version v3.4.10
db.version();
3.4.10
db.persons.drop()
db.persons.insert([{name:'alice'}, {name:'ben'}])
db.persons.find()
{
"_id" : ObjectId("5a0b058a61bf6b35a74b0fb1"),
"name" : "alice"
}
{
"_id" : ObjectId("5a0b058a61bf6b35a74b0fb2"),
"name" : "ben"
}
save 与 insert 操作有何区别呢
当遇到_id相同的情况下,save完成保存操作,insert则会报错。
db.persons.insert([
{_id:'A001', name:'alice'},
{_id:'A002', name:'ben'}
]);
db.persons.insert({_id:'A001', name:'carl'})
db.persons.save({_id:'A001', name:'carl'})
{
"_id" : "A001",
"name" : "carl"
}
插入多维数据
# 向goods集合中添加文档
db.goods.insert({
name:'xiaomi5',
price:1500,
weight:159,
number:100,
area:{
privince:"beijing",
city:"beijing",
}
});
# 向goods集合中添加文档
db.goods.insert({
name:"xiaoxi note",
price:2000,
weight:150,
number:100,
area:{
privince:"beijing",
city:"beijing"
},
color:["red","green","blue"]
})
# 查询goods集合中所有的文档
db.goods.find()
{
"_id" : ObjectId("5a09b56d61bf6b35a74b0fa8"),
"name" : "xiaomi5",
"price" : 1500.0,
"weight" : 159.0,
"number" : 100.0,
"area" : {
"privince" : "beijing",
"city" : "beijing"
}
}
{
"_id" : ObjectId("5a09b63561bf6b35a74b0fa9"),
"name" : "xiaoxi note",
"price" : 2000.0,
"weight" : 150.0,
"number" : 100.0,
"area" : {
"privince" : "beijing",
"city" : "beijing"
},
"color" : [
"red",
"green",
"blue"
]
}
插入并保存文档
db.users.insert({"username":"alice", "nickname":"alan", "realname":"lily"})
db.users.findOne()
{
"_id" : ObjectId("5a04ca939486f0b25bd10143"),
"username" : "alice",
"nickname" : "alan",
"realname" : "lily"
}
文档删除
db.doc.remove()
- 查询表达式依然是json对象
- 查询表达式匹配的行将被删除
- 若无查询表达式则集合中所有文档将被删除
# 删除user文档中性别为男的一条集合
db.user.remove({gender:'m'}, true)
删除集合doc中所有文档,但不会删除集合本身和集合的元信息。remove()
可接受一个查询文档作为可选参数,给定参数后仅符合条件的文档才被删除。删除数据是永久的,不能撤销也不能恢复。
db.doc.remove({optout: true})
删除速度
删除文档通常很快但要清空整个文档,使用 drop()
会更快,删除后在这个空集合上重建各项索引。
# 插入插入100w测试数据
for(var i=0; i<1000000; i++){
db.test.insert({foo:bar, baz:i , z:10-i});
}
# 删除文档并记录时间
var timeRemove= function(){
var start = (new Date()).getTime();
//db.test.drop();//清空
db.test.remove();//删除
db.findOne();//确保删除完成
var timeDiff = (new Date()).getTime() - start;
print(' remove took : '+timeDiff);
}
timeRemove();
删除集合中所有数据
db.[docname].drop()
集合本身和索引不会受到影响。
db.persons.remove()
Failed to execute script.
Error: remove needs a query:...
# 查看系统索引
db.system.indexes.find()
根据条件删除
db.[docname].remove({})
删除集合persons中name等于alice的记录
db.persons.remove({name:'alice'})
若要清除一个数据量十分庞大的集合,直接删除并重建索引的办法比remove的效率高。
文档更新
文档存入数据库后可使用 update()
来更新,update 有两个参数,一个是查询器用于定位需更新的目标文档,一个是修改器(modifier)用于说明要对找到的文档进行那种修改。
db.users.find()
{"_id" : ObjectId("5a32af65f1ad61e2459d90aa"),"name" : "alice","sn" : "001"}
{"_id" : ObjectId("5a32afe4f1ad61e2459d90ab"),"sn" : "002","name" : "ben"}
{"_id" : ObjectId("5a32afe4f1ad61e2459d90ac"),"sn" : "003","name" : "carl"}
# 新文档直接替换了旧文档而非修改
db.users.update({name:'alice'}, {name:'junchow'})
{ "_id" : ObjectId("5a32af65f1ad61e2459d90aa"),"name" : "junchow" //更新后其它字段消失了}
{ "_id" : ObjectId("5a32afe4f1ad61e2459d90ab"),"sn" : "002","name" : "ben"}
{"_id" : ObjectId("5a32afe4f1ad61e2459d90ac"),"sn" : "003", "name" : "carl"}
# 若要修改文档中某字段应使用 $set
db.users.update({name:'ben'}, {$set:{name:'avalon'}})
# 若要删除某个列应使用 $unset
# 若要重命名某个列应使用 $rename
# 若要自增某个列应使用 $inc
db.users.insert({name:'avalon', gender:1, age:30, nickname:'f4'})
db.users.update({name:'avalon'}, {$unset:{nickname:1}, $rename:{name:'username'}, $inc:{age:1}})
{"_id" : ObjectId("5a32b783f1ad61e2459d90ad"),"gender" : 1.0,"age" : 31.0,"username" : "avalon"}
更新可选参数
{upsert:true/false, multi:true/false}
upsert 表示若无匹配的行则直接插入,和mysql中的replace一样。
multi 修改多行时即使查询表达式命中多行也只会修改一行
db.users.update({name:'junchow'}, {$set:{name:'avatar'}}, {upsert:true})
$setOnInsert 当upsert为true时并发生了insert操作时,可以补充的字段。
db.users.update({name:'junchow'}, {$set:{name:'jay'}, $setOnInsert:{gender:1} }, {upsert:true})
更新操作是不可分割的:若是两个更新同时发生,先到达服务器的先执行,接着执行另一个。两个需要同时进行更新会迅速接连完成,此过程不会破坏文档。
文档查询
深入查询
如何将MySQL中的数据插入到MongoDB呢?将MySQL中内容查询出来后使用 json_encode 转换为JSON格式数据后插入MongoDB。
查看出商品编号为32的数据
db.goods.find({id:32})
查询出不属于栏目为3的所有商品
db.goods.find({category_id:{$ne:3}}, {category_id:1, id:1, title:1, _id:0})
查询价格高于3000的商品
db.goods.find({price:{$gt:3000}}, {category_id:1, id:1, title:1, _id:0})
查询价格低于或等于100元的商品
db.goods.find({price:{$lte:100}}, {category_id:1, id:1, title:1, _id:0})
查询栏目为4或栏目为11的商品
db.goods.find({category_id:{$in:[4, 11]}}, {category_id:1, id:1, title:1, _id:0})
db.goods.find({$or:[{$category_id:4}, {$category_id:1}]}, {category_id:1, id:1, title:1, _id:0})
查询价格大于等于100且小于等于500的商品
db.goods.find({$and:[{price:{$gte:100}}, {price:{$lte:500}}]}, {category_id:1, id:1, title:1, _id:0})
文档替换
最简单的更新就是用一个新文档完全替换匹配的文档,适用于进行大规模模式迁移的情况。
db.user.insert({name:'joe', friends:32, enemies:2});
db.user.find()
{
"_id" : ObjectId("5a32829bd3d21dc2700cbc49"),
"name" : "joe",
"friends" : 32.0,
"enemies" : 2.0
}
# 将friends和enemies移动到relationshiops子文档中
var joe = db.user.findOne({name:'joe'});
joe.relationships = {friends:joe.friends, enemies:joe.enemies};
joe.username = joe.name;
delete joe.friends;
delete joe.enemies;
delete joe.name;
db.user.update({name:'joe'}, joe);
{
"_id" : ObjectId("5a32829bd3d21dc2700cbc49"),
"relationships" : {
"friends" : 32.0,
"enemies" : 2.0
},
"username" : "joe"
}
常见的错误是查询条件匹配到多个文档,然后更新时由于第二个参数的存在产生重复的 _id
,数据库会跑出错误,任何文档都不会更新。
db.user.insert({name:'joe', age:29});
db.user.insert({name:'joe', age:90});
db.user.insert({name:'joe', age:40});
db.user.find()
{
"_id" : ObjectId("5a3284d9d3d21dc2700cbc4c"),
"name" : "joe",
"age" : 29.0
}
{
"_id" : ObjectId("5a3284d9d3d21dc2700cbc4d"),
"name" : "joe",
"age" : 90.0
}
{
"_id" : ObjectId("5a3284d9d3d21dc2700cbc4e"),
"name" : "joe",
"age" : 40.0
}
var joe = db.user.findOne({name:'joe', age:90});
joe.age++;
db.user.update({name:'joe'}, joe);
文档更新时的内存机制
当 document 被创建时,MongoDB为其分配内存和预留内存。当修改操作不超过预留内存时速度非常快,一旦超过预留内存MongoDB就会分配新内存,此时会消耗时间。
db.collection.update(criteria, objNew, upsert, multi)
- criteria update的查询条件,类似sql update查询内where后面的
- objNew update的对象和一些更新的操作符(如$,$inc...)等,也可以理解为sql - update查询内set后面的
- upsert 可选,若不存在update的记录,是否插入objNew,为true则插入,默认false不插入。
- multi 可选,默认是false只更新找到的第一条记录,为true就把按条件查出来多条记录全部更新。
- witeConcern 可选,抛出异常的级别
强硬的文档替换时更新
db.[docname].update({查询器},{修改器})
错误的更新方式
db.persons.insert({name:'alice', age:27})
# 强硬的更新会用新文档替换老文档
db.persons.update({name:'alice'}, {age:30})
db.persons.find()
{
"_id" : ObjectId("5a0b0a6b61bf6b35a74b0fb4"),
"age" : 30.0
}
主键冲突时会报错并停止更新
强硬替换当替换的文档和已有文档id冲突时,系统会报错。
db.persons.insert([{_id:'A01', name:'alice'}, {_id:'A02', name:'ben'}])
插入或更新操作
db.docname.update({查询器}, {修改器}, true)
查询器查询出结果则更新,查询无结果则插入。
db.persons.find()
{
"_id" : "A01",
"name" : "alice"
}
{
"_id" : "A02",
"name" : "ben"
}
db.persons.update({_id:'A03'},{_id:'A03', name:'carl'},true);
db.persons.find();
{
"_id" : "A01",
"name" : "alice"
}
{
"_id" : "A02",
"name" : "ben"
}
{
"_id" : "A03",
"name" : "carl"
}
批量更新
db.docname.update({查询器}, {修改器}, false, true)
db.persons.drop();
db.persons.insert([
{_id:'A01', name:'alice'},
{_id:'A02', name:'alice'},
{_id:'A03', name:'alice'}
]);
db.persons.update({name:'alice'},{name:'ben'});
db.persons.find();
{
"_id" : "A01",
"name" : "ben"
}
{
"_id" : "A02",
"name" : "alice"
}
{
"_id" : "A03",
"name" : "alice"
}
db.persons.update({name:'alice'},{$set:{name:'carl'}},false,true);
db.persons.find();
{
"_id" : "A01",
"name" : "carl"
}
{
"_id" : "A02",
"name" : "carl"
}
{
"_id" : "A03",
"name" : "carl"
}
使用修改器完成局部更新
# $set multi为集合追加字段
db.persons.drop();
db.persons.insert({_id:'A01', name:'junchow'});
db.persons.update({_id:'A01'},{$set:{addr:[]}}, {multi:true});
db.persons.find();
{
"_id" : "A01",
"name" : "junchow",
"addr" : []
}
# $push 向集合字段的数组内追加元素
db.persons.drop();
db.persons.insert({_id:'A01', name:'junchow'});
db.persons.update({_id:'A01'}, {$set:{fullname:[]}}, {multi:true});
db.persons.update({_id:'A01'}, {$push:{fullname:'jun'}});
db.persons.update({_id:'A01'}, {$push:{fullname:'chow'}});
db.persons.find();
{
"_id" : "A01",
"name" : "junchow",
"fullname" : [
"jun",
"chow"
]
}
# $push时若不存在指定键则创建数组类型的键值对
db.persons.update({_id:'A01'}, {$push:{skills:'mongodb'}});
db.persons.find();
{
"_id" : "A01",
"name" : "junchow",
"fullname" : [
"jun",
"chow"
],
"skills" : [
"mongodb",
]
}
# $addToSet 目标数组不存在元素则追加
db.persons.update({_id:'A01'}, {$addToSet:{fullname:'jun'}});
db.persons.find();
{
"_id" : "A01",
"name" : "junchow",
"fullname" : [
"jun",
"chow"
]
}
# $pushAll 批量添加数组元素
db.persons.update({_id:'A01'}, {$pushAll:{tags:['handsome','dream','happy']}});
db.persons.find();
/* 1 */
{
"_id" : "A01",
"name" : "junchow",
"fullname" : [
"jun",
"chow"
],
"skills" : [
"mongodb",
"mongodb"
],
"tags" : [
"handsome",
"dream",
"happy"
]
}
# $unset 删除集合中的字段
db.persons.update({name:'junchow'}, {$unset:{addr:[]}}, false, true);
db.persons.find();
{
"_id" : "A01",
"name" : "junchow"
}
db.persons.find();
/* 1 */
{
"_id" : "A01",
"name" : "junchow",
"fullname" : [
"jun",
"chow"
],
"skills" : [
"mongodb",
"php",
"js",
"mysql",
"mongodb"
],
"tags" : [
"happy"
]
}
# $pop 为1时删除数组中末尾元素
db.persons.update({_id:'A01'}, {$pop:{skills:1}});
db.persons.find();
{
"_id" : "A01",
"name" : "junchow",
"fullname" : [
"jun",
"chow"
],
"skills" : [
"mongodb",
"php",
"js",
"mysql"
],
"tags" : [
"happy"
]
}
# $pop 为-1时删除数组中首位元素
db.persons.update({_id:'A01'}, {$pop:{skills:-1}});
db.persons.find();
{
"_id" : "A01",
"name" : "junchow",
"fullname" : [
"jun",
"chow"
],
"skills" : [
"php",
"js",
"mysql"
],
"tags" : [
"happy"
]
}
# $pull 删除数组中指定的单个元素
db.persons.update({_id:'A01'}, {$pull:{skills:'php'}});
db.persons.find();
{
"_id" : "A01",
"name" : "junchow",
"fullname" : [
"jun",
"chow"
],
"skills" : [
"js",
"mysql"
],
"tags" : [
"happy"
]
}
# $pullAll 删除指定的多个元素
db.persons.update({_id:'A01'}, {$pullAll:{skills:['js','mysql']}});
db.persons.find();
/* 1 */
{
"_id" : "A01",
"name" : "junchow",
"fullname" : [
"jun",
"chow"
],
"skills" : [],
"tags" : [
"happy"
]
}
db.persons.update({_id:'A01'}, {$pushAll:{skills:[{type:'db', name:'mongodb'},{type:'js', name:'jquery'},{type:'php', name:'laravel'}]}});
db.persons.find();
{
"_id" : "A01",
"name" : "junchow",
"fullname" : [
"jun",
"chow"
],
"skills" : [
{
"type" : "js",
"name" : "jquery"
},
{
"type" : "db",
"name" : "mongodb"
},
{
"type" : "js",
"name" : "jquery"
},
{
"type" : "php",
"name" : "laravel"
}
],
"tags" : [
"happy"
]
}
# bug:使用对象点引用属性时需添加引号包裹,否则报错。
db.persons.update({skills.type:'db'}, {$set:{skills.$.author:'lily'}});
db.persons.find();
Error: Line 1: Unexpected token .
# 使用$数组定位器为数组追加属性
db.persons.update({'skills.type':'db'}, {$set:{'skills.$.author':'lily'}});
db.persons.find();
{
"_id" : "A01",
"name" : "junchow",
"fullname" : [
"jun",
"chow"
],
"skills" : [
{
"type" : "db",
"name" : "mongodb",
"author" : "lily"
},
{
"type" : "php",
"name" : "laravel"
},
{
"type" : "js",
"name" : "jquery"
}
],
"tags" : [
"happy"
]
}
切记修改器是放在最外层,查询器都是放在内层。
批量数组更新
db.docname.update({key:val}, {$addToSet:{attr:{$each:[el1,el2,el3]}}})
$each 会循环数组,并对数组元素执行 $addToSet操作。$addToSet会判断若不存在则导入。
db.persons.update({_id:'A01'}, {$addToSet:{favorite:{$each:['reading','programing','swimming']}}});
db.persons.find();
{
"_id" : "A01",
"name" : "junchow",
"fullname" : [
"jun",
"chow"
],
"skills" : [
{
"type" : "db",
"name" : "mongodb",
"author" : "lily"
},
{
"type" : "php",
"name" : "laravel"
},
{
"type" : "js",
"name" : "jquery"
}
],
"tags" : [
"happy"
],
"favorite" : [
"reading",
"programing",
"swimming"
]
}
推荐阅读
-
abp(net core)+easyui+efcore实现仓储管理系统——展现层实现增删改查之增删改视图(八)
-
MySQL增删改查语句
-
python cx_Oracle的基础使用方法(连接和增删改查)
-
关于小程序云开发数据库的增删改查操作
-
C#学习--Oracle数据库基本操作(连接、增、删、改、查)封装
-
sql server连接oracle并实现增删改查
-
tp5(thinkPHP5)框架数据库Db增删改查常见操作总结
-
Android之采用execSQL与rawQuery方法完成数据的添删改查操作详解
-
abp(net core)+easyui+efcore实现仓储管理系统——展现层实现增删改查之菜单与测试(九)
-
Django 笔记(六)mysql增删改查