2016年3月28日 星期一

台灣花季地圖

這份「台灣花季地圖」紀錄了我從網路上收集而來的賞花資訊。包括台灣各地的賞花景點以及它們的花期。希望大家能有機會利用此地圖,安排出外旅遊行程。

本地圖連結為
https://www.google.com/maps/d/viewer?mid=zRgPfb9BIGoE.kpr_bUJzRbC0





使用說明


地圖上每個標記都是一個賞花景點,不同顏色代表不同的行政區域。點選圖示後會顯示該地點在不同月份盛開的花卉種類等等資訊。



點選左上角的圖示可以進一步開啟地點列表,方便尋找。



電腦版使用者可以利用地圖左下角的 + / - 符號,或是滑鼠滾輪來放大、縮小地圖。在地圖的空白地方按住滑鼠左鍵,可以移動地圖。

點選右上角的方框圖示,或進入本地圖連結
https://www.google.com/maps/d/viewer?mid=zRgPfb9BIGoE.kpr_bUJzRbC0
可以進入全螢幕模式,將有更大的空間瀏覽地圖。

在全螢幕模式中,可以使用搜尋功能(點選列表上端的放大鏡圖示),搜尋想要尋找的地點或是花卉種類等等。



資料來源


這份地圖的資料來源主要來自於
其中特別感謝玩台灣賞花的管理人員、以及初心小格的格主小白鯨同意分享資料。


更多資料


這份地圖將會不定時更新,並修改、加入更多的賞花資料,歡迎大家經常回來點選觀看。另外,也歡迎大家提供自己知道的賞花景點資訊。

2016年1月31日 星期日

Node.js 的中文與英文髒話過濾

在聊天室之類的應用之中,我們時常會需要使用到髒話過濾的功能。這篇文章簡單描述一下個人在後端過濾中文與英文髒話的方法。

Mikey Wilson, 2002

檢測髒話


首先,讓我們試著判斷一個句子是否含有任何髒話。注意中文與英文的判斷方式略有不同,底下將分開討論。

2016年1月6日 星期三

利用 Async.js 平行處理多筆 MongoDB 查詢

在使用 MongoDB 資料庫的時候,我們常常需要同時送出多筆查詢,並在所有資料都讀取完畢後進行下一步動作,例如將資料顯示在網頁上。這篇文章將會簡單介紹利用 Async.jsmongoose 達成上述需求的方法與程式碼。

Copyright jaketrent.com

Async.js 簡介


在 javascript 中雖然有著 callback 機制,可以在事件完成後接著執行其他動作。但如果要等待很多事件完成,可能會寫成很多層的 callback:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
var findData = function(input, callback){
    var data = new Data;
    findMoreData(inputA, function(dataA){
        data.add(dataA);
        findMoreData(inputB, function(dataB){
            data.add(dataB);
            findMoreData(inputC, function(dataC){
                ...
            });
        });
    });
    callback(data);
}

這種寫法不僅難看,而且由於並非平行處理,在每個 findMoreData 函式中等待 I/O 處理的時間都會累積起來,影響效能。

我們可以使用 Async.js 套件提供的 parallel 方法,來改寫上面的程式碼:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
var async = require('async');
var findData = function(input, callback){
    async.parallel({
        A: function(cb){
            findMoreData(inputA, function(dataA){
                cb(null, dataA);
            });
        },
        B: function(cb){
            findMoreData(inputB, function(dataB){
                cb(null, dataB);
            });
        }, 
        ...
    }, 
    // 這裡是 async.parallel 的 callback function
    function(err, results) {
        // 在這裡 results 的內容是個物件:{A: dataA, B: dataB, ...}
        callback(results);
    });
}

上面的 async.parallel 方法接受一個物件當作參數,裡面包含各個需要平行處理的工作。範例中為了獲得資料 A,我們呼叫了  findMoreData 來處理,而在 findMoreData 等待 I/O 處理的時間中,parallel 會試著繼續處理資料 B 的部分。因此,我們可以平行化的處理這些工作,不用等待前一個完成才接著執行。

程式碼中的 cb 是 Async.js 的一個 callback function。執行後,parallel 就會認為這部分的工作已經完成,而我們要確保每個工作都會呼叫到 cb,整個 parallel 的處理流程才會正常結束。cb 接受兩個參數,第一個是 error 物件(可以為 null),第二個則是我們要的資料,它會存到 results 裡面。

等到獲得所有資料,最後一段的 callback function就會被執行,讓使用者可以處理後續工作。

parallel 與 Mongoose 的整合


對於 Async.js 有簡單的認識後,我們正式把 mongoose 讀取資料的 API 整合進來:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
var async = require('async');
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var dataASchema = new Schema({...});
var dataAModel = mongoose.model('dataA', dataASchema);
...
var findData = function(callback){
    async.parallel({
        A: function(cb){
            dataAModel.find({}, function(err, data, count){
                cb(null, data);
            });
        },
        B: function(cb){
            dataBModel.find({}, function(err, data, count){
                cb(null, data);
            });
        }, 
        ...
    }, 
    function(err, results) {
        callback(results);
    });
}

這邊用 find 函式來查詢資料庫的資料,並把得到的全部結果原封不動的存放到 results 去。藉由修改 find 的參數,我們也可以在這邊針對不同資料,自訂各項查詢條件。

改用 each 取得資料


使用 parallel 可以自訂要同步執行的不同工作,因此有著很大的彈性。但有時候我們所需要的只是針對不同資料執行相同的工作,用 parallel 必須要不斷的重複相同的程式碼(如上例中,不斷重複 find 的動作),看起來有些雜亂。

Async.js 另外提供其他流程控制與資料處理方法,其中一個方法是 each,它可以針對多筆資料平行處理相同的一件工作。例如以下範例,使用 each 來取得資料:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
var findData = function(array, callback){
    var results = {};
    async.each(array, function(item, cb){
        var model = mongoose.model(item);
        model.find({},function(err, data, count){
            results[item] = data;
            cb();
        });
    },
    function(err){
        callback(results);
    });
}

findData(['dataA', 'dataB'], function(results){
    // results 物件的內容為 {'dataA': ... , 'dataB': ...}
    // 省略部分為資料庫中的資料
});

我們輸入一個參數 array,裡面存放不同 MongoDB collection 的名字,async.each 會去讀取這些 collection 名字,再利用 find 找到當中的所有資料,存放到物件變數 results 當中,最後呼叫 cb 結束。

結論


這篇文章簡單介紹 Async.js 裡的 parallel 與 each 兩種方法,並配合 mongoose 來達到平行處理多筆 MongoDB 查詢的方法。