Google App Engine を語る上でデータストアは避けて通れません。 軽く復習しておきましょう。 データストアは関係データベースではなく、データ取得のためのクエリにも様々な制限があります。 大雑把な理解としては、 関係データベースでテーブルに相当するものはカインド、 行(レコード)に当たるのはエンティティ、 列(カラム)に当たるのはプロパティです。
複数のプロパティが絡むクエリーを発行する際には、それ用のインデックスが必要です。 これは Python でも Java でも同じです。 このインデックスを定義するファイルは、Python では index.yaml、 Java では datastore-indexes.xml になります。 (繰り返しになりますが実は Java でも yaml 設定ファイルを使うことができるようです。 Java App Configuration Using app.yaml
Java には JDO、JPA といった Java 標準に従った別系統の API もありますが、 ここでは取り上げません。 書籍「Slim3 on Google App Engine for Java」によると、 遅くて使えないそうです。
Python でのエンティティの格納
Python でエンティティを扱う場合、Model
クラスを継承してカインドごとにクラスを作成します。
Model
クラスは google.appengine.ext.db
にあります。
プロパティはクラス定義の際に、型に従った Property
クラスを設定します。
こうして作ったクラスのインスタンスに各プロパティの値を与え、
最後に put
メソッドを呼んでデータストアに格納します。
Java でのエンティティの格納
Java でエンティティを扱う場合、Entity
クラスを使います。
Entity
クラスは com.google.appengine.api.datastore
にあります。
Entity
クラスをインスタンス化する際に文字列としてカインド名を渡します。
プロパティの設定は setProperty
メソッドにプロパティ名とプロパティ値を渡します。
プロパティ値は Object
型なので、何でも入れられます。
最後に DatastoreService
インスタンスの put
メソッドにエンティティを渡してデータストアに格納します。
Python におけるクエリー
Python においてクエリーの構築には2種類の方法があります。 一つはモデルクラスを経由する方法、もう一つは GQL を使う方法です。
モデルクラスを経由する方法では、格納に利用した Model
を継承したクラスを使います。
これのクラスメソッド all
を呼び出すと無条件にそのカインドのエンティティを取得するクエリーが生成されます。
このクエリーに filter
メソッドで取得条件を追加したり、order
メソッドで順番を設定したりします。
GQL はこういった操作をせずに、SQL 風のコマンドを記述して一気に望みのクエリーを構築します。
どちらの作り方をしたクエリーも、最後に run
メソッドを呼ぶとイテレータを返すので、for
文などに渡して一つずつエンティティを取り出します。
Python コード例:
# クラス定義 class Person(Model): name = StringProperty() age = IntegerProperty() # 格納 person = Person(name="Guido", age=57) person.put() # 取り出し q = Person.all().fiter("age =", 57).order("+name") for person in q.run(): print person.name
Java におけるクエリー
Java では Query
にカインド名を渡して Python の all
呼び出し相当のクエリーを作ります。
フィルターの追加は Query.Filter
クラス(及びその派生クラス)を作って、setFilter
メソッドで設定します。
一方、順序の設定は直接 addSort
メソッドを使います。
こうして作ったクエリーを DatastoreService
インスタンスの prepare
メソッドで PreparedQuery
に変換し、最後に asIterable
メソッドを呼ぶことで Iterable
を返すので for each で一つずつエンティティを取り出します。
Java コード例:
// DatastoreService インスタンスの取得 DatastoreService datastore = DatastoreServiceFactory.getDatastoreService(); // 格納 Entity person = new Entity("Person"); person.setProperty("name", "Guido"); person.setProperty("age", 57); datastore.put(person); // 取り出し Filter f = new FilterPredicate("age", FilterOperator.EQUAL, 57); Query q = new Query("Person").setFilter(f).addSort("name", SortDirection.ASCENDING); PreparedQuery pq = datastore.prepare(q); for (Entity entity : pq.asIterable()) { System.out.println((String)entity.getProperty("name")); }
index.yaml と datastore-indexes.xml
上記サンプルで必要になるインデックスです。
index.yaml
indexes: - kind: Person ancestor: no properties: - name: age - name: name direction: asc
index.yaml は開発サーバーでアプリケーションを実行してみると自動で作られるので、 ほとんど手書きする機会はありません。
datastore-indexes.xml
<?xml version="1.0" encoding="utf-8"?> <datastore-indexes autoGenerate="true"> <datastore-index kind="Person" ancestor="false"> <property name="age" direction="asc" /> <property name="name" direction="asc" /> </datastore-index> </datastore-indexes>
datastore-indexes.xml の datastore-indexes タグの autoGenerate 属性は必須で、 true にしておくと開発サーバーで使ったインデックスが war/WEB-INF/appengine-generated/datastore-indexes-auto.xml に記録されていきます。ですので、ひと通りローカルでアプリケーションを実行したあと、 この内容を書き写せば OK です。必要なインデックスが揃ったら autoGenerate 属性を false に変えましょう。
次回予告
次回は各種サービスについて比較します。