本記事では、OBKV-Tableのクエリ機能について説明します。OBKV-Tableは、ポイントクエリ(主キーによるクエリ)と範囲スキャンをサポートしています。範囲スキャンを実行する際には、フィルター、スキャン順序、limit条件などを指定できます。また、OBKV-Tableは単純集計もサポートしており、集計クエリを実行する際も同様にフィルター、スキャン順序、limit条件などを指定できます。
OBKV-TableはSQLスタイルのORDER BYをサポートしていませんが、格納されたデータの自然な順序を利用して、主表またはインデックス表から主キーの昇順または降順で結果を読み取ることができます。ScanOrderを設定する際にはソート列を指定する必要はなく、現在は主キー列によるソートのみをサポートしています。
ポイントクエリ
API
ObTableClientは以下のポイントクエリAPIを提供します:
API |
説明 |
|---|---|
Get get(String tableName) |
主キーによる単一行データのクエリ |
例
前提条件
データをクエリする前に、以下の操作を完了してください:
ObTableClientを初期化します。データベースにリレーショナルテーブルを作成します。以下の例ではこのテーブルを使用します:
CREATE TABLE IF NOT EXISTS `employees` ( `employeeId` int NOT NULL, `name` varchar(100) NOT NULL, `department` varchar(50), `position` varchar(50), `workYear` int, `comment` varchar(100), PRIMARY KEY (`employeeId`) ) PARTITION BY KEY(`employeeId`) PARTITIONS 97;
補足:
- JSONをサポートしています。
単一行クエリ
以下の例では、主キーemployeeIdが1001の行データを1行クエリし、name列を返します。
// Assume the following data already exists in the database
/*
+------------+----------+------------+----------+----------+---------+
| employeeId | name | department | position | workYear | comment |
+------------+----------+------------+----------+----------+---------+
| 1001 | zhangsan | R&D | dev | 10 | NULL |
+------------+----------+------------+----------+----------+---------+
*/
private static void getRow(ObTableClient obkvHandle) throws Exception {
Row rowKey = row().add(colVal("employeeId", 1001));
Map<String, Object> res = obkvHandle.get("employees")
.setRowKey(rowKey)
.select("name")
.execute();
System.out.println(res.get("name"));
}
バッチクエリ
以下の例では、主キーemployeeIdがそれぞれ1001と1002の2行のデータをクエリし、各行のname列を返します。
// Assume the following data already exists in the database
/*
+------------+----------+------------+----------+----------+---------+
| employeeId | name | department | position | workYear | comment |
+------------+----------+------------+----------+----------+---------+
| 1001 | zhangsan | R&D | dev | 10 | NULL |
| 1002 | lisi | R&D | dev | 10 | NULL |
+------------+----------+------------+----------+----------+---------+
*/
private static void batchGetRow(ObTableClient obkvHandle) throws Exception {
Row rowKey1 = row().add(colVal("employeeId", 1001));
Row rowKey2 = row().add(colVal("employeeId", 1002));
Get getOp1 = get().setRowKey(rowKey1).select("name");
Get getOp2 = get().setRowKey(rowKey2).select("name");
BatchOperationResult res = obkvHandle.batchOperation("employees")
.addOperation(getOp1,getOp2)
.execute();
System.out.println(res.size());
System.out.println(res.get(0).getOperationRow().get("name"));
System.out.println(res.get(1).getOperationRow().get("name"));
}
範囲スキャン
API
ObTableClient は、以下の範囲スキャン API を提供します:
API |
説明 |
|---|---|
TableQuery query(String tableName) |
指定された範囲内のデータを照会します |
よく使われる TableQuery メソッド
メソッド |
説明 |
必須かどうか |
|---|---|---|
TableQuery select(String... columns) |
クライアントに返す列。省略した場合、すべての列を返します。
|
はい |
TableQuery indexName(String indexName) |
このクエリで使用するインデックス。 | はい |
TableQuery addScanRange(Object[] starts, boolean startEquals, Object[] ends, boolean endEquals) |
クエリの主キー範囲。
|
はい |
TableQuery setFilter(ObTableFilter filter) |
クエリで使用するフィルター。
|
はい |
TableQuery scanOrder(boolean forward) |
スキャンの方向。
|
はい |
TableQuery limit(int offset, int limit) |
クライアントに返す結果セットの行範囲。
|
はい |
QueryResultSet execute() |
データベースに操作を送信します。 | いいえ |
フィルター
OBKV-Table は以下のフィルターをサポートしています:
- ObTableValueFilter
- ObTableFilterList
- ObTableInFilter
ObTableValueFilter
TableValueFilter は最も一般的に使用されるフィルターで、列値と指定値との関係に基づいて行をフィルタリングできます。サポートされている比較演算子は ObCompareOp 列挙で定義されています。
enum ObCompareOp {
LT, // less than
GT, // greater than
LE, // less than or equal to
GE, // greater than or equal to
NE, // not equal to
EQ, // equal to
IS, // is null
IS_NOT // is not null
}
ObTableFilterList
ObTableFilterList は AND または OR を使用して複数のフィルターを組み合わせて複合フィルターを作成します。ObTableFilterList.operator 列挙は以下のように定義されています:
enum ObTableFilterListOperator {
AND, // logical AND
OR // logical OR
}
ObTableInFilter
ObTableInFilter は OBKV-Table が提供するシンタクティカルシュガーで、その意味は SQL の IN と類似しています:
ObTableInFilterは本質的にObTableFilterListです。- リスト内の各フィルターは
ObCompareOp::EQを使用したObTableValueFilterです。
インデックスの編成例
前提条件
データを照会する前に、以下の操作を完了してください:
ObTableClientを初期化します。データベースにヒープ組織のテーブルを作成します。例:
CREATE TABLE IF NOT EXISTS `employees` ( `employeeId` int NOT NULL, `name` varchar(100) NOT NULL, `department` varchar(50), `position` varchar(50), `workYear` int, `comment` varchar(100), PRIMARY KEY (`employeeId`) ) PARTITION BY KEY(`employeeId`) PARTITIONS 97;
スキャン範囲フィルターの使用
以下の例では、範囲スキャンを実行します。範囲列は主キーのemployeeIdとdepartmentIdで、スキャン範囲は[{1000, 1},{1010, 1}]です。クエリはname列を返します。
private static void queryWithScanRange(ObTableClient obkvHandle) throws Exception {
TableQuery query = obkvHandle.query("employees");
QueryResultSet res = query.setScanRangeColumns("employeeId","departmentId")
.addScanRange(new Object[] { 1000, 1 }, new Object[] { 1010, 1 })
.select("name")
.execute();
while (res.next()) {
System.out.println(res.getRow().get("name"));
}
}
フィルターの使用
以下の例では、範囲スキャンを実行します。範囲列は主キーのemployeeIdとdepartmentIdで、スキャン範囲は[{1000, 1},{1010, 1}]です。クエリはname列を返します。同時にObTableValueFilterを適用し、workYearが3以上の行のみを返します。
private static void queryWithFilter(ObTableClient obkvHandle) throws Exception {
TableQuery query = obkvHandle.query("employees");
ObTableValueFilter filter = compareVal(ObCompareOp.GE, "workYear", 3);
QueryResultSet res = query.setScanRangeColumns("employeeId","departmentId")
.addScanRange(new Object[] { 1000, 1 }, new Object[] { 1010, 1 })
.setFilter(filter)
.select("name")
.execute();
while (res.next()) {
System.out.println(res.getRow().get("name"));
}
}
インデックスを使用したクエリ
以下の例では、範囲スキャンを実行します。範囲列はインデックスキーのdepartmentIdで、スキャン範囲は[1, 10]です。クエリはname列を返し、インデックスidx_departmentIdを使用します。
注意
OBKV-Tableでは、セカンダリインデックスにはパーティションキーを含める必要があります。これにより、セカンダリインデックスのスキャン範囲を指定する際にパーティションキーの範囲を特定できます。
private static void queryWithIndex(ObTableClient obkvHandle) throws Exception {
TableQuery query = obkvHandle.query("employees");
QueryResultSet res = query.setScanRangeColumns("departmentId")
.addScanRange(new Object[] { 1 }, new Object[] { 10 })
.indexName("idx_departmentId")
.select("name")
.execute();
while (res.next()) {
System.out.println(res.getRow().get("name"));
}
}
ヒープ組織の例
前提条件
データをクエリする前に、以下の操作を完了してください:
ObTableClientを初期化します。データベースにヒープ組織のテーブルを作成します。例:
CREATE TABLE test ( c1 bigint, c2 varchar(20), c3 bigint, UNIQUE INDEX idx_unique_c1 (c1) -- Creating a unique index can speed up queries ) ORGANIZATION = HEAP PARTITION BY KEY(c1) PARTITIONS 97; -- Use the ORGANIZATION parameter to specify a heap table
インデックスを使用したクエリ
以下の例では、範囲スキャンを実行します。範囲列は主キーのc1で、スキャン範囲は[1000, 1003]です。クエリはc1、c2、c3列を返し、インデックスidx_unique_c1を使用します。
private static void queryWithIndex(ObTableClient obkvHandle) throws Exception {
try {
// Use BatchOperation for batch insert
BatchOperation batch = client.batchOperation(tableName);
// Add multiple insert operations
// You must set the partition key c1 via setPartitionKey
// You must also set c1 via addMutateColVal
for (int i = 1; i <= 3; i++) {
Insert insert = new Insert();
insert.setPartitionKey(row(colVal("c1", (long)(1000 + i))))
.addMutateColVal(colVal("c1", (long)(1000 + i)), colVal("c2", "batch_test"), colVal("c3", (long)(3000 + i)));
batch.addOperation(insert);
}
batch.execute();
System.out.println("Batch insert successful: 3 operations executed");
// Specify index scan range [1000, 1003]
// Specify index idx_unique_c1
TableQuery query = client.query(tableName)
.addScanRange(1000L, 1003L)
.setScanRangeColumns("c1")
.indexName("idx_unique_c1")
.select("c1", "c2", "c3");
QueryResultSet res = query.execute();
int count = 0;
while (res.next()) {
count++;
Map<String, Object> valueMap = res.getRow();
long c1 = (long)valueMap.get("c1");
String c2 = (String)valueMap.get("c2");
long c3 = (long)valueMap.get("c3");
assertEquals("c1 should match", c1 >= 1000 && c1 <= 1003, true);
assertEquals("c2 should match", c2.equals("batch_test"), true);
assertEquals("c3 should match", c3 >= 3000 && c3 <= 3003, true);
}
assertEquals("Should have 3 records", 3, count);
} finally {
truncateTable();
}
}