我的網誌已搬家至blog.miaout17.net,本網誌將不再更新

2010年10月26日 星期二

AlgoCat 開發筆記 (Rails3+MongoDB)

大概從三、四個月前起,為了加強自己coding的基本功,我開始參加TopCoderCodeforces的線上算法解題競賽。到目前為止,成績其實不太理想。在這些比賽的過程中,最能督促自己努力上進的事,莫過於被其他人慘電吧(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。

沒有留言: