インデックスを作成してソート条件を複数指定する


以前本ブログの記事(クエリを用いて検索条件を指定する)にて、Google App Engine(以後GAE)でソート条件を指定してデータストアからデータを取得する方法について解説しました。

今回はその応用として、ソート条件を複数指定してデータの取得を行いたいと思います。
(クエリを用いて検索条件を指定する)を読まれた方なら、ソート条件を複数設定するならQueryクラスのaddsort()メソッドを2つ設定するだけでよいのでは?と思われるかもしれません。
しかし実際には、addSort()メソッドを2つ設定するだけでは、クエリがエラーになってしまい、データが取得できません。
ソート条件を複数設定するには、addSort()メソッドを2つ設定する以外にもう一つ、インデックスを作成しておく必要があります。

下図はインデックスを利用したデータ取得のイメージです。こちらについても記事中で解説していきます。

それでは続きをどうぞ

インデックスについて

データストアの検索は、インデックスに基づいて行われます。
インデックスとは、ランダムに並んだデータテーブルからデータを検索してくるのではなく、検索が行われる前からあらかじめ特定の条件での並び順を定義しておき、実際に検索を行う際、その並び順を利用してデータを取得することで、検索を効率的に行う仕組みです。
以下はそのイメージ図で、KindテーブルをNoプロパティーによってあらかじめソートした順番を定義しておき、実際にクエリが行われると、定義しておいた順番でエンティティーを返します。

今回は、以下のようなカインドをあらかじめ用意し、nameプロパティーとageプロパティーでソートした際のインデックスを作成します。

カインド名はBatchPut(複数のEntityをまとめて保存するで作成したものを利用しています)、プロパティーは”age”、gender””、”name”の3つ、エンティティーは14個保存されています。

インデックスを作成する

インデックスを作成する前に、まずは今回データストアにクエリを行うソースコードを見ておきます。
ポイントはソートの指定を行う部分です。

IndexServlet.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public class IndexServlet extends HttpServlet {
 
    DatastoreService dss = DatastoreServiceFactory.getDatastoreService();
    public void doGet(HttpServletRequest req, HttpServletResponse resp)
    throws IOException {
 
        Velocity.init();
 
        Query q = new Query("BatchPut");
        // ソートを2つ指定する
        q.addSort("age", SortDirection.ASCENDING);
        q.addSort("name", SortDirection.ASCENDING);
 
        PreparedQuery pq = dss.prepare(q);
 
        List sortedList = new ArrayList();
        for(Entity entity : pq.asIterable()){
                String name = entity.getProperty("name").toString();
                    String age = entity.getProperty("age").toString();
                    String gender = entity.getProperty("gender").toString();
                    String str = name + "/" + age +  "/" + gender;
                    sortedList.add(str);
        }
 
        VelocityContext vContext = new VelocityContext();
            vContext.put("queryList", sortedList);
 
            resp.setContentType("text/html");
            resp.setCharacterEncoding("utf-8");
 
            Template template = Velocity.getTemplate("WEB-INF/querydisp.vm", "Shift-JIS");
            template.merge(vContext, resp.getWriter());
    }
}

ほとんどの部分は、クエリを用いて検索条件を指定するで解説したものと同様なのでここでは詳細な解説は省略します。
今回重要なのは11行目、12行目でクエリにソートを2つ指定している部分です。
今回のサンプルでは、ageプロパティーで昇順nameプロパティーで昇順の2つのソートを指定しています。
一度このままデプロイして実行してみてください。
ソートを2つ指定しているためにクエリがエラーとなるはずです。
ソートを2つ指定する場合には、新たにインデックスを作成する必要があります。

それでは、インデックスを作成していきます。
インデックスの指定は、WEB-INF/の下にあるdatastore-indexes.xml(なければ作成してください)で行います。
datastore-indexes.xmlでは、ソートしたいプロパティーと、昇順/降順を指定します。

datastore-indexes.xml

1
2
3
4
5
6
7
8
<!--?xml version="1.0" encoding="utf-8"?-->
<?xml version="1.0" encoding="utf-8"?>
<datastore-indexes autoGenerate="true">
    <datastore-index kind="BatchPut" ancestor="false">
        <property name="age" direction="asc"/>
        <property name="name" direction="asc"/>
    </datastore-index>
</datastore-indexes>

2行目のdatastore-indexesタグ内にインデックスを作成します。
3行目のdatastore-indexタグ内に、作成するインデックスの条件を指定します。
4行目、propertyタグname属性で、ソートしたいプロパティー名(ここではageを指定)を指定します。
direction属性で、ソートの順番(昇順/降順)を指定します(ここでは昇順を指定)。
5行目、4行目同様、ソートしたいプロパティー(name)と、ソート順(昇順)を指定します。

あとはアプリケーションをデプロイすると、インデックスが作成されます。
インデックスが作成されていることは、DashboardのDatastore Indexesで確認できます。

実際に検索が行われると、作成されたインデックスに基づいて検索結果を返します。

それでは実行して確認してみます。先ほどはエラーとなり検索結果を取得できませんでしたが、今回は下図のような結果になります。

実行結果は、”name/age/gender”の形式で表示されています。
ageが昇順に並んでいるのが確認できます。
また、nameもアルファベット順に並んでいるのが確認できると思います。

ソートをしなかった場合の結果(下図)と比較すると、順番が入れ替わっているのがわかります。

このように、インデックスを用いることで、より柔軟な条件で効率的な検索を行うことができます。