Go言語(golang)でTUIアプリを作ろう 第二部入魂編 ( その2 新データベースツールの採用 )
BoltDBを使ってみる。
[1] 新データベースの選択
前回述べたように、当初アプリケーションのデータベースとしてSQLite3をターゲットに進めていたのですが、いささか思惑違いの部分があり、方向転換を余儀なくされてしまいました。database/sqlベースでコーディングを進めていたわけですから、代替のデータベースは、golangで書かれたRDBがあれば最高なのでしょうが、世の中それほど甘くはありません。
今回のアプリケーションは、特に複雑なデータ処理をするわけではないので、RDBでなければ、実装できないというものではありません。それならば、KVS(key/value store)のようなシンプルな構造でも問題はなさそうです。ここは考えを改めて、golangで書かれたKVSを探してみましょう。
そこで、見つけたのが下記のBoltです。
ドキュメントには、
Bolt is a pure Go key/value store inspired by Howard Chu’s LMDB project. The goal of the project is to provide a simple, fast, and reliable database for projects that don’t require a full database server such as Postgres or MySQL.
とあります。Other Projects Using Boltを見ると、相当量の採用事例もあるようですから、今回はこれを使ってみることにしました。
[2] Boltの機能
さて、Boltは、
Since Bolt is meant to be used as such a low-level piece of functionality, simplicity is key. The API will be small and only focus on getting values and setting values. That’s it.
とあるように、APIは単純でわかりやすいものになっています。基本機能を理解するには、ドキュメント内の「Getting Started」を読めば十分でしょう。
Boltでは、データをKey/Value形式で扱うわけですが、そのコレクションをBucketと呼びます。Boltの利用に当たっては、まずはBucketという概念を理解することが重要です。
(1) Bucketの概念
ドキュメントにある例から見ていきましょう。ここでは、“MyBucket"というBucketを作成しています。
db.Update(func(tx *bolt.Tx) error {
b, err := tx.CreateBucket([]byte("MyBucket"))
if err != nil {
return fmt.Errorf("create bucket: %s", err)
}
return nil
})
このBucketに対し、実際のキーとバリューを、Putメソッドで追加していきます。なお、キーはユニークでなければいけません。
db.Update(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte("MyBucket"))
err := b.Put([]byte("answer"), []byte("42"))
return err
})
追加されたデータを読み出すのは、Getメソッドです。削除したい場合は、Deleteメソッドを使用します。
db.View(func(tx *bolt.Tx) error {
b := tx.Bucket([]byte("MyBucket"))
v := b.Get([]byte("answer"))
fmt.Printf("The answer is: %s\n", v)
return nil
})
なお、Bucketからのデータ取り出しには、Cursor、Seek、ForeEachといったメソッドも用意されています。ドキュメントの例を見ながら、アプリケーションの要件に合うものを選択すればよいでしょう。
また、Bucketのなかに、Bucketを入れることが可能です。Nested bucketsと呼ぼれるこの機能は、複数のデータを管理するのに有効でしょう。
(2) Transaction
次に理解しておきたいのは、transactionの概念です。
Boltでは、データをハンドリングする間はtransactionを生成しておかないといけません。
先の例で、
db.Update(func(tx *bolt.Tx) error {})
db.View(func(tx *bolt.Tx) error {})
と設定されていた部分です。
Boltでは、ある時点で、唯一のread-write transactionと複数のread-only transactionsをサポートします。このtransactionの間のみ、データが有効になるわけです。言うまでもないことですが、更新を伴う処理には、read-write transactionを、読み込みにはread-only transactionsを使用します。上記のメソッド以外に、手動でtransactionを形成することも可能です。
Boltの基本は、これだけ理解しておけば十分だと思います。データベースのオープン、クローズ、データの取り出しなどは、サンプルを見れば直感的にわかります。
(3) Bucketのvalueになにを入れるのか
Bucketを設計するに当たって、まず考えないといけないのは、valueをどのように設定するかでしょう。keyは、ユニークでないといけないことさえ気をつけておけば、自ずと決まってしまいます。また、Autoincrementな連番をキーとして設定することも出来ます。
keyとvalueは1対1ですから、valueも基本的には1項目です。ただ、ここに、1フィールドデータだけ入れていては、とてもアプリケーションニーズに対応できません。したがって、valueには複数項目を入れることになります。例えば、単純なデータなら、CSVのような形式でも良いでしょう。しかし、それでは表現力に乏しいですから、複雑な構造を表現できる形式が必要になります。汎用性があって、golangと相性の良いものといえば、JSONしかありません。
これには、具体例が必要なようです。次回は、Bolt上にListDBを定義し、実際にデータを入れてみましょう。