MongoDB入門 (1)

by vivid 15. 六月 2016 09:56

.NET Magazine國際中文電子雜誌
作 者:許薰尹
審 稿:張智凱
文章編號:
N160617202
出刊日期:2016/6/15
資料庫:MongoDB

文件導向資料庫(Document Oriented Databases)是一種新類型的資料庫,相較於傳統的SQL資料庫,它以完全不同的方式來儲存資料。MongoDB不使用傳統的資料表(Tables)的觀念來儲存資料,而是使用「document」來記錄你的應用程式之中使用到的實體(Entity)。

 

MongoDB vs. 傳統資料庫

文件導向資料庫不需要傳統的主鍵或外部參考鍵(Foreign-key)來識別相關的資料,它使用的資料結構描述資訊可以讓你不需使用傳統的聯結(join)方式找尋相關聯的資料,特別適用於一對一,與一對多的情境。

MongoDB資料庫的架構的最上層是MongoDB伺服器,其下可以有一到多個資料庫,資料庫下可以包含多個集合,集合中可以包含Document,請參考下圖所示:

clip_image002

圖 1:MongoDB資料庫的架構。

根據官網的文件說明 https://www.mongodb.com/compare/mongodb-mysql,MongoDB與傳統資料庫(MySQL)術語對應如下:

傳統MySQL資料庫

MongoDB

資料表 (Table)

集合 (Collection)

資料橫列 (Row)

文件(Document)

資料直欄(Column)

欄位(Field)

聯結(Joins)

內嵌文件(Embedded documents),連結(linking)

使用MongoDB儲存資料的好處是,MongoDB document的表達方式類似當下最流行的物件導向程式開發語言的語法,不需要再多透過ORM層(Object-relational mapping),將程式之中的物件轉存到關聯式資料庫資料表,而是把物件當做文件直接儲存到資料庫。

 

資料表示方式

MongoDB資料表示方式採用時下最流行、類似JSON的語法來表達,這種表達方式和傳統資料庫完全不同。以下範例是MongoDB官方提供的範例資料,可從官網以下網址下載,https://raw.githubusercontent.com/mongodb/docs-assets/primer-dataset/primer-dataset.json

{
        "_id" : ObjectId("5705e0ae16dfa0ae82083766"),
        "address" : {
                "building" : "1007",
                "coord" : [
                        -73.856077,
                        40.848447
                ],
                "street" : "Morris Park Ave",
                "zipcode" : "10462"
        },
        "borough" : "Bronx",
        "cuisine" : "Bakery",
        "grades" : [
                {
                        "date" : ISODate("2014-03-03T00:00:00Z"),
                        "grade" : "A",
                        "score" : 2
                },
                {
                        "date" : ISODate("2013-09-11T00:00:00Z"),
                        "grade" : "A",
                        "score" : 6
                },
                {
                        "date" : ISODate("2013-01-24T00:00:00Z"),
                        "grade" : "A",
                        "score" : 10
                },
                {
                        "date" : ISODate("2011-11-23T00:00:00Z"),
                        "grade" : "A",
                        "score" : 9
                },
                {
                        "date" : ISODate("2011-03-10T00:00:00Z"),
                        "grade" : "B",
                        "score" : 14
                }
        ],
        "name" : "Morris Park Bake Shop",
        "restaurant_id" : "30075445"
}

 

使用MongoDB資料庫

要使用MongoDB資料庫的第一件是便是安裝,安裝的檔案和說明都可以從官網取得,網址在 https://docs.mongodb.com/manual/installation/。支援多種作業系統,如Linux、Windows、OSX、Ubuntu...等等。

安裝完成之後,便可以使用Mongod.exe 來啟動MongoDB 伺服器,本文以Windows作業系統為例,只要開啟一個命令提示字元,執行以下指令,使用dbpath參數設定資料庫資料檔案路徑(資料夾要事先建立好):

mongod --dbpath c:\data\db

接著開啟另一個命令提示字元,執行mongo.exe程式,就可以透過這個互動式介面來操作資料庫:

C:\Program Files\MongoDB\Server\3.2\bin>mongo

 

載入測試資料

從官網以下網址下載範例資料庫,存成primer-dataset.json檔案:

https://raw.githubusercontent.com/mongodb/docs-assets/primer-dataset/primer-dataset.json

開新的命令提示字元,執行mongoimport.exe,輸入以下指令,匯入primer-dataset.json檔案中的資料到test資料庫:

mongoimport --db test --collection restaurants --drop --file c:\temp\Mongo\primer-dataset.json

 

使用MongoDB資料庫互動式介面

MongoDB資料庫的供一個互動式介面(Interactive Shell)可以操作資料庫,例如,進行資料庫的管理或資料的新增、查詢…等等。開啟一個命令提示字元,啟動mongo,預設便會連上你建立的資料庫,指令執行的結果,請參考下圖所示:

clip_image004

圖 2:使用MongoDB資料庫互動式介面。

若資料庫有多個,我們可以透過show dbs指令來進行查詢:

> show dbs

指令執行的結果,請參考下圖所示:

clip_image006

圖 3:顯示資料庫清單。

若要切換資料庫,可以使用use指令,後面跟隨的是資料庫的名稱,以下指定將切換到名為「local」 的資料庫:

> use local

switched to db local

以下指定將切換到名為「test」的資料庫:

> use test

switched to db test

Collections是一個容器,其中可以包含document,「show collection」指令可以顯示出目前資料庫中包含的集合清單,restaurants是匯入資料時設定的集合:

> show collections

Restaurants

上述指令執行的結果,請參考下圖所示:

clip_image008

圖 4::顯示集合清單。

查詢集合資料

若要查詢集合資料,可以先使用use指令切換資料庫,接著再利用db參照到目前使用到的資料庫。舉例來說,若想要知道目前test資料庫restaurants集合中document的筆數,我們可以叫用count()方法:

> use test

switched to db test

> db.restaurants.count()

25359

若想要知道集合支援哪些方法可以叫用,可以透過help()方法,參考如下範例:

> db.restaurants.help()
DBCollection help
        db.restaurants.find().help() - show DBCursor help
        db.restaurants.bulkWrite( operations, <optional params> ) - bulk execute write operations, optional parameters are: w, wtimeout, j
        db.restaurants.count( query = {}, <optional params> ) - count the number of documents that matches the query, optional parameters are: limit, skip, hint, maxTimeMS
        db.restaurants.copyTo(newColl) - duplicates collection by copying all documents to newColl; no indexes are copied.

以下略

 

Mongo.exe是一個JavaScript 主控台程式,因此你可以透過JavaScript程式碼與之互動。例如以下例子,利用var關鍵字宣告一個「name」變數,並設定它的值為「'Berkely」,接著印出name的值:

> var name='Berkely'

> name

Berkely

指令執行的結果,請參考下圖所示:

clip_image010

圖 5::使用JavaScript程式碼。

在此工作階段中,你都可以使用這個變數來操作,例如以下範例叫用find()方法進行查詢,在方法中傳入一個物件來設定篩選條件,找出name的值相符於目前name變數值的document,並計算其筆數:

> db.restaurants.find({"name":name}).count()

1

以下範例則是找出name的值相符於目前name變數值的document:

> db.restaurants.find({"name":name})

{ "_id" : ObjectId("5705e0ae16dfa0ae82083792"), "address" : { "building" : "437", "coord" : [ -73.975393, 40.757365 ], "street" : "Madison Avenue", "zipcode" : "10022" }, "borough" : "Manhattan", "cuisine" : "American ", "grades" : [ { "date" : ISODate("2014-06-03T00:00:00Z"), "grade" : "A", "score" : 9 }, { "date" : ISODate("2013-06-07T00:00:00Z"), "grade" : "A", "score" : 5 }, { "date" : ISODate("2012-06-29T00:00:00Z"), "grade" : "A", "score" : 12 }, { "date" : ISODate("2012-02-06T00:00:00Z"), "grade" : "A", "score" : 11 }, { "date" : ISODate("2011-06-23T00:00:00Z"), "grade" : "A", "score" : 13 } ], "name" : "Berkely", "restaurant_id" : "40363685" }

我們也可以透過索引來找出特定的文件,例如以下範例使用中括號傳入索引「0」,找出集合中的第一筆資料,資料輸出到主控台時,會自動進行排版,較易進行閱讀:

> db.restaurants.find()[0]
{
        "_id" : ObjectId("5705e0ae16dfa0ae82083766"),
        "address" : {
                "building" : "1007",
                "coord" : [
                        -73.856077,
                        40.848447
                ],
                "street" : "Morris Park Ave",
                "zipcode" : "10462"
        },
        "borough" : "Bronx",
        "cuisine" : "Bakery",
        "grades" : [
                {
                        "date" : ISODate("2014-03-03T00:00:00Z"),
                        "grade" : "A",
                        "score" : 2
                },
                {
                        "date" : ISODate("2013-09-11T00:00:00Z"),
                        "grade" : "A",
                        "score" : 6
                },
                {
                        "date" : ISODate("2013-01-24T00:00:00Z"),
                        "grade" : "A",
                        "score" : 10
                },
                {
                        "date" : ISODate("2011-11-23T00:00:00Z"),
                        "grade" : "A",
                        "score" : 9
                },
                {
                        "date" : ISODate("2011-03-10T00:00:00Z"),
                        "grade" : "B",
                        "score" : 14
                }
        ],
        "name" : "Morris Park Bake Shop",
        "restaurant_id" : "30075445"
}

若想要查詢滿足條件的第一筆,也可以使用finOne()方法,上例的查詢語法可以修改如下,將得到一樣的執行結果:

> db.restaurants.findOne()

依此類推,若要找出集合中的第二筆資料,可以使用索引1:

> db.restaurants.find()[1]
{
        "_id" : ObjectId("5705e0ae16dfa0ae82083767"),
        "address" : {
                "building" : "469",
                "coord" : [
                        -73.961704,
                        40.662942
                ],
                "street" : "Flatbush Avenue",
                "zipcode" : "11225"
        },
        "borough" : "Brooklyn",
        "cuisine" : "Hamburgers",
        "grades" : [
                {
                        "date" : ISODate("2014-12-30T00:00:00Z"),
                        "grade" : "A",
                        "score" : 8
                },
                {
                        "date" : ISODate("2014-07-01T00:00:00Z"),
                        "grade" : "B",
                        "score" : 23
                },
                {
                        "date" : ISODate("2013-04-30T00:00:00Z"),
                        "grade" : "A",
                        "score" : 12
                },
                {
                        "date" : ISODate("2012-05-08T00:00:00Z"),
                        "grade" : "A",
                        "score" : 12
                }
        ],
        "name" : "Wendy'S",
        "restaurant_id" : "30112340"
}

 

若不想要印出所有文件的欄位,要從回傳的document,篩選特定的欄位輸出,可以利用屬性的語法「.」,以下指令將會取回name欄位的值:

> db.restaurants.find()[1].name

Wendy'S

以下指令將會取回address欄位的值:

> db.restaurants.find()[1].address
{
        "building" : "469",
        "coord" : [
                -73.961704,
                40.662942
        ],
        "street" : "Flatbush Avenue",
        "zipcode" : "11225"
}

 

以下指令將會取回borough欄位的值:

> db.restaurants.find()[1].borough

Brooklyn

 

使用Projection物件設定篩選條件

叫用find()方法時,可以在第二個參數傳入Projection物件來指定要顯示,或不顯示的欄位,若欄位要顯示,可將其值設為「1」,若不想顯示此欄位,則可將其值設定為「0」。若只想要回傳name與restaurant_id兩個欄位的內容,可以設定「{name:1,restaurant_id:1}」參數,預設會回傳object id,以下印出第一筆資料的值:

> db.restaurants.find({"cuisine":"Irish"},{name:1,restaurant_id:1})[0]

{

"_id" : ObjectId("5705e0ae16dfa0ae82083768"),

"name" : "Dj Reynolds Pub And Restaurant",

"restaurant_id" : "30191841"

}

若不想取回Object ID欄位,則可以設定「"_id":0」,查詢結果如下,將不顯示「_id」的值:

> db.restaurants.find({"cuisine":"Irish"},{"_id":0,name:1,restaurant_id:1})[0]

{ "name" : "Dj Reynolds Pub And Restaurant", "restaurant_id" : "30191841" }

我們也可以指定要回傳內嵌文件(Embedded document)grades的值:

> db.restaurants.find({"cuisine":"Irish"},{name:1,restaurant_id:1,grades:1})[0]
{
        "_id" : ObjectId("5705e0ae16dfa0ae82083768"),
        "grades" : [
                {
                        "date" : ISODate("2014-09-06T00:00:00Z"),
                        "grade" : "A",
                        "score" : 2
                },
                {
                        "date" : ISODate("2013-07-22T00:00:00Z"),
                        "grade" : "A",
                        "score" : 11
                },
                {
                        "date" : ISODate("2012-07-31T00:00:00Z"),
                        "grade" : "A",
                        "score" : 12
                },
                {
                        "date" : ISODate("2011-12-29T00:00:00Z"),
                        "grade" : "A",
                        "score" : 12
                }
        ],
        "name" : "Dj Reynolds Pub And Restaurant",
        "restaurant_id" : "30191841"
}

如同前文提及,Mongo.exe是的JavaScript的互動式視窗,因此你可以利用JavaScript的語法來進一步操控它,以下範例取出第一個「cuisine」的值為「Irish」的document,並使用length取出grades陣列的個數放到變數loop,接著再利用for迴圈,將grade的值一一印出:

> var loop=db.restaurants.find({"cuisine":"Irish"},{name:1,restaurant_id:1,grades:1})[0].grades.length;

> for (i=0;i<loop;i++){

... print(db.restaurants.find({"cuisine":"Irish"},{name:1,restaurant_id:1,grades:1})[0].grades[i].score);

... }

2

11

12

12

再者我們也可以透過「.」符號取得內嵌文件的內容,例如以下語法找出「address」欄位的「zipcode」為「10003」的文件筆數:

> db.restaurants.find({"address.zipcode":"10003"}).count()

686

同樣地,以下是使用grades.grade語法,取回「grade」欄位為「C」的文件,只要有其中一份內嵌文件滿足篩選條件,就會取回整份文件:

> db.restaurants.find({"grades.grade":"C"}).count()

2708

讓我們印出滿足條件中的第一份文件,值得注意的是,滿足「grade」為「C」條件的內嵌文件只有一筆,但是查詢的結果仍是回傳整份文件:

> db.restaurants.find({"grades.grade":"C"})[0]
{
        "_id" : ObjectId("5705e0ae16dfa0ae82083772"),
        "address" : {
                "building" : "1269",
                "coord" : [
                        -73.871194,
                        40.6730975
                ],
                "street" : "Sutter Avenue",
                "zipcode" : "11208"
        },
        "borough" : "Brooklyn",
        "cuisine" : "Chinese",
        "grades" : [
                {
                        "date" : ISODate("2014-09-16T00:00:00Z"),
                        "grade" : "B",
                        "score" : 21
                },
                {
                        "date" : ISODate("2013-08-28T00:00:00Z"),
                        "grade" : "A",
                        "score" : 7
                },
                {
                        "date" : ISODate("2013-04-02T00:00:00Z"),
                        "grade" : "C",
                        "score" : 56
                },
                {
                        "date" : ISODate("2012-08-15T00:00:00Z"),
                        "grade" : "B",
                        "score" : 27
                },
                {
                        "date" : ISODate("2012-03-28T00:00:00Z"),
                        "grade" : "B",
                        "score" : 27
                }
        ],
        "name" : "May May Kitchen",
        "restaurant_id" : "40358429"
}

 

若只想要印出滿足「grade」為「C」條件的這一個內嵌文件內容,可以在設定Projection物件時,使用$elemMatch運算子:

> db.restaurants.find({"grades.grade":"C"},{grades:{$elemMatch :{'grade':'C'} }})[0]
{
        "_id" : ObjectId("5705e0ae16dfa0ae82083772"),
        "grades" : [
                {
                        "date" : ISODate("2013-04-02T00:00:00Z"),
                        "grade" : "C",
                        "score" : 56
                }
        ]
}

使用$elemMatch運算子的語法看起來有點冗長,我們可以改用「$」符號,來取代上述的語法,例如以下的查詢結果同上:

> db.restaurants.find({"grades.grade":"C"},{'grades.$':1 })[0]
{
        "_id" : ObjectId("5705e0ae16dfa0ae82083772"),
        "grades" : [
                {
                        "date" : ISODate("2013-04-02T00:00:00Z"),
                        "grade" : "C",
                        "score" : 56
                }
        ]
}

Tags:

.NET Magazine國際中文電子雜誌 | 許薰尹Vivid Hsu | MongoDB

新增評論




  Country flag
biuquote
  • 評論
  • 線上預覽
Loading






NET Magazine國際中文電子雜誌

NET Magazine國際中文電子版雜誌,由恆逸資訊創立於2000,自發刊日起迄今已發行超過500篇.NET相關技術文章,擁有超過40000名註冊讀者群。NET Magazine國際中文電子版雜誌希望藉於電子雜誌與NET Developer達到共同學習與技術新知分享,歡迎每一位對.NET 技術有興趣的朋友們多多支持本雜誌,讓作者群們可以有持續性的動力繼續爬文。<請加入免費訂閱>

月分類Month List