3. Query検索
キー検索のほかに、App Engineでは、Queryを使用した条件検索が可能です。ただし、App Engineの条件検索はRDBに比べて制約が多く、この点が分散キー・バリュー・ストアの欠点として評価される場合が多いようです。例えば、JOIN検索ができない、集計関数が使用できない、などが挙げられます。
このように、条件検索についてはマイナスのイメージでとらえられることが多いApp Engineですが、実際どの程度の検索・参照が可能なのでしょうか。以下では具体例を挙げて説明します。
[App Engineで検索パターン]
- Query(String kind)
- Query(String kind, Key , ancestorKey)
- Query(Key ancestorKey)
- Query()
App EngineにおけるQuery検索は、上記の4種類のパターンで行われます。実際には、1.または2.のパターンが多いはずです。以下からは、この順番で、それぞれの検索例を解説します。
3.1. Query(String kind)
Queryの引数にkind名を指定するパターンは、条件検索で最も多く使われる一般的なパターンです。つまり、テーブル(kind)と検索条件を指定して参照するパターンです。
(1)全件検索(revAllOrdTran1)
【リスト3】: 「Query(String kind)」パターンでの全件検索
03 | public String revAllOrdTran1() { |
05 | Query query = new Query( "ordTran" ); |
06 | List<Entity> otran = ds.prepare(query).asList( |
07 | FetchOptions.Builder.withOffset( 0 )); |
08 | for (Entity o : otran) { |
09 | String shopno = (String) o.getProperty( "shopno" ); |
10 | String itemName = (String) o.getProperty( "itemName" ); |
11 | String cpu = (String) o.getProperty( "cpu" ); |
12 | String os = (String) o.getProperty( "os" ); |
13 | String memory = (String) o.getProperty( "memory" ); |
14 | String price = o.getProperty( "price" ).toString(); |
15 | String orddate = o.getProperty( "orddate" ).toString(); |
16 | out += "{\"shopno\": \"" + shopno + "\", \"itemName\": \"" + itemName + "\", \"cpu\": \"" + cpu + "\", \"os\": \"" + os + "\", \"memory\": \"" + memory + "\", \"price\": \"" + price + "\", \"orddate\": \"" + orddate + "\"}," ; |
最初に全件検索ですが、この検索では、
- (1)で、検索対象のテーブル(kind)を指定してQueryインスタンスを生成し、
- (2)で、検索結果をエンティティ・リストで返しますが、込み入った書式なので、その内容を確認します。
- ds.prepare(query)
DatastoreServiceのインスタンスであるdsに適用するメソッドprepare(query)は、実行するクエリーを準備します。
- asListは、引数内で生成される配列をListに変換して返します。
- FetchOptions.Builderはstaticクラスであり、Fetch(検索)用のstaticメソッドを指定して実行します。
- withOffset(0)は、データ・ストア(kind)検索において、先頭エンティティから値を返します。

|
画面5: revAllOrdTran1での検索結果(クリックで拡大) |
画面5は、revAllOrdTran1の検索結果です。例えば、withOffset(1)を指定した場合は、2番目のエンティティ項目から値が返されます。つまり、withOffsetの「引数値-1」番目のエンティティから検索データが返されます。
DWRのメソッド実行画面で確認した場合、withOffset(0)では画面6のように、withOffset(1)では画面7のように、先頭部分が表示されます。

|
画面6: withOffset(0)でのDWR実行表示(クリックで拡大) |

|
画面7: withOffset(1)でのDWR実行表示 |
その後、for文で取得したList形式のotranから、表示データを作成します。getPropertyにおけるプロパティ値取得は、これまでと同じです。
(2)イコール条件(revOrdTran1)
【リスト4】: イコール条件追加でのエンティティ検索
03 | DatastoreService ds = DatastoreServiceFactory.getDatastoreService(); |
05 | public String revOrdTran1() { |
07 | Query query = new Query( "ordTran" ); |
08 | query.addFilter( "kind" , Query.FilterOperator.EQUAL, "desktop" ); |
09 | List<Entity> otran = ds.prepare(query).asList( |
10 | FetchOptions.Builder.withOffset( 0 )); |
11 | for (Entity o : otran) { |
12 | String shopno = (String) o.getProperty( "shopno" ); |
13 | String itemName = (String) o.getProperty( "itemName" ); |
14 | String cpu = (String) o.getProperty( "cpu" ); |
15 | String os = (String) o.getProperty( "os" ); |
16 | String memory = (String) o.getProperty( "memory" ); |
17 | String price = o.getProperty( "price" ).toString(); |
18 | String orddate = o.getProperty( "orddate" ).toString(); |
19 | out += "{\"shopno\": \"" + shopno + "\", \"itemName\": \"" + itemName + "\", \"cpu\": \"" + cpu + "\", \"os\": \"" + os + "\", \"memory\": \"" + memory + "\", \"price\": \"" + price + "\", \"orddate\": \"" + orddate + "\"}," ; |
App Engineの条件検索指定で「フィルタオペレータ」を使います。リスト4の(1)と(3)は、リスト3と同じですが、(2)でフィルタ指定(query.addFilter)を行っています。ここでは、プロパティkind*1に対して「equality filter」が適用され、kind名が「desktop」のエンティティだけが選択されます。
- [*1] App Engineではテーブル名をkindと呼ぶのでまぎわらしいですが、ここでのkindはプロパティ名です。
なお、App Engineで使用できるフィルタオペレータには、以下のようなものがあります。
- Query.FilterOperator.LESS_THAN
- Query.FilterOperator.LESS_THAN_OR_EQUAL
- Query.FilterOperator.EQUAL
- Query.FilterOperator.GREATER_THAN
- Query.FilterOperator.GREATER_THAN_OR_EQUAL
- Query.FilterOperator.NOT_EQUAL
- Query.FilterOperator.IN (equal to any of the values in the provided list)
(3)複数フィルタ(revOrdTran21)
【リスト5】:
03 | public String revOrdTran21() { |
05 | Query query = new Query( "ordTran" ); |
06 | query.addFilter( "shopno" , Query.FilterOperator.EQUAL, "akiba01" ); |
07 | query.addFilter( "price" , Query.FilterOperator.GREATER_THAN, 50000 ); |
08 | List<Entity> otran = ds.prepare(query).asList( |
09 | FetchOptions.Builder.withOffset( 0 )); |
フィルタは、複数指定することもできます。リスト5では、店番号(shopno)に「EQUAL」を、価格(price)に「GREATER_THAN」を指定して、秋葉原1号店で価格が5万円以上の販売実績に限って表示しています。