標籤:
添加
下面我們來建立一個test 的集合并寫入一些資料。建立兩個對象j 和t , 並儲存到集合中去。在例子裡 “>” 來表示是 shell 輸入提示符
> j = { name : "mongo" };
{"name" : "mongo"}
> t = { x : 3 };
{ "x" : 3 }
> db.things.save(j);
> db.things.save(t);
> db.things.find();
{ "_id" : ObjectId("4c2209f9f3924d31102bd84a"), "name" : "mongo" }
{ "_id" : ObjectId("4c2209fef3924d31102bd84b"), "x" : 3 }
>
有幾點需要注意一下:
1、不需要預先建立一個集合. 在第一次插入資料時候會自動建立。
2、在文檔中其實可以儲存任何結構的資料, 當然在實際應用我們儲存的還是相同類型文檔的集合. 這個特性其實可以在應用裡很靈活, 你不需要類似alter table 語句來修改你的資料結構。
3、每次插入資料時候集合中都會有一個ID, 名字叫 _id,待會會詳細介紹!
下面再加點資料(迴圈添加):
> for( var i = 1; i < 10; i++ ) db.things.save( { x:4, j:i } );
> db.things.find();
{"name" : "mongo" , "_id" : ObjectId("497cf60751712cf7758fbdbb")}
{"x" : 3 , "_id" : ObjectId("497cf61651712cf7758fbdbc")}
{"x" : 4 , "j" : 1 , "_id" : ObjectId("497cf87151712cf7758fbdbd")}
{"x" : 4 , "j" : 2 , "_id" : ObjectId("497cf87151712cf7758fbdbe")}
{"x" : 4 , "j" : 3 , "_id" : ObjectId("497cf87151712cf7758fbdbf")}
{"x" : 4 , "j" : 4 , "_id" : ObjectId("497cf87151712cf7758fbdc0")}
{"x" : 4 , "j" : 5 , "_id" : ObjectId("497cf87151712cf7758fbdc1")}
{"x" : 4 , "j" : 6 , "_id" : ObjectId("497cf87151712cf7758fbdc2")}
{"x" : 4 , "j" : 7 , "_id" : ObjectId("497cf87151712cf7758fbdc3")}
{"x" : 4 , "j" : 8 , "_id" : ObjectId("497cf87151712cf7758fbdc4")}
請注意一下, 這裡迴圈次數是10, 但是只顯示到第8 條, 還有2 條資料沒有顯示。 如果想繼續查詢下面的資料只需要使用”it”命令, 就會繼續顯示下面的資料:
{ "_id" : ObjectId("4c220a42f3924d31102bd866"), "x" : 4, "j" : 17 }
{ "_id" : ObjectId("4c220a42f3924d31102bd867"), "x" : 4, "j" : 18 }
has more
> it
{ "_id" : ObjectId("4c220a42f3924d31102bd868"), "x" : 4, "j" : 19 }
{ "_id" : ObjectId("4c220a42f3924d31102bd869"), "x" : 4, "j" : 20 }
從技術上講 find() 返回一個遊標對象. 但在上面的例子裡, 並沒有拿到一個遊標的變數。所以 shell 自動遍曆遊標, 返回一個初始化的set, 並允許我們繼續用 it 迭代輸出。當然我們也可以直接用遊標來輸出, 不過這個是”遊標”部分的內容了。
_id key
MongoDB 支援的資料類型中,_id 是其自有產物,下面對其做些簡單的介紹。儲存在MongoDB 集合中的每個文檔(document)都有一個預設的主鍵_id,這個主鍵名稱是固定的,它可以是MongoDB 支援的任何資料類型,預設是ObjectId。在關聯式資料庫schema設計中,主鍵大多是數值型的,比如常用的int 和long,並且更通常的是主鍵的取值由資料庫自增獲得,這種主鍵數值的有序性有時也表明了某種邏輯。反觀MongoDB,它在設計之初就定位於分布式儲存系統,所以它原生的不支援自增主鍵。
_id key 舉例說明 :
當我們在往一個集合中寫入一條文檔時,系統會自動產生一個名為_id 的key.如:
> db.c1.find()
{ "_id" : ObjectId("4fb5faaf6d0f9d8ea3fc91a8"), "name" : "Tony", "age" : 20 }
{ "_id" : ObjectId("4fb5fab96d0f9d8ea3fc91a9"), "name" : "Joe", "age" : 10 }
這裡多出了一個類型為ObjectId 的key ,在插入時並沒有指定,這有點類似Oracle 的rowid的資訊,屬於自動產生的。在MongoDB 中,每一個集合都必須有一個叫做_id 的欄位,欄位類型預設是ObjectId ,換句話說,欄位類型可以不是ObjectId,例如:
> db.c1.find()
{ "_id" : ObjectId("4fb5faaf6d0f9d8ea3fc91a8"), "name" : "Tony", "age" : 20 }
{ "_id" : ObjectId("4fb5fab96d0f9d8ea3fc91a9"), "name" : "Joe", "age" : 10 }
{ "_id" : 3, "name" : "Bill", "age" : 55 }
雖然_id 的類型可以自由指定,但是在同一個集合中必須唯一,如果插入重複的值的話,系統將會拋出異常,具體如下:
> db.c1.insert({_id:3, name:"Bill_new", age:55})
E11000 duplicate key error index: test.c1.$_id_ dup key: { : 3.0 }
>
查詢
普通查詢
在沒有深入查詢之前, 我們先看看怎麼從一個查詢中返回一個遊標對象. 可以簡單的通過find() 來查詢, 他返回一個任意結構的集合. 如果實現特定的查詢稍後講解.
實現上面同樣的查詢, 然後通過 while 來輸出:
> var cursor = db.things.find();
> while (cursor.hasNext()) printjson(cursor.next());
{ "_id" : ObjectId("4c2209f9f3924d31102bd84a"), "name" : "mongo" }
{ "_id" : ObjectId("4c2209fef3924d31102bd84b"), "x" : 3 }
{ "_id" : ObjectId("4c220a42f3924d31102bd856"), "x" : 4, "j" : 1 }
{ "_id" : ObjectId("4c220a42f3924d31102bd857"), "x" : 4, "j" : 2 }
{ "_id" : ObjectId("4c220a42f3924d31102bd858"), "x" : 4, "j" : 3 }
{ "_id" : ObjectId("4c220a42f3924d31102bd859"), "x" : 4, "j" : 4 }
{ "_id" : ObjectId("4c220a42f3924d31102bd85a"), "x" : 4, "j" : 5 }
上面的例子顯示了遊標風格的迭代輸出. hasNext() 函數告訴我們是否還有資料, 如果有則可以調用 next() 函數。
當我們使用的是 JavaScript shell, 可以用到JS 的特性, forEach 就可以輸出遊標了. 下面的例子就是使用 forEach() 來迴圈輸出: forEach() 必須定義一個函數供每個遊標元素調用。
> db.things.find().forEach(printjson);
{ "_id" : ObjectId("4c2209f9f3924d31102bd84a"), "name" : "mongo" }
{ "_id" : ObjectId("4c2209fef3924d31102bd84b"), "x" : 3 }
{ "_id" : ObjectId("4c220a42f3924d31102bd856"), "x" : 4, "j" : 1 }
{ "_id" : ObjectId("4c220a42f3924d31102bd857"), "x" : 4, "j" : 2 }
{ "_id" : ObjectId("4c220a42f3924d31102bd858"), "x" : 4, "j" : 3 }
{ "_id" : ObjectId("4c220a42f3924d31102bd859"), "x" : 4, "j" : 4 }
{ "_id" : ObjectId("4c220a42f3924d31102bd85a"), "x" : 4, "j" : 5 }
在 MongoDB shell 裡, 我們也可以把遊標當作數組來用:
> var cursor = db.things.find();
> printjson(cursor[4]);
{ "_id" : ObjectId("4c220a42f3924d31102bd858"), "x" : 4, "j" : 3 }
使用遊標時候請注意佔用記憶體的問題, 特別是很大的遊標對象, 有可能會記憶體溢出. 所以應該用迭代的方式來輸出. 下面的樣本則是把遊標轉換成真實的數群組類型:
> var arr = db.things.find().toArray();
> arr[5];
{ "_id" : ObjectId("4c220a42f3924d31102bd859"), "x" : 4, "j" : 4 }
請注意這些特性只是在MongoDB shell 裡使用, 而不是所有的其他應用程式驅動都支援。MongoDB 遊標對象不是沒有快照,如果有其他使用者在集合裡第一次或者最後一次調用next(), 你可能得不到遊標裡的資料. 所以要明確的鎖定你要查詢的遊標。
條件查詢
到這裡我們已經知道怎麼從遊標裡實現一個查詢並返回資料對象, 下面就來看看怎麼根據指定的條件來查詢。
下面的樣本就是說明如何執行一個類似SQL 的查詢, 並示範了怎麼在 MongoDB 裡實現。這是在MongoDB shell 裡查詢, 當然你也可以用其他的應用程式驅動或者語言來實現:
SELECT * FROM things WHERE name="mongo"
> db.things.find({name:"mongo"}).forEach(printjson);
{ "_id" : ObjectId("4c2209f9f3924d31102bd84a"), "name" : "mongo" }
SELECT * FROM things WHERE x=4
> db.things.find({x:4}).forEach(printjson);
{ "_id" : ObjectId("4c220a42f3924d31102bd856"), "x" : 4, "j" : 1 }
{ "_id" : ObjectId("4c220a42f3924d31102bd857"), "x" : 4, "j" : 2 }
{ "_id" : ObjectId("4c220a42f3924d31102bd858"), "x" : 4, "j" : 3 }
{ "_id" : ObjectId("4c220a42f3924d31102bd859"), "x" : 4, "j" : 4 }
{ "_id" : ObjectId("4c220a42f3924d31102bd85a"), "x" : 4, "j" : 5 }
查詢條件是 { a:A, b:B, … } 類似 “where a==A and b==B and …”。
上面顯示的是所有的元素, 當然我們也可以返回特定的元素, 類似於返回表裡某欄位的值,只需要在 find({x:4}) 裡指定元素的名字。
SELECT j FROM things WHERE x=4
> db.things.find({x:4}, {j:true}).forEach(printjson);
{ "_id" : ObjectId("4c220a42f3924d31102bd856"), "j" : 1 }
{ "_id" : ObjectId("4c220a42f3924d31102bd857"), "j" : 2 }
{ "_id" : ObjectId("4c220a42f3924d31102bd858"), "j" : 3 }
{ "_id" : ObjectId("4c220a42f3924d31102bd859"), "j" : 4 }
{ "_id" : ObjectId("4c220a42f3924d31102bd85a"), "j" : 5 }
為了方便考慮, MongoDB shell 避免遊標可能帶來的開銷, 提供一個findOne() 函數. 這個函數和 find() 函數一樣, 不過它返回的是遊標裡第一條資料, 或者返回null,即空資料。作為一個例子, name=”mongo” 可以用很多方法來實現, 可以用 next() 來迴圈遊標或者當做數組返回第一個元素,但是用 findOne() 方法則更簡單和高效:
> printjson(db.things.findOne({name:"mongo"}));
{ "_id" : ObjectId("4c2209f9f3924d31102bd84a"), "name" : "mongo" }
如果需要限制結果集的長度, 那麼可以調用 limit 方法。這是強烈推薦解決效能問題的方法, 就是通過限制條數來減少網路傳輸, 例如:
> db.things.find().limit(3);
{ "_id" : ObjectId("4c2209f9f3924d31102bd84a"), "name" : "mongo" }
{ "_id" : ObjectId("4c2209fef3924d31102bd84b"), "x" : 3 }
{ "_id" : ObjectId("4c220a42f3924d31102bd856"), "x" : 4, "j" : 1 }
修改
將name 是mongo 的記錄的name 修改為mongo_new
> db.things.update({name:"mongo"},{$set:{name:"mongo_new"}});
我們來查詢一下是否改過來了
> db.things.find();
{ "_id" : ObjectId("4faa9e7dedd27e6d86d86371"), "x" : 3 }
{ "_id" : ObjectId("4faa9e7bedd27e6d86d86370"), "name" : "mongo_new" }
刪除
將使用者name 是mongo_new 的記錄從集合things 中刪除、
> db.things.remove({name:"mongo_new"});
> db.things.find();
{ "_id" : ObjectId("4faa9e7dedd27e6d86d86371"), "x" : 3 }
經驗證,該記錄確實被刪除了。
至此,增刪改差案例都差不多完整了,或者如果有不完整的地方,歡迎大家留言!
MongoDB整理筆記のCRUD