はじめに
GridDBは、Googleのプログラミング言語Go 用のデータベースコネクタをリリースしました。 Python やRuby のように、 SWIG ライブラリを使用して構築されています。このコネクタは、Go 1.9以上のバージョンをサポートしており、CentOSバージョン6および7で動作します。
Go言語 は、Google によって開発されたオープンソースのプログラミング言語です。並列処理と並行処理のために、 goroutines と呼ばれる軽量スレッドを使用して最適化されています。 これにより、Go言語を、GridDBのような高速メモリ内の指向データベースと組み合わせた高性能プログラミング言語にすることができます。
このブログでは、Go client構築のためのGridDB用のデータベースコネクタの設定とテストに関する一般的な手順を説明します。
Table of Contents
- 1 クライアントへのインストール&セットアップ
- 2 Go言語を使ってGridDBに接続する
- 3 スキーマとコンテナの作成
- 4 データのクエリと取得
- 5 タイムスタンプとバイナリデータの処理
-
- 5.1 タイムスタンプデータ
- 5.2 バイナリまたはBlobデータ
- 6 参照
-
- 6.1 ソースコード
クライアントへのインストール&セットアップ
まずはGo APIのGithubリポジトリをクローン作成してください。
$ git clone https://github.com/griddb/go_client
システムにGo clientを構築するには、 GridDB C clientを構築し、 インストールする必要があります。 C clientの設定とテストの方法については、このブログ記事を参照してください。
-
注意:
make
してGo clientをシステムに構築する前に、Go言語 バージョン1.9以上を使用していることを確認してください。 Go言語バージョン1.9の入手方法と設定方法については、このページを参照してください。
Githubからソースコードを入手したら、Go ClientのGithub pageに従って進めてください。
Go clientの構築時に起こりうる問題
場合によっては、Go clientパッケージで make
を発行するときに次のようなエラーが発生することがあります。
go install github.com/griddb/go_client can't load package: package github.com/griddb/go_client: cannot find package "github.com/griddb/go_client"
これは、 GOPATH
または GODEBUG
環境変数の問題が原因である可能性があります。 修正するには、GridDB Go clientプロジェクトディレクトリをクリーンアップしてから、適切な値に設定します。
$ make clean $ export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:<Path to GridDB c_client bin/ directory> $ export GOPATH=$GOPATH:<Path to go_client directory> $ export GODEBUG=cgocheck=0 $ make
NOTE: 予期しない実行時エラーを防ぐため、常に GODEBUG
を cgocheck = 0
にしてください。
Go言語を使ってGridDBに接続する
Go言語プログラムでGridDBに接続する場合は、 import statement ( "github.com/griddb/go_client" )
を使用してGo APIをインポートします。 Go言語のGridDBパッケージの名前は griddb_go
です。
そこから、GridDBライブラリから StoreFactoryInstance
を取得し、それをFactoryインスタンスの.GetStore()
メソッドを使ってGridDBクラスタに接続することができます。 このメソッドに与えるパラメータは、map[string]interface{}{}
オブジェクト(文字列キーを持つ HashMapまたはPython dictionaryに相当)です。キーは、GridDBに接続するための設定フィールドです。
ポートは整数として指定する必要がありますが、ホスト名やクラスタ名などのその他のデータベース設定値はすべて文字列にすることができます。
package main import ( "github.com/griddb/go_client" "fmt" "os" "strconv" ) func main(){ factory := griddb_go.StoreFactoryGetInstance() //Get a GridStoreFactory instance //The 'notificationPort' value to connect to GridDB must be an integer //when using the StoreFactory.GetStore() method to connect to GridDB //Read notification-port from command-line //Parse port reading to integer port, err := strconv.Atoi(os.Args[2]) if err != nil { fmt.Println(err) os.Exit(2) } //Connect to GridDB cluster //Note that port must be an integer gridstore := factory.GetStore(map[string]interface{}{ "host": os.Args[1], // "239.0.0.1" "port": os.Args[2], // 31999 "cluster_name": os.Args[3], //"defaultCluster" "user": os.Args[4], // "admin" "password": os.Args[5], // "admin" //"notification_member": os.Args[6], //"notification_provide": os.Args[7], }) //Close access to GridDB gridstore.Close(0) //Note that you must specify an integer flag when using the .Close() function call }
スキーマとコンテナの作成
コンテナと行のスキーマは、Go言語のインターフェースを使って作成できます。
コンテナの列タイプをマップするには、インターフェイスの 2次元配列を使用します。 そこからStoreFactory.CreateContainerInfo()
メソッドを使用してコンテナスキーマ( ContainerInfo Type
)を作成できます。 これらのスキーマを使用して挿入すると、コンテナを作成できます。
行はインターフェース配列( [] interface {}
)としてモデル化できます。 行を作成するには、インターフェイス配列を作成し、各インデックスの値をコンテナに格納する予定の対応する型にマップさせます。
たとえば、コンテナ内で2番目の列に 'count'
という名前の整数が格納されている場合、配列row := []interface{}{"A_1",3,true}
の index 1 の値3
は 'count'
列にマップされます。
//Container schema for our timeseries container //The first column is always the row-key row_schema := [][]interface{}{ {"timestamp",griddb_go.TYPE_TIMESTAMP}, {"water_temperature",griddb_go.TYPE_FLOAT}, {"turbidity",griddb_go.TYPE_FLOAT}, {"depth",griddb_go.TYPE_FLOAT}, {"wave_height",griddb_go.TYPE_FLOAT}, {"wave_period",griddb_go.TYPE_FLOAT}, {"battery_life",griddb_go.TYPE_FLOAT}, {"beach_name",griddb_go.TYPE_STRING}} timeseries_name := "water_monitor_ts_2" //Name for our container //Create a container info to use to insert containers into GridDB containerInfo, err := griddb_go.CreateContainerInfo(timeseries_name, row_schema, griddb_go.CONTAINER_TIME_SERIES, true) timeseries := gridstore.PutContainer(containerInfo,false) //Use an interface array to create and insert rows row := []interface{}{time.Now().UTC(),16.2,1.26,1.514,0.147,4.0,11.7,"Calumet Beach"} // You can also initialize an empty row with ( row := make([]interface{},8) ) //Insert row into Timeseries container err = timeseries.Put(row)
行キーを使用しコンテナの .Get()
メソッドを呼び出すことで、簡単に行を取得することもできます。 前述のように、行は単なる一般的なGo言語 インターフェースの配列であり、列の行インデックス を参照するだけで、検索する列の値にアクセスすることができます。。
たとえば、コンテナのスキーマの 4番目の列であるfloat型の '経度'
列のコンテナを作成するとしましょう。 経度列にアクセスするためにこのコンテナから行を取得するには、index 4 にアクセスするだけです。
また、行キーでもコンテナ内の行を削除することもできます。
//Schema of our collection container schema, err := griddb_go.CreateContainerInfo(collection_name,[][]interface{}{ {"sid",griddb_go.TYPE_STRING}, {"name",griddb_go.TYPE_STRING}, {"type",griddb_go.TYPE_STRING}, {"latitude",griddb_go.TYPE_DOUBLE}, {"longitude",griddb_go.TYPE_DOUBLE}, {"installation",griddb_go.TYPE_TIMESTAMP}}, griddb_go.CONTAINER_COLLECTION, true) collection, err := gridstore.PutContainer(schema,false) // (snip) //Find a row in the collection container with row-key ('sid') "W_1003" retrieved, err := collection.Get("W_1003") if len(retrieved) > 0 { //Access and display longitude column of row (is column 4 in row) fmt.Printf("Longitude value of row with row-key %s is %f\n","W_1003",retrieved[4] } //We can also remove the "W_1003" row by calling .remove() collection.Remove("W_1003") //Attempt to access row again, this should return an empty row (an empty array) if the removal was successful attempt, err := collection.Get("W_1003") if len(attempt) == 0 { fmt.Printf("Row with key %s removed successfully\n","W_1003") }
データのクエリと取得
コンテナにデータが格納され、GridDBに挿入されると、データのクエリとフェッチが可能になります。 PythonやJava APIと同様に、コンテナに発行したいTQLクエリを使って Queryオブジェクト(または griddb_go.Query
型)を構築するだけです。 これが完了したら、クエリの結果を取得して griddb_go.RowSet
オブジェクトに格納します。
- 注:更新する特定の行を選択する場合は、更新しようとしているコンテナのコミットモードを false に設定してください。
griddb_go.Container.Put()
またはgriddb_go.RowSet.Update()
メソッドを使用して行の値を更新し終えたら、変更をCommit()
します。
標準TQLクエリ
//Set commit-mode to false so you can update the rows collection.SetAutoCommit(false) //Search for a row with row-key that ends in '2' and has a column value of 'weather' in the type column query,err := collection.Query("select * WHERE CHAR_LENGTH(sid) = 6 AND sid LIKE 'W%2' AND LOWER(type) = 'weather'") //Fetch the rows and make them available for update rowSet,err := query.Fetch(true) for rowSet.HasNext(){ //Retrieve and iterate through the rowset row-by-row rrow, err := rowSet.NextRow() if err != nil{ fmt.Println("Error retrieving row") } //Update the fourth column value of the row rrow[4] = -88.0 //Save updated row back to the rowSet rowSet.Update(rrow) } //Commit and save the updated RowSet back to the container collection.Commit()
集計TQL
また、 TQL を使用して集約クエリを作成し発行することもできます。 コンテナからgriddb_go.AggregationResult
を実行してフェッチする方法は、Python APIによく似ています。 rowSet.NextRow()
を使わなくても、.NextAggregation()
に変更するだけでクエリから取得した RowSet を取得することができます。 その AggregationResult
オブジェクトでは、必要な番号の型を取得するために.Get()
を使用します。
column := "turbidity" aggQuery, err := timeseries.Query(fmt.Sprintf("SELECT TIME_AVG(%s)",column)) //Get the time-weighted average of all turbidity values in our container if err != nil { fmt.Printf("Failed to issue query to timeseries container %s\n",container_name) fmt.Println(err) os.Exit(2) } aggRowSet, err := aggQuery.Fetch(false) //Issue query and fetch results if err != nil { fmt.Printf("Error retrieving query results from timeseries container %s\n",container_name) fmt.Println(err) os.Exit(2) } for aggRowSet.HasNext(){ aggregation, err := aggRowSet.NextAggregation() //Get the aggregation result from the rowset. if err != nil { fmt.Println("Failed to get aggregation result!") } else { avg, err := aggregation.Get(griddb_go.TYPE_DOUBLE) //Get the aggregation result's numerical value if err != nil { fmt.Printf("Error parsing aggreagtion result\n") } fmt.Printf("The time average for the %s column in the %s container is %f\n",column,container_name,avg) } }
タイムスタンプとバイナリデータの処理
タイムスタンプデータ
GridDBのGo言語clientは、タイムスタンプ操作のための他のAPI client(Python、C)と同じ機能を提供します。 GridDBでは時刻または日付の値をGridDBコンテナに挿入するために、その値をGo言語で Time.time
型にキャストする必要があることに注意してください。
つまり、タイムスタンプ値を純粋な数値として挿入することはできません。 タイムスタンプ値が行キー列にあり、データがコンテナに挿入されない場合、container.Put(row)
の呼び出しができません。
もう一つの注意点は、GridDBタイムスタンプがミリ秒で精度を持つことです。
GridDBタイムスタンプ番号をタイプ time.Time
に変換するには、タイムスタンプ番号をナノ秒に変換し、 time.Unix ()
を使用してtime型に変換してください。
import ( "time" "github.com/griddb/go_client" ) // (snip) //A way to express the current time as a time Type in Golang now := time.Now().UTC() //This has less precision than GridDB TimestampUtils // GridDB method of getting the current time as a number now_ts := time.Unix(griddb_go.TimestampUtilsCurrent() * 1000000)// The number is then converted into a UTC time // Converting GridDB to Time.time values allows for precision and lowers the chance that // a row will inadvertently replace another row with the same row-key. row := []interface{}{now_ts.UTC(),true,5.6} err = timeseries.Put(row)
バイナリまたはBlobデータ
GridDBでは、バイナリまたは BLOB データは、 bytearray
として表すことができます。 画像データや大きなバッファを読み込まなければならない場合は、空のバイト配列または [] byte
を割り当ててください。
parseBlob の下の関数は、画像データを bytearray に読み込みます。
func parseBlob(filename string) (blob []byte){ file, err := os.Open(filename) //Open image file if err != nil { fmt.Printf("Error opening file %s\n",filename) fmt.Println(err) os.Exit(1) } defer file.Close() fileInfo, _ := file.Stat() //Get meta-information related to the image file var size int64 = fileInfo.Size() //Obtain file size outputStream := make([]byte,size) //Allocate a byte array that is the size of the image's data buffer := bufio.NewReader(file) //Create a file reader for reading byte data from image file _, err = buffer.Read(outputStream) //Read image file's data into output byte array return outputStream }
これにより、 parseBlob
のデータを BLOB 列の値に使用することができるようになります。
image_blob := parseBlob("liveimage1.jpg") //Get bytearray for image file's data now := time.Now().UTC() //Obtain current time //Create a Row containing our BLOB data (byte array) and insert it into Timeseries container first_row := []interface{}{now,"Bright Sky",500,"liveimage1.jpg",image_blob) err = timeseries.Put(first_row) blob := []byte{65, 66, 67, 68, 69, 70, 71, 72, 73, 74} //Create Blob as a simple byte array third_row := []interface{}{time.Now().UTC(),"Generic",10,"NULL",blob} //Create and insert GridDB row containing smaller byte array err = timeseries.Put(third_row)
結論
Go言語を使用してGridDBクラスタを接続、管理するのは非常に簡単であることがお分かりいただけたでしょうか。 GridDB Go clientが提供するさまざまなユーティリティをすべて使用することで、GridDBを使ったデータの保存や高性能Goアプリケーションの開発は、とても面白いものとなります。
参照
- CentOS 7.3オペレーティングシステム上で、Go言語 version 1.9.2 linux/amd64を使用しました。
- GridDBデータベースは、GridDB Community Edition 3.0.1 を使用しました。
- GridDB Go clientのソースコードについては、official Github repositoryをご参照ください。
- このリファレンスページ に、Go clientからアクセスできるGridDBのすべてのオブジェクトとリソースが載っています。
- GridDBのGo libraryが構築された後は、griddb_goパッケージで使用されるすべてのメソッドと属性は
src/griddb_go.go
ファイルに保存されます。 - Go言語のより詳しい使い方については、official documentationをご参照ください。
ソースコード
このブログで使用されているすべてのソースコードは、以下からダウンロードできます。
ブログの内容について疑問や質問がある場合は Q&A サイトである Stack Overflow に質問を投稿しましょう。 GridDB 開発者やエンジニアから速やかな回答が得られるようにするためにも "griddb" タグをつけることをお忘れなく。 https://stackoverflow.com/questions/ask?tags=griddb