このドキュメントでは、OBKV-Tableのクエリ機能について説明します。OBKV-Tableは、ポイントルックアップクエリ(主キーに基づくクエリ)と範囲クエリをサポートしています。範囲クエリでは、フィルター条件、スキャン順序、LIMIT条件などを指定できます。さらに、OBKV-Tableは簡単な集計機能もサポートしており、集計クエリでもフィルター条件、スキャン順序、LIMIT条件などを指定できます。
OBKV-TableはSQLのORDER BYに相当する機能をサポートしていません。ただし、データが主キー順に格納される特性を利用し、ベーステーブルまたはインデックステーブルを主キーの昇順または降順でスキャンできます。そのため、ScanOrderではソート対象の列を指定する必要はありません。現在、スキャン順序は主キー列の順序に限られます。
ポイントルックアップクエリ
インターフェース
ObTableClientがポイントルックアップクエリをサポートするインターフェースは以下のとおりです。
インターフェース |
説明 |
|---|---|
Get get(String tableName) |
主キーに基づいて1行のデータを取得する |
例
前提条件
クエリを実行する前に、以下の準備が必要です:
- 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型がサポートされています。詳細な説明と書き込み例については、OBKV-Table データ型を参照してください。
単一行のクエリ
この例では、主キーemployeeIdが1001の行からname列の値を取得します。
// データベースには既に以下のデータが存在するものと仮定します
/*
+------------+----------+------------+----------+----------+---------+
| 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列の値を取得します。
// データベースには既に以下のデータが存在するものと仮定します。
/*
+------------+----------+------------+----------+----------+---------+
| 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"));
}
範囲クエリ
インターフェース
ObTableClientが範囲検索機能を提供するインターフェースは以下のとおりです。
インターフェース |
説明 |
|---|---|
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) |
クエリのfilterを設定します。
|
はい |
TableQuery scanOrder(boolean forward) |
スキャンの順序を設定します。
|
はい |
TableQuery limit(int offset, int limit) |
クエリ結果セット内で、クライアントに返すレコードの範囲を設定します。
|
はい |
QueryResultSet execute() |
操作をデータベースに送信します。 | いいえ |
Filter
OBKV-TableでサポートされているFilterには、以下のものが含まれます:
- ObTableValueFilter
- ObTableFilterList
- ObTableInFilter
ObTableValueFilter
TableValueFilterは最も一般的に使用されるFilterで、データテーブル内の列値とユーザーが指定する値との関係に基づいてデータをフィルタリングします。OBKV-Tableでサポートされている演算関係は、ObCompareOp列挙型で定義されています。
enum ObCompareOp {
LT, // より小さい
GT, // より大きい
LE, // 以下
GE, // 以上
NE, // 等しくない
EQ, // 等しい
IS, // NULLである
IS_NOT // NULLではない
}
ObTableFilterList
ObTableFilterListは、ANDまたはORを使用して複数のFilterを連結し、単一の複合Filterを形成します。以下はObTableFilterList.Operatorの定義です:
enum ObTableFilterListOperator {
AND, // 論理積
OR // 論理和
}
ObTableInFilter
ObTableInFilterは、OBKV-Tableが提供するシンタクスシュガーであり、SQLのINと同様の意味を提供します:
- ObTableInFilterは本質的にObTableFilterListです。
- その各Filterは、すべて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"));
}
}
filterを使用したフィルタリング
この例では、複合主キー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"));
}
}
インデックスを使用したフィルタリング
この例では、インデックスidx_departmentIdを使用し、インデックスキーdepartmentIdのスキャン範囲を[1, 10]に設定して、name列を取得します。
注意
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) -- ユニークインデックスを作成するとクエリが高速化されます。 ) ORGANIZATION = HEAP PARTITION BY KEY(c1) PARTITIONS 97; -- ORGANIZATIONパラメータでヒープテーブルを指定します。
インデックスを使用したクエリの実行
この例では、インデックスidx_unique_c1を使用し、c1のスキャン範囲を[1000, 1003]に設定して、c1、c2、c3列を取得します。
private static void queryWithIndex(ObTableClient obkvHandle) throws Exception {
try {
// BatchOperationを使用した一括挿入
BatchOperation batch = client.batchOperation(tableName);
// 複数の挿入操作を追加
// パーティションキー c1 の値は、setPartitionKey を使用して設定する必要があります。
// c1 の値は 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");
// インデックスのスキャン範囲を [1000, 1003] と指定
// インデックス 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();
}
}