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就会分配新内存,此时会消耗时间。

《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"
}

使用修改器完成局部更新

《MongoDB增删改查》 使用修改器完成局部更新1

# $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"
}

《MongoDB增删改查》 使用修改器完成局部更新2

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"
    ]
}
    原文作者:JunChow520
    原文地址: https://www.jianshu.com/p/5860f3ab2db6
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞