MongoDB 文档操作
文档的数据结构和 JSON 基本一样。
所有存储在集合中的数据都是 BSON 格式。
BSON 是一种类似 JSON 的二进制形式的存储格式,是 Binary JSON 的简称。
插入文档
MongoDB 使用 insert() 或 save() 方法向集合中插入文档,语法如下:
db.COLLECTION_NAME.insert(document)
或
db.COLLECTION_NAME.save(document)
- save():如果 _id 主键存在则更新数据,如果不存在就插入数据。该方法新版本中已废弃,可以使用 db.collection.insertOne() 或 db.collection.replaceOne() 来代替。
- insert(): 若插入的数据主键已经存在,则会抛 org.springframework.dao.DuplicateKeyException 异常,提示主键重复,不保存当前数据。
3.2 版本之后新增了 db.collection.insertOne() 和 db.collection.insertMany()。
db.collection.insertOne() 用于向集合插入一个新文档,语法格式如下:
db.collection.insertOne(
<document>,
{
writeConcern: <document>
}
)
db.test.iusertone( { item: "card", qty: 15 } );
db.collection.insertMany() 用于向集合插入一个多个文档,语法格式如下:
db.collection.insertMany(
[ <document 1> , <document 2>, ... ],
{
writeConcern: <document>,
ordered: <boolean>
}
)
db.test.insertMany([
{ item: "card", qty: 15 },
{ item: "envelope", qty: 20 },
{ item: "stamps", qty:30 }
]);
参数说明:
- document:要写入的文档。
- writeConcern:写入策略,默认为 1,即要求确认写操作,0 是不要求。
- ordered:指定是否按顺序写入,默认 true,按顺序写入。
- 如果为 true,在数组中执行文档的有序插入,并且如果其中一个文档发生错误,MongoDB 将返回而不处理数组中的其余文档;
- 如果为 false,则执行无序插入,若其中一个文档发生错误,则忽略错误,继续处理数组中的其余文档。
实例
以下文档可以存储在 MongoDB 的 student 数据库 的 col 集合中:
>db.col.insert({name: 'xxx',
description: '天资聪颖,好学',
age: 15,
sex: '男',
hobbies: ['小说', '篮球', '游泳']
})
以上实例中 col 是我们的集合名,如果该集合不在该数据库中, MongoDB 会自动创建该集合并插入文档。
插入不指定 _id 字段,mongod 将创建 _id 字段并为其分配唯一的 Objectld 值。
查看已插入文档:
> db.col.find()
{ "_id" : ObjectId("56064886ade2f21f36b03134"), "name" : "xxx", "description" : "天资聪颖,好学", "age" : 15, "sex" : "男" , "hobbies" : ['小说', '篮球', '游泳'] }
>
我们也可以将数据定义为一个变量,如下所示:
> document=({name: 'xxx',
description: '天资聪颖,好学',
age: 15,
sex: '男',
hobbies: ['小说', '篮球', '游泳'],
});
> db.col.insert(document)
WriteResult({ "nInserted" : 1 })
>
插入指定 _id 字段的文档,值 _id 必须在集合中唯一,以避免重复键错误,代码如下:
> db.col.insert(
{ _id: 10, item: "box", qty: 20 }
)
> db.col.find()
{ "_id" : 10, "item" : "box" , "qty": 20 }
有序地插入多条文档:
> db.col.insert([
{_id:10, item:"pen", price:"20" },
{_id:12, item:"redpen", price: "30" },
{_id:11, item:"bluepen", price: "40" }
],
{ordered:true}
)
在设置 ordered:true 时,插入的数据是有序的,如果存在某条待插入文档和集合的某文档 _id 相同的情况,_id 相同的文档与后续文档都将不再插入。在设置 ordered:false 时,除了出错记录(包括 _id 重复)外其他的记录继续插入。
插入文档你也可以使用 db.col.save(document) 命令。如果不指定 _id 字段 save() 方法类似于 insert() 方法。如果指定 _id 字段,则会更新该 _id 的数据。
更新文档
MongoDB 使用 update() 和 save() 方法来更新集合中的文档。接下来让我们详细来看下两个函数的应用及其区别。
update() 方法
update() 方法用于更新已存在的文档。语法格式如下:
db.collection.update(
<query>,
<update>,
{
upsert: <boolean>,
multi: <boolean>,
writeConcern: <document>
}
)
参数说明:
- query : update的查询条件,类似sql update查询内where后面的。
- update : update的对象和一些更新的操作符(如$,$inc...)等,也可以理解为sql update查询内set后面的
- upsert : 可选,这个参数的意思是,如果不存在update的记录,是否插入这个新的文档,true为插入,默认是false,不插入。
- multi : 可选,mongodb 默认是false,只更新找到的第一条记录,如果这个参数为true,就把按条件查出来多条记录全部更新。
- writeConcern :可选,抛出异常的级别。
- collation:指定语言。
实例
我们在集合 col 中插入如下数据:
>db.col.insert({name: 'xxx',
description: '天资聪颖,好学',
age: 15,
sex: '男',
hobbies: ['小说', '篮球', '游泳']
})
接着我们通过 update() 方法来更新姓名(name):
>db.col.update({'name':'xxx'},{$set:{'name':'lili'}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 }) # 输出信息
> db.col.find().pretty()
{
"_id" : ObjectId("56064f89ade2f21f36b03136"),
"title" : "lili",
"description" : "天资聪颖,好学",
"age" : 15,
"sex" : "男",
"hobbies" : [
"小说",
"篮球",
"游泳"
]
}
>
以上语句只会修改第一条发现的文档,如果你要修改多条相同的文档,则需要设置 multi 参数为 true。
>db.col.update({'name':'xxx'},{$set:{'name':'lili'}},{multi:true})
save() 方法
save() 方法通过传入的文档来替换已有文档,_id 主键存在就更新,不存在就插入。语法格式如下:
db.collection.save(
<document>,
{
writeConcern: <document>
}
)
参数说明:
- document : 文档数据。
- writeConcern :可选,抛出异常的级别。
实例
以下实例中我们替换了 _id 为 56064f89ade2f21f36b03136 的文档数据:
>db.col.save({
"_id" : ObjectId("56064f89ade2f21f36b03136"),
"name" : "tom",
"description" : "tom 很调皮",
"age" : 13,
"sex" : "男",
"hobbies" : [
"游戏",
"游泳"
]
})
替换成功后,我们可以通过 find() 命令来查看替换后的数据
>db.col.find().pretty()
{
"_id" : ObjectId("56064f89ade2f21f36b03136"),
"name" : "tom",
"description" : "tom 很调皮",
"age" : 13,
"sex" : "男",
"hobbies" : [
"游戏",
"游泳"
]
}
>
如果使用 insert 插入记录,若新增数据的主键已经存在,则会抛出 DuplicateKeyException 异常提示主键重复,不保存当前数据。
更多实例
只更新第一条记录:
db.col.update( { "count" : { $gt : 1 } } , { $set : { "test2" : "OK"} } );
全部更新:
db.col.update( { "count" : { $gt : 3 } } , { $set : { "test2" : "OK"} },false,true );
只添加第一条:
db.col.update( { "count" : { $gt : 4 } } , { $set : { "test5" : "OK"} },true,false );
全部添加进去:
db.col.update( { "count" : { $gt : 5 } } , { $set : { "test5" : "OK"} },true,true );
全部更新:
db.col.update( { "count" : { $gt : 15 } } , { $inc : { "count" : 1} },false,true );
只更新第一条记录:
db.col.update( { "count" : { $gt : 10 } } , { $inc : { "count" : 1} },false,false );
删除文档
remove() 方法
语法
remove() 方法的基本语法格式如下所示:
db.collection.remove(
<query>,
<justOne>
)
如果你的 MongoDB 是 2.6 版本以后的,语法格式如下:
db.collection.remove(
<query>,
{
justOne: <boolean>,
writeConcern: <document>
}
)
参数说明:
- query :(可选)删除的文档的条件。
- justOne : (可选)如果设为 true 或 1,则只删除一个文档,如果不设置该参数,或使用默认值 false,则删除所有匹配条件的文档。
- writeConcern :(可选)抛出异常的级别。
实例
以下文档我们执行两次插入操作:
>db.col.insert({name: 'xxx',
description: '天资聪颖,好学',
age: 15,
sex: '男',
hobbies: ['小说', '篮球', '游泳']
})
使用 find() 函数查询数据:
> db.col.find()
{ "_id" : ObjectId("56066169ade2f21f36b03137"), "name" : "xxx", "description" : "天资聪颖,好学", "age" : 15, "sex" : "男", "hobbies" : ['小说', '篮球', '游泳'] }
{ "_id" : ObjectId("5606616dade2f21f36b03138"), "name" : "xxx", "description" : "天资聪颖,好学", "age" : 15, "sex" : "男", "hobbies" : ['小说', '篮球', '游泳'] }
接下来我们移除 name 为 'xxx' 的文档:
>db.col.remove({'name':'xxx'})
WriteResult({ "nRemoved" : 2 }) # 删除了两条数据
>db.col.find()
…… # 没有数据
如果你只想删除第一条找到的记录可以设置 justOne 为 1,如下所示:
>db.COLLECTION_NAME.remove(DELETION_CRITERIA,1)
如果你想删除所有数据,可以使用以下方式(类似常规 SQL 的 truncate 命令):
>db.col.remove({})
>db.col.find()
>
delete() 方法
官方推荐使用 deleteOne() 和 deleteMany() 方法删除文档,语法格式如下:
db.collection.deleteMany ({})
db.collection.deleteMany ({ status : "A" })
db.collection.delete.One ({ status : "D" })
第一条语句删除集合下所有的文档,第二条语句删除 status 等于 A 的全部文档,第三条语句删除 status 等于 D 的一个文档。
查询文档
find() 简介
MongoDB 查询数据的语法格式如下:
db.collection.find(query, projection)
- query :可选,使用查询操作符指定查询条件
- projection :可选,表示使用投影操作符指定返回的字段,如果忽略此选项则返回所有字段。(默认省略)。
如果你需要以易读的方式来读取数据,可以使用 pretty() 方法,语法格式如下:
>db.col.find().pretty()
pretty() 方法以格式化的方式来显示所有文档。
实例
以下实例我们查询了集合 col 中的数据:
> db.col.find().pretty()
{
"_id" : ObjectId("56063f17ade2f21f36b03133"),
"name" : "xxx",
"description" : "天资聪颖,好学",
"age" : 15,
"sex" : "男",
"hobbies" : [
"小说",
"篮球",
"游泳"
]
}
除了 find() 方法之外,还有一个 findOne() 方法,它只返回一个文档。
查询条件
MongoDB 与 RDBMS 的查询比较
操作符 | 格式 | 实例 | 与 RDBMS where 语句比较 |
---|---|---|---|
等于(=) | { |
db.test.find( {price : 24} ) | where price = 24 |
大于(>) | { |
db.test.find( {price : {$gt : 24}} ) | where price > 24 |
小于(<) | { |
db.test.find( {price : {$lt : 24}} ) | where price < 24 |
大于等于(>=) | { |
db.test.find( {price : {$gte : 24}} ) | where price >= 24 |
小于等于(<=) | { |
db.test.find( {price : {$lte : 24}} ) | where price <= 24 |
不等于(!=) | { |
db.test.find( {price : {$ne : 24}} ) | where price != 24 |
与(and) | {key01 : value01, key02 : value02, ...} | db.test.find( {name : "《MongoDB 入门教程》", price : 24} ) | where name = "《MongoDB 入门教程》" and price = 24 |
或(or) | {$or : [{key01 : value01}, {key02 : value02}, ...]} | db.test.find( {$or:[{name : "《MongoDB 入门教程》"},{price : 24}]} ) | where name = "《MongoDB 入门教程》" or price = 24 |
MongoDB AND 条件
MongoDB 的 find() 方法可以传入多个键(key),每个键(key)以逗号隔开,即常规 SQL 的 AND 条件。
语法格式如下:
>db.col.find({key1:value1, key2:value2}).pretty()
实例
> db.col.find({"name":"xxx", "age":15}).pretty()
{
"_id" : ObjectId("56063f17ade2f21f36b03133"),
"name" : "xxx",
"description" : "天资聪颖,好学",
"age" : 15,
"sex" : "男",
"hobbies" : [
"小说",
"篮球",
"游泳"
]
}
以上实例中类似于 WHERE 语句:WHERE name='xxx' AND age=15
MongoDB OR 条件
MongoDB OR 条件语句使用了关键字 $or,语法格式如下:
>db.col.find(
{
$or: [
{key1: value1}, {key2:value2}
]
}
).pretty()
实例
>db.col.find({$or:[{"name":"xxx"},{"age": 16}]}).pretty()
{
"_id" : ObjectId("56063f17ade2f21f36b03133"),
"name" : "xxx",
"description" : "天资聪颖,好学",
"age" : 15,
"sex" : "男",
"hobbies" : [
"小说",
"篮球",
"游泳"
]
}
>
AND 和 OR 联合使用
AND 和 OR 联合使用,类似常规 SQL 语句为: 'where age=15 AND (name= 'xxx' OR sex= '男')'
>db.col.find({"age": 15, $or: [{"name": "xxx"},{"sex": "男"}]}).pretty()
{
"_id" : ObjectId("56063f17ade2f21f36b03133"),
"name" : "xxx",
"description" : "天资聪颖,好学",
"age" : 15,
"sex" : "男",
"hobbies" : [
"小说",
"篮球",
"游泳"
]
}
特定类型查询
以 test 集合中以下文档为基础:
> db.test.find()
{"_id" : Objectld("5ba7342c7f9318ea62161351"), "name" : "《MongoDB教程》", "price" : 24, "tags" : [ "MongoDB", "NoSQL", "database" ]}
{"_id" : Objectld("5ba747bd7f9318ea62161352"), "name" : "ava 教程", "price" : 36, "tags" : ["编程语言", "Java语言", "面向对象程序设计语言"]}
{"_id" : Objectld("5ba75a057f9318ea62161356"), "name" : "王二", "age" : null }
查询 age 为 null 的字段的语法格式如下:
> db.test.find({age:null})
此语句不仅匹配出 age 为 null 的文档,其他不同类型的文档也会被查出。这是因为 null 不仅会匹配某个键值为 null 的文档,而且还会匹配不包含这个键的文档。
查询数组:
> db.test.find(
{
tags:['MongoDB', 'NoSQL', 'database']
}
)
{"_id" : ObjectId("5ba7342c7f9318ea62161351"), "name": "《MongoDB教程》", "price" : 24, "tags" : [ "MongoDB", "NoSQL", "database"]}
查询有 3 个元素的数组:
> db.test.find(
{
tags:{$size:3}
}
)
{"_id" : Objectld("5baf9b6663ba0fb3ccccle77"), "name" : "《MongoDB 教程》", ''price" : 24, "tags" : ["MongoDB","NoSQL", "database"]}
{"_id" : Objectld ("5baf 9bc763ba0fk>3ccccle78"), "name" : "《Java 教程》", "price" : 36, "tags" : ["编程语言", "Java语言", "面向对象程序设计语言"]}
查询数组里的某一个值:
> db.test.find(
{
tags: "MongoDB"
}
)
{"_id" : Objectld("5baf9b6663ba0fb3ccccle77"), "name" : "《MongoDB 教程》", ''price" : 24, "tags" : ["MongoDB","NoSQL", "database"]}
limit() 、skip()、sort() 、$regex
-
limit()
用于限制查询结果的个数,若匹配的结果不到 限制 个数,则返回匹配数量的结果
db.test.find().limit(3)
-
skip()
用于略过指定个数的文档
>db.test.find().skip(1)
-
sort()
用于对查询结果进行排序,1 是升序,-1 是降序
>db.test.find().sort({"price" : 1})
-
$regex
正则表达式,不同于全文检索,使用正则表达式无须进行任何配置
> db.test.find({tags:{$regex:"MongoDB"}}) {"_id" : Objectld("5baf9b6663ba0fb3ccccle77"), "name" : "《MongoDB 教程》", ''price" : 24, "tags" : ["MongoDB","NoSQL", "database"]}
游标
游标是指对数据一行一行地进行操作,在 MongoDB 数据库中对游标的控制非常简单,只需使用 firid() 函数就可以返回游标。
方法名 | 作用 |
---|---|
hasNext | 判断是否有更多的文档 |
next | 用来获取下一条文档 |
toArray | 将查询结构放到数组中 |
count | 查询的结果为文档的总数量 |
limit | 限制查询结果返回数量 |
skip | 跳过指定数目的文档 |
sort | 对查询结果进行排序 |
objsLeftlnBatch | 查看当前批次剩余的未被迭代的文档数量 |
addOption | 为游标设置辅助选项,修改游标的默认行为 |
hint | 为查询强制使用指定索引 |
explain | 用于获取查询执行过程报告 |
snapshot | 对查询结果使用快照 |
使用游标时,需要注意下面 4 个问题。
-
当调用 find() 函数时,Shell 并不立即查询数据库,而是等真正开始获取结果时才发送查询请求。
-
游标对象的每个方法几乎都会返回游标对象本身,这样可以方便进行链式函数的调用。
-
在 MongoDB Shell 中使用游标输出文档包含两种情况,如果不将 find() 函数返回的游标赋值给一个局部变量进行保存,在默认情况下游标会自动迭代 20 次。如果将 find() 函数返回的游标赋值给一个局部变量,则可以使用游标对象提供的函数进行手动迭代。
-
使用清空后的游标,进行迭代输出时,显示的内容为空。
游标从创建到被销毁的整个过程存在的时间,被称为游标的生命周期,包括游标的创建、使用及销毁三个阶段。当客户端使用 find() 函数向服务器端发起一次查询请求时,会在服务器端创建一个游标,然后就可以使用游标函数来操作查询结果。
以下三种情况会让游标被销毁。
- 客户端保存的游标变量不在作用域内。
- 游标遍历完成后,或者客户端主动发送终止消息。
- 在服务器端 10 分钟内未对游标进行操作。
以下语句显示使用游标查找所有文档:
>var cursor = db.test.find()
>while (cursor.hasNext()){
var doc = cursor.next();
print(doc.name); //把每一条数据都单独拿出来进行逐行的控制
print(doc); //将游标数据取出来后,其实每行数据返回的都是一个[object BSON]型的内容
printjson(doc); //将游标获取的集合以JSON的形式显示
}