Tablestore は、クエリ結果をページングするための複数の方法を提供する分散ストレージシステムです。このトピックでは、クエリ結果をページングする方法について説明します。
検索インデックスのないテーブルからのクエリ結果のページング
テーブルに検索インデックスが作成されていない場合は、次のいずれかの方法を使用して、テーブルからクエリ結果をページングできます。
テーブルのプライマリキーに基づいてデータをクエリする場合、行の総数またはページの総数を取得することはできません。
nextStartPrimaryKey パラメーターを使用します。 nextStartPrimaryKey パラメーターは、各 GetRange リクエストのレスポンスで返されます。このパラメーターの値をページングトークンとして使用し、次のリクエストでページングトークンを指定して、新しいページの結果を取得できます。
GetRangeIterator を使用します。
iterator.next()
メソッドを呼び出して、データの次の行を読み続けることができます。
テーブルのプライマリキーに基づいてデータをクエリする場合、オフセットを指定して特定のページのクエリ結果を取得することはできません。ただし、クライアントで nextStartPrimaryKey パラメーターまたは iterator.next() メソッドを使用して、ページクエリをシミュレートできます。
検索インデックスのあるテーブルからのクエリ結果のページング
テーブルに検索インデックスが作成されている場合は、次のいずれかの方法を使用して、テーブルからクエリ結果をページングできます。
検索インデックスを使用してデータをクエリする場合、行の総数とページの総数を取得できます。行の総数を取得するには、getTotalCount リクエストパラメーターを true に設定します。行の総数を limit パラメーターの値で割ると、ページの総数になります。 getTotalCount パラメーターを true に設定すると、データのクエリ時により多くのリソースが消費されます。そのため、クエリのパフォーマンスに影響します。
offset パラメーターと limit パラメーターを使用します。この方法を使用して、クエリ結果で取得する行を指定できます。 limit パラメーターと offset パラメーターの値の合計は 100,000 を超えることはできません。合計が 100,000 を超える場合は、next_token パラメーターを使用してクエリ結果をページングします。
next_token パラメーターを使用します。 next_token パラメーターは、各 Search リクエストのレスポンスで返されます。このパラメーターの値をページングトークンとして使用し、次のリクエストでページングトークンを指定して、新しいページの結果を取得できます。
SearchIterator を使用します。
iterator.next()
メソッドを呼び出して、データの次の行を読み続けることができます。
検索インデックスのないテーブルに対するページクエリの実装例
次のサンプルコードは、offset パラメーターに基づいて行をスキップし、指定されたページサイズに基づいて特定の数の行を読み取るメソッドの実装を示しています。
/**
* プライマリキー値が特定の範囲内にあるデータをクエリし、指定されたページサイズに基づいて特定の数の行を返し、offset パラメーターに基づいて行をスキップします。
*/
private static Pair<List<Row>, PrimaryKey> readByPage(SyncClient client, String tableName,
PrimaryKey startKey, PrimaryKey endKey, int offset, int pageSize) {
Preconditions.checkArgument(offset >= 0, "オフセットは負の値であってはなりません。");
Preconditions.checkArgument(pageSize > 0, "ページサイズは0より大きくなければなりません。");
List<Row> rows = new ArrayList<Row>(pageSize);
int limit = pageSize;
int skip = offset;
PrimaryKey nextStart = startKey;
// 1つのリクエストで返せない大量のデータをクエリする必要がある場合は、複数のリクエストを送信する必要があります。
while (limit > 0 && nextStart != null) {
// GetRange 操作のクエリパラメーターを作成します。
// 注:startPrimaryKey パラメーターは、最後のクエリが停止した位置に設定する必要があります。これにより、複数の範囲クエリリクエストを送信して残りのデータをクエリできます。
RangeRowQueryCriteria criteria = new RangeRowQueryCriteria(tableName);
criteria.setInclusiveStartPrimaryKey(nextStart);
criteria.setExclusiveEndPrimaryKey(endKey);
criteria.setMaxVersions(1);
// limit パラメーターを正しく指定します。この例では、limit パラメーターは、1ページで返される行の最大数と、offset パラメーターに基づいてスキップする必要がある行の数の合計に設定されています。
criteria.setLimit(skip + limit);
GetRangeRequest request = new GetRangeRequest();
request.setRangeRowQueryCriteria(criteria);
GetRangeResponse response = client.getRange(request);
for (Row row : response.getRows()) {
if (skip > 0) {
skip--; // データが読み取られた後、クライアントで offset パラメーターによって指定された位置より前のデータをスキップします。
} else {
rows.add(row);
limit--;
}
}
// 次のクエリが開始される位置を設定します。
nextStart = response.getNextStartPrimaryKey();
}
return new Pair<List<Row>, PrimaryKey>(rows, nextStart);
}
次のサンプルコードは、プライマリキー値が特定の範囲内にあるすべてのデータをページ単位で順番に読み取るメソッドの実装を示しています。
private static void readByPage(SyncClient client, String tableName) {
int pageSize = 8;
int offset = 33;
PrimaryKeyBuilder primaryKeyBuilder= PrimaryKeyBuilder.createPrimaryKeyBuilder();
primaryKeyBuilder.addPrimaryKeyColumn("gid", PrimaryKeyValue.INF_MIN);
primaryKeyBuilder.addPrimaryKeyColumn("uid", PrimaryKeyValue.INF_MIN);
PrimaryKey startKey = primaryKeyBuilder.build();
primaryKeyBuilder= PrimaryKeyBuilder.createPrimaryKeyBuilder();
primaryKeyBuilder.addPrimaryKeyColumn("gid", PrimaryKeyValue.INF_MAX);
primaryKeyBuilder.addPrimaryKeyColumn("uid", PrimaryKeyValue.INF_MAX);
PrimaryKey endKey = primaryKeyBuilder.build();
// 範囲内の33行目から最初のページを読み取ります。これは、offset パラメーターが33に設定されているためです。
Pair<List<Row>, PrimaryKey> result = readByPage(client, tableName, startKey, endKey, offset, pageSize);
for (Row row : result.getFirst()) {
System.out.println(row);
}
System.out.println("行の総数: " + result.getFirst().size());
// プライマリキー値が特定の範囲内にあるすべてのデータを順番に読み取り、クエリ結果をページングします。
startKey = result.getSecond();
while (startKey != null) {
System.out.println("============= 次のページの読み取り開始 ==============");
result = readByPage(client, tableName, startKey, endKey, 0, pageSize);
for (Row row : result.getFirst()) {
System.out.println(row);
}
startKey = result.getSecond();
System.out.println("行の総数: " + result.getFirst().size());
}
}
検索インデックスのあるテーブルに対するページクエリの実装例
詳細については、ソートとページング を参照してください。