今Golangで作っているSaraswatiの機能をまとめておく
SaraswatiはSedna XML Database をミドルウエアに使用したバックエンドフレームワークだ。
1.サーバの起動
go run Saraswati
2.クエリの準備
Sarawsatiのあるフォルダ以下、
queryRoot/etc/qr/query.txtに以下を記述
queryRootはHTTPアクセスされた際の起点となるルートフォルダとなる。
開発時はlocalhost:5000で開発しているので、
フロントから、http:localhost:5000/etc/qrをアクセスした際に実行されるクエリとなる。
query.txtは見て分かる通りXQueryだ。
このXQueryはSaraswatiがミドルウェアに使っている Sedna XML Databaseで実行される。
クエリ内の(:PARAMS:)はXMLではタダのコメントだが、SaraswatiはフロントからJSONで渡ってきたデータをこの位置にバインドする。ちなみに、se:qrは今回C++でSedna XML Database用に作成したQRコード作成プラグインだ。このようにSedna XML DatabaseはXQueryに元から無い機能でも、後からプラグインとして実装が可能な仕組みを提供している。
declare function se:qr($a as xs:string*) as xs:string external;
(:PARAMS:)
(: convert $text input to qr image :)
(: size as pixel for image size :)
let $a := doc('test1.db')//pages
let $qr := se:qr($text)
return
<DATA TYPE='JSON' OUTPATH="//DATA/QRIMG">
<QRIMG>{$qr}</QRIMG>
</DATA>
ちょっと引っかかるのは、let $aの部分だが、実はダミーとしてtest1.dbに対してクエリを実行するために書かれている。
クエリはどのDBに対して実行するのかという指定が必要なため、QRコードのような、本来DBアクセスをしないクエリであってもダミーとして実行対象のDBが必要となる。
ただし、現在のバージョンのSaraswatiでは、ダミーが無くとも適当なデータベースをダミーとして実行できるようになっている。
3.フロントの準備
クエリを呼び出して、結果を表示するためのフロントをJQueryで作成する
http:localhost:5000/etc/qrを呼び出し、
$(".qimg").attr('src','data:image/png;base64,' +myArray.QRIMG)で、戻ってきたJSONのQRIMGをイメージとして設定するだけだ。
4.実行
以下はSedna XML Database の吐き出したログ
Saraswatiは(:PARAMS:)の部分にフロントから渡されたJSONがXMLの変数として記述し、Sedna XML Databaseに渡して実行、その後、Sedna XML Databaseからの出力(XMLでなければならない)をJSONに変換してフロントへ戻す
ただし、このバインドの仕方は今後変わる予定で、強制的に全体を<VAL>で囲うことになると思う
let $text := <VALあいうえおかきくけこ</VAL>のようになる予定
--- declare function se:qr($a as xs:string*) as xs:string external;
--- let $text := 'あいうえおかきくけこ'
---
--- (: convert $text input to qr image :)
--- (: size as pixel for image size :)
---
--- let $a := doc('test1.db')//pages
--- let $qr := se:qr($text)
--- return
--- <DATA TYPE='JSON' OUTPATH="//DATA/QRIMG">
--- <QRIMG>{$qr}</QRIMG>
--- </DATA>
---
5.出力結果
フロント側でレスポンスを見てみると、このようにQRIMGにはBase64エンコードされた画像が含まれている。
フロントはこれを使って表示すればよい
6.食品成分表からのデータ取得
クエリはqueryRoot/foodinfo/nutrients/query.txtに以下の内容で用意する
(:PARAMS:)
let $a := doc('test2.db')//FOODS/FOOD[@FOOD_ID=$id]
return
<DATA TYPE='JSON' OUTPATH="//DATA/FOOD">
<FOOD>
{$a/JP_DISP_NAME}
{$a/ENERC_KCAL}
{$a/PROT-}
{$a/FAT-}
{$a/CHOCDF-}
{$a/VITA_RAE}
{$a/TOCPHA}
{$a/NE}
</FOOD>
</DATA>
FOOD_IDでしか検索しない場合のクエリだ
let $id :='2002'を入れて、実行するとSaraswatiは上記XMLの出力内容を以下のようなJSONに変換してフロントに戻す。
ちなみに2002は食品成分表の通し番号になっている。
{
"FOOD": {
"NE": "1.1",
"JP_DISP_NAME": "さつまいも(塊根:皮なし:生)",
"ENERC_KCAL": "126",
"PROT-": "1.2",
"FAT-": "0.2",
"CHOCDF-": "31.9",
"VITA_RAE": "2",
"TOCPHA": "1.5"
}
}
これを表示するフロントを作れば、一応の完成だ
7.さらなる改良
これだとIDでしか検索できないので、データ全件検索できるようにする。
IDだと結果は一件だったのだが、
名称などで検索すると、複数件の結果を返さなければならない。
以下のように複数件の検索結果を返すようにクエリを修正する。
<DATA TYPE='JSON' OUTPATH="//DATA/LIST" FORCEARRAY="//LIST/FOOD">
(: 複数のFOODを返す場合には、その上位にラッパーを作り、OUTPATHに指定をする :)
(: FORCEARRAYはのラッパーの下位のFOODが1件の場合でもJSON配列で返すために指定する :)
<LIST>
{
(:PARAMS:)
for $food in doc('test2.db')//FOODS/FOOD
where contains($food/SEARCH_NAME,$id) or contains($food/ADDITIONAL,$id) or $food/@FOOD_ID=$id
order by $food/@FOOD_ID
return
<FOOD>
{$food/JP_DISP_NAME}
{$food/ENERC_KCAL}
{$food/PROT-}
{$food/FAT-}
{$food/CHOCDF-}
{$food/VITA_RAE}
{$food/TOCPHA}
{$food/NE}
</FOOD>
}
</LIST>
</DATA>
この時、適当な検索文字列(さつまいも)で検索した結果は以下の様になる。
{
"LIST": {
"FOOD": [
{
"JP_DISP_NAME": "さつまいも(塊根:皮なし:生)",
"ENERC_KCAL": "126",
"PROT-": "1.2",
"FAT-": "0.2",
"CHOCDF-": "31.9",
"VITA_RAE": "2",
"TOCPHA": "1.5",
"NE": "1.1"
},
{
"JP_DISP_NAME": "さつまいも(塊根:皮なし:蒸し)",
"ENERC_KCAL": "131",
"PROT-": "1.2",
"FAT-": "0.2",
"CHOCDF-": "31.9",
"VITA_RAE": "2",
"TOCPHA": "1.5",
"NE": "1.1"
},
{
"NE": "1.3",
"JP_DISP_NAME": "さつまいも(塊根:皮なし:焼き)",
"ENERC_KCAL": "151",
"PROT-": "1.4",
"FAT-": "0.2",
"CHOCDF-": "39.0",
"VITA_RAE": "1",
"TOCPHA": "1.3"
},
{
"FAT-": "0.6",
"CHOCDF-": "71.9",
"VITA_RAE": "(0)",
"TOCPHA": "1.3",
"NE": "2.4",
"JP_DISP_NAME": "さつまいも(蒸し切干)",
"ENERC_KCAL": "277",
"PROT-": "3.1"
},
{
"JP_DISP_NAME": "さつまいもでん粉",
"ENERC_KCAL": "340",
"PROT-": "0.1",
"FAT-": "0.2",
"CHOCDF-": "82.0",
"VITA_RAE": "0",
"TOCPHA": "-",
"NE": "Tr"
},
{
"VITA_RAE": "(0)",
"TOCPHA": "(0)",
"NE": "0",
"JP_DISP_NAME": "はるさめ(普通はるさめ:乾)",
"ENERC_KCAL": "346",
"PROT-": "0",
"FAT-": "0.2",
"CHOCDF-": "86.6"
},
{
"PROT-": "0.9",
"FAT-": "0.5",
"CHOCDF-": "33.1",
"VITA_RAE": "3",
"TOCPHA": "1.0",
"NE": "0.8",
"JP_DISP_NAME": "さつまいも(塊根:皮つき:生)",
"ENERC_KCAL": "127"
},
{
"NE": "0.9",
"JP_DISP_NAME": "さつまいも(塊根:皮つき:蒸し)",
"ENERC_KCAL": "129",
"PROT-": "0.9",
"FAT-": "0.2",
"CHOCDF-": "33.7",
"VITA_RAE": "4",
"TOCPHA": "1.4"
},
{
"PROT-": "1.4",
"FAT-": "6.8",
"CHOCDF-": "38.4",
"VITA_RAE": "5",
"TOCPHA": "2.6",
"NE": "1.0",
"JP_DISP_NAME": "さつまいも(塊根:皮つき:天ぷら)",
"ENERC_KCAL": "205"
}
]
}
}
あとはこれを表示するフロントを作ればよい
こんな感じに表示すればよいだろうか?
もちろん、成分表をDIVやCANVASを使って棒グラフにしてもよい
8.まとめ
このようにSaraswatiを使った開発では、フロント開発者が、自分の都合のいいようにクエリを編集できる。
SaraswatiはJSONで渡ってきたデータをXML化し、XQueryにバインドし、結果XMLをJSONに戻す仕組みを提供する。
この際、Saraswatiそのものは停止する必要が無く、コンパイルし直したりデプロイし直したりする必要もなく、動かしっぱなしでクエリを編集すればいい。
ところで、Saraswatiにはドメイン層が無い。
無いは言い過ぎかもしれない。
取得したデータの計算などはXQueryで行う仕組みだ。
既存DBでもPL/SQLなどで実現していることと同じことをXQueryで行えばいい。
また、Sednaには連続してデータの更新をするような機能はないが、Saraswatiはその機能を提供する。
今回、連続したDBの更新など、データの更新についてはここで記述しなかったが、Saraswatiは<DATA>タグ以外に<SEDNA><PROCS><PROC>というエンベロープで包むことにより、連続データの更新が出来るようになっている。
//SEDNA/PROCS/PROCに複数のデータ更新系クエリを記述できる。
PROCS/PROCが全部正常に実行されればコミットされ、
途中で失敗したらロールバックという仕組みになっている。
つまり、データ更新に於いてもクエリを編集すれば良い
0 件のコメント:
コメントを投稿