# MongoDB insert 小知識 分享在 MongoDB 常常容易忘記,但卻很重要的事項 1. 不會遇到 (db, colleciton) 不存在的問題 2. 遇到 **E11000 duplicate key error** 的處理方式 ## 背景 有天一樣快樂地在練習 MongoDB 的過程中,因為太心急,產生了一些錯誤,這才去細看關於 insert command 的細節 在一個 collection 內正常新增一個名字為 amy 的資料 ```shell Atlas atlas-gu26kd-shard-0 [primary] demo> db.dummy.insert({"_id":1,"name":"amy"}) { acknowledged: true, insertedIds: { '0': 1 } } Atlas atlas-gu26kd-shard-0 [primary] demo> db.dummy.find({}) [ { _id: 1, name: 'amy' } ] ``` 後面心急,敲錯指令,又再度打了一次新增 ```shell Atlas atlas-gu26kd-shard-0 [primary] demo> db.dummy.insert({"_id":1,"name":"amy"}) Uncaught: MongoBulkWriteError: E11000 duplicate key error collection: demo.dummy index: _id_ dup key: { _id: 1 } Result: BulkWriteResult { result: { ok: 1, writeErrors: [ WriteError { err: { index: 0, code: 11000, errmsg: 'E11000 duplicate key error collection: demo.dummy index: _id_ dup key: { _id: 1 }', errInfo: undefined, op: { _id: 1, name: 'amy' } } } ], writeConcernErrors: [], insertedIds: [ { index: 0, _id: 1 } ], nInserted: 0, nUpserted: 0, nMatched: 0, nModified: 0, nRemoved: 0, upserted: [], opTime: { ts: Timestamp({ t: 1669558344, i: 8 }), t: Long("448") } } } ``` 看到跳出 duplicate key error 的錯誤不意外,因為 _id:1 的紀錄剛才已經被新增到了 collection 中,所以基於 _id 要保持 unique 的原則,這次操作當然要失敗。 ## 實驗 於是就開始在想,那如果是一次新增多筆記錄,然後其中有幾筆紀錄是 duplicate key 的話,那這次的操作結果會是如何? 實驗環境準備,先新增一筆紀錄(2) ```javascript! db.dummy.insert({"_id":2,"name":"user2"}) ``` 接著一次新增三筆紀錄 ```shell Atlas atlas-gu26kd-shard-0 [primary] demo> db.dummy.insert([{"_id":1,"name":"user1"},{"_id":2,"name":"user2"},{"_id":3,"name":"user3"}]) Uncaught: MongoBulkWriteError: E11000 duplicate key error collection: demo.dummy index: _id_ dup key: { _id: 2 } Result: BulkWriteResult { result: { ok: 1, writeErrors: [ WriteError { err: { index: 1, code: 11000, errmsg: 'E11000 duplicate key error collection: demo.dummy index: _id_ dup key: { _id: 2 }', errInfo: undefined, op: { _id: 2, name: 'user2' } } } ], writeConcernErrors: [], insertedIds: [ { index: 0, _id: 1 }, { index: 1, _id: 2 }, { index: 2, _id: 3 } ], nInserted: 1, nUpserted: 0, nMatched: 0, nModified: 0, nRemoved: 0, upserted: [], opTime: { ts: Timestamp({ t: 1669560013, i: 5 }), t: Long("448") } } } ``` 上述的錯誤中有一個亮點,**nInserted: 1**,雖然遇到了 duplicate key error,但只有一筆資料(_id:1)被成功新增了 ```shell Atlas atlas-gu26kd-shard-0 [primary] demo> db.dummy.find({}) [ { _id: 2, name: 'user2' }, { _id: 1, name: 'user1' } ] ``` 這時心裡想的是我的 _id:3 紀錄也想要跳過這個錯誤,新增進去不行嗎? ```javascript db.collection.insert( <document or array of documents>, { writeConcern: <document>, ordered: <boolean> } ) ``` 關於 ordered 的介紹 > Optional. If true, perform an ordered insert of the documents in the array, and if an error occurs with one of documents, MongoDB will return without processing the remaining documents in the array. > > If false, perform an unordered insert, and if an error occurs with one of documents, continue processing the remaining documents in the array. > > Defaults to true. ## 關閉 insert ordered 那就重新來過一次 先新增一筆紀錄(2) ```javascript! db.dummy.insert({"_id":2,"name":"user2"}) ``` 接著一次新增三筆紀錄 ```shell Atlas atlas-gu26kd-shard-0 [primary] demo> db.dummy.insert([{"_id":1,"name":"user1"},{"_id":2,"name":"user2"},{"_id":3,"name":"user3"}],{ "ordered": false }) Uncaught: MongoBulkWriteError: E11000 duplicate key error collection: demo.dummy index: _id_ dup key: { _id: 2 } Result: BulkWriteResult { result: { ok: 1, writeErrors: [ WriteError { err: { index: 1, code: 11000, errmsg: 'E11000 duplicate key error collection: demo.dummy index: _id_ dup key: { _id: 2 }', errInfo: undefined, op: { _id: 2, name: 'user2' } } } ], writeConcernErrors: [], insertedIds: [ { index: 0, _id: 1 }, { index: 1, _id: 2 }, { index: 2, _id: 3 } ], nInserted: 2, nUpserted: 0, nMatched: 0, nModified: 0, nRemoved: 0, upserted: [], opTime: { ts: Timestamp({ t: 1669560222, i: 8 }), t: Long("448") } } } ``` 雖然一樣跳出了 duplicate key error,但有個地方改變了,**nInserted: 2**,我們這次成功寫進去的資料有兩筆,不會因為遇到錯誤而停下來! ```shell Atlas atlas-gu26kd-shard-0 [primary] demo> db.dummy.find({}) [ { _id: 2, name: 'user2' }, { _id: 1, name: 'user1' }, { _id: 3, name: 'user3' } ] ``` ## 要小心發生於MongoDB的typo 一般來說,我們都預期如果 insert 的 collection 不存在,應該會報某種 not exist 的 runtime exception。 目前可以看到在 demo(db) 底下只有一個 collection(dummy)。 ```shell Atlas atlas-gu26kd-shard-0 [primary] demo> show collections dummy ``` 如果這個時候我下了這樣的指令,那是會成功的,如果你在很緊張的情況下,本來就容易打錯字,可能會造成一些誤會,所以要特別注意 mongodb 對於不存在的 db, collection 新增資料的行為那是會順利執行下去的 ```shell Atlas atlas-gu26kd-shard-0 [primary] demo> db.dummyeee.insert({"_id":2,"name":"user2"}) { acknowledged: true, insertedIds: { '0': 2 } } ``` 那如果我選擇使用一個根本不存在的 db 呢?像是 **yeeeeee**,沒錯,它就直接切過去了!不會跳出錯誤停醒你說這個 db 根本不存在,所以一樣要小心緊急情況下可能的 typo ```shell Atlas atlas-gu26kd-shard-0 [primary] demo> show dbs demo 112.00 KiB sample_airbnb 52.61 MiB sample_analytics 9.13 MiB sample_geospatial 1.36 MiB sample_guides 40.00 KiB sample_mflix 47.41 MiB sample_restaurants 7.63 MiB sample_supplies 1.12 MiB sample_training 51.82 MiB sample_weatherdata 3.85 MiB admin 336.00 KiB local 1.09 GiB Atlas atlas-gu26kd-shard-0 [primary] demo> use yeeeeee switched to db yeeeeee Atlas atlas-gu26kd-shard-0 [primary] yeeeeee> ```