この章では、Google Cloud Datastoreの基礎と、 それがどれだけApp Engineアプリに最適なものであるかを学習します。
Cloud DatastoreはGoogleによって完全に管理された非リレーショナルデータベースであり、 スケーリング、サイズ変更、パッチ適用、またはいくつかの他の種類のメンテナンスについて心配する必要はありません。
ここ でCloud Datastoreの詳細を見つけられますが、 このチュートリアルではApp Engineアプリから構造化データを保存する最も簡単な方法としてのCloud Datastoreを検討します。
App EngineのGoランタイムは、webアプリケーションから使用したいと思う多くのサービスへアクセスさせる一連のパッケージを提供します。 そのうちの1つはCloud Datastoreであり、APIはとてもよく ドキュメント化 されています。
Datastoreの使用を開始する前に理解しておくべき3つの概念があります:
- エンティティ、
- カインド、そして、
- キー。
エンティティはデータストアに格納された値です。 これはリレーショナルデータベースに格納された行に多少似ていますが、 フィールドのリストとそれに対応する値を持っています。 しかしリレーショナルデータストアとは異なり、これらの行を記述するスキーマはなく、 各エンティティは異なるフィールドセットを持つことができます。
エンティティは与えられた カインド であり、リレーショナルデータベースの行と同じ方法で与えられたテーブルに属します。
前に使用した型 Person
を仮定すると、データストアに カインド Person
があると想像することができます。
データストア内のすべての値はキーに割り当てられて格納されます。 キーはデータストア内の値を識別して参照する手段です。 キーには2種類あります:
- 完全キー: 現にデータストア内の値を指します
- 不完全キー: 値がまだデータストア内にない場合に使用されます
短い時間で不完全キーの詳細を見てみます。
google.golang.org/appengine/datastore
パッケージは Put
関数を提供します:
func Put(c appengine.Context, key *Key, src interface{}) (*Key, error)
-
最初の引数は
appengine.Context
で、与えられたリクエストに関連するすべての操作をリンクする手段です。 -
二つ目の引数は
*datastore.Key
で、以下のコードスニペットで作成する方法で見ることができます。 -
そして、最後の引数は保存される値です。
-
この関数は別の
*datastore.Key
を返し、データの格納中に何らかのエラーが発生した場合は、nil以外のエラーを返します。
datastore.Put
関数の使い方の例を見てみましょう:
func completeHandler(w http.ResponseWriter, r *http.Request) {
// HTTPリクエストから新しいApp Engineコンテキストを作成する。
ctx := appengine.NewContext(r)
p := &Person{Name: "gopher", AgeYears: 5}
// Personカインドとgopher値の新しい完全キーを作成する。
key := datastore.NewKey(ctx, "Person", "gopher", 0, nil)
// データストアにpを設定する。
key, err := datastore.Put(ctx, key, p)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
fmt.Fprintf(w, "gopher stored with key %v", key)
}
このコードをローカルで実行し、ハンドラーが実行されるたびにキーの値が毎回同じであることを確認します: /Person,gopher
。
ご覧のように、datastore.NewKey
を呼び出して datastore.Key
を作成しています:
func NewKey(c appengine.Context, kind, stringID string, intID int64, parent *Key) *Key
完全キーを作成するときは、string
値(この例のように)または int64
を使用するかどうか選択する必要があります。
stringID
と intID
のフィールドのうち一つだけが空でなければなりません(""
or 0
)。
最後の引数は作成しているキーの親を指し示す *datastore.Key
です。
とりあえず常にnilを使用します。
私たちが使用したいと思う正確なキーを知らないとどうなりますか?
例えば Person
の異なる値は同じ名前と権利を持つことができ、
それらのキーは同じであるためお互いを上書きします。
解決策は自動生成されたキーを使用することで、これはまさに不完全キーのためのものです。
datastore.NewIncompletekey
でそれらを作成します:
func NewIncompleteKey(c appengine.Context, kind string, parent *Key) *Key
この関数には stringID
や intID
はなく、
キーの最終的な値としてデータストアに値を入れると決定されます。
最終的な値は datastore.Put
によって返されたキーを使って取得することができます。
func incompleteHandler(w http.ResponseWriter, r *http.Request) {
// HTTPリクエストから新しいApp Engineコンテキストを生成する。
ctx := appengine.NewContext(r)
p := &Person{Name: "gopher", AgeYears: 5}
// Personカインドの新しい不完全キーを生成する。
key := datastore.NewIncompleteKey(ctx, "Person", nil)
// データストアにpを設定する。
key, err := datastore.Put(ctx, key, p)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
fmt.Fprintf(w, "gopher stored with key %v", key)
}
アプリケーションでこのハンドラーを使用すると、生成されたキーは毎回異なるキーであり、 特定の順序に従わないことがわかります。
アプリケーションをローカルで実行している場合は、 http://localhost:8000/datastore にアクセスしてローカルデータストアの内容を調べることができます。
実稼働環境にデプロイされたApp EngineアプリのCloud Datastoreの内容を調査する場合は、 このページ をご覧ください。
Google Cloud Datastoreからデータを取得するには、主に2つの方法があります:
- キーを指定して値を取得する、または、
- フィルターを使用してデータストアにクエリーを実行する。
キーがある場合、Datastoreから値を取得することは datastore.Get
関数を呼び出すことと同じくらい簡単です:
func Get(c appengine.Context, key *Key, dst interface{}) error
最後の引数は取得するフィールドを含む構造体へのポインターでなければなりません。 たとえば、次のようにします:
func getHandler(w http.ResponseWriter, r *http.Request) {
ctx := appengine.NewContext(r)
key := datastore.NewKey(ctx, "Person", "gopher", 0, nil)
var p Person
err := datastore.Get(ctx, key, &p)
if err != nil {
http.Error(w, "Person not found", http.StatusNotFound)
return
}
fmt.Fprintln(w, p)
}
この関数には一度に複数の値を取得できるバージョンもあり、バッチ処理が可能な場合にパフォーマンスが向上します。
この関数は datastore.GetMulti
です:
func GetMulti(c appengine.Context, key []*Key, dst interface{}) error
非常に多くの場合、カインド のすべての値や、特定のフィールドが特定の値と等しいすべての値など、
条件に一致するデータストア内のすべての値を検索したい時があります。
これはSQLの SELECT
文に似ています。
それらはDatastoreでこれを行う手段として datastore.Query
型を使用しています。
datastore.Query
の値は datastore.NewQuery
で作成できます:
func NewQuery(kind string) *Query
ご覧のとおり、すべてのクエリーは特定の カインド に関連付けられています。 クエリーを取得したら、ビルダーパターンとこれらのメソッドのいくつかを使用してフィルターを追加できます:
func (q *Query) Ancestor(ancestor *Key) *Query
func (q *Query) Filter(filterStr string, value interface{}) *Query
func (q *Query) KeysOnly() *Query
func (q *Query) Limit(limit int) *Query
func (q *Query) Order(fieldName string) *Query
そして最後に、クエリーを実行してクエリーに一致する値を取得できます:
func (q *Query) Count(c appengine.Context) (int, error)
func (q *Query) GetAll(c appengine.Context, dst interface{}) ([]*Key, error)
func (q *Query) Run(c appengine.Context) *Iterator
- Countは、クエリーに一致した値の数を返します
- GetAllは、クエリーに一致したすべての値を
dst
で取得します - Runは、クエリーに一致するすべての結果を反復処理するために使用できる
*datastore.Iterator
を返します。
名前で並べ替えた10歳以下の カインド Person
のすべての値を取得する例を見てみましょう。
func queryHandler(w http.ResponseWriter, r *http.Request) {
ctx := appengine.NewContext(r)
var p []Person
// Personカインドで新しいクエリーを生成する。
q := datastore.NewQuery("Person")
// Ageフィールドが10以下の値だけを取得する。
q = q.Filter("Age <=", 10)
// 名前フィールドで全ての値を並べ替える。
q = q.Order("Name")
// 最後に全ての値をpで取得するクエリーを実行する。
_, err := q.GetAll(ctx, &p)
if err != nil {
// handle the error
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
fmt.Fprintln(w, p)
}
Filter
と Order
は *datastore.Query
を返します。
つまり、これらの操作をひとつの式に連結することができます。
q := datastore.NewQuery("Person").
Filter("Age <=", 10).
Order("Name")
ここ でDatastore Queryの詳細を見つけられます。
Datastoreについて(今日のうちに)知っておくべきことをすべて知ったので、 それを使用してイベントアプリケーションに保存されたデータを永続化してみましょう。
ステップ2 で作業し、完了したらここに戻ってきてください。
あなたはGoogle Cloud Datastoreからデータを保存したり取得したりできるようになりました。 これであなたが想像できるほとんどのアプリケーションを構築する準備が整いました!
それとも、もっと欲しいですか?
その場合は 次の章 に進んで urlfetch
を使用してリモートリソースにアクセスする方法を学習してください。