MongoDB文档
理解Collection对象中用于操作集合中文档的方法后,就能对数据库进行初始填充、必要时更新文档以及删除不需要的数据。
重点介绍如何修改集合:添加、更新和删除文档:
- 设置数据库修改请求的写入关注(write concern);
- 在集合中插入新文档;
- 更新集合中既有文档的字段;
- 使用upsert更新文档(如果它存在)或插入文档(如果它不存在);
- 从集合中删除文档;
- 检查修改请求的错误。
理解写入关注
连接到MongoDB数据库并更新其数据前,需要决定所需的写入关注等级。写入关注指的是MongoDB连接报告写入操作成功时提供的写入保障等级。写入关注程度越强,保障等级越高。 这里的基本理念是,写入关注程度较高时,MongoDB必须等到数据完全写入磁盘后才做出响应;而写入关注程度较低时,MongoDB在成功调度要写入的修改后就会做出响应。较高的写入关注程度的缺点是,MongoDB必须等待一段时间才响应客户端,因此写入请求的速度较慢。 写入关注是针对数据库连接设置的,适用于对相应数据库的所有写入操作。 MongoDB数据库连接的写入关注等级
- -1 忽略网络错误
- 0 不要求进行写入确认
- 1 要求进行写入确认
- 2 要求已写入到副本集的主服务器和一个备用服务器
- majority 要求已写入到副本集中的大多数服务器
配置数据库连接错误处理
通过使用Database对象的命令getLastError,可配置数据库连接在写入关注和超时方面的行为。getLastError命令是一个文档,描述了用于数据库请求的选项。当您修改MongoDB数据库中的文档时,在命令getLastError中设置的选项指定了连接将为操作完成等待多长时间、是否使用日记功能(journaling)等。 要执行命令getLastError,可使用Database对象的方法runCommand()。命令getLastError的语法如下:
myDB.runCommand( { getLastError: 1,
w: 1,
j: true,
wtimeout: 1000
} );
在Database对象的命令getLastError中可设置的选项
- w 指定数据库连接的写入关注等级,可能取值请参阅表8.1
- wtimeout 指定为写入操作完成等待多长时间,单位为毫秒;将这个值与正常的连接超时时间相加
- fsync 布尔值。如果为true,写入请求将等到fsync结束再返回
- j 布尔值。如果为true,写入请求将等到日记同步完成后再返回
获取数据库写入请求的状态
如果检测到写入错误,这种错误将被存储,并可使用Database对象的命令getLastError或方法getLastError()来获取。 如果对Database对象执行的最后一次请求返回的是true,方法getLastError()将返回null,否则将返回一个包含错误消息的字符串。例如,要检查文档添加操作的情况,可使用类似于下面的代码:
mongo = new Mongo('localhost'); wordsDB = mongo.getDB('words'); wordsDB.runCommand( { getLastError: 1, w: 1, j: true, wtimeout: 1000 } ); wordsColl = wordsDB.getCollection('word_stats'); wordsColl.insert({word:"the"}); lastError = wordsDB.getLastError(); if(lastError){ print("ERROR: " + lastError); }
命令getLastError返回一个对象,其中包含详细得多的错误消息,如最后一次请求的状态、修改的文档数、错误消息等。 下面是一个使用命令getLastError的示例:
mongo = new Mongo('localhost'); wordsDB = mongo.getDB('words'); wordsDB.runCommand( { getLastError: 1, w: 1, j: true, wtimeout: 1000 } ); wordsColl = wordsDB.getCollection('word_stats'); wordsColl.insert({word:"the"}); results = wordsDB.runCommand( { getLastError: 1}); if(results.err){ print("ERROR: " + results.err); }
Database对象的命令getLastError返回的对象的属性
- ok 布尔值。在命令getLastError成功完成了时为true
- err 描述错误的字符串,在最后一个请求没有发生错误时为null
- code 最后一次操作的错误代码
- connectionId 连接的ID
- lastOp 在最后一个操作是对副本集成员的写入或更新操作时,为存储请求修改的oplog中的optime时间戳
- n 最后一次操作是更新或删除操作时,为更新或删除的文档数
- updateExisting 布尔值。如果最后一个操作为更新操作,且至少影响到一个文档,同时没有导致upsert时,这个属性将为true
- upserted 如果最后一个操作为更新请求且导致的是插入,该属性将为插入的文档的ObjectId
- wnote 布尔值。如果错误与写入关注相关,则为true
- wtimeout 布尔值,如果getLastError因wtimeout设置而超时,则为true
- waited 如果最后一个操作因wtimeout设置而超时,则为超时前等待的毫秒数
- wtime 最后一次操作完成前等待的毫秒数。如果getLastError超时,wtime和waited应相同
理解数据库更新运算符
更新MongoDB数据库中的文档时,需要指定要修改哪些字段以及如何修改它们。在SQL中,使用很长的查询字符串来定义更新,而在MongoDB中,可使用简单的update对象,其中包含如何对文档中数据进行修改的运算符。 可根据需要在update对象中包含任意数量的运算符。update对象的格式类似于下面这样:
{
<operator>: {<field_operation>, <field_operation>, . . .},
<operator>: {<field_operation>, <field_operation>, . . .}
. . .
}
例如,对于下面的文档:
{
name: "myName",
countA: 0,
countB: 0,
days: ["Monday", "Wednesday"],
scores: [ {id:"test1", score:94}, {id:"test2", score:85}, {id:"test3", score:97}]
}
如果要将字段countA和countB分别加上5和1,将字段name设置为New Name,在数组字段days中添加Friday,并将数组字段scores中的元素按字段score排序,可使用下面的update对象:
{
$inc:{countA:5, countB:1},
$set:{name:"New Name"},
$push{days:"Friday},
$sort:{score:1}
}
列出了更新文档时可在update对象中指定的运算符。
- $inc 将字段值增加指定的量,格式为field:inc_value
- $rename 重命名字段,格式为field:new_name
- $setOnInsert 在更新操作中新建文档时设置字段的值,格式为field:value
- $set 设置既有文档的字段值,格式为field:new_value
- $unset 从既有文档中删除指定字段,格式为field:””
- $ 充当占位符,更新与查询条件匹配的第一个元素
- $addToSet 在既有数组中添加元素(如果这些元素没有包含在数组中),格式为array_field:new_value
- $pop 删除数组的第一个或最后一个元素。如果pop_value为-1,则删除第一个元素;如果pop_value为1,则删除最后一个元素。格式为array_field:pop_value
- $pullAll 从数组中删除多个值。要删除的值是以数组方式指定的。格式为array_field:[value1, value2, …]
- $pull 从数组中删除与查询条件匹配的元素,其中查询条件是一个基本的查询对象,指定了字段名和匹配条件。格式为array_field:[
] - $push 在数组中添加一个元素。对于简单数组,格式为array_field:new_value;对于对象数组,格式为array_field:{field:value}
- $each 用于运算符$push和$addToSet的限定符,用于在数组中添加多个元素。格式为array_field:{$each:[value1, …] }
- $slice 用于运算符$push的限定符,用于限制更新后的数组的长度。格式为array_field:{$slice:
} - $sort 用于运算符$push的限定符,用于将数组中的文档重新排序
- $bit 对整数值执行按位与和或运算。格式为format:integer_field:{and:
}和integer_field:{or: }
使用MongoDB shell在集合中添加文档
与MongoDB数据库交互时,另一种常见任务是在集合中插入文档。要插入文档,首先要创建一个表示该文档的JavaScript对象。插入操作之所以使用JavaScript对象,是因为MongoDB使用的BSON格式基于JavaScript表示法。 有新文档的JavaScript版本后,就可将其存储到MongoDB数据库中,为此可对相应的Collection对象实例调用方法insert()。方法insert()的语法如下,其中参数docs可以是单个文档对象,也可以是一个文档对象数组:
insert(docs)
例如,下面的示例在集合中插入一个简单的文档:
mongo = new Mongo('localhost');
myDB = mongo.getDB('myDB');
myColl = myDB.getCollection('myCollection');
myColl.insert({color:"blue", value:7, name:"Ordan"});
使用MongoDB shell将文档保存到集合中
Collection对象的方法save()很有趣,可用于在数据库中插入或更新文档;尽管其效率不如直接使用insert()或update()那么高,但在有些情况下更容易使用。例如,修改从MongoDB检索的对象时,可使用方法save()而不是update(),这样无需指定query和update对象。 方法save()的语法如下,其中参数doc是一个要保存到集合中的文档对象:
save(doc)
使用方法save()时,指定的文档对象要么是要加入到集合中的全新JavaScript对象,要么是从集合中取回的对象(您对其做了修改,并想将修改保存到数据库中)。 例如,下面的代码保存对既有文档所做的修改并插入一个新文档:
existingObject = myCollection.findOne({name:"existingObj"});
existingObject.name = "updatedObj";
myCollection.save(existingObj);
myCollection.save({name:"newObj"});
使用MongoDB shell在集合中更新或插入文档
可对文档执行的另一种操作是upsert,这将在文档存在时更新它,在文档不存在时插入它。常规更新不会自动插入文档,因为这需要判断文档是否存在。如果您确定文档存在,可使用常规update(),其效率高得多;同样,如果您确定文档不存在,可使用insert()。 要实现upsert,只需将方法update()的参数upsert设置为true。这告诉请求,如果文档存在,就尝试更新它;否则就插入一个新文档,其字段值由方法update()的参数在update指定。
例如,在下面的代码中,如果数据库中包含color字段为azure的文档,就更新它;否则就插入指定的文档:
update({color:"azure"}, {$set:{red:0, green:127, blue:255}}, true, false);
使用MongoDB shell从集合中删除文档
为减少消耗的空间,改善性能以及保持整洁,需要从MongoDB集合中删除文档。Collection对象的方法remove()使得从集合中删除文档非常简单,其语法如下:
remove([query], [justOne])
其中参数query是一个对象,指定要了删除哪些文档。请求将指定的属性和值与文档的字段和值进行比较,进而删除匹配的文档。如果没有指定参数query,将删除集合中的所有文档。 参数justOne是个布尔值;如果为true,将只删除与查询匹配的第一个文档。如果没有指定参数query和justOne,将删除集合中所有的文档。 例如,要删除集合words_stats中所有的文档,可使用如下代码:
collection = myDB.getCollection('word_stats');
collection.remove();
下面的代码删除集合words_stats中所有以a打头的单词:
collection = myDB.getCollection('word_stats');
collection.remove({first:'a'}, false);
下面的代码只删除集合words_stats中第一个以a打头的单词:
collection = myDB.getCollection('word_stats');
collection.remove({first:'a'}, true);
插入文档
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.collection.insertMany() 用于向集合插入一个多个文档,语法格式如下:
db.collection.insertMany(
[ <document 1> , <document 2>, ... ],
{
writeConcern: <document>,
ordered: <boolean>
}
)
参数说明:
- document:要写入的文档。
- writeConcern:写入策略,默认为 1,即要求确认写操作,0 是不要求。
- ordered:指定是否按顺序写入,默认 true,按顺序写入。
插入文档你也可以使用 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的记录,是否插入objNew,true为插入,默认是false,不插入。
- multi : 可选,mongodb 默认是false,只更新找到的第一条记录,如果这个参数为true,就把按条件查出来多条记录全部更新。
- writeConcern :可选,抛出异常的级别。
save() 方法
save() 方法通过传入的文档来替换已有文档,_id 主键存在就更新,不存在就插入。语法格式如下:
db.collection.save(
<document>,
{
writeConcern: <document>
}
)
参数说明:
- document : 文档数据。
- writeConcern :可选,抛出异常的级别。
删除文档
MongoDB remove() 函数是用来移除集合中的数据。
MongoDB 数据更新可以使用 update() 函数。在执行 remove() 函数前先执行 find() 命令来判断执行的条件是否正确,这是一个比较好的习惯。
remove() 方法的基本语法格式如下所示:
db.collection.remove(
<query>,
<justOne>
)
如果你的 MongoDB 是 2.6 版本以后的,语法格式如下:
db.collection.remove(
<query>,
{
justOne: <boolean>,
writeConcern: <document>
}
)
参数说明:
- query :(可选)删除的文档的条件。
- justOne : (可选)如果设为 true 或 1,则只删除一个文档,如果不设置该参数,或使用默认值 false,则删除所有匹配条件的文档。
- writeConcern :(可选)抛出异常的级别。 remove() 方法已经过时了,现在官方推荐使用 deleteOne() 和 deleteMany() 方法。