注:古い記事の為、内容が最新ではない可能性がありますm(_ _)m
どうもこんにちわ!マツウラです。
今回はGo言語版のDatastoreインターフェースにおいて、
Java,Pythonとは異なる特徴的な機能を見てゆこうと思います。
参考:The datastore package - Go — Google Developers The datastore package
エンティティの内容は一般的に構造体のポインタですが、PropertyLoadSaverインターフェースを実装することで任意の型で表現することができます。
構造体ポインタを使用する一般的な利用ではPropertyLoadSaverインターフェースは実装する必要はありません。
reflectionによって自動的に変換されるためです。
PropertyLoadSaverインターフェース
エンティティのコンテンツはPropertyLoadSaverインターフェースを実装することで任意の型で表現できます。
この型は構造体ポインタである必要はありません。
データストアパッケージはエンティティのコンテンツを取得する際にLoadメソッドを呼び出し、格納する際はSaveメソッドを呼び出します。
有効な使い方としては、格納しなかったフィールド値の設定、フィールドを検証する、または値が正しい場合にのみフィールドをインデックス付けする、などがあります。
次はPropertyLoadSaverインターフェースの実装例になります。
type CustomPropsExample struct { I int J int Slice []string Sum int `datastore:"-"` } func (x *CustomPropsExample) Load(c <-chan datastore.Property) error { err := datastore.LoadStruct(x, c) if err != nil { return err } x.Sum = x.I + x.J return nil } func (x *CustomPropsExample) Save(c chan<- datastore.Property) error { defer close(c) // Sumフィールドの検証 if x.Sum != x.I+x.J { return errors.New("CustomPropsExample has inconsistent sum.") } // スライス表現がなければdatastore.SaveStruct(x, c)でも良いです。 c <- datastore.Property{Name: "I", Value: int64(x.I)} c <- datastore.Property{Name: "J", Value: int64(x.J)} for _, v := range x.Slice { c <- datastore.Property{Name: "Slice", Value: v, Multiple: true} } return nil } func CuntomPropsHandler(w http.ResponseWriter, r *http.Request) { c := appengine.NewContext(r) e1 := &CustomPropsExample{ I: 3, J: 4, Slice: []string{"one", "two", "three"}, } e1.Sum = e1.I + e1.J key := datastore.NewKey(c, "CustomPropsExample", "custom", 0, nil) _, err := datastore.Put(c, key, e1) if err != nil { c.Errorf("%v", err) return } var e2 CustomPropsExample err = datastore.Get(c, key, &e2) if err != nil { c.Errorf("%v", err) return } fmt.Fprintf(w, "%d\n", e2.Sum) }
スライス表現を行うためにMultipleにtrueを渡していますが、正しいサンプルが見当たりませんでした。
動作はしましたが、これがベストかどうか少々怪しいです。^^;
次は上記の例で使用したメソッドや型についてです。
PropertyList
type PropertyList []Property
PropertyListはPropertyLoadSaverを実装するため[]Propertyに変換します。
func (l *PropertyList) Load(c <-chan Property) error func (l *PropertyList) Save(c chan<- Property) error
Loadはc
のプロパティ全てをl
にロードします。
その際最初に*l
が空のスライスにリセットされることはありません。
Saveはc
にl
のプロパティ全てを保存します。
#
func SaveStruct(src interface{}, c chan<- Property) error func LoadStruct(dst interface{}, c <-chan Property) error
SaveStructはcにsrcからプロパティを保存します(完了した時cを閉じます)。
LoadStructはdstにcからプロパティを読み込みます(cが閉じられるまで)。
srcおよびdstは構造体ポインタである必要があります。
PropertyLoadSaver
type PropertyLoadSaver interface { Load(<-chan Property) error Save(chan<- Property) error }
プロパティ配列<->プロパティと変換することができます。
Loadはエラーが発生しても、終了までチャンネルを開けておく必要があります。
Saveはエラーが発生しても、チャンネルが完了したら閉じる必要があります。
上記のコード例ではdeferを用いてこのSaveの条件を解決しています。
type Property struct { Name string Value interface{} Noindex bool Multiple bool }
Valueプロパティについて
Valueフィールドについては有効な型が制限されています。
これはデータストアで有効な値型よりもさらに少なくなっています。
次のリストが有効な型の一覧です。
- int64
- bool
- string
- float64
- *Key
- time.Time
- appengine.BlobKey
- appengine.GeoPoint
[]byte
(up to 1 megabyte in length)
これらの値のスライスは無効([]byteは別)になっており、
さらに独自の型を定義しても無効です。(type myInt64 int64のような型)
プロジェクションクエリを使用した際は、PropertyLoadSaverの代わりに構造体にエンティティがロードされます。
Noindexプロパティについて
データストアがこのプロパティをインデックス付け可能か否かです。
Multipleプロパティについて
プロパティにスライスで値を格納するか否かです。
Valueでは上記でも指摘した通りスライスが無効です。
しかし、コード例のようにすることでスライスの形式を取ることが可能になります。
今回はGo言語版のDatastoreインターフェースにおいて、
Java,Pythonとは異なる特徴的な機能、PropertyLoadSaverについて見てきました。
次回はエンティティのプロパティ関連でGo言語特有の機能と仕様を見てゆこうと思います。