假設我們現在的問題是:「離我最近的5個wifi熱點在哪裡」
工程師看到問題,一般來說,就會想到至少要下面二樣東西才能查找相關資訊
1.我(目標) 目前的位置(經度及緯度)
2.要幾個結果數 (本問題是5個,當然你也可以寫死,比較彈性的方式是多一個參數或設定可以傳入想要的結果數)
依上述的問題,我們定義的函數如下
public colleciton<QueryMatch> queryKNN(double lat , double lon , int n)
{
1.把目標轉成geohash
2.迴圈找出目標和他的9宮格鄰居內的wifi 熱點資料
3.對上述找出之資料進行排序,找出最近距離的n 個點回傳
}
大致上的程式碼如下
public Collection<QueryMatch> query(double lat , double lon , int n )
{
DstanceComparator comp = new DistanceComparator(lon,lat);
Collection<QueryMatch> ret = MinMaxPriorityQueue.orderedBy(comp).maximumSize(n).create();
GeoHsh target = GeoHash.withCharacterPrecision(lat,lon,7);
//先確認目標地區的grid 有無資料
ret.addAll( take N( comp , target.toBase32() , n ));
//找出鄰居9宮格並確認grid 有無資料
for (GeoHash h : target.getAdjacent())
{
ret.addAll( takeN(comp,h.toBase32(),n))
}
return ret;
}
public Collection<QueryMatch> takeN(Comparator<QueryMatch> comp , String prefix , int n )
{
Collection<QueryMatch> candidates = MinMaxPriorityQueye.orderBy(comp).maxmumSize(n).create();
Scan scan = new Scan( prefix.getBytes());
scan.setFilter( new PrefixFilter(prefix.getBytes()))
scan.addFamily(Family)
scan.setMaxVersion(1)
// cache 設置為大於1的值可減少RPC 呼叫
scan.setCaching(1)
HTableInterface table = pool.getTable("wifi")
ResultScanner scanner = table.getScanner(scan)
for ( Result r : scanner)
{
String hash = new String(r.getRow)
String id = new String( r.getValue (FAMILY,ID))
double lon = Bytes.toDouble( r.getValue(FAMILY, X_COL))
double lat = Bytes.toDuble( r.getValue(FAMILY,Y_COL))
candidates.add( new QueryMatch(id,hash,lon,lat))
}
table.close();
return candidates;
}
這邊有一個重要的點是,你怎麼知道要使用幾碼geohash去作 row key掃描? 如果用的geohash 太長(精確度太高),則可能無法得到n個符合的資料,如果geohash太短,則可能得到資料比預期多很多的資料量,意思是不同資料和不同n值會需要不同的geohash精確度,所以你必須了解你的資料和系統,才有辦法反覆測試找出一個合理的n值。
(本資訊由HBase-搞定big data-no sql實戰 整理而出)