讀古今文學網 > MongoDB實戰 > 2.1 深入MongoDB Shell >

2.1 深入MongoDB Shell

MongoDB JavaScript Shell能讓你玩轉數據,對文檔、集合以及MongoDB的特殊查詢語言有切實的體驗。你可以把以下內容當做對MongoDB的實用入門。

我們先說說Shell的啟動與運行,然後再看看JavaScript是如何表示文檔並將其插入MongoDB集合的。為了驗證插入是否成功,你可以查詢集合內容。緊隨其後的是更新集合。最後,你還將瞭解到如何清除並刪除集合。

2.1.1 啟動Shell

如果已按照附錄A的說明進行了安裝,那麼電腦上現在應該已經有一個可正常工作的MongoDB了。請確保有一個正在運行的mongod實例,隨後運行可執行文件mongo啟動MongoDB shell:

./mongo
  

如果Shell程序啟動成功,你將看到如圖2-1所示的界面。Shell開始的地方顯示了正運行的MongoDB版本,還有和當前選中的數據庫相關的一些信息。

圖2-1 啟動後的MongoDB JavaScript Shell

如果你懂點JavaScript,馬上就可以鍵入代碼使用Shell。如果不懂,就請繼續讀下去,看看如何插入自己的第一條數據。

2.1.2 插入與查詢

如果啟動時沒有指定其他數據庫,Shell會選擇名為test的默認數據庫。為了讓後續的教學練習都在同一個命名空間裡,我們先切換到tutorial數據庫:

> use tutorial
switched to db tutorial
  

你會看到一行消息,說明你已經切換了數據庫。

創建數據庫與集合

你也許會感到奇怪:我們並沒有創建tutorial數據庫,又怎麼能切換過去呢?實際上,創建數據庫並不是必需的操作。數據庫與集合只有在第一次插入文檔時才會被創建。這個行為與MongoDB對數據的動態處理方式是一致的;因為不用事先定義文檔的結構,單獨的集合和數據庫可以在運行時才被創建。這能簡化並加速開發過程,而且有利於動態分配命名空間,很多時候這都很管用。如果你擔心數據庫或集合被意外創建,大多數驅動都能開啟嚴格模式(strict mode),避免此類由疏忽引起的錯誤。

現在是時候創建你的第一個文檔了。因為正在使用JavaScript Shell,所以將用JSON(JavaScript Object Notation)來描述文檔。舉個例子,一個最簡單的描述用戶的文檔如下:

{username: \"jones\"}
  

該文檔包含一對鍵和值,存儲了Jones的用戶名。要保存這個文檔,需要選擇一個集合,像下面這樣把它保存到users集合裡就再恰當不過了:

> db.users.insert({username: \"smith\"})
  

在鍵入這行代碼後你會感覺到一絲延遲。這時tutorial數據庫和users集合都還沒在磁盤上創建出來,延遲是因為要為它們的初始化數據文件分配空間。

如果插入成功,那麼就已經成功地保存了第一個文檔。可以通過一條簡單的查詢來進行驗證:

> db.users.find
  

查詢的結果看起來是這樣的:

{ _id : ObjectId(\"4bf9bec50e32f82523389314\"), username : \"smith\" }
  

請注意,文檔中添加了_id字段,你可以把它當做文檔的主鍵。每個MongoDB文檔都要求有一個_id,如果文檔創建時沒有提供該字段,就會生成一個特殊的MongoDB對像ID並添加到文檔裡。在你——控制台裡出現的對象ID與示例中的並不一樣,但它在集合的所有_id值裡是唯一的,這是對該字段的唯一硬性要求。

在下一章裡我會詳細介紹對像ID。現在繼續向集合中添加用戶:

> db.users.save({username: \"jones\"})
  

集合裡現在應該有兩個文檔了。接下來通過count命令驗證一下:

> db.users.count
2
  

既然集合裡文檔的數量已經不止一個了,那麼就能看些稍微複雜一點兒的查詢了。與之前一樣,可以把集合裡所有的文檔都查出來:

> db.users.find
{ _id : ObjectId(\"4bf9bec50e32f82523389314\"), username : \"smith\" }
{ _id : ObjectId(\"4bf9bec90e32f82523389315\"), username : \"jones\" }
  

但也可以給find方法傳入一個簡單的查詢選擇器。查詢選擇器(query selector)是一個文檔,用來和集合中所有的文檔進行匹配。要查詢所有用戶名是jones的文檔,可以像下面這樣傳入一個簡單的文檔,將其作為查詢選擇器:

> db.users.find({username: \"jones\"})
{ _id : ObjectId(\"4bf9bec90e32f82523389315\"), username : \"jones\" }
  

查詢選擇器{username: \"jones\"}返回了所有用戶名是jones的文檔——它會逐字匹配現有的文檔。

我剛才演示了創建和讀取數據的基本方法,現在再來看看如何更新數據。

2.1.3 更新文檔

所有的更新操作都要求至少有兩個參數,第一個指明要更新的文檔,第二個定義被選中的文檔應該如何更新。有兩種風格的更新;本節只關注針對性更新(targeted modification),這是MongoDB獨有特性中最具代表性的。

舉例來說,假設用戶smith想要向自己的住所中添加國家信息,可以使用如下更新語句:

> db.users.update({username: \"smith\"}, {$set: {country: \"Canada\"}})
  

這條更新語句告訴MongoDB應找到用戶名是smith的文檔,將其country屬性值設置為Canada。如果現在執行查詢,你將看到更新後的文檔:

> db.users.find({username: \"smith\"})
{ \"_id\" : ObjectId(\"4bf9ec440e32f82523389316\"),
  \"country\" : \"Canada\", username : \"smith\" }
  

稍後,如果用戶決定檔案中不再保留國家信息,使用$unset操作符就能輕鬆去除該值:

> db.users.update({username: \"smith\"}, {$unset: {country: 1}})
  

讓我們再豐富一下這個例子。如第1章所示,你使用文檔來表示數據,其中能包含複雜的數據結構。因此,讓我們假設一下,除了存儲個人檔案信息,用戶還能用列表來存儲自己喜歡的東西。一個好的文檔表述看起來可能是這樣的:

{ username: \"smith\",
  favorites: {
    cities: [\"Chicago\", \"Cheyenne\"],
    movies: [\"Casablanca\", \"For a Few Dollars More\", \"The Sting\"]
  }
}
  

favorites鍵指向一個對象,後者包含兩個其他的鍵,它們分別指向喜歡的城市列表和電影列表。就目前所知道的知識,你能否想出一個辦法將原來的smith文檔修改成這樣?你應該能想到$set操作符。請注意,本例實際是在改寫文檔,這也是$set的合理用法:

> db.users.update( {username: \"smith\"},
{ $set: {favorites:
   {
     cities: [\"Chicago\", \"Cheyenne\"],
     movies: [\"Casablanca\", \"The Sting\"]
   }
}
})
  

讓我們對jones做類似修改,但這裡就添加兩部喜歡的電影:

db.users.update( {username: \"jones\"},
  {\"$set\": {favorites:
    {
       movies: [\"Casablanca\", \"Rocky\"]
    }
  }
})
  

現在查詢users集合,確保兩個更新都成功了:

> db.users.find
  

有了之前的幾個示例文檔,現在可以一窺MongoDB查詢語言的威力了。尤其值得一提的是,它的查詢引擎能深入內嵌對象,匹配數組元素,這種情況下這種能力特別有用。你可以使用特殊的點符號來實現這類查詢。假設想找到所有喜歡電影《卡薩布蘭卡》(Casablanca)的用戶,能用這樣的查詢:

> db.users.find({\"favorites.movies\": \"Casablanca\"})
  

favoritesmovies之間的點告訴查詢引擎應找一個名為favorites的鍵,它指向一個對像(該對像有一個名為movies的內部鍵),然後匹配它的值。這條查詢會把兩個用戶文檔都返回。更進一步,假設你知道每個喜歡《卡薩布蘭卡》的用戶都喜歡《馬耳他之鷹》(The Maltese Falcon),且想更新數據庫來反映這個情況,該如何用一條MongoDB的更新語句來表示呢?

你可以再次請出$set操作符,但這要求改寫並發送整個movies數組。既然你想做的只是向列表裡添加一個元素,最好使用$push$addToSet,這兩個操作符都是向數組中添加一個元素,但後者會保證唯一性,防止重複添加。下面就是你要找的更新語句:

db.users.update( {\"favorites.movies\": \"Casablanca\"},
    {$addToSet: {\"favorites.movies\": \"The Maltese Falcon\"} },
          false,
          true )
  

這條語句大體上還是易懂的:第一個參數是一個查詢選擇器,匹配電影列表裡有Casablanca的用戶;第二個參數使用$addToSet操作符向列表中添加了The Maltese Falcon;第三個參數false現在暫時忽略;第四個參數true說明這是一個多項更新(multi-update)。MongoDB的更新操作默認只會應用於查詢選擇器匹配到的第一個文檔。如果希望操作被應用於匹配到的所有文檔,需要顯式說明。因為你希望對smithjones都進行更新,所以多項更新是必需的。

我們稍後會對更新做更詳細的說明,但請先試試這些例子。

2.1.4 刪除數據

你已經知道了在MongoDB Shell中創建、讀取和更新數據的基本方法了,最後我們來看最簡單的操作——刪除數據。

如果不加參數,刪除操作會清空集合。要幹掉foo集合,只需鍵入:

> db.foo.remove
  

通常只需要刪除集合文檔的一個子集,為此可以給remove方法傳入一個查詢選擇器。如果想要刪除所有喜歡城市Cheyenne的用戶,用下面這個表達式就行了:

> db.users.remove({\"favorites.cities\": \"Cheyenne\"})
  

請注意,remove操作不會刪除集合,它只是從集合中刪除文檔。你可以把它想像成SQL中的DELETETRUNCATE TABLE指令。

如果想刪除集合以及它的全部索引,可以使用drop方法:

> db.users.drop
  

創建、讀取、更新和刪除是所有數據庫的基本操作。如果你讀過了前面的內容,現在應該能在MongoDB中實踐這些基本的CRUD操作了。在下一節裡,你將瞭解到二級索引,通過它來提高查詢、更新和刪除的性能。