翻译MongoDB指南/CRUD操作
Posted 甜橙很酸
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了翻译MongoDB指南/CRUD操作相关的知识,希望对你有一定的参考价值。
【原文地址】https://docs.mongodb.com/manual/
MongoDB CRUD操作(二)
主要内容:
更新文档,删除文档,批量写操作,SQL与MongoDB映射图,读隔离(读关注),写确认(写关注)
1 更新文档
1.1 更新
MongoDB提供下列方法用于更新一个集合
更新使用指定过滤器匹配到的文档,即使过滤器匹配到多个文档,也只会更新一个文档。 3.2版本新增特性。 |
|
更新使用指定过滤器匹配到的所有文档。 3.2版本新增特性。 |
|
替换使用指定过滤器匹配到的文档,即使过滤器匹配到多个文档,也只会更新一个文档。 3.2版本新增特性。 |
|
更新或者替换一个使用指定过滤器匹配到的文档,或者更新使用指定过滤器匹配到的所有文档。 默认只更新一个文档。为了更新多个文档,请使用multi 选项。 |
上述方法接受以下参数:
- 过滤器文档,确定要更新哪些文档。这些过滤器与查询操作中使用的过滤器有相同的句法规则。
- 查询过滤器文档,使用表达式<field>:<value>指定相等条件,找出所有字段<field>的值为<value>的文档:
{ <field1>: <value1>, ... }
- 查询过滤器文档,可使用查询操作符指定条件:
{ <field1>: { <operator1>: <value1> }, ... }
- 指定了更新内容的更新文档;或一个替换的文档,替换掉匹配到的文档而保持_id字段不变。
- 一个选项文档。
1.2 行为
原子性
MongoDB 中写操作在单文档级别具有原子性。
_id字段
文档一旦创建,_id字段值就固定了,不能被更新,也不能用一个_id字段值与原文档不同的文档来替换原文档。
文档大小
当执行更新操作时,导致文档变大并超出已分配的大小时,更新操作会在磁盘上重新定位文件。
字段顺序
MongoDB 保持字段写入时的顺序,除非遇到下列情况:
- _id字段总是处在首位。
- 更新的时候对某一个或某些字段重命名可能导致字段顺序变更
2.6版本中的变化:从2.6版本开始,MongoDB 尽可能地保持字段写入时的顺序,但之前的版本并不是这样的。
Upsert 选项
如果db.collection.update(), db.collection.updateOne(), db.collection.updateMany(), 或者db.collection.replaceOne()包括
“upsert : true”并且使用指定的过滤器没有匹配到任何文档,那么此操作将会创建一个新文档并插入数据库。如果匹配到文档,那么此操作将修改或者替换匹配到的一个或多个文档。
1.3示例集合
本页的例子在mongo shell中使用db.collection.find() 方法。在mongo shell中,如果没有将游标赋给一个var类型的变量,那么游标将会自动迭代20次以打印结果集中的前20个文档。
在mongo shell中执行下面的语句,将数据灌入users 集合。
db.users.insertMany(
[
{
_id: 1,
name: "sue",
age: 19,
type: 1,
status: "P",
favorites: { artist: "Picasso", food: "pizza" },
finished: [ 17, 3 ],
badges: [ "blue", "black" ],
points: [
{ points: 85, bonus: 20 },
{ points: 85, bonus: 10 }
]
},
{
_id: 2,
name: "bob",
age: 42,
type: 1,
status: "A",
favorites: { artist: "Miro", food: "meringue" },
finished: [ 11, 25 ],
badges: [ "green" ],
points: [
{ points: 85, bonus: 20 },
{ points: 64, bonus: 12 }
]
},
{
_id: 3,
name: "ahn",
age: 22,
type: 2,
status: "A",
favorites: { artist: "Cassatt", food: "cake" },
finished: [ 6 ],
badges: [ "blue", "Picasso" ],
points: [
{ points: 81, bonus: 8 },
{ points: 55, bonus: 20 }
]
},
{
_id: 4,
name: "xi",
age: 34,
type: 2,
status: "D",
favorites: { artist: "Chagall", food: "chocolate" },
finished: [ 5, 11 ],
badges: [ "Picasso", "black" ],
points: [
{ points: 53, bonus: 15 },
{ points: 51, bonus: 15 }
]
},
{
_id: 5,
name: "xyz",
age: 23,
type: 2,
status: "D",
favorites: { artist: "Noguchi", food: "nougat" },
finished: [ 14, 6 ],
badges: [ "orange" ],
points: [
{ points: 71, bonus: 20 }
]
},
{
_id: 6,
name: "abc",
age: 43,
type: 1,
status: "A",
favorites: { food: "pizza", artist: "Picasso" },
finished: [ 18, 12 ],
badges: [ "black", "blue" ],
points: [
{ points: 78, bonus: 8 },
{ points: 57, bonus: 7 }
]
}
]
)
1.4 更新一个文档的指定字段
为了改变字段,MongoDB提供了更新操作符,例如,使用$set修改字段值。
更新文档的格式为:
{
<update operator>: { <field1>: <value1>, ... },
<update operator>: { <field2>: <value2>, ... },
...
}
有些更新操作符会在被更新字段不存在的情况下创建该字段,如 $set。
db.collection.updateOne()
3.2版本新增
下面的例子演示使用db.collection.updateOne()方法和匹配条件favorites.artist等于“Picasso”,更新匹配出的多个文档中的第一个:
- 使用操作符$set将字段favorites.food的值修改为“pie”并将字段的类型值改为3.
- 使用操作符 $currentDate将字段lastModified 的值更改为当前时间。如果字段lastModified 不存在,$currentDate 会创建此字段。
db.users.updateOne(
{ "favorites.artist": "Picasso" },
{
$set: { "favorites.food": "pie", type: 3 },
$currentDate: { lastModified: true }
}
)
db.collection.updateMany()
3.2版本新增
下面的例子演示使用db.collection.updateMany()方法和匹配条件favorites.artist等于“Picasso”,更新匹配出的所有文档:
- 使用操作符$set将字段favorites.food的值修改为“pie”并将字段的类型值改为3.
- 使用操作符 $currentDate将字段lastModified 的值更改为当前时间。如果字段lastModified不存在,$currentDate会创建此字段。
db.users.updateMany(
{ "favorites.artist": "Picasso" },
{
$set: { "favorites.artist": "Pisanello", type: 3 },
$currentDate: { lastModified: true }
})
Db.collection.update()
下面的例子演示使用db.collection.updateOne()方法和匹配条件favorites.artist等于“Picasso”,更新匹配出的多个文档中的第一个:
- 使用操作符$set将字段favorites.food的值修改为“pie”并将字段的类型值改为0.
- 使用操作符 $currentDate将字段lastModified 的值更改为当前时间。如果字段lastModified 不存在,$currentDate 会创建此字段。
db.users.update(
{ "favorites.artist": "Pisanello" },
{
$set: { "favorites.food": "pizza", type: 0, },
$currentDate: { lastModified: true }
})
使用db.collection.update()方法和multi: true 选项更新多个文档
db.users.update(
{ "favorites.artist": "Pisanello" },
{
$set: { "favorites.food": "pizza", type: 0, },
$currentDate: { lastModified: true }
},
{ multi: true })
1.5 替换文档
为了替换一个文档中除_id字段以外的所有内容,将一个新文档作为db.collection.replaceOne()或db.collection.update()的第二个参数进行传递。替换文档必须由<field> : <value>组成。
替换文档可以包含不同于原文档的字段。因为_id字段是不变的,所以替换文档中的_id字段可以省略,如果替换文档中包含_id字段,那么替换文档的
_id字段值必须与原文档相同。
db.collection.replaceOne
下面的例子演示了使用db.collection.replaceOne()方法和过滤条件条件为name 等于"abc" ,将集合users 中匹配到的第一个文档替换为一个新文档。
db.users.replaceOne(
{ name: "abc" },
{ name: "amy", age: 34, type: 2, status: "P", favorites: { "artist": "Dali", food: "donuts" } })
db.collection.update
下面的例子演示了使用db.collection.update()方法和过滤条件为name 等于"xyz" ,将集合users 中匹配到的第一个文档替换为一个新文档。
db.users.update(
{ name: "xyz" },
{ name: "mee", age: 25, type: 1, status: "A", favorites: { "artist": "Matisse", food: "mango" } })
其他方法
下面列举了删除文档的其他方法:
- db.collection.findOneAndReplace().
- db.collection.findOneAndUpdate().
- db.collection.findAndModify().
- db.collection.save().
- db.collection.bulkWrite().
写确认
对于写确认,可以为写操作指定要求的确认级别,具体参见 Write Concern
2 删除文档
2.1 删除方法
MongoDB提供下列方法删除集合中的文档。
删除使用指定过滤器匹配到的一个或全部文档 |
|
至多删除一个文档,即使使用指定过滤器匹配到多个文档。 3.2版本中新增 |
|
删除匹配到的所有文档。 3.2版本中新增 |
你可以使用准则、过滤器确定要删除的文档。这些过滤器与读操作所使用的过滤器具有相同的语法规则。
- 查询过滤器文档使用<field>:<value>指定相等条件,筛选出所有字段<field>的值为<value>的文档:
{ <field1>: <value1>, ... }
- 查询过滤器文档可以使用查询操作符指定匹配条件:
{ <field1>: { <operator1>: <value1> }, ... }
2.2 删除行为
索引
执行删除操作时,即使删除一个集合中的全部文档,也不会删除索引。
原子性
MongoDB中所有写操作在单文档级别具有原子性。
2.3示例集合
本页提供了在mongo shell中使用删除操作的例子。在mongo shell中执行下面语句,向集合users 中灌入数据。
注:
如果待插入文档的_id字段值与集合已有文档_id字段值相同,那么在插入数据前要先将集合删除(db.users.drop())。
db.users.insertMany(
[
{
_id: 1,
name: "sue",
age: 19,
type: 1,
status: "P",
favorites: { artist: "Picasso", food: "pizza" },
finished: [ 17, 3 ],
badges: [ "blue", "black" ],
points: [
{ points: 85, bonus: 20 },
{ points: 85, bonus: 10 }
]
},
{
_id: 2,
name: "bob",
age: 42,
type: 1,
status: "A",
favorites: { artist: "Miro", food: "meringue" },
finished: [ 11, 25 ],
badges: [ "green" ],
points: [
{ points: 85, bonus: 20 },
{ points: 64, bonus: 12 }
]
},
{
_id: 3,
name: "ahn",
age: 22,
type: 2,
status: "A",
favorites: { artist: "Cassatt", food: "cake" },
finished: [ 6 ],
badges: [ "blue", "red" ],
points: [
{ points: 81, bonus: 8 },
{ points: 55, bonus: 20 }
]
},
{
_id: 4,
name: "xi",
age: 34,
type: 2,
status: "D",
favorites: { artist: "Chagall", food: "chocolate" },
finished: [ 5, 11 ],
badges: [ "red", "black" ],
points: [
{ points: 53, bonus: 15 },
{ points: 51, bonus: 15 }
]
},
{
_id: 5,
name: "xyz",
age: 23,
type: 2,
status: "D",
favorites: { artist: "Noguchi", food: "nougat" },
finished: [ 14, 6 ],
badges: [ "orange" ],
points: [
{ points: 71, bonus: 20 }
]
},
{
_id: 6,
name: "abc",
age: 43,
type: 1,
status: "A",
favorites: { food: "pizza", artist: "Picasso" },
finished: [ 18, 12 ],
badges: [ "black", "blue" ],
points: [
{ points: 78, bonus: 8 },
{ points: 57, bonus: 7 }
]
}
])
2.4删除所有文档
为了删除全部文档,使用db.collection.deleteMany() 或db.collection.remove()方法并将空过滤器文档{}传给方法。
db.collection.deleteMany()
例如使用db.collection.deleteMany()方法删除users 集合中所有文档:
db.users.deleteMany({})
返回结果文档中包含操作状态:
{ "acknowledged" : true, "deletedCount" : 7 }
db.collection.remove()
或者使用db.collection.remove() 方法来删除所有文档:
db.users.remove({})
为了删除一个集合中的所有文档,db.collection.drop() 方法或许更高效;使用db.collection.drop() 方法删除集合中的所有文档及其索引,然后重新创建集合和索引。
3.5删除匹配到的所有文档
为了删除所有匹配到的文档,传递一个过滤器给db.collection.deleteMany() 或 db.collection.remove()方法。
db.collection.deleteMany()
例如,使用db.collection.deleteMany() 方法删除users 集合中status 字段值等于“A”的文档
db.users.deleteMany({ status : "A" })
返回结果:
{ "acknowledged" : true, "deletedCount" : 3 }
db.collection.remove()
或者使用db.collection.remove()方法删除users 集合中status 字段值等于“P”的文档
db.users.remove( { status : "P" } )
对于大的删除操作,先将想保留的文档拷贝到新的集合中,然后使用db.collection.drop() 将原来的集合删除,这种方法或许更高效。
2.6 仅删除匹配到的文档中的一个
即使匹配到了多个文档,也只删除其中的一个,使用db.collection.deleteOne() 方法,或者使用db.collection.remove()方法和使用<justOne>参数并将其设置为true或1 。
db.collection.deleteOne()
例如,使用db.collection.deleteOne() 删除集合中status字段值为“D”的文档中的第一个:
db.users.deleteOne( { status: "D" } )
db.collection.remove()
或者使用db.collection.remove()方法删除users 集合中status 字段值等于“D”的文档中的第一个:
db.users.remove( { status: "D" }, 1)
其他方法:
- db.collection.findOneAndDelete():该方法提供了sort选项,sort选项可以删除按指定顺序排序的文档中的第一个。
- db.collection.findOneAndModify():该方法提供了sort选项,sort选项可以删除按指定顺序排序的文档中的第一个。
- db.collection.bulkWrite()
2.7 写确认
对于写确认,可以为写操作指定需要的确认级别,具体参见 Write Concern
3 批量写入操作
3.1 概要
MongoDB客户端具有执行批量写的能力。批量写操作只会影响一个集合。MongoDB由应用程序决定可接受的批量写操作安全级别。
3.2版本中新增:
db.collection.bulkWrite()方法提供了批量插入、更新、删除。使用db.collection.insertMany()方法也可批量插入。
3.2排序与非排序操作
可以批量写入一批已排序或未排序的文档。
对于有序的操作列表,MongoDB 按顺序执行操作。如果在执行一个写操作时发生错误,MongoDB 将会返回而不处理列表中剩下的操作。
对于无序的操作列表,MongoDB 并行地执行操作,但这种行为是无保障的。如果在执行一个写操作时发生错误,MongoDB 将会继续执行列表中剩下的操作。
MongoDB处理有序列表的速度比处理无序列表的速度要慢,因为处理有序列表时,每一个操作都要等待前一个操作执行完毕。
bulkWrite()方法默认依序执行操作。在选项文档中设置ordered : false,可以按无序方式执行。
3.3 bulkWrite()
bulkWrite()支持下列写操作:
- insertOne
- updateOne
- updateMany
- replaceOne
- deleteOne
- deleteMany
每个写操作作为数组中的一个文档被传递给bulkWrite() 。
例如,下面执行多个写操作:
集合characters包含下面的文档:
{ "_id" : 1, "char" : "Brisbane", "class" : "monk", "lvl" : 4 },
{ "_id" : 2, "char" : "Eldon", "class" : "alchemist", "lvl" : 3 },
{ "_id" : 3, "char" : "Meldane", "class" : "ranger", "lvl" : 3 }
下面bulkWrite() 方法执行多个操作:
try {
db.characters.bulkWrite(
[
{ insertOne :
{
"document" :
{
"_id" : 4, "char" : "Dithras", "class" : "barbarian", "lvl" : 4
}
}
},
{ insertOne :
{
"document" :
{
"_id" : 5, "char" : "Taeln", "class" : "fighter", "lvl" : 3
}
}
},
{ updateOne :
{
"filter" : { "char" : "Eldon" },
"update" : { $set : { "status" : "Critical Injury" } }
}
},
{ deleteOne :
{ "filter" : { "char" : "Brisbane"} }
},
{ replaceOne :
{
"filter" : { "char" : "Meldane" },
"replacement" : { "char" : "Tanys", "class" : "oracle", "lvl" : 4 }
}
}
]
);}catch (e) {
print(e);}
操作返回结果为:
{
"acknowledged" : true,
"deletedCount" : 1,
"insertedCount" : 2,
"matchedCount" : 2,
"upsertedCount" : 0,
"insertedIds" : {
"0" : 4,
"1" : 5
},
"upsertedIds" : {
}}
3.4文档批量插入一个分片集策略
大块插入操作,包括初始数据插入和常规数据导入,都能影响分片集群的性能。对于大块的插入操作,考虑下面的策略:
集合预裂(Pre-Split the Collection)
如果分片集合是空的,那么集合仅有一个初始块,这一块驻留在一片中。然后,MongoDB必须花时间来接收数据,创建分片,并将多个块分布到可用的片上。为了避免降低性能,你可以提前对一个集合分片。
无序写入
为了改进写入分片集群的性能,将 bulkWrite()方法的选项参数ordered 设置为false。mongos 将会试着同时写多片。
避免单调调节
如果插入文档的同时片键单调递增,所有已插入的数据都会跑到集合的最后一块,这总在一片上发生。因此集群的插入容量永远都不会超过一片的插入容量。
如果插入的量比一片所能处理的最大量还大,并且不能避免片键随着插入操作而增大,那么考虑按下面的策略修改你的应用程序:
- 修改片键的二进制比特数,这保留了信息,同时也避免了插入顺序与增加值序列关联。
- 交换第一个和最后一个16比特的词来调整插入。
例如,下面的C++代码,交换BSON ObjectIds头与尾16比特单词,使其不再单调增加。
using namespace mongo;
OID make_an_id() {
OID x = OID::gen();
const unsigned char *p = x.getData();
swap( (unsigned short&) p[0], (unsigned short&) p[10] );
return x;
}
void foo() {
// create an object
BSONObj o = BSON( "_id" << make_an_id() << "x" << 3 << "name" << "jane" );
// now we may insert o into a sharded collection
}
4 SQL与MongoDB映射图
4.1术语和概念
下表展示了SQL与MongoDB的术语和概念对应关系
SQL Terms/Concepts |
MongoDB Terms/Concepts |
database(数据库) |
database(数据库) |
table(表) |
collection(集合) |
row(行) |
|
column(列) |
field(字段) |
index(索引) |
index(索引) |
table joins(表链接) |
embedded documents and linking(嵌入式文档和连接) |
primary key(主键) 指定唯一的一列或几列做主键 |
primary key(主键) 在MongoDB中,主键被自动设置为_id字段。 |
aggregation (聚集操作)(例如group by) |
aggregation pipeline(聚集管道) |
4.2可执行程序
下面的表格列举了目前数据库可执行程序与MongoDB可执行程序的对照。
此表并未列举全。
|
MongoDB |
MySQL |
Oracle |
Informix |
DB2 |
数据库服务器 |
oracle |
IDS |
DB2 Server |
||
数据库客户端 |
mysql |
sqlplus |
DB-Access |
DB2 Client |
4.3例子
下面列出了SQL语句与MongoDB语句的对应关系。假设有如下的条件:
SQL语句中的表名为users。
MongoDB 中集合的名称为users并且包含下面的文档模型:
{
_id: ObjectId("509a8fb2f3f4948bd2f983a0"),
user_id: "abc123",
age: 55,
status: \'A\'
}
创建和更改
下面展示了表级操作对应关系。
SQL模式语句 |
MongoDB模式语句 |
CREATE TABLE users ( id MEDIUMINT NOT NULL AUTO_INCREMENT, user_id Varchar(30), age Number, status char(1), PRIMARY KEY (id) ) |
第一次执行insert() 操作会隐式创建集合。 如果没有指定_id字段,主键_id被自动添加. db.users.insert( { user_id: "abc123", age: 55, status: "A" } ) 也可显示创建集合: db.createCollection("users") |
ALTER TABLE users ADD join_date DATETIME |
集合不会描述和强制文档的结构;在集合这一级无文档结构的改变。 但在文档级, 可使用update()操作和 $set操作符向现有文档中添加字段。 db.users.update( { }, { $set: { join_date: new Date() } }, { multi: true } ) |
ALTER TABLE users DROP COLUMN join_date |
集合不会描述和强制文档的结构;在集合这一级无文档结构的改变。 但在文档级, 可使用update()操作和 $unset操作符删除文档中的字段。 db.users.update( { }, { $unset: { join_date: "" } }, { multi: true } ) |
CREATE INDEX idx_user_id_asc ON users(user_id) |
db.users.createIndex( { user_id: 1 } ) |
CREATE INDEX idx_user_id_asc_age_desc ON users(user_id, age DESC) |
db.users.createIndex( { user_id: 1, age: -1 } ) |
DROP TABLE users |
db.users.drop() |
插入
下表展示了插入操作的对应关系
SQL插入语句 |
MongoDB insert() 语句 |
INSERT INTO users(user_id,age,status) VALUES ("bcd001",45,"A") |
db.users.insert( { user_id: "bcd001", age: 45, status: "A" }) |
查询
下表展示了查询操作的对应关系
注:
find() 方法的执行结果返回文档中总是包含_id字段,除非通过投影器(projection)排除此字段。考虑到这一点,下面的SQL语句可能包含_id字段,即使在相应的find()方法中不包含_id字段。
SQL查询语句 |
MongoDB find()语句 |
SELECT *FROM users |
db.users.find() |
SELECT id, user_id, status FROM users |
db.users.find( { }, { user_id: 1, status: 1 }) |
SELECT user_id, statusFROM users |
db.users.find( { }, { user_id: 1, status: 1, _id: 0 }) |
SELECT *FROM usersWHERE status = "A" |
db.users.find( { status: "A" }) |
SELECT user_id, statusFROM usersWHERE status = "A" |
db.users.find( { status: "A" }, { user_id: 1, status: 1, _id: 0 }) |
SELECT *FROM usersWHERE status != "A" |
db.users.find( { status: { $ne: "A" } }) |
SELECT *FROM usersWHERE status = "A"AND age = 50 |
db.users.find( { status: "A", age: 50 }) |
SELECT *FROM usersWHERE status = "A"OR age = 50 |
db.users.find( { $or: [ { status: "A" } , { age: 50 } ] }) |
SELECT *FROM usersWHERE age > 25 |
db. 以上是关于翻译MongoDB指南/CRUD操作的主要内容,如果未能解决你的问题,请参考以下文章 MongoDB学习笔记-使用 MongoDB 进行 CRUD 操作(上) |