2015年11月30日

如何使用 MongoDB

以下記錄如何以 cli 方式啟動 MongoDB,以及基本的 CRUD operations。

安裝 MongoDB

要在 centos linux 環境安裝 MongoDB,除了用 yum 安裝之外,還可以直接在 網頁 下載 tgz 檔案,以 CentOS 6 為例,選擇作業系統版本 RHEL 6 Linux 64-bit,點擊 DOWNLOAD 後,就可以下載檔案 mongodb-linux-x86_64-rhel62-3.0.7.tgz。

該壓縮檔裡面,只有一個 bin 目錄,裡面是 mongodb 的所有執行檔,我們將裡面的資料解壓縮到 /usr/share/mongodb 裡面。

tar zxvf mongodb-linux-x86_64-rhel62-3.0.7.tgz /usr/share/
cd /usr/share
mv mongodb-linux-x86_64-rhel62-3.0.7 mongodb
cd mongodb

mkdir -p data/db
mkdir -p logs

執行 mongod ,同時指定 db 以及 logfile 路徑,就可以啟動 mongodb。

/usr/share/mongodb/bin/mongod --dbpath=/usr/share/mongodb/data/db -logpath=/usr/share/mongodb/logs/mongodb.log

這是直接將啟動的參數放在 mongod 命令列中,也可以製作一個設定檔 mongodb.conf,並以 -f 指定設定檔就可以了。

/usr/share/mongodb/bin/mongod -f /etc/mongod.conf

如果希望在背景啟動 mongodb,必須加上 --fork 的參數。

[root@server mongodb]# /usr/share/mongodb/bin/mongod -dbpath=/usr/share/mongodb/data/db -logpath=/usr/share/mongodb/logs/mongodb.log --fork
about to fork child process, waiting until server is ready for connections.
forked process: 9544
child process started successfully, parent exiting
[root@server mongodb]# ps aux|grep mongo
root      9544  1.0  1.3 499800 50308 ?        Sl   15:36   0:00 /usr/share/mongodb/bin/mongod -dbpath=/usr/share/mongodb/data/db -logpath=/usr/share/mongodb/logs/mongodb.log --fork
root      9556  0.0  0.0 105388   912 pts/0    S+   15:36   0:00 grep --color mongo

mongod 的參數分為 一般參數、 windows參數、replication 參數、replica set 參數以及隱藏參數。常用的參數如下:

  1. dbpath
  2. logpath
  3. logappend: logfile 以 append 方式附加到檔案後面
  4. bind_ip
  5. port
  6. fork: 以 daemon 方式啟動 mongod
  7. journal: 開啟記錄檔功能
  8. syncdelay: 系統同步更新 disk 的時間,預設為 60s
  9. directoryperdb: 每個 db 儲存在單獨的目錄中,這樣比較容易區分不同的資料庫
  10. maxConns: 最大的 client 連線數
  11. repairpath: 執行 repair 的臨時目錄,如果沒有啟用 journal 功能,異常當機重新啟動後,必須執行 repair

如果要停止 mongod,可使用 db.shutdownServer() 指令

# mongo
> use admin;
switched to db admin
> db.shutdownServer();
2015-11-09T17:23:30.415+0800 I NETWORK  DBClientCursor::init call() failed
server should be down...
2015-11-09T17:23:30.417+0800 I NETWORK  trying reconnect to 127.0.0.1:27017 (127.0.0.1) failed
2015-11-09T17:23:30.417+0800 W NETWORK  Failed to connect to 127.0.0.1:27017, reason: errno:111 Connection refused
2015-11-09T17:23:30.417+0800 I NETWORK  reconnect 127.0.0.1:27017 (127.0.0.1) failed failed couldn't connect to server 127.0.0.1:27017 (127.0.0.1), connection attempt failed

不能用 kill -9 PID 直接將 mongodb process 刪除,這樣可能會導致 mongodb 的資料損壞。只能透過 kill -2 PID 或 kill -15 PID 停止 mongod process。

CRUD

mongo: MongoDB Shell 是 MongoDB 操作及管理的界面,同時也是一個 JavaScript shell 可以執行 js code。

以下幾點是 mongodb 的一些基本使用概念

  1. mongo 不需要預先建立 collection,在第一次儲存資料時,就會自動建立
  2. 文件資料可以儲存任何結構的資料,也不需要修改 schema
  3. 每一次儲存資料,都會產生一個欄位 _id,該欄位可儲存任何資料類型,未指定時會自動產生,資料類型為 ObjectId,ObjectId 裡面就隱藏了產生這筆資料的時間。

    ObjectId: 是 12 bytes BSON 格式,包含以下資訊:

    1. 4-byte: 表示 Unix epoch 到現在的秒數,也就是 timestamp
    2. 3-byte: machine identifier
    3. 2-byte: process id
    4. 3-byte: counter,啟始值為一個亂數值
  • 儲存

    > user1={name:"sun", age: 12};
    { "name" : "sun", "age" : 12 }
    > user2={name:"moon", age: 14};
    { "name" : "moon", "age" : 14 }
    > 
    > db.solar.save(user1);
    WriteResult({ "nInserted" : 1 })
    > db.solar.save(user2);
    WriteResult({ "nInserted" : 1 })
    > 
    > db.solar.find();
    { "_id" : ObjectId("56416af4997bc79c93b12e23"), "name" : "sun", "age" : 12 }
    { "_id" : ObjectId("56416af4997bc79c93b12e24"), "name" : "moon", "age" : 14 }
    
  • 修改

    > db.solar.update({name:"moon"},{$set:{age:"16"}});
    WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
    > db.solar.find();
    { "_id" : ObjectId("56416af4997bc79c93b12e23"), "name" : "sun", "age" : 12 }
    { "_id" : ObjectId("56416af4997bc79c93b12e24"), "name" : "moon", "age" : "16" }
    
  • 刪除

    > db.solar.remove({name:"moon"});
    WriteResult({ "nRemoved" : 1 })
    > db.solar.find();
    { "_id" : ObjectId("56416af4997bc79c93b12e23"), "name" : "sun", "age" : 12 }
    > db.solar.remove({name:"moon"});
    WriteResult({ "nRemoved" : 0 })
    > db.solar.find();
    { "_id" : ObjectId("56416af4997bc79c93b12e23"), "name" : "sun", "age" : 12 }
    
  • _id 欄位可以自訂,但不能重複

    > user3={_id:"id_star", name:"star", age: 18};
    { "_id" : "id_star", "name" : "star", "age" : 18 }
    > db.solar.save(user3);
    WriteResult({ "nMatched" : 0, "nUpserted" : 1, "nModified" : 0, "_id" : "id_star" })
    >
    > user4={_id:"id_star", name:"star2", age: 20};
    { "_id" : "id_star", "name" : "star2", "age" : 20 }
    > db.solar.save(user4);
    WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
    > 
    > db.solar.find();
    { "_id" : ObjectId("56416af4997bc79c93b12e23"), "name" : "sun", "age" : 12 }
    { "_id" : "id_star", "name" : "star2", "age" : 20 }
    > db.solar.insert(user4);
    WriteResult({
      "nInserted" : 0,
      "writeError" : {
          "code" : 11000,
          "errmsg" : "E11000 duplicate key error index: test.solar.$_id_ dup key: { : \"id_star\" }"
      }
    })
    
  • 查詢後回傳 cursor,it可取得下一頁資料

    > for( var i=1; i<=40; i++ ) db.users.save( {userid:"user_"+i} );
    WriteResult({ "nInserted" : 1 })
    > db.users.find( );
    { "_id" : ObjectId("5641882a997bc79c93b12e39"), "userid" : "user_1" }
    { "_id" : ObjectId("5641882a997bc79c93b12e3a"), "userid" : "user_2" }
    { "_id" : ObjectId("5641882a997bc79c93b12e3b"), "userid" : "user_3" }
    { "_id" : ObjectId("5641882a997bc79c93b12e3c"), "userid" : "user_4" }
    { "_id" : ObjectId("5641882a997bc79c93b12e3d"), "userid" : "user_5" }
    { "_id" : ObjectId("5641882a997bc79c93b12e3e"), "userid" : "user_6" }
    { "_id" : ObjectId("5641882a997bc79c93b12e3f"), "userid" : "user_7" }
    { "_id" : ObjectId("5641882a997bc79c93b12e40"), "userid" : "user_8" }
    { "_id" : ObjectId("5641882a997bc79c93b12e41"), "userid" : "user_9" }
    { "_id" : ObjectId("5641882a997bc79c93b12e42"), "userid" : "user_10" }
    { "_id" : ObjectId("5641882a997bc79c93b12e43"), "userid" : "user_11" }
    { "_id" : ObjectId("5641882a997bc79c93b12e44"), "userid" : "user_12" }
    { "_id" : ObjectId("5641882a997bc79c93b12e45"), "userid" : "user_13" }
    { "_id" : ObjectId("5641882a997bc79c93b12e46"), "userid" : "user_14" }
    { "_id" : ObjectId("5641882a997bc79c93b12e47"), "userid" : "user_15" }
    { "_id" : ObjectId("5641882a997bc79c93b12e48"), "userid" : "user_16" }
    { "_id" : ObjectId("5641882a997bc79c93b12e49"), "userid" : "user_17" }
    { "_id" : ObjectId("5641882a997bc79c93b12e4a"), "userid" : "user_18" }
    { "_id" : ObjectId("5641882a997bc79c93b12e4b"), "userid" : "user_19" }
    { "_id" : ObjectId("5641882a997bc79c93b12e4c"), "userid" : "user_20" }
    Type "it" for more
    > it
    { "_id" : ObjectId("5641882a997bc79c93b12e4d"), "userid" : "user_21" }
    { "_id" : ObjectId("5641882a997bc79c93b12e4e"), "userid" : "user_22" }
    { "_id" : ObjectId("5641882a997bc79c93b12e4f"), "userid" : "user_23" }
    { "_id" : ObjectId("5641882a997bc79c93b12e50"), "userid" : "user_24" }
    { "_id" : ObjectId("5641882a997bc79c93b12e51"), "userid" : "user_25" }
    { "_id" : ObjectId("5641882a997bc79c93b12e52"), "userid" : "user_26" }
    { "_id" : ObjectId("5641882a997bc79c93b12e53"), "userid" : "user_27" }
    { "_id" : ObjectId("5641882a997bc79c93b12e54"), "userid" : "user_28" }
    { "_id" : ObjectId("5641882a997bc79c93b12e55"), "userid" : "user_29" }
    { "_id" : ObjectId("5641882a997bc79c93b12e56"), "userid" : "user_30" }
    { "_id" : ObjectId("5641882a997bc79c93b12e57"), "userid" : "user_31" }
    { "_id" : ObjectId("5641882a997bc79c93b12e58"), "userid" : "user_32" }
    { "_id" : ObjectId("5641882a997bc79c93b12e59"), "userid" : "user_33" }
    { "_id" : ObjectId("5641882a997bc79c93b12e5a"), "userid" : "user_34" }
    { "_id" : ObjectId("5641882a997bc79c93b12e5b"), "userid" : "user_35" }
    { "_id" : ObjectId("5641882a997bc79c93b12e5c"), "userid" : "user_36" }
    { "_id" : ObjectId("5641882a997bc79c93b12e5d"), "userid" : "user_37" }
    { "_id" : ObjectId("5641882a997bc79c93b12e5e"), "userid" : "user_38" }
    { "_id" : ObjectId("5641882a997bc79c93b12e5f"), "userid" : "user_39" }
    { "_id" : ObjectId("5641882a997bc79c93b12e60"), "userid" : "user_40" }
    

    可用 while 搭配 cursor.hasNext 進行迴圈處理

    > var cursor = db.users.find();
    > while ( cursor.hasNext() ) printjson(cursor.next());
    { "_id" : ObjectId("5641882a997bc79c93b12e39"), "userid" : "user_1" }
    { "_id" : ObjectId("5641882a997bc79c93b12e3a"), "userid" : "user_2" }
    { "_id" : ObjectId("5641882a997bc79c93b12e3b"), "userid" : "user_3" }
    { "_id" : ObjectId("5641882a997bc79c93b12e3c"), "userid" : "user_4" }
    { "_id" : ObjectId("5641882a997bc79c93b12e3d"), "userid" : "user_5" }
    //..... 省略
    

    也可以用 forEach

    > db.users.find().forEach(printjson);
    { "_id" : ObjectId("5641882a997bc79c93b12e39"), "userid" : "user_1" }
    { "_id" : ObjectId("5641882a997bc79c93b12e3a"), "userid" : "user_2" }
    { "_id" : ObjectId("5641882a997bc79c93b12e3b"), "userid" : "user_3" }
    { "_id" : ObjectId("5641882a997bc79c93b12e3c"), "userid" : "user_4" }
    { "_id" : ObjectId("5641882a997bc79c93b12e3d"), "userid" : "user_5" }
    

    也可以把 cursor 當成陣列

    > var cursor = db.users.find();
    > printjson(cursor[5]);
    { "_id" : ObjectId("5641882a997bc79c93b12e3e"), "userid" : "user_6" }
    > while ( cursor.hasNext() ) printjson(cursor.next());
    > printjson(cursor[6]);
    { "_id" : ObjectId("5641882a997bc79c93b12e3f"), "userid" : "user_7" }
    

    _id 裡面就隱藏了資料建立時間

    > db.users.find().forEach(function (doc){ d = doc._id.getTimestamp(); print(d.getFullYear()+"-"+(d.getMonth()+1)+"-"+d.getDate() + " " + d.getHours() + ":" + d.getMinutes() + ":" + d.getSeconds()) })
    2015-11-10 14:1:14
    2015-11-10 14:1:14
    2015-11-10 14:1:14
    2015-11-10 14:1:14
    //.... 省略
    

    條件查詢

    > db.users.find({userid:"user_10"}).forEach(printjson);
    { "_id" : ObjectId("5641882a997bc79c93b12e42"), "userid" : "user_10" }
    

    自訂回傳欄位的條件查詢

    > db.users.find({userid:"user_10"},{_id:true}).forEach(printjson);
    { "_id" : ObjectId("5641882a997bc79c93b12e42") }
    

    findOne: 只取得第一筆資料

    > printjson(db.users.findOne({userid:"user_10"}));
    { "_id" : ObjectId("5641882a997bc79c93b12e42"), "userid" : "user_10" }
    

    limit: 限制回傳資料的筆數

    > db.users.find().limit(3);
    { "_id" : ObjectId("5641882a997bc79c93b12e39"), "userid" : "user_1" }
    { "_id" : ObjectId("5641882a997bc79c93b12e3a"), "userid" : "user_2" }
    { "_id" : ObjectId("5641882a997bc79c93b12e3b"), "userid" : "user_3" }
    

進階查詢

首先準備測試資料

> db.mate.save({userid:"john", username:"John Lin", city:"Taipei", age: 20, room:"301"});
WriteResult({ "nInserted" : 1 })
> db.mate.save({userid:"muder", username:"Muder Yen", city:"Taichung", age: 19, room:"301"});
WriteResult({ "nInserted" : 1 })
> db.mate.save({userid:"mary", username:"Mary Wang", city:"Tainan", age: 23, room:"601"});
WriteResult({ "nInserted" : 1 })
> db.mate.save({userid:"celina", username:"Celina Lin", city:"Taichung", age: 20, room:"601"});
WriteResult({ "nInserted" : 1 })
> db.mate.save({userid:"lunar", username:"Lunar Wang", city:"I Lan", age: 22, room:"302"});
WriteResult({ "nInserted" : 1 })
> db.mate.save({userid:"danny", username:"Danny Huang", city:"Taipei"});
WriteResult({ "nInserted" : 1 })
> db.mate.save({userid:"johnny", username:"Johnny Lo", city:"Taipei", age: null});
WriteResult({ "nInserted" : 1 })
> db.mate.find();
{ "_id" : ObjectId("5641980c997bc79c93b12e75"), "userid" : "john", "username" : "John Lin", "city" : "Taipei", "age" : 20, "room" : "301" }
{ "_id" : ObjectId("5641980c997bc79c93b12e76"), "userid" : "muder", "username" : "Muder Yen", "city" : "Taichung", "age" : 19, "room" : "301" }
{ "_id" : ObjectId("5641980c997bc79c93b12e77"), "userid" : "mary", "username" : "Mary Wang", "city" : "Tainan", "age" : 23, "room" : "601" }
{ "_id" : ObjectId("5641980c997bc79c93b12e78"), "userid" : "celina", "username" : "Celina Lin", "city" : "Taichung", "age" : 20, "room" : "601" }
{ "_id" : ObjectId("5641980c997bc79c93b12e79"), "userid" : "lunar", "username" : "Lunar Wang", "city" : "I Lan", "age" : 22, "room" : "302" }
{ "_id" : ObjectId("5641980c997bc79c93b12e7a"), "userid" : "danny", "username" : "Danny Huang", "city" : "Taipei" }
{ "_id" : ObjectId("5641980c997bc79c93b12e7b"), "userid" : "johnny", "username" : "Johnny Lo", "city" : "Taipei", "age" : null }

$gt 超過 22 歲的人

> db.mate.find( {age:{ $gt: 22}} );
{ "_id" : ObjectId("5641980c997bc79c93b12e77"), "userid" : "mary", "username" : "Mary Wang", "city" : "Tainan", "age" : 23, "room" : "601" }

$gte 大於等於 22 歲

> db.mate.find( {age:{ $gte: 22}} );
{ "_id" : ObjectId("5641980c997bc79c93b12e77"), "userid" : "mary", "username" : "Mary Wang", "city" : "Tainan", "age" : 23, "room" : "601" }
{ "_id" : ObjectId("5641980c997bc79c93b12e79"), "userid" : "lunar", "username" : "Lunar Wang", "city" : "I Lan", "age" : 22, "room" : "302" }

$ne: 不等於 22

> db.mate.find( {age:{ $ne: 22}} );
{ "_id" : ObjectId("5641980c997bc79c93b12e75"), "userid" : "john", "username" : "John Lin", "city" : "Taipei", "age" : 20, "room" : "301" }
{ "_id" : ObjectId("5641980c997bc79c93b12e76"), "userid" : "muder", "username" : "Muder Yen", "city" : "Taichung", "age" : 19, "room" : "301" }
{ "_id" : ObjectId("5641980c997bc79c93b12e77"), "userid" : "mary", "username" : "Mary Wang", "city" : "Tainan", "age" : 23, "room" : "601" }
{ "_id" : ObjectId("5641980c997bc79c93b12e78"), "userid" : "celina", "username" : "Celina Lin", "city" : "Taichung", "age" : 20, "room" : "601" }
{ "_id" : ObjectId("5641980c997bc79c93b12e7a"), "userid" : "danny", "username" : "Danny Huang", "city" : "Taipei" }
{ "_id" : ObjectId("5641980c997bc79c93b12e7b"), "userid" : "johnny", "username" : "Johnny Lo", "city" : "Taipei", "age" : null }

$mod: 除以10餘0

> db.mate.find( {age:{ $mod: [10,0]}} );
{ "_id" : ObjectId("5641980c997bc79c93b12e75"), "userid" : "john", "username" : "John Lin", "city" : "Taipei", "age" : 20, "room" : "301" }
{ "_id" : ObjectId("5641980c997bc79c93b12e78"), "userid" : "celina", "username" : "Celina Lin", "city" : "Taichung", "age" : 20, "room" : "601" }

$all: 完全等於 20

> db.mate.find( {age:{ $all:[20]}} );
{ "_id" : ObjectId("5641980c997bc79c93b12e75"), "userid" : "john", "username" : "John Lin", "city" : "Taipei", "age" : 20, "room" : "301" }
{ "_id" : ObjectId("5641980c997bc79c93b12e78"), "userid" : "celina", "username" : "Celina Lin", "city" : "Taichung", "age" : 20, "room" : "601" }

$in: 22 或 23 歲

> db.mate.find( {age:{ $in:[22, 23]}} );
{ "_id" : ObjectId("5641980c997bc79c93b12e77"), "userid" : "mary", "username" : "Mary Wang", "city" : "Tainan", "age" : 23, "room" : "601" }
{ "_id" : ObjectId("5641980c997bc79c93b12e79"), "userid" : "lunar", "username" : "Lunar Wang", "city" : "I Lan", "age" : 22, "room" : "302" }

$nin: 不等於 22 或 23

> db.mate.find( {age:{ $nin:[22, 23]}} );
{ "_id" : ObjectId("5641980c997bc79c93b12e75"), "userid" : "john", "username" : "John Lin", "city" : "Taipei", "age" : 20, "room" : "301" }
{ "_id" : ObjectId("5641980c997bc79c93b12e76"), "userid" : "muder", "username" : "Muder Yen", "city" : "Taichung", "age" : 19, "room" : "301" }
{ "_id" : ObjectId("5641980c997bc79c93b12e78"), "userid" : "celina", "username" : "Celina Lin", "city" : "Taichung", "age" : 20, "room" : "601" }
{ "_id" : ObjectId("5641980c997bc79c93b12e7a"), "userid" : "danny", "username" : "Danny Huang", "city" : "Taipei" }
{ "_id" : ObjectId("5641980c997bc79c93b12e7b"), "userid" : "johnny", "username" : "Johnny Lo", "city" : "Taipei", "age" : null }

$exists: 不存在 age 欄位

> db.mate.find( {age:{$exists:false}} );
{ "_id" : ObjectId("5641980c997bc79c93b12e7a"), "userid" : "danny", "username" : "Danny Huang", "city" : "Taipei" }

null: age 欄位為 null,不存在 age 也包含在內

> db.mate.find( {age:null} );
{ "_id" : ObjectId("5641980c997bc79c93b12e7a"), "userid" : "danny", "username" : "Danny Huang", "city" : "Taipei" }
{ "_id" : ObjectId("5641980c997bc79c93b12e7b"), "userid" : "johnny", "username" : "Johnny Lo", "city" : "Taipei", "age" : null }

有 age 欄位且 age 為 null

> db.mate.find( {age:{$in:[null], $exists:true}} );
{ "_id" : ObjectId("5641980c997bc79c93b12e7b"), "userid" : "johnny", "username" : "Johnny Lo", "city" : "Taipei", "age" : null }

$regex: username 以 M 為開頭

> db.mate.find( {username:{ $regex:/^M.*/ }} );
{ "_id" : ObjectId("5641980c997bc79c93b12e76"), "userid" : "muder", "username" : "Muder Yen", "city" : "Taichung", "age" : 19, "room" : "301" }
{ "_id" : ObjectId("5641980c997bc79c93b12e77"), "userid" : "mary", "username" : "Mary Wang", "city" : "Tainan", "age" : 23, "room" : "601" }

$not: username 不以 M 為開頭

> db.mate.find( {username:{ $not:/^M.*/ }} );
{ "_id" : ObjectId("5641980c997bc79c93b12e75"), "userid" : "john", "username" : "John Lin", "city" : "Taipei", "age" : 20, "room" : "301" }
{ "_id" : ObjectId("5641980c997bc79c93b12e78"), "userid" : "celina", "username" : "Celina Lin", "city" : "Taichung", "age" : 20, "room" : "601" }
{ "_id" : ObjectId("5641980c997bc79c93b12e79"), "userid" : "lunar", "username" : "Lunar Wang", "city" : "I Lan", "age" : 22, "room" : "302" }
{ "_id" : ObjectId("5641980c997bc79c93b12e7a"), "userid" : "danny", "username" : "Danny Huang", "city" : "Taipei" }
{ "_id" : ObjectId("5641980c997bc79c93b12e7b"), "userid" : "johnny", "username" : "Johnny Lo", "city" : "Taipei", "age" : null }

以 javascript function 進行搜尋

> f=function() {return this.age>22;}
function () {return this.age>22;}
> db.mate.find(f);
{ "_id" : ObjectId("5641980c997bc79c93b12e77"), "userid" : "mary", "username" : "Mary Wang", "city" : "Tainan", "age" : 23, "room" : "601" }

count(): 計算資料筆數

> db.mate.find().count();
7

skip(): 跳過幾筆資料
limit(): 限制回傳筆數

> db.mate.find().skip(2).limit(2);
{ "_id" : ObjectId("5641980c997bc79c93b12e77"), "userid" : "mary", "username" : "Mary Wang", "city" : "Tainan", "age" : 23, "room" : "601" }
{ "_id" : ObjectId("5641980c997bc79c93b12e78"), "userid" : "celina", "username" : "Celina Lin", "city" : "Taichung", "age" : 20, "room" : "601" }
> db.mate.find().skip(3).limit(1);
{ "_id" : ObjectId("5641980c997bc79c93b12e78"), "userid" : "celina", "username" : "Celina Lin", "city" : "Taichung", "age" : 20, "room" : "601" }

sort: 針對 age 排序

> db.mate.find().sort({age:1});
{ "_id" : ObjectId("5641980c997bc79c93b12e7a"), "userid" : "danny", "username" : "Danny Huang", "city" : "Taipei" }
{ "_id" : ObjectId("5641980c997bc79c93b12e7b"), "userid" : "johnny", "username" : "Johnny Lo", "city" : "Taipei", "age" : null }
{ "_id" : ObjectId("5641980c997bc79c93b12e76"), "userid" : "muder", "username" : "Muder Yen", "city" : "Taichung", "age" : 19, "room" : "301" }
{ "_id" : ObjectId("5641980c997bc79c93b12e75"), "userid" : "john", "username" : "John Lin", "city" : "Taipei", "age" : 20, "room" : "301" }
{ "_id" : ObjectId("5641980c997bc79c93b12e78"), "userid" : "celina", "username" : "Celina Lin", "city" : "Taichung", "age" : 20, "room" : "601" }
{ "_id" : ObjectId("5641980c997bc79c93b12e79"), "userid" : "lunar", "username" : "Lunar Wang", "city" : "I Lan", "age" : 22, "room" : "302" }
{ "_id" : ObjectId("5641980c997bc79c93b12e77"), "userid" : "mary", "username" : "Mary Wang", "city" : "Tainan", "age" : 23, "room" : "601" }

> db.mate.find().sort({age:-1});
{ "_id" : ObjectId("5641980c997bc79c93b12e77"), "userid" : "mary", "username" : "Mary Wang", "city" : "Tainan", "age" : 23, "room" : "601" }
{ "_id" : ObjectId("5641980c997bc79c93b12e79"), "userid" : "lunar", "username" : "Lunar Wang", "city" : "I Lan", "age" : 22, "room" : "302" }
{ "_id" : ObjectId("5641980c997bc79c93b12e75"), "userid" : "john", "username" : "John Lin", "city" : "Taipei", "age" : 20, "room" : "301" }
{ "_id" : ObjectId("5641980c997bc79c93b12e78"), "userid" : "celina", "username" : "Celina Lin", "city" : "Taichung", "age" : 20, "room" : "601" }
{ "_id" : ObjectId("5641980c997bc79c93b12e76"), "userid" : "muder", "username" : "Muder Yen", "city" : "Taichung", "age" : 19, "room" : "301" }
{ "_id" : ObjectId("5641980c997bc79c93b12e7a"), "userid" : "danny", "username" : "Danny Huang", "city" : "Taipei" }
{ "_id" : ObjectId("5641980c997bc79c93b12e7b"), "userid" : "johnny", "username" : "Johnny Lo", "city" : "Taipei", "age" : null }

以下為所有測試的指令

db.mate.save({userid:"john", username:"John Lin", city:"Taipei", age: 20, room:"301"});
db.mate.save({userid:"muder", username:"Muder Yen", city:"Taichung", age: 19, room:"301"});
db.mate.save({userid:"mary", username:"Mary Wang", city:"Tainan", age: 23, room:"601"});
db.mate.save({userid:"celina", username:"Celina Lin", city:"Taichung", age: 20, room:"601"});
db.mate.save({userid:"lunar", username:"Lunar Wang", city:"I Lan", age: 22, room:"302"});
db.mate.save({userid:"danny", username:"Danny Huang", city:"Taipei"});
db.mate.save({userid:"johnny", username:"Johnny Lo", city:"Taipei", age: null});

db.mate.find( {age:{ $gt: 22}} );

db.mate.find( {age:{ $gte: 22}} );

db.mate.find( {age:{ $ne: 22}} );

db.mate.find( {age:{ $mod: [10,0]}} );

db.mate.find( {age:{ $all:[20]}} );

db.mate.find( {age:{ $in:[22, 23]}} );

db.mate.find( {age:{ $nin:[22, 23]}} );

db.mate.find( {age:{$exists:false}} );

db.mate.find( {age:null} );

db.mate.find( {age:{$in:[null], $exists:true}} );

db.mate.find( {username:{ $regex:/^M.*/ }} );

db.mate.find( {username:{ $not:/^M.*/ }} );


f=function() {return this.age>22;}
db.mate.find(f);


db.mate.find().count();

db.mate.find().skip(2).limit(2);

db.mate.find().skip(3).limit(1);


db.mate.find().sort({age:1});

db.mate.find().sort({age:-1});

MapReduce

MongoDB 的 Map, Reduce 函數都可以用 javascript 實作,Map 函數中必須呼叫 emit(key, value),對 collection 中所有紀錄運算過一次之後,再將 {key, [value1, value2, ....]} 傳遞給 reduce 進行處理,最後可再利用 finalize(),針對 reduce 的結果做更進一步的處理。

在 mongo 中,可以用 mapReduce 或是 runCommand 進行 MapReduce,help mr 看到線上的簡略說明

> help mr

See also http://dochub.mongodb.org/core/mapreduce

function mapf() {
  // 'this' holds current document to inspect
  emit(key, value);
}

function reducef(key,value_array) {
  return reduced_value;
}

db.mycollection.mapReduce(mapf, reducef[, options])

options
{[query : <query filter object>]
 [, sort : <sort the query.  useful for optimization>]
 [, limit : <number of objects to return from collection>]
 [, out : <output-collection name>]
 [, keeptemp: <true|false>]
 [, finalize : <finalizefunction>]
 [, scope : <object where fields go into javascript global scope >]
 [, verbose : true]}

query: 在進行 Map 運算前,先針對 collection 進行一次資料篩檢
sort: 針對 collection 資料排序,可減少 reduce 的處理時間
limit: 限制由 collection 回傳的資料筆數
out: 儲存統計結果的 collection,如果不指定,會放在暫存的 collection 中,連線中斷後,會自動刪除
keeptemp: 是否要保留暫存的 collection
finalize: 對 reduce 的結果再進行一次處理
scope: 指定可在 map, reduce, finalize 中使用的 global 變數
verbose: 顯示詳細的統計時間資訊

首先準備 map 與 reduce function

> mfunction = function() { emit(this.room, 1 )};
function () { emit(this.room, 1 )}
> 
> rfunction = function(key, values) {
... var x = 0;
... values.forEach( function(v) { x += v; } );
... return x;
... }

function (key, values) {
var x = 0;
values.forEach( function(v) { x += v; } );
return x;
}

runCommand,並指定輸出到 mate_res

> 
> res = db.runCommand( {
... mapreduce:"mate",
... map: mfunction,
... reduce: rfunction,
... out: "mate_res"
... });
{
    "result" : "mate_res",
    "timeMillis" : 1,
    "counts" : {
        "input" : 7,
        "emit" : 7,
        "reduce" : 3,
        "output" : 4
    },
    "ok" : 1
}
> 
> db.mate_res.find();
{ "_id" : null, "value" : 2 }
{ "_id" : "301", "value" : 2 }
{ "_id" : "302", "value" : 1 }
{ "_id" : "601", "value" : 2 }

加上一個 finalize 函數,將結果變成 room 與 count

> ffunction = function(key,value) {return {room:key, count:value};}
function (key,value) {return {room:key, count:value};}
> res = db.runCommand( {
... mapreduce:"mate",
... map: mfunction,
... reduce: rfunction,
... out: "mate_res",
... finalize: ffunction
... });
{
    "result" : "mate_res",
    "timeMillis" : 1,
    "counts" : {
        "input" : 7,
        "emit" : 7,
        "reduce" : 3,
        "output" : 4
    },
    "ok" : 1
}
> 
> db.mate_res.find();
{ "_id" : null, "value" : { "room" : null, "count" : 2 } }
{ "_id" : "301", "value" : { "room" : "301", "count" : 2 } }
{ "_id" : "302", "value" : { "room" : "302", "count" : 1 } }
{ "_id" : "601", "value" : { "room" : "601", "count" : 2 } }

加上 query,過濾 mate collection,只需要大於等於 22 歲的人

> res = db.runCommand( {
... mapreduce:"mate",
... map: mfunction,
... reduce: rfunction,
... out: "mate_res",
... finalize: ffunction,
... query: {age:{$gte: 22}}
... });
{
    "result" : "mate_res",
    "timeMillis" : 1,
    "counts" : {
        "input" : 2,
        "emit" : 2,
        "reduce" : 0,
        "output" : 2
    },
    "ok" : 1
}
> 
> db.mate_res.find();
{ "_id" : "302", "value" : { "room" : "302", "count" : 1 } }
{ "_id" : "601", "value" : { "room" : "601", "count" : 1 } }

以下為測試指令的集合

mfunction = function() { emit(this.room, 1 )};

rfunction = function(key, values) {
    var x = 0;
    values.forEach( function(v) { x += v; } );
    return x;
}

res = db.runCommand( {
    mapreduce:"mate",
    map: mfunction,
    reduce: rfunction,
    out: "mate_res"
});

db.mate_res.find();


ffunction = function(key,value) {return {room:key, count:value};}
res = db.runCommand( {
    mapreduce:"mate",
    map: mfunction,
    reduce: rfunction,
    out: "mate_res",
    finalize: ffunction
});

db.mate_res.find();


res = db.runCommand( {
    mapreduce:"mate",
    map: mfunction,
    reduce: rfunction,
    out: "mate_res",
    finalize: ffunction,
    query: {age:{$gte: 22}}
});

db.mate_res.find();