2014年2月26日 星期三

MongoDB 開發筆記 - Aggregate 之 Group BY Timestamp / GROUP BY ObjectID getTimestamp @ MongoDB Server v2.4.9

上回看文件時,上頭說系統預設 ObjectID(_id) 中,已經有時間戳記了,並且建議不需要額外在儲存一個時間:
ObjectId is a 12-byte BSON type, constructed using:
a 4-byte value representing the seconds since the Unix epoch,
a 3-byte machine identifier,
a 2-byte process id, and
a 3-byte counter, starting with a random value.
開發環境:MongoDB Server v2.4.9
目標:SELECT `timestamp`, count(*) AS count FROM `test` GROUP BY `timestamp`;

儘管 ObjectID 有提供 _id.getTimestamp() 方式存取時間,但是在用 aggregate 時卻無法動態取用($year, $month, $dayOfMonth),如:

mongodb> db.test.aggregate({
$group:{
_id: {
year: { $year: "$_id.getTimestamp()" } ,
month: { $month: "$_id.getTimestamp()" } ,
day: { $dayOfMonth: "$_id.getTimestamp()" }
},
count: {
$sum:1
}
}
})
mongodb> aggregate failed: {
        "errmsg" : "exception: can't convert from BSON type EOO to Date",
        "code" : 16006,
        "ok" : 0
}


看來在 v2.4.9 版的應用時,還是需要建立一個 timestamp 出來,然而 db.collection.update 無法拿 document 自身資料來更新,例如從 ObjectID 抽出時間來用:

mongodb> db.test.update({timestamp: null }, { $set : { timestamp: _id.getTimestamp() } }, {multi:true})
ReferenceError: _id is not defined


需要改用 forEach 來一筆筆更新:

mongodb> db.test.find({timestamp:null}).snapshot().forEach(
function (item) {
item.timestamp = item._id.getTimestamp();
//db.test.save(item);
db.test.update(
# query
{
'_id': item['_id']
},
# update
{
'$set':
{
'timestamp': item['timestamp']
}
},
upsert=False, multi=False
);
}
)


接著終於可以 GROUP BY DATE 了 Orz

mongodb> db.test.aggregate(
{
$group:{
_id: {
year: { $year: "$timestamp" } ,
month: { $month: "$timestamp" } ,
day: { $dayOfMonth: "$timestamp" }
},
count: {
$sum:1
}
}
}
)
{
        "result" : [
                {
                        "_id" : {
                                "year" : 2014,
                                "month" : 2,
                                "day" : 22
                        },
                        "count" : 23
                },
                {
                        "_id" : {
                                "year" : 2014,
                                "month" : 2,
                                "day" : 21
                        },
                        "count" : 15
                },
                {
                        "_id" : {
                                "year" : 2014,
                                "month" : 2,
                                "day" : 20
                        },
                        "count" : 200
                }
        ],
        "ok" : 1
}

[Linux] 使用 Node.js 與 MongoDB 溝通與帳號認證 @ Ubuntu 12.04

透過 apt-get 安裝 node.js:

$ sudo apt-get install python-software-properties
$ sudo add-apt-repository ppa:chris-lea/node.js
$ sudo apt-get update
$ sudo apt-get install nodejs


建立 project:

$ cd project
$ npm install mongodb
$ vim test.js
var Db = require('mongodb').Db,
        MongoClient = require('mongodb').MongoClient;

var db_host = 'localhost';
var db_port = 27017;
var auth_db = 'admin';
var auth_name = 'account';
var auth_pass = 'password';

MongoClient.connect("mongodb://"+auth_name+":"+auth_pass+"@"+db_host+":"+db_port, function(err, db) {
        console.log(err);
        console.log(db.databaseName);
        db.close();
});

MongoClient.connect("mongodb://"+db_host+":"+db_port, function(err, db) {
        db.authenticate(auth_name, auth_pass, function(err, ret) {
                console.log(err);
       console.log(db.databaseName);
                console.log(ret);
       db.close();
        });
});
$ node test.js
Failed to load c++ bson extension, using pure JS version
null
admin
null
admin
true


以上為兩種跟 mongodb 進行認證的方式,一種是寫在 URI 中,另一種則是透過 db.authenticate 方式,至於這份程式的輸出不見得 "true" 是在最後一列,也有可能在第三列,這是因為 node.js async 架構關係,在這份 code 中不保證哪個 function 先跑。

如果 mongodb 本身要求認證,而未認證則會有錯誤訊息:

{ [MongoError: unauthorized] name: 'MongoError', ok: 0, errmsg: 'unauthorized' }

比較疑惑的地方是對於 auth_db 的部分,之前在用 pymongo 時,或是直接用 mongo 指令時,必須透過指定 authenticationDatabase 才能通過認證,但在 node.js 中,似乎沒有問題?再來摸看看好了。

2014年2月25日 星期二

[Linux] 從 MongoDB Standalone (Single-Noe) 到 MongoDB HA (Replica Set) 的線上轉移方式(without shutdown/offline) @ Ubuntu 12.04

不關機的轉換原理應該都差不多?例如把水管從 A 處改接到到 B 處,只是情境跟操作手法適不適用是需要考慮的重要因素,在此先敘述使用情境:
  • 一隻 PHP 定期收到資料後,把資料存在 /tmp 區,再透過 popen 在背景發動一隻 pymongo 程式讀取 /tmp  資料,由 pymongo client 連到 mongodb server 塞資料,塞完後就斷線的模式。所以 CGI 有自己的 PID,而 pymongo 寫的 tool 也有自己的 PID。
  • 資料的單純作 insert 為主,還沒有負責處理 query 的需求,故 mongodb 只要能確保資料有被記錄起來,沒有損失即可,但無法保證讓 Replica Set 的資料馬上就跟 Standalone 一致。
線上轉換原理:
  1. 架設 MongoDB Replica Set
  2. 將更改 pymongo 程式,將 mongodb server 位置改到 Replica Set 即可
  3. 將 Standalone MongoDB 用 mongoexport 匯出資料 table.json, table2.json, ...
  4. 將資料用 mongoimport 方式匯入到 Replica Set 之 PRIMARY node 即可
在上述的使用情境下,資料的收集就不會間斷,至於官方介紹的方式也可以參考一下,屬於把 mongod 關掉後重開:Convert a Standalone to a Replica Set

此外,一些心得:
  • 原先資料就使用系統 _id 管理,不覆蓋 _id ,而 _id 有時間資訊,匯入新的機器(Replica Set) 沒有問題
  • 匯入資料若碰到 _id 一樣時,會 skip 掉,所以不必擔心重複匯入的問題
  • 如果匯錯資料時,例如預計到 table 1,結果不小心匯到 table 2 時,假設兩種資料格式有關鍵的差異點,例如在 table 1 的 record 才有 {"helloworld":0} 的屬性,那就好辦,把這類 record 找出來刪掉即可

# 找尋 table2 中,record 裡 hellworld 屬性的資料筆數
firstset:PRIMARY> db.table2.find({helloworld: { $ne : null } }).count()
firstset:PRIMARY> db.table2.remove({helloworld: { $ne : null } })

[Linux] ~/.dropbox-dist/dropboxd : Segmentation fault (core dumped) @ Ubuntu 12.04 64Bit

三天前還正常...不知為何突然掛了。

$ ~/.dropbox-dist/dropboxd
Segmentation fault (core dumped)

試過了砍掉 ~/.dropbox 和重裝還是一樣

$ rm -rf ~/.dropbox ~/.dropbox-dist
$ cd ~ && wget -O - "https://www.dropbox.com/download?plat=lnx.x86_64" | tar xzf -
$ ~/.dropbox-dist/dropboxd
Segmentation fault (core dumped)


最後找到解法:

$ sudo apt-get install nautilus
$ ~/.dropbox-dist/dropboxd
This computer isn't linked to any Dropbox account...
Please visit https://www.dropbox.com/cli_link?host_id=xxxxxxxx to link this device.

2014年2月23日 星期日

人生各自精彩!



話說,若要這麼計較的話,從第一份工作就是跟 startup 相關的,在一個鼓吹以技術進行 spinoff 的單位且之後的旅程也差不多...但 startup/spinoff 不見得是因為理想,而是為了營運管理類的。

隨著年紀增長,週邊的同輩、學長弟妹一個個地跳入大家口中的 startup 了,不少人在喊:離開舒適圈、精實創業。今年初漸漸對這些口號麻木了,大概也得知許多八卦吧(大多是底心底薪的欲望),據前輩的分享,在矽谷的生態中,其實 startup 可是早就被當成商品在公司之間賣來賣去的,所以...聽首歌好了 XD



今年的新體悟:
口號一切都源自于人性層面,所謂的 startup ,粗略定義:自己當老闆。廣義一點是做自己想做的事,甚至第一手享受到成果。然而,回鄉接爸媽的事業就稱不上嗎?把自己的生活過得更充實稱不上嗎?把家庭顧好稱不上嗎? 體驗不一樣的生活稱不上嗎?
我想,站在經濟推動上,可以增加工作機會,對社會有幫助仍是十分鼓勵的。但是,單純在喊離開舒適圈等等的說詞(有些startup還過得比較爽),就比較像廣告台詞、算命先常說,那種統計學很容易打到底心話:你是否爾偶覺得沒人懂你,或是天氣預報:晴時多雲偶陣雨 XD

現在我比較會推薦別人,先想想自己要過怎樣的生活 :) 至於要不要 startup 倒是其次的(但鼓勵持著 startup 珍惜資源的精神去做規劃),並且其含義可以延伸許多相關事物,看看自己要挑哪樣就好:
  • 個人角度,珍惜時間並規劃時程,看是要學哪個語言、樂器、興趣、運動或是旅行增廣閱歷都是十分好的以個人為基準的 startup ,不見得一定要掙錢角度,只要珍惜光陰都是!因為時間是生命中絕對有限資源
  • 公司角度,珍惜資源,把角色做好尋求貢獻度,也稱得上團體中的 startup 吧 XD 有股份的就幫公司掙錢,沒股份的,就找尋個人成就感、作出自己的代表作