我的網誌已搬家至blog.miaout17.net,本網誌將不再更新
顯示具有 MongoDB 標籤的文章。 顯示所有文章
顯示具有 MongoDB 標籤的文章。 顯示所有文章

2010年10月26日 星期二

AlgoCat 開發筆記 (Rails3+MongoDB)

大概從三、四個月前起,為了加強自己coding的基本功,我開始參加TopCoder及Codeforces的線上算法解題競賽。到目前為止,成績其實不太理想。在這些比賽的過程中,最能督促自己努力上進的事,莫過於被其他人慘電吧(Ex. 白俄羅斯高中生tourist名列TopCoder Algorithm Rating前五名)TopCoder官網雖然可以簡單的查詢一個人的成績,但無法追踨其他人的競賽狀況,並做出比較。

因為最近在練Rails3 + MongoDB,於是選了這個當作自己練習的題目,專案名稱暫定為"AlgoCat",也就是「演算貓」(Algorithm Cat)。這個名字是想告訴自己「在演算法的世界中,一隻大貓努力向上爬」。AlgoCat的目標為「追踨算法競賽的活動及競賽成績,並標記算法競賽及題庫中,解過及待學習的題目」。

AlgoCat還相當的不完整,可以說還在Pre-Alpha的階段吧。目前AlgoCat提供的功能僅有:查詢TopCoder Algorithm Rating的排行、觀看TopCoder使用者的成績記錄、追踨某些特定的TopCoder使用者並進行比較(以折線圖方式呈現)。

這篇文章其實不是打算announce這個網站,而是將自己的開發筆記整理一下。目前的成果可以在algocat.heroku.com看到。

Environment & Framework
  • Rails 3
  • MongoDB
  • Mongoid
  • Devise
目前是Deploy在Heroku+MongoHQ上,不排除移到其他地方。這些環境的設定可以參考上一篇。

Fetching Data from TopCoder

TopCoder本身提供了XML Feed的介面,用Ruby抓取資料只要簡單地用Net::HTTP和和REXML即可。

由於AlgoCat並不是一開始就抓好TopCoder的所有資料。有些資料是等使用者查詢才向TopCoder抓,並存放在資料庫。這方面的行為應該用Delayed Job之類的方法放在背景處理。AlgoCat在開發初期就有想到這點,但因為早期開發實作方便,還是暫時在前景處理。之後會移到background task去。BTW, Heroku第一隻worker就要收錢....之後可能會想些替代方案。

Mongoid

基本上照著官網的教學,應該可以做到大部份的功能。但要用NoSQL的資料庫,只學Object Mapper而不了解資料庫本身,應該會造成最佳化的困難。討論一些我踩到的問題好了:

@users = User.asc(:rating).limit(10)
現在,呼叫多次@users.each,其實會造成多次的query。解決方法官網有寫,只要呼叫cache,或是直接@user=@user.to_a亦可

上面這個算是小問題,只是要熟析Mongoid的細節。但另一個問題是Mongoid缺乏了對MongoDB的一些簡單低階操作。舉例來說,如果db中有一個articles的collection,而每篇文章中有一個以tags為key的array,array中存放了tag的字串。依MongoDB的官方教學,要增加一個tag的query可以這樣下:
db.articles.update( { title: "Rails3" }, { $addToSet : { tags: "Web" } } );
或者這樣亦可
db.articles.update( {title: "Rails3", tags: {"$ne": "Web"}}, {"$push": { tags : "Web" } } );
這兩者者可以確保「在文章原本沒有該tag的情況下,將tag插入」

但Mongoid的封裝似乎只支援這樣的動作:
article = Article.where( :title=>"Rails3" ).first
unless article.tags.elem? then
article.tags << Devise
article.save
end

原本是DB上的一個query,變成一定要將資料拉回app server判斷,再將「所有tags」寫回去,效能應有不小的差距。

Rails jquery-ujs

Rails內建了一些與JavaScript&AJAX相關的helpers,並以rails.js與prototyle javascript library橋接。如果想用jQuery,又想保留rails的helpers功能的話,可以用jquery-ujs中的rails.js將原本的蓋掉,就可以將原本prototype相關的javascript刪掉,換上jquery。

AlgoCat目前的「追踨使用者」(Watch)功能是用AJAX + jQuery做的。

Chart Library
要在網頁中繪製圖表,目前應該有三大主流:
1.在Server端繪製圖表,再傳送圖片到Client端
2.萬惡Flash (?)
3.以JavaScript為基礎,操作HTML Canvas之類的方式繪作圖表

其中第一點是傳統的方法,從以前的GnuPlot,到現在可以把資料丟到雲裡給Google畫圖(Google Chart API),但其缺點是比較不容易做圖表的互動操作。我個人不喜歡使用Flash作圖表,覺得多了一層整合上的麻煩,而且有點拖慢網頁的速度,但Flash應是最容易實作圖表互動操作的方式。而隨著瀏覽器對Canvas的支援趨近完整,JavaScript Engine運行的速度越來越快,用JavaScript在Client繪製圖表也成了選擇之一。而且靠JavaScript及DOM,同樣也可以進行與圖表的互動操作。

在開發AlgoCat時,一開始我選擇嘗試googlecharts gem,結果發現目前的版本還滿雷的。首先,我遇到了Linc Chart中,y軸座標值完全不對的問題。後來我發現其實這個Issue是同一個問題。試了一下後,決定git clone一份googlecharts下來,git bisect找bug。後來是把這個問題修掉了,但似乎還有其他小問題存在。之後有空應該會整理一下,發patch或pull request吧…

後來考慮到可能要在圖表上做互動,所以還是換回JavaScript-based的flot。Flot是一套以jQuery為基礎的JavaScript Plotting Library。

BTW,因為flot相依於jQuery,所以rails內建的Prototype一定要移掉,不然會相衝。我使用了前段提到的jquery-ujs。

Ongoing work
目前最大的問題,應該就是Fetch XML DATA應該要放在背景做吧。然後會補強自動更新的機制、加上Codeforces或Uva題庫功能等…

Source Code?
目前還沒有打算open source,而且可能還會大規模的重構,等我整理好code後有可能會open。

Rails3, Mongoid, Devise, Heroku, MongoHQ筆記

最近在看一些NoSQL的東西,於是想以MongoDB進行一些實作的練習。這篇文章是我在練習過程中,環境設定的筆記。

Installing Rails 3

請看我的上一篇文章

Installing MongoDB

我的開發環境是Ubuntu和Windows(在家用Ubuntu、出外用Windows,所以試了兩個環境)。Linux的話,個人建議不要用package system(apt/yum)的MongoDB。首先,絕對不要apt-get install mongodb。我一開始練習MongoDB時,是用apt-get裝的,會抓到一個舊到爆炸(1.4.4)的stable版本,這個版本mongo console client打錯指令還會crash(這是stable版嗎?)。

如果一定要用package的話可以考慮apt-get install mongodb-unstable或apt-get install mongodb-snapshot。但我是直接去官網抓build好的版本,再sudo copy到/usr/bin就好了。

MongoDB Object Mapper: Mongoid

Mongo的Ruby Object Mapper有很多種,可以參考官網上的列表。我選用Mongoid的原因有二:
  1. Mongoid的官網做的還滿有模有樣的,看樣子應該有認真維護 XD
  2. Devise認證機制內建支援Mongoid
安裝照著官方installation文件做就好了:

gem i mongoid --pre
gem i bson_ext

如果是Windows系統的話,mongo gem需要build一些native code,所以推薦裝RubyDevKit,裝完就可以順利的安裝mongo相關gems。

Mongoid使用上非常的簡單,Mongoid蓋掉了ActiveRecord的generators。要快速建出一個能動的環境,在設定好gems後只需要:

rails g mongoid:config
rails g scaffold article title:string content:string

只要mongod開著,這樣就可以動了,不用做data migration。唯一會踩到的雷可能是用了舊版的mongodb,所以就如前面提到的,請不要apt-get install。

Authentication: Devise

Devise是一套不錯的rails認證機制,而且內建對Mongoid的支援
一樣照著官方文件做就能動,整合非常簡單。安裝:
gem i devise

整合進app:
rails generate devise:install
rails generate devise User

接下來只要在view裡把link接一接就好。如果原本就用過devise,基本上用ActiveRecord或Mongoid沒啥差別。要速查link path helpers的話可以用rake routes。

唯一要注意的地方是要記得自己打index,我原本以為Model中的
devise :database_authenticatable, :registerable ......
這行,會自動在Mongoid Model上加上index。但rake db:mobgoid:create_indexes後,User collection上還是沒有任何index,所以要自己在Model上加:
index :email, :unique => true
然後再執行一次rake db:mongoid:create_indexes,把index打上去。

另外,有人做了Rails3+Mongoid+Devise的整合包。但Devise和Mongoid的整合實在太簡單了,不彷自己做一遍,也比較容易了解細節。

Deployment Environment: Heroku & MongoHQ

Heroku是一個雲端的Rails Hosting Platform,其使用方式應該很多文章介紹過了,這裡就不多廢話。基本上注冊好heroku帳號後,在rails app路徑下:
heroku create appname
git push heroku master
就可以把app deploy上去。

MongoHQ是一個雲端的MongoDB Hosting服務,提供了免費(16MB Limit)及付費的MongoDB空間。由於Heroku與MongoHQ有合作,並將MongoHQ做成了Heroku的一個addon,所以整合特別簡單。這裡是官方文件,只要下一行指令:
heroku addons:add mongohq:free
heroku就會自動向MongoHQ註冊一個資料庫,並設定rails的環境變數。注意官方文件有個地方沒講的很清楚,在config/mongoid.yml中,多加上mongohq的設定:
production:
uri: <%= ENV['MONGOHQ_URL'] %>
設定好後,MongoID就可以吃到MongoHQ的環境變數,並正確地建立資料庫連線。

順便列一下Heroku合作的資料庫中,免費方案的比較:
  • Heroku內建Database: 5MB (Postgre SQL)
  • Redis To Go: 5MB (Redis)
  • MongoHQ: 16MB (MongoDB)
  • Cloudant: 100MB, 500k requrest per month (CouchDB)
開發筆記待續…