水曜日, 11月 10, 2010

rhinoから"Google Storage"にupload!

GSUtil というコマンドラインツールが用意されているものの、Python(?Rubyだっけ)なので使っていなかった、が、以下ソースでuploadに成功。
"WebOS Goodies"さんの情報を参考にしました。ありがとうございます!
importPackage(javax.crypto);
importPackage(javax.crypto.spec);
importPackage(org.apache.commons.codec.binary);

importPackage(org.apache.commons.httpclient);
importPackage(org.apache.commons.httpclient.methods);

var ACCESS_KEY = 'GOOG4C72MAOUMK7xxx3Z';
var SECRET     = 'xy4jTrxxxxuTZEtqUItfZIViVUF3xEKx4xxxp';
var HOST       = 'commondatastorage.googleapis.com';

var strDate    = getDate();
var strPath    = '/test/start.txt';

var headers = {
//  'Content-Length': '0',
'Content-Type': 'text/plain',
'Date': strDate,
    'User-Agent': 'Jakarta Commons-HttpClient/3.1',
    'Host': 'commondatastorage.googleapis.com'
};

// rhino + Java package/class
// http://d.hatena.ne.jp/terurou/20100918/1284791541

// http://www.fireproject.jp/feature/uzumi/httpclient/logging.html
java.lang.System.setProperty("org.apache.commons.logging.Log", "org.apache.commons.logging.impl.SimpleLog");
java.lang.System.setProperty("org.apache.commons.logging.simplelog.showdatetime", "true");
java.lang.System.setProperty("org.apache.commons.logging.simplelog.log.httpclient.wire.header", "debug");
java.lang.System.setProperty("org.apache.commons.logging.simplelog.log.org.apache.commons.httpclient", "debug");

var httpclient = new HttpClient();

if(false){ 
var httpget = new GetMethod("http://commondatastorage.googleapis.com" + strPath); 
httpget.setRequestHeader(new Header("Date",strDate));
httpget.setRequestHeader(new Header("Authorization",getAuthHeader('GET', headers,strPath)));

var statusCode = httpclient.executeMethod(httpget);  
print(httpget.getStatusLine());
}else{
var httpput = new PutMethod("http://commondatastorage.googleapis.com" + '/tf0054'+strPath);
var jstrTmp = "Hello this is a test in one line that is longer 12345677890\n";

httpput.setRequestHeader(new Header("Authorization",getAuthHeader('PUT', headers, strPath)));
httpput.setRequestHeader(new Header("Date",strDate));
httpput.setRequestHeader(new Header("x-goog-acl","public-read"));
httpput.setRequestHeader(new Header("Content-Type","text/plain"));
httpput.setRequestHeader(new Header("Content-Length","" + jstrTmp.length));
httpput.setRequestBody(jstrTmp);

httpclient.executeMethod(httpput);
print(httpput.getResponseBodyAsString());
}

function getAuthHeader(strMethod, headers, strPath){

function getHmacSign(strTmp){
var strType = 'HmacSHA1';
var jstrTmp = new java.lang.String(strTmp);
var jstrSec = new java.lang.String(SECRET);
var mac = Mac.getInstance(strType);
mac.init(new SecretKeySpec(jstrSec.getBytes(), strType));
return new java.lang.String(Base64.encodeBase64(mac.doFinal(jstrTmp.getBytes())),"UTF-8");
}

var strHeaders = strMethod + "\n";
strHeaders += "\n"; // Content-MD5 value
for(var i in headers){
if(i.toLowerCase() == 'date' || i.toLowerCase() == 'content-type'){
strHeaders += headers[i] + "\n";
} else {
// strHeaders += i.toLowerCase()+':'+headers[i]+"\r\n";
}
}
strHeaders += "x-goog-acl:public-read\n";
strHeaders += '/tf0054' + strPath;
print(">"+strHeaders);
return 'GOOG1 ' + ACCESS_KEY + ':' + getHmacSign(strHeaders);
}

function getDate(){
var d = new Date(new Date().getTime());
return d.toGMTString().replace(/UTC/,'GMT');
}

適当なソースですが、ご参考までに。

水曜日, 11月 03, 2010

BigQueryを試してみた

GoogleさんからBigQueryの利用OKがメール出来たので、(公開されているお試し手順に沿って)自らuploadしたデータに検索ができる、までを確認しました。

また、このときPythonが手元にない環境だったので、先に作ったcygwin環境のシェル+curlでお手軽に試せることも分かりました。
- - -
①操作用Tokenを取得
curl -X POST -d accountType=HOSTED_OR_GOOGLE -d Email=tf0054@gmail.com -d Passwd=xxx -d service=ndev -d source=google-bigquery-manualimport-1 -H "Content-Type: application/x-www-form-urlencoded" https://www.google.com/accounts/ClientLogin

xxxの部分はご自分のpwに変えて下さい。このcurlコマンド実行で得られるレスポンスにある、AUTH=yyyのyyy部分ががTokenになります。けっこう長いです。

②各種操作用シェルスクリプトを保存
Librariesのページにあるシェルをファイルに落とします。

check_import_status.sh
createtable.sh
delete_table.sh
import.sh
query.sh

このとき、curlからhttpsアクセスをするので、(私の環境では)各シェルの中にあるcurlのオプションに、"-k"を足す必要がありました。

③SSAのサンプルデータをダウンロード&配置
本家には日本からアクセスできなさそう?なので、ココからダウンロードし、以下絵の感じに「tf0054/yob1880.txt」として管理画面からupload。


④テーブルを作成
$ ./createtable.sh tf0054/tables/babynames

上のGoogleStorage管理画面キャプチャのとおりですが、ここの階層にあるディレクトリは自動的に掘られるようです(上記tablesなど)。

⑤データをインポート
$ ./import.sh tf0054/tables/babynames tf0054/yob1880.txt

これまでにGoogleStorageへ上げたデータがあれば、それをBigQueryで見られるようにする(BigQueryの管理下に置く)ことができました。

⑥インポート処理状況を確認
$ ./check_import_status.sh tf0054/tables/babynames b85867e864bf1259

先のインポートコマンド投入時にもらえるレスポンスに、ジョブIDがもらえるのでそれを最終パラメータにします。

⑦セレクトしてみる
$ ./query.sh "SELECT name,count FROM [tf0054/tables/babynames] WHERE gender = 'F' ORDER BY count DESC LIMIT 5;"

テーブル名はカギ括弧に括って記述し、さらにGoogleStorage的にはフルパスで参照する感じになりました。
- - -

レスポンスがJSONでもらえるだけど、見た目には分かりずらい。Parseエラーなどになっていないことを随時確認しましょう。例えば⑦の結果は以下です。

{"result":{"kind":"bigquery#queryResults","fields":[{"id":"name","type":"string"},{"id":"COUNT","type":"integer"}],"rows":[{"f":[{"v":"Mary"},{"v":"7065"}]},{"f":[{"v":"Anna"},{"v":"2604"}]},{"f":[{"v":"Emma"},{"v":"2003"}]},{"f":[{"v":"Elizabeth"},{"v":"1939"}]},{"f":[{"v":"Minnie"},{"v":"1746"}]}]}}

何やら可能性を感じますが、Hadoop上のHIVE、みたいな位置づけですね(でも、だとするとGoogleStorageがHadoopにあたるものとなる、、、のでしょうか)。また、対応できているSQL(?)の情報も公開されていました。

火曜日, 11月 02, 2010

JavascriptでHIVEを操作する(JDBC@Thrift)

rhinoを使って、JavascriptからHIVEを操作してみるテスト。普通にできた(あたりまえ)。

/*
http://wiki.apache.org/hadoop/Hive/HiveClient#Thrift_Java_Client
*/

importPackage(java.sql);
importPackage(java.lang);

Class.forName("org.apache.hadoop.hive.jdbc.HiveDriver");

var tableName = "testHiveDriverTable";

// make connection
var conn = DriverManager.getConnection("jdbc:hive://localhost:10000/default", "", "");
var stmt = conn.createStatement();

// init
stmt.executeQuery("drop table " + tableName);
stmt.executeQuery("create table " + tableName + " (key int, value string)");

// show tables
var sql = "show tables '" + tableName + "'";
print("Running: " + sql);
var resp = stmt.executeQuery(sql);
if (resp.next()) {
print(resp.getString(1));
}

// describe table
sql = "describe extended " + tableName;
print("Running: " + sql);
resp = stmt.executeQuery(sql);
while (resp.next()) {
print(resp.getString(1) + "\t" + resp.getString(2));
}

// load data into table
var filepath = "/tmp/a.txt";
sql = "load data local inpath '" + filepath + "' into table " + tableName;
print("Running: " + sql);
resp = stmt.executeQuery(sql);

// select * query
sql = "select * from " + tableName;
print("Running: " + sql);
resp = stmt.executeQuery(sql);
while (resp.next()) {
print(String(resp.getInt(1)) + "\t" + resp.getString(2));
}

// regular hive query
sql = "select count(1) from " + tableName;
print("Running: " + sql);
resp = stmt.executeQuery(sql);
while (resp.next()) {
print(resp.getString(1));
}

一応、走らせるときのbuild.xmlもサンプルとして。

<path id="clspath">
<fileset dir="build/jar">
<!-- メインの読込み -->
<include name="*.jar"/>
</fileset>
<fileset dir="${hive.dir}">
<!-- HIVEのjarを読込み -->
<include name="**/*.jar"/>
</fileset>
<fileset dir="${hadoop.dir}">
<!-- Hadoopのjarを読込み -->
<include name="*.jar"/>
</fileset>
</path> 

<!-- blog-gae>ant rhino-run -Dfile.encoding=UTF-8 -Dscript="env0.js" -->
<target name="rhino-run">
<echo message="Running ${script}"/> 
<java fork="yes"
classname="org.mozilla.javascript.tools.shell.Main"
failonerror="true">
<classpath>
<path refid="clspath" />
<pathelement path="js.jar" />
</classpath>
<arg value="${script}" />
</java>
</target>

HIVEの力なんだけど、とても簡単にM/Rが動くのは素晴らしい、と思います。