|
| 1 | +# MongoDB CRUD 操作 |
| 2 | + |
| 3 | +<!-- TOC depthFrom:2 depthTo:3 --> |
| 4 | + |
| 5 | +<!-- /TOC --> |
| 6 | + |
| 7 | +## 一、基本 CRUD 操作 |
| 8 | + |
| 9 | +MongoDB 的 CRUD 操作是针对 document 的读写操作。 |
| 10 | + |
| 11 | +### Create 操作 |
| 12 | + |
| 13 | +MongoDB 提供以下操作向一个 collection 插入 document |
| 14 | + |
| 15 | +- [`db.collection.insertOne()`](https://docs.mongodb.com/manual/reference/method/db.collection.insertOne/#db.collection.insertOne):插入一条 document |
| 16 | +- [`db.collection.insertMany()`](https://docs.mongodb.com/manual/reference/method/db.collection.insertMany/#db.collection.insertMany):插入多条 document |
| 17 | + |
| 18 | +> 注:以上操作都是原子操作。 |
| 19 | +
|
| 20 | + |
| 21 | + |
| 22 | +插入操作的特性: |
| 23 | + |
| 24 | +- MongoDB 中的所有写操作都是单个文档级别的原子操作。 |
| 25 | +- 如果要插入的 collection 当前不存在,则插入操作会自动创建 collection。 |
| 26 | +- 在 MongoDB 中,存储在集合中的每个文档都需要一个唯一的 [`_id`](https://docs.mongodb.com/manual/reference/glossary/#term-id) 字段作为主键。如果插入的文档省略 `_id` 字段,则 MongoDB 驱动程序会自动为 `_id` 字段生成 ObjectId。 |
| 27 | +- 可以 MongoDB 写入操作的确认级别来控制写入行为。 |
| 28 | + |
| 29 | +【示例】插入一条 document 示例 |
| 30 | + |
| 31 | +```javascript |
| 32 | +db.inventory.insertOne({ |
| 33 | + item: 'canvas', |
| 34 | + qty: 100, |
| 35 | + tags: ['cotton'], |
| 36 | + size: { h: 28, w: 35.5, uom: 'cm' }, |
| 37 | +}) |
| 38 | +``` |
| 39 | + |
| 40 | +【示例】插入多条 document 示例 |
| 41 | + |
| 42 | +```javascript |
| 43 | +db.inventory.insertMany([ |
| 44 | + { |
| 45 | + item: 'journal', |
| 46 | + qty: 25, |
| 47 | + tags: ['blank', 'red'], |
| 48 | + size: { h: 14, w: 21, uom: 'cm' }, |
| 49 | + }, |
| 50 | + { |
| 51 | + item: 'mat', |
| 52 | + qty: 85, |
| 53 | + tags: ['gray'], |
| 54 | + size: { h: 27.9, w: 35.5, uom: 'cm' }, |
| 55 | + }, |
| 56 | + { |
| 57 | + item: 'mousepad', |
| 58 | + qty: 25, |
| 59 | + tags: ['gel', 'blue'], |
| 60 | + size: { h: 19, w: 22.85, uom: 'cm' }, |
| 61 | + }, |
| 62 | +]) |
| 63 | +``` |
| 64 | + |
| 65 | +### Read 操作 |
| 66 | + |
| 67 | +MongoDB 提供 [`db.collection.find()`](https://docs.mongodb.com/manual/reference/method/db.collection.find/#db.collection.find) 方法来检索 document。 |
| 68 | + |
| 69 | + |
| 70 | + |
| 71 | +### Update 操作 |
| 72 | + |
| 73 | +MongoDB 提供以下操作来更新 collection 中的 document |
| 74 | + |
| 75 | +- [`db.collection.updateOne()`](https://docs.mongodb.com/manual/reference/method/db.collection.updateOne/#db.collection.updateOne):更新一条 document |
| 76 | +- [`db.collection.updateMany()`](https://docs.mongodb.com/manual/reference/method/db.collection.updateMany/#db.collection.updateMany):更新多条 document |
| 77 | +- [`db.collection.replaceOne()`](https://docs.mongodb.com/manual/reference/method/db.collection.replaceOne/#db.collection.replaceOne):替换一条 document |
| 78 | + |
| 79 | +语法格式: |
| 80 | + |
| 81 | +- [`db.collection.updateOne(<filter>, <update>, <options>)`](https://docs.mongodb.com/manual/reference/method/db.collection.updateOne/#db.collection.updateOne) |
| 82 | +- [`db.collection.updateMany(<filter>, <update>, <options>)`](https://docs.mongodb.com/manual/reference/method/db.collection.updateMany/#db.collection.updateMany) |
| 83 | +- [`db.collection.replaceOne(<filter>, <update>, <options>)`](https://docs.mongodb.com/manual/reference/method/db.collection.replaceOne/#db.collection.replaceOne) |
| 84 | + |
| 85 | + |
| 86 | + |
| 87 | +【示例】插入测试数据 |
| 88 | + |
| 89 | +```javascript |
| 90 | +db.inventory.insertMany([ |
| 91 | + { |
| 92 | + item: 'canvas', |
| 93 | + qty: 100, |
| 94 | + size: { h: 28, w: 35.5, uom: 'cm' }, |
| 95 | + status: 'A', |
| 96 | + }, |
| 97 | + { item: 'journal', qty: 25, size: { h: 14, w: 21, uom: 'cm' }, status: 'A' }, |
| 98 | + { item: 'mat', qty: 85, size: { h: 27.9, w: 35.5, uom: 'cm' }, status: 'A' }, |
| 99 | + { |
| 100 | + item: 'mousepad', |
| 101 | + qty: 25, |
| 102 | + size: { h: 19, w: 22.85, uom: 'cm' }, |
| 103 | + status: 'P', |
| 104 | + }, |
| 105 | + { |
| 106 | + item: 'notebook', |
| 107 | + qty: 50, |
| 108 | + size: { h: 8.5, w: 11, uom: 'in' }, |
| 109 | + status: 'P', |
| 110 | + }, |
| 111 | + { item: 'paper', qty: 100, size: { h: 8.5, w: 11, uom: 'in' }, status: 'D' }, |
| 112 | + { |
| 113 | + item: 'planner', |
| 114 | + qty: 75, |
| 115 | + size: { h: 22.85, w: 30, uom: 'cm' }, |
| 116 | + status: 'D', |
| 117 | + }, |
| 118 | + { |
| 119 | + item: 'postcard', |
| 120 | + qty: 45, |
| 121 | + size: { h: 10, w: 15.25, uom: 'cm' }, |
| 122 | + status: 'A', |
| 123 | + }, |
| 124 | + { |
| 125 | + item: 'sketchbook', |
| 126 | + qty: 80, |
| 127 | + size: { h: 14, w: 21, uom: 'cm' }, |
| 128 | + status: 'A', |
| 129 | + }, |
| 130 | + { |
| 131 | + item: 'sketch pad', |
| 132 | + qty: 95, |
| 133 | + size: { h: 22.85, w: 30.5, uom: 'cm' }, |
| 134 | + status: 'A', |
| 135 | + }, |
| 136 | +]) |
| 137 | +``` |
| 138 | + |
| 139 | +【示例】更新一条 document |
| 140 | + |
| 141 | +```javascript |
| 142 | +db.inventory.updateOne( |
| 143 | + { item: 'paper' }, |
| 144 | + { |
| 145 | + $set: { 'size.uom': 'cm', status: 'P' }, |
| 146 | + $currentDate: { lastModified: true }, |
| 147 | + } |
| 148 | +) |
| 149 | +``` |
| 150 | + |
| 151 | +【示例】更新多条 document |
| 152 | + |
| 153 | +```javascript |
| 154 | +db.inventory.updateMany( |
| 155 | + { qty: { $lt: 50 } }, |
| 156 | + { |
| 157 | + $set: { 'size.uom': 'in', status: 'P' }, |
| 158 | + $currentDate: { lastModified: true }, |
| 159 | + } |
| 160 | +) |
| 161 | +``` |
| 162 | + |
| 163 | +【示例】替换一条 document |
| 164 | + |
| 165 | +```javascript |
| 166 | +db.inventory.replaceOne( |
| 167 | + { item: 'paper' }, |
| 168 | + { |
| 169 | + item: 'paper', |
| 170 | + instock: [ |
| 171 | + { warehouse: 'A', qty: 60 }, |
| 172 | + { warehouse: 'B', qty: 40 }, |
| 173 | + ], |
| 174 | + } |
| 175 | +) |
| 176 | +``` |
| 177 | + |
| 178 | +更新操作的特性: |
| 179 | + |
| 180 | +- MongoDB 中的所有写操作都是单个文档级别的原子操作。 |
| 181 | +- 一旦设置了,就无法更新或替换 [`_id`](https://docs.mongodb.com/manual/reference/glossary/#term-id) 字段。 |
| 182 | +- 除以下情况外,MongoDB 会在执行写操作后保留文档字段的顺序: |
| 183 | + - `_id` 字段始终是文档中的第一个字段。 |
| 184 | + - 包括重命名字段名称的更新可能导致文档中字段的重新排序。 |
| 185 | +- 如果更新操作中包含 `upsert : true` 并且没有 document 匹配过滤器,MongoDB 会新插入一个 document;如果有匹配的 document,MongoDB 会修改或替换这些 document。 |
| 186 | + |
| 187 | +### Delete 操作 |
| 188 | + |
| 189 | +MongoDB 提供以下操作来删除 collection 中的 document |
| 190 | + |
| 191 | +- [`db.collection.deleteOne()`](https://docs.mongodb.com/manual/reference/method/db.collection.deleteOne/#db.collection.deleteOne):删除一条 document |
| 192 | +- [`db.collection.deleteMany()`](https://docs.mongodb.com/manual/reference/method/db.collection.deleteMany/#db.collection.deleteMany):删除多条 document |
| 193 | + |
| 194 | + |
| 195 | + |
| 196 | +删除操作的特性: |
| 197 | + |
| 198 | +- MongoDB 中的所有写操作都是单个文档级别的原子操作。 |
| 199 | + |
| 200 | +## 二、批量写操作 |
| 201 | + |
| 202 | +MongoDB 通过 [`db.collection.bulkWrite()`](https://docs.mongodb.com/manual/reference/method/db.collection.bulkWrite/#db.collection.bulkWrite) 方法来支持批量写操作(包括批量插入、更新、删除)。 |
| 203 | + |
| 204 | +此外,[`db.collection.insertMany()`](https://docs.mongodb.com/manual/reference/method/db.collection.insertMany/#db.collection.insertMany) 方法支持批量插入操作。 |
| 205 | + |
| 206 | +### 有序和无序的操作 |
| 207 | + |
| 208 | +批量写操作可以有序或无序。 |
| 209 | + |
| 210 | +- 对于有序列表,MongoDB 串行执行操作。如果在写操作的处理过程中发生错误,MongoDB 将不处理列表中剩余的写操作。 |
| 211 | +- 对于无序列表,MongoDB 可以并行执行操作,但是不能保证此行为。如果在写操作的处理过程中发生错误,MongoDB 将继续处理列表中剩余的写操作。 |
| 212 | + |
| 213 | +在分片集合上执行操作的有序列表通常比执行无序列表要慢,因为对于有序列表,每个操作必须等待上一个操作完成。 |
| 214 | + |
| 215 | +默认情况下,[`bulkWrite()`](https://docs.mongodb.com/manual/reference/method/db.collection.bulkWrite/#db.collection.bulkWrite) 执行有序操作。要指定无序写操作,请在选项文档中设置 `ordered : false`。 |
| 216 | + |
| 217 | +### bulkWrite() 方法 |
| 218 | + |
| 219 | +[`bulkWrite()`](https://docs.mongodb.com/manual/reference/method/db.collection.bulkWrite/#db.collection.bulkWrite) 支持以下写操作: |
| 220 | + |
| 221 | +- [insertOne](https://docs.mongodb.com/manual/reference/method/db.collection.bulkWrite/#bulkwrite-write-operations-insertone) |
| 222 | +- [updateOne](https://docs.mongodb.com/manual/reference/method/db.collection.bulkWrite/#bulkwrite-write-operations-updateonemany) |
| 223 | +- [updateMany](https://docs.mongodb.com/manual/reference/method/db.collection.bulkWrite/#bulkwrite-write-operations-updateonemany) |
| 224 | +- [replaceOne](https://docs.mongodb.com/manual/reference/method/db.collection.bulkWrite/#bulkwrite-write-operations-replaceone) |
| 225 | +- [deleteOne](https://docs.mongodb.com/manual/reference/method/db.collection.bulkWrite/#bulkwrite-write-operations-deleteonemany) |
| 226 | +- [deleteMany](https://docs.mongodb.com/manual/reference/method/db.collection.bulkWrite/#bulkwrite-write-operations-deleteonemany) |
| 227 | + |
| 228 | +【示例】批量写操作示例 |
| 229 | + |
| 230 | +```javascript |
| 231 | +try { |
| 232 | + db.characters.bulkWrite([ |
| 233 | + { |
| 234 | + insertOne: { |
| 235 | + document: { |
| 236 | + _id: 4, |
| 237 | + char: 'Dithras', |
| 238 | + class: 'barbarian', |
| 239 | + lvl: 4, |
| 240 | + }, |
| 241 | + }, |
| 242 | + }, |
| 243 | + { |
| 244 | + insertOne: { |
| 245 | + document: { |
| 246 | + _id: 5, |
| 247 | + char: 'Taeln', |
| 248 | + class: 'fighter', |
| 249 | + lvl: 3, |
| 250 | + }, |
| 251 | + }, |
| 252 | + }, |
| 253 | + { |
| 254 | + updateOne: { |
| 255 | + filter: { char: 'Eldon' }, |
| 256 | + update: { $set: { status: 'Critical Injury' } }, |
| 257 | + }, |
| 258 | + }, |
| 259 | + { deleteOne: { filter: { char: 'Brisbane' } } }, |
| 260 | + { |
| 261 | + replaceOne: { |
| 262 | + filter: { char: 'Meldane' }, |
| 263 | + replacement: { char: 'Tanys', class: 'oracle', lvl: 4 }, |
| 264 | + }, |
| 265 | + }, |
| 266 | + ]) |
| 267 | +} catch (e) { |
| 268 | + print(e) |
| 269 | +} |
| 270 | +``` |
| 271 | + |
| 272 | +### 批量写操作策略 |
| 273 | + |
| 274 | +大量的插入操作(包括初始数据插入或常规数据导入)可能会影响分片集群的性能。对于批量插入,请考虑以下策略: |
| 275 | + |
| 276 | +#### 预拆分 collection |
| 277 | + |
| 278 | +如果分片集合为空,则该集合只有一个初始 [chunk](https://docs.mongodb.com/manual/reference/glossary/#term-chunk),该 [chunk](https://docs.mongodb.com/manual/reference/glossary/#term-chunk) 位于单个分片上。然后,MongoDB 必须花一些时间来接收数据,创建拆分并将拆分的块分发到可用的分片。为了避免这种性能成本,您可以按照拆分群集中的拆分块中的说明预拆分 collection。 |
| 279 | + |
| 280 | +#### 无序写操作 |
| 281 | + |
| 282 | +要提高对分片集群的写入性能,请使用 [`bulkWrite()`](https://docs.mongodb.com/manual/reference/method/db.collection.bulkWrite/#db.collection.bulkWrite),并将可选参数顺序设置为 false。[`mongos`](https://docs.mongodb.com/manual/reference/program/mongos/#bin.mongos) 可以尝试同时将写入操作发送到多个分片。对于空集合,首先按照分片群集中的分割 [chunk](https://docs.mongodb.com/manual/reference/glossary/#term-chunk) 中的说明预拆分 collection。 |
| 283 | + |
| 284 | +#### 避免单调节流 |
| 285 | + |
| 286 | +如果在一次插入操作中,分片 key 单调递增,那么所有的插入数据都会存入 collection 的最后一个 chunk,也就是存入一个分片中。因此,集群的插入容量将永远不会超过该单个分片的插入容量。 |
| 287 | + |
| 288 | +如果插入量大于单个分片可以处理的插入量,并且无法避免单调递增的分片键,那么请考虑对应用程序进行以下修改: |
| 289 | + |
| 290 | +- 反转分片密钥的二进制位。这样可以保留信息,并避免将插入顺序与值序列的增加关联起来。 |
| 291 | +- 交换第一个和最后一个 16 位字以“随机”插入。 |
| 292 | + |
| 293 | +## SQL 和 MongoDB 对比 |
| 294 | + |
| 295 | +### 术语和概念 |
| 296 | + |
| 297 | +| SQL 术语和概念 | MongoDB 术语和概念 | |
| 298 | +| :-------------------------- | :----------------------------------------------------------- | |
| 299 | +| database | [database](https://docs.mongodb.com/manual/reference/glossary/#term-database) | |
| 300 | +| table | [collection](https://docs.mongodb.com/manual/reference/glossary/#term-collection) | |
| 301 | +| row | [document](https://docs.mongodb.com/manual/reference/glossary/#term-document) 或 [BSON](https://docs.mongodb.com/manual/reference/glossary/#term-bson) | |
| 302 | +| column | [field](https://docs.mongodb.com/manual/reference/glossary/#term-field) | |
| 303 | +| index | [index](https://docs.mongodb.com/manual/reference/glossary/#term-index) | |
| 304 | +| table joins | [`$lookup`](https://docs.mongodb.com/manual/reference/operator/aggregation/lookup/#pipe._S_lookup)、嵌入式文档 | |
| 305 | +| primary key | [primary key](https://docs.mongodb.com/manual/reference/glossary/#term-primary-key)<br>MongoDB 中自动设置主键为 [`_id`](https://docs.mongodb.com/manual/reference/glossary/#term-id) 字段 | |
| 306 | +| aggregation (e.g. group by) | aggregation pipeline<br>参考 [SQL to Aggregation Mapping Chart](https://docs.mongodb.com/manual/reference/sql-aggregation-comparison/). | |
| 307 | +| SELECT INTO NEW_TABLE | [`$out`](https://docs.mongodb.com/manual/reference/operator/aggregation/out/#pipe._S_out)<br>参考 [SQL to Aggregation Mapping Chart](https://docs.mongodb.com/manual/reference/sql-aggregation-comparison/) | |
| 308 | +| MERGE INTO TABLE | [`$merge`](https://docs.mongodb.com/manual/reference/operator/aggregation/merge/#pipe._S_merge) (MongoDB 4.2 开始支持)<br>参考 [SQL to Aggregation Mapping Chart](https://docs.mongodb.com/manual/reference/sql-aggregation-comparison/). | |
| 309 | +| UNION ALL | [`$unionWith`](https://docs.mongodb.com/manual/reference/operator/aggregation/unionWith/#pipe._S_unionWith) (MongoDB 4.4 开始支持) | |
| 310 | +| transactions | [transactions](https://docs.mongodb.com/manual/core/transactions/) | |
| 311 | + |
| 312 | +## 参考资料 |
| 313 | + |
| 314 | +- **官方** |
| 315 | + - [MongoDB 官网](https://www.mongodb.com/) |
| 316 | + - [MongoDB Github](https://github.com/mongodb/mongo) |
| 317 | + - [MongoDB 官方免费教程](https://university.mongodb.com/) |
| 318 | +- **教程** |
| 319 | + - [MongoDB 教程](https://www.runoob.com/mongodb/mongodb-tutorial.html) |
| 320 | + - [MongoDB 高手课](https://time.geekbang.org/course/intro/100040001) |
0 commit comments