obvec_jdbcは、OceanBaseのベクトルストレージシナリオとJSON Table仮想テーブルシナリオ向けに実装されたJava SDKです。本記事では、obvec_jdcbcの使用方法を紹介します。
インストール
obvec_jdbcは以下のいずれかの方法でインストールできます。
maven依存関係
プロジェクトの pom.xml ファイルにobvec_jdbcの依存関係を追加します。
<dependency>
<groupId>com.oceanbase</groupId>
<artifactId>obvec_jdbc</artifactId>
<version>1.0.6</version>
</dependency>
ソースコードからのインストール
obvec_jdbcをインストールします。
# obvec_jdbcリポジトリのクローン git clone https://github.com/oceanbase/obvec_jdbc.git # obvec_jdbcディレクトリへ移動 cd obvec_jdbc # obvec_jdbcのインストール mvn install依存関係を追加します
<dependency> <groupId>com.oceanbase</groupId> <artifactId>obvec_jdbc</artifactId> <version>1.0.6</version> </dependency>
APIの定義と使用
obvec_jdbcは、OceanBaseのベクトル検索機能とJSON Table仮想テーブル機能を操作するための ObVecClient オブジェクトを提供します。
ベクトル検索機能の使用
クライアントの作成
以下のインターフェース定義を用いて、ObVecClientオブジェクトを構築します:
# uri:接続文字列。アドレスとポート、接続するデータベース名などが含まれます
# user:ユーザー名
# password:パスワード
public ObVecClient(String uri, String user, String password);
例:
import com.oceanbase.obvec_jdbc.ObVecClient;
String uri = "jdbc:oceanbase://127.0.0.1:2881/test";
String user = "root@test";
String password = "";
String tb_name = "JAVA_TEST";
ObVecClient ob = new ObVecClient(uri, user, password);
ObFieledSchemaクラス
このクラスはテーブルの列Schemaを定義するために使用され、コンストラクターは以下のとおりです:
# name:列名
# dataType:列タイプ
public ObFieldSchema(String name, DataType dataType);
列タイプで現在サポートされているデータ型は以下のとおりです:
| DataType | 記述 |
|---|---|
| BOOL | TINYINTと同等 |
| INT8 | TINYINTと同等 |
| INT16 | SMALLINTと同等 |
| INT32 | INTと同等 |
| INT64 | BIGINTと同等 |
| FLOAT | FLOATと同等 |
| DOUBLE | DOUBLEと同等 |
| STRING | LONGTEXTと同等 |
| VARCHAR | VARCHARと同等 |
| JSON | JSONと同等 |
| FLOAT_VECTOR | VECTORと同等 |
説明
より複雑なタイプ、制約などについては、OceanBase JDBCのインターフェースを独自に使用して作成することができ、obvec_jdbcは使用しません。
インターフェース定義は以下のとおりです:
| API | 記述 |
|---|---|
| String getName() | 列名の取得。 |
| ObFieldSchema Name(String name) | 列名を設定し、オブジェクト自身を返してチェーン操作を可能にします。 |
| ObFieldSchema DataType(DataType dataType) | データ方を設定します。 |
| boolean getIsPrimary() | 主キー列かどうか。 |
| ObFieldSchema IsPrimary(boolean isPrimary) | 主キーかどうかを設定します。 |
| ObFieldSchema IsAutoInc(boolean isAutoInc) | 自動インクリメントかどうかを設定します。
に注意IsAutoIncはIsPrimaryが真の場合にのみ有効になります。 |
| ObFieldSchema IsNullable(boolean isNullable) | NULL値を許可するかどうかを設定します。
注意IsNullableはデフォルトでfalseです。これはMySQLの動作とは異なります。 |
| ObFieldSchema MaxLength(int maxLength) | VARCHARタイプの最大長を設定します。 |
| ObFieldSchema Dim(int dim) | VECTORタイプに対して次元を設定します。` |
IndexParams/IndexParam
IndexParamは、単一のインデックスパラメータを設定するために使用されます。IndexParamsは、一連のベクトルインデックスパラメータを設定するために使用され、特定のテーブルに複数のベクトルインデックスを作成する場合に使用されます。
注意
obvec_jdbcはベクトルインデックスの作成のみをサポートしており、その他のインデックスを作成するにはOceanBase JDBCを使用する必要があります。
IndexParamのコンストラクターは以下のとおりです:
# vidx_name:インデックス名
# vector_field_name:ベクトル列名
public IndexParam(String vidx_name, String vector_field_name);
インターフェース定義は以下のとおりです:
| API | 記述 |
|---|---|
| IndexParam M(int m) | HNSWアルゴリズムにおける各ベクトルの最大隣接数を取得します。 |
| IndexParam EfConstruction(int ef_construction) | HNSWアルゴリズム構築時の検索における最大候補ベクトル数を設定します。 |
| IndexParam EfSearch(int ef_search) | HNSWアルゴリズムにおける検索時の最大候補ベクトル数を設定します。 |
| IndexParam Lib(String lib) | 使用するベクトルライブラリのタイプを設定します。 |
| IndexParam MetricType(String metric_type) | ベクトル距離関数のタイプを設定します。 |
IndexParamsのコンストラクターは次のとおりです:
public IndexParams();
インターフェース定義は以下のとおりです:
| API | 記述 |
|---|---|
| void addIndex(IndexParam index_param) | インデックスの定義を加えます。 |
ObCollectionSchemaクラス
テーブルを作成する際には、ObCollectionSchemaオブジェクトの設定に依存する必要があるため、まずこのクラスのコンストラクタメソッドとインターフェースについて説明します。
ObCollectionSchemaのコンストラクターは次のとおりです:
public ObCollectionSchema();
インターフェース定義は以下のとおりです:
| API | 記述 |
|---|---|
| void addField(ObFieldSchema field) | 列の定義を追加します。 |
| void setIndexParams(IndexParams index_params) | テーブルのベクトルインデックスのパラメータを設定します。 |
テーブルの削除
コンストラクターは以下のとおりです:
# table_name:ターゲットテーブル名
public void dropCollection(String table_name);
チェックリストが存在するかどうか
コンストラクターは以下のとおりです:
# table_name:ターゲットテーブル名
public boolean hasCollection(String table_name);
テーブルの作成
コンストラクターは以下のとおりです:
# table_name:ターゲットテーブル名
# collection:ObCollectionSchemaタイプのデータ構造、テーブルのSchema
public void createCollection(String table_name, ObCollectionSchema collection);
ObFieldSchema、OCollectionSchemaおよびIndexParamsを使用してテーブルを作成する例を以下に示します。
import com.oceanbase.obvec_jdbc.DataType;
import com.oceanbase.obvec_jdbc.ObCollectionSchema;
import com.oceanbase.obvec_jdbc.ObFieldSchema;
import com.oceanbase.obvec_jdbc.IndexParam;
import com.oceanbase.obvec_jdbc.IndexParams;
# テーブルschemaの定義
ObCollectionSchema collectionSchema = new ObCollectionSchema();
ObFieldSchema c1_field = new ObFieldSchema("c1", DataType.INT32);
c1_field.IsPrimary(true).IsAutoInc(true);
ObFieldSchema c2_field = new ObFieldSchema("c2", DataType.FLOAT_VECTOR);
c2_field.Dim(3).IsNullable(false);
ObFieldSchema c3_field = new ObFieldSchema("c3", DataType.JSON);
c3_field.IsNullable(true);
collectionSchema.addField(c1_field);
collectionSchema.addField(c2_field);
collectionSchema.addField(c3_field);
# インデックスの定義
IndexParams index_params = new IndexParams();
IndexParam index_param = new IndexParam("vidx1", "c2");
index_params.addIndex(index_param);
collectionSchema.setIndexParams(index_params);
ob.createCollection(tb_name, collectionSchema);
後から作成されるベクトルインデックス
コンストラクターは以下のとおりです:
# table_name:ターゲットテーブル名
# index_param:IndexParamタイプのデータ構造、テーブルのベクトルインデックスパラメータ
public void createIndex(String table_name, IndexParam index_param)
データの挿入
コンストラクターは以下のとおりです:
# table_name:ターゲットテーブル名
# column_names:目標テーブルの列名配列
# rows:データ行。ArrayList<Sqlizable[]>、各行データはSqlizableの配列です。Sqlizableは、Javaデータ型をSQLデータ型に変換するためのラッパークラスです。
public void insert(String table_name, String[] column_names, ArrayList<Sqlizable[]> rows);
rowsがサポートするデータ型は次のとおりです:
- SqlInteger:Intタイプのデータをラップします。
- SqlFloat:Floatタイプのデータをラップします。
- SqlDouble:Doubleタイプのデータをラップします。
- SqlText:Stringタイプのデータをラップします。
- SqlVector:Vectorタイプのデータをラップします。
例:
import com.oceanbase.obvec_jdbc.SqlInteger;
import com.oceanbase.obvec_jdbc.SqlText;
import com.oceanbase.obvec_jdbc.SqlVector;
import com.oceanbase.obvec_jdbc.Sqlizable;
ArrayList<Sqlizable[]> insert_rows = new ArrayList<>();
Sqlizable[] ir1 = { new SqlVector(new float[] {1.0f, 2.0f, 3.0f}), new SqlText("{\"doc\": \"oceanbase doc 1\"}") };
insert_rows.add(ir1);
Sqlizable[] ir2 = { new SqlVector(new float[] {1.1f, 2.2f, 3.3f}), new SqlText("{\"doc\": \"oceanbase doc 2\"}") };
insert_rows.add(ir2);
Sqlizable[] ir3 = { new SqlVector(new float[] {0f, 0f, 0f}), new SqlText("{\"doc\": \"oceanbase doc 3\"}") };
insert_rows.add(ir3);
ob.insert(tb_name, new String[] {"c2", "c3"}, insert_rows);
データの削除
コンストラクターは以下のとおりです:
# table_name:ターゲットテーブル名
# primary_key_name:主キー列名
# primary_keys:ターゲット行の主キー列の値の配列
public void delete(String table_name, String primary_key_name, ArrayList<Sqlizable> primary_keys);
例:
ArrayList<Sqlizable> ids = new ArrayList<>();
ids.add(new SqlInteger(2));
ids.add(new SqlInteger(1));
ob.delete(tb_name, "c1", ids);
ANNクエリ
コンストラクターは以下のとおりです:
# table_name:ターゲットテーブル名
# vec_col_name:ベクトル列名
# metric_type:ベクトル距離関数タイプ。l2:l2距離関数に対応します。cosine:cosine距離関数に対応します。ip:negative_inner_product距離関数に対応します
# qv:クエリするベクトル値
# topk:top k個の最も類似する結果を返します
# output_fields:投影列は、返されるフィールドの配列です
# output_datatypes:投影列のデータ型、つまり返されるフィールドのデータ型であり、Javaデータ型への直接変換に使用されます
# where_expr:WHERE条件式
public ArrayList<HashMap<String, Sqlizable>> query(
String table_name,
String vec_col_name,
String metric_type,
float[] qv,
int topk,
String[] output_fields,
DataType[] output_datatypes,
String where_expr);
例:
ArrayList<HashMap<String, Sqlizable>> res = ob.query(tb_name, "c2", "l2",
new float[] {0f, 0f, 0f}, 10,
new String[] {"c1", "c3", "c2"},
new DataType[] {
DataType.INT32,
DataType.JSON,
DataType.FLOAT_VECTOR,
"c1 > 0"});
if (res != null) {
for (int i = 0; i < res.size(); i++) {
for (HashMap.Entry<String, Sqlizable> entry : res.get(i).entrySet()) {
System.out.printf("%s : %s, ", entry.getKey(), entry.getValue().toString());
}
System.out.print("\n");
}
} else {
System.out.println("res is null");
}
JSON Table機能の使用
obvec_jdbcのJSON Table機能は、OceanBaseのJSONデータ型の操作処理能力(JSON_VALUE / JSON_TABLE / JSON_REPLACE など)に基づいて、仮想テーブルメカニズムを実装しています:複数のユーザー(ユーザーidで区別)は、同一の物理テーブル上で仮想テーブルに対するDDLまたはDML操作を同時に実行でき、ユーザー間のデータ分離を保証します。管理者ユーザーはDDLを、一般ユーザーはDMLを操作できます。
この設計は、リレーショナルデータベースの構造化管理能力とJSONの柔軟性を組み合わせたものであり、OceanBaseデータベースのマルチモーダル統合能力を体現しています。ユーザーは、SQLの強力な機能と使いやすさを享受できると同時に、半構造化データも扱うことができるため、データモデルの多様性に対する現代のアプリケーションのニーズに対応できます。操作対象は依然として「テーブル」ですが、その内部ではデータをより柔軟なJSON形式で格納できるため、複雑で変化の激しいアプリケーションシナリオをより効果的にサポートします。
原理の説明
ユーザー操作: ユーザーは、使い慣れた標準SQL文(例:
CREATE TABLEでのテーブル作成、INSERTでのデータ挿入、SELECTでのデータ検索)を引き続き使用してシステムと対話します。データが内部でどのように格納されているかを気にする必要はなく、まるで通常のリレーショナルデータベースのテーブルを操作するかのようです。ユーザーがSQL文で作成したテーブルは論理テーブルであり、OceanBaseデータベース内部ではmeta_json_tとdata_json_tという2つの物理テーブルに対応します。JSONテーブルSDK: アプリケーション内部には、JSONテーブルSDK(ソフトウェア開発キット)があります。このSDKは、ユーザーのSQL操作とOceanBaseデータベースの実際のストレージとを繋ぐ重要な役割を果たします。SQL文が実行されると、SDKはこれらのリクエストを捕捉し、OceanBaseデータベース内部の
meta_json_tおよびdata_json_tテーブルへの読み書き操作にインテリジェントに変換します。OceanBaseデータベース内部:
meta_json_t(テーブル構造の格納): ユーザーが作成した論理テーブルのメタデータ、つまりテーブルの構造情報(例:作成したテーブルにどの列があるか、各列のデータ型は何か)を格納するために使用されます。CREATE TABLEが実行されると、SDKはこれらの構造情報をmeta_json_tに記録します。data_json_t(行データをJSON型として格納): 実際に挿入されたデータを格納するために使用されます。従来のリレーショナルデータベースのように行データを直接格納するのとは異なり、JSONテーブル機能では、挿入された各行データを1つのJSONオブジェクトにカプセル化し、data_json_tテーブルの特定の列に格納します。これにより、柔軟なデータ構造であっても効率的に格納できます。
データクエリ:
SELECTなどのクエリ操作が実行されると、SDKはdata_json_tからJSON形式のデータを読み取り、meta_json_tのテーブル構造情報と組み合わせて、これらのJSONデータを使い慣れたテーブル形式に再解析してアプリケーションに返します。
meta_json_tテーブルは、JSONテーブルのメタデータ情報、すなわちユーザーがCREATE TABLEで定義した論理テーブルの構造を格納します。各論理テーブルの列情報を記録し、テーブル構造は以下の通りです:
| フィールド名 | 説明 | 例 |
|---|---|---|
user_id |
ユーザーID。異なるユーザーの論理テーブルを区別するために使用されます。 | 0, 1, 2 |
jtable_name |
論理テーブルの名称。 | test_count |
jcol_id |
論理テーブル内の列ID。 | 1, 2, 3 |
jcol_name |
論理テーブル内の列名。 | c1, c2, c3 |
jcol_type |
列のデータ型。 | INT, VARCHAR(124), DECIMAL(10,2) |
jcol_nullable |
列がNULLを許容するかどうか。 | 0, 1 |
jcol_has_default |
列にデフォルト値があるかどうか。 | 0, 1 |
jcol_default |
列のデフォルト値。 | {'default': null} |
ユーザーがCREATE TABLEを実行すると、JSONテーブルSDKはこれらの列定義情報を解析し、meta_json_tテーブルに挿入します。
data_json_tテーブルは、JSONテーブルの実際のデータ、すなわちユーザーがINSERT挿入したデータを格納します。各論理テーブルの行データを記録し、テーブル構造は以下の通りです。
| フィールド名 | 説明 | 例 |
|---|---|---|
user_id |
ユーザーID。異なるユーザーの論理テーブルを区別するために使用されます。 | 0, 1, 2 |
admin_id |
管理者ユーザーID。 | 0 |
jtable_name |
論理テーブルの名称。meta_json_tのメタデータと関連付けるために使用されます。 |
test_count |
jdata_id |
データID。JSONデータの一意な識別子で、論理テーブルの各行に対応します。 | 1, 2, 3 |
jdata |
JSON型の列。論理テーブルの実際の行データを格納するために使用されます。 | {"c1": 1, "c2": "test", "c3": 1.23} |
使用例
クライアントの作成
コンストラクタは以下の通りです。
# uri:接続文字列。アドレスとポート、接続するデータベース名などが含まれます
# user:ユーザー名
# password:パスワード
# user_id:ユーザーid
# log_level:ログレベル
public ObVecJsonClient(String uri, String user, String password, String user_id, Level log_level);
例は以下の通りです。
import com.oceanbase.obvec_jdbc.ObVecJsonClient;
String uri = "jdbc:oceanbase://127.0.0.1:2881/test";
String user = "root@test";
String password = "";
ObVecJsonClient client = new ObVecJsonClient(uri, user, password, 0, Level.INFO);
DDL文の実行
parseJsonTableSQL2NormalSQLインターフェースを直接呼び出して、具体的なSQLステートメントを渡せば使用できます。
テーブルを作成する
String sql = "CREATE TABLE `t2` (c1 INT NOT NULL DEFAULT 10, c2 VARCHAR(30) DEFAULT 'ca', c3 VARCHAR NOT NULL, c4 DECIMAL(10, 2), c5 TIMESTAMP DEFAULT CURRENT_TIMESTAMP);"; client.parseJsonTableSQL2NormalSQL(sql);ALTER TABLE CHANGE COLUMN
sql = "ALTER TABLE t2 CHANGE COLUMN c2 changed_col INT"; client.parseJsonTableSQL2NormalSQL(sql);ALTER TABLE ADD COLUMN
sql = "ALTER TABLE t2 ADD COLUMN email VARCHAR(100) default 'example@example.com'"; client.parseJsonTableSQL2NormalSQL(sql);ALTER TABLE MODIFY COLUMN
sql = "ALTER TABLE t2 MODIFY COLUMN changed_col TIMESTAMP NOT NULL DEFAULT current_timestamp"; client.parseJsonTableSQL2NormalSQL(sql);ALTER TABLE DROP COLUMN
sql = "ALTER TABLE t2 DROP c1"; client.parseJsonTableSQL2NormalSQL(sql);ALTER TABLE RENAME
sql = "ALTER TABLE t2 RENAME TO alter_test"; client.parseJsonTableSQL2NormalSQL(sql);