多次元インデックスは JSON 型フィールドをサポートしており、半構造化データを効率的に格納し、柔軟にクエリできます。この機能により、ログ分析、ユーザー行動の記録、構成管理といった複雑なビジネスシナリオにおいて、ネストデータ構造を取得・分析できます。JSON 型フィールドを使用することで、クエリ効率とデータ処理の精度が大幅に向上します。
多次元インデックスで JSON 型を使用するには、、Tablestore の技術サポートに連絡してこの機能を有効にする必要があります。
仕組み
データテーブルの String 型フィールドを、多次元インデックスの JSON 型フィールドにマッピングできます。これにより、階層構造を持つ半構造化データを処理できます。さまざまなクエリやパフォーマンス要件に対応するため、Tablestore は Object 型と Nested 型という 2 つの JSON ストレージモードを提供しています。
Object 型: ネスト構造をフラット化して個別のフィールドにします。この型は、単純なフィールド取得が中心で、高いクエリパフォーマンスが求められるシナリオに適しています。
Nested 型: ネストされたオブジェクトの独立性と、そのフィールド間の関係を維持します。この型は、オブジェクト内のフィールドの組み合わせに対して完全に一致する検索が求められる複雑なクエリシナリオに適しています。
コアな違い
2 つの JSON 型は、データ処理方式、クエリ構文、フィールド間の関係、およびパフォーマンス特性において大きな違いがあります。
データ処理方式: Object 型はネストされたデータをフラットな構造で格納します。Nested 型は、ネストされた各オブジェクトを個別のドキュメントとして格納します。
クエリ方式: Object 型は基本クエリを使用します。Nested 型は NestedQuery を使用する必要があります。
フィールド間の関係: Object 型では、異なるネストされたオブジェクトのフィールド間でクロスマッチングが可能です。Nested 型では、各ネストされたオブジェクト内でフィールドが独立してマッチングされることが保証されます。
パフォーマンス: Object 型のクエリは軽量で効率的であり、リソース消費が少ないです。Nested 型のクエリは強力ですが、より多くのリソースを消費します。
選択の原則
クエリがネストされたオブジェクト内のフィールドの関係と精度を厳密に維持する必要がある場合は、Nested 型を選択してください。高いクエリパフォーマンスが必要で、フィールド間の関係に厳密な要件がない場合は、Object 型を選択してください。
JSON 型インデックスの構成と使用
このセクションでは、JSON 型インデックスの完全な構成フローについて説明します。これにより、半構造化データのための効率的で信頼性の高い取得システムを構築できます。このプロセスには、型の選択、インデックスの作成、およびクエリの検証が含まれます。
Object 型または Nested 型のどちらの JSON 型を選択するかにかかわらず、すべてのサブフィールドに対してフィールド型を明示的に定義する必要があります。インデックスシステムは、型が定義されていないフィールドを無視します。これらのフィールドはクエリに含めることができません。
ステップ 1: JSON 型とデータ形式の選択
特定のクエリニーズ、パフォーマンス要件、およびデータ構造に基づいて、最適な JSON 型とデータ形式を選択します。
JSON 型の選択ポリシー
Object 型:単純なフィールドに対する独立したクエリに適しています。優れたクエリパフォーマンスと低いリソース消費を提供します。
Nested 型:フィールド間の関係を維持する必要がある正確なクエリに適しています。これにより、クエリ結果の精度が保証されます。
混合使用:複雑なビジネスシナリオの要件を満たすために、同じインデックス内で Object 型と Nested 型を組み合わせることができます。
データ書き込み形式の仕様
JSON フィールドは、配列形式と非配列形式の両方のデータ形式をサポートしています。データ構造に合った形式を選択してください。
// 配列形式
[{ "country": "China", "city": "hangzhou" }, { "country": "usa", "city": "Seattle" }]
// 非配列形式
{ "country": "China", "city": "hangzhou" }データ書き込みの例
private static void putRow(SyncClient client) {
// プライマリキーを構築します。
PrimaryKeyBuilder primaryKeyBuilder = PrimaryKeyBuilder.createPrimaryKeyBuilder();
primaryKeyBuilder.addPrimaryKeyColumn("id", PrimaryKeyValue.fromString("10001"));
PrimaryKey primaryKey = primaryKeyBuilder.build();
// データテーブル名を設定します。
RowPutChange rowPutChange = new RowPutChange("<TABLE_NAME>", primaryKey);
// 生の JSON データを構築します。
List<Map<String, Object>> addresses = Arrays.asList(
new HashMap<String, Object>() {{ put("country", "China"); put("city", "hangzhou"); }},
new HashMap<String, Object>() {{ put("country", "usa"); put("city", "Seattle"); }}
);
String jsonString = JSON.toJSONString(addresses);
rowPutChange.addColumn(new Column("address", ColumnValue.fromString(jsonString)));
client.putRow(new PutRowRequest(rowPutChange));
}ステップ 2: フィールド構造の構成とインデックスの作成
この例では、2 つのサブフィールドを持つ単一層の JSON オブジェクトの完全なインデックス構成プロセスを示します。フィールド型を定義し、プロパティを正しく設定することで、インデックスの完全性を保証し、クエリパフォーマンスを最適化します。
List<FieldSchema> subFieldSchemas = new ArrayList<FieldSchema>();
subFieldSchemas.add(new FieldSchema("country", FieldType.KEYWORD)
.setIndex(true).setEnableSortAndAgg(true));
subFieldSchemas.add(new FieldSchema("city", FieldType.KEYWORD)
.setIndex(true).setEnableSortAndAgg(true));
FieldSchema jsonFieldSchema = new FieldSchema("address", FieldType.Json)
.setJsonType(JsonType.OBJECT) // JsonType.OBJECT または JsonType.NESTED に設定します
.setSubFieldSchemas(subFieldSchemas);ステップ 3: データのクエリ
クエリをテストして、インデックス構成を検証できます。同じデータとクエリ条件を使用して、Object 型と Nested 型の JSON のクエリ動作と結果を比較します。
Object 型: ネストされたデータをフラット化します。クエリするには、親フィールド名と子フィールド名をピリオド (.) で連結したパスを使用します。このフラットな構造により、異なるネストされたオブジェクトのフィールド値をクロスマッチングできます。
Nested 型: 各ネストされたオブジェクトの独立性と完全性を維持します。クエリ条件を NestedQuery でラップする必要があります。これにより、フィールドが同じネストされたオブジェクト内でのみマッチングされることが保証されます。
インデックス作成後の形式
`address` フィールドのデータが `[{ "country": "China", "city": "Hangzhou" }, { "country": "USA", "city": "Seattle" }]` であると仮定します。
インデックス作成後の Object 型 JSON 形式:
{"address.country": ["China", "USA"], "address.city": ["Hangzhou","Seattle"]}インデックス作成後の Nested 型 JSON 形式: 個別のドキュメント
{ "country": "China", "city": "Hangzhou" }と{ "country": "USA", "city": "Seattle" }
前述の例に基づくと、クエリ条件が address.country ="China" および address.city="Seattle" の場合、Object 型のクエリでは一致が返されますが、Nested 型のクエリでは返されません。 クエリ条件が address.country ="China" および address.city="Hangzhou" の場合、Object 型と Nested 型の両方のクエリで一致が返されます。
クエリの例
JSON Nested 型のクエリ例
次の例では、`address` フィールドの同じネストされたオブジェクト内で 2 つの条件を満たす行をクエリします: address.country が "China" であり、かつ address.city が "Seattle" であること。
public static void nestedQuery(SyncClient client) {
// 条件 1: address サブ行の country フィールドの値は "China" である必要があります。
TermQuery termQuery1 = new TermQuery();
termQuery1.setFieldName("address.country");
termQuery1.setTerm(ColumnValue.fromString("China"));
// 条件 2: address サブ行の city フィールドの値は "Seattle" である必要があります。
TermQuery termQuery2 = new TermQuery();
termQuery2.setFieldName("address.city");
termQuery2.setTerm(ColumnValue.fromString("Seattle"));
// BoolQuery の AND 条件を使用して、両方の条件を満たすサブ行をクエリします。
List<Query> mustQueries = new ArrayList<>();
mustQueries.add(termQuery1);
mustQueries.add(termQuery2);
BoolQuery boolQuery = new BoolQuery();
boolQuery.setMustQueries(mustQueries);
// NestedQuery 内に BoolQuery を設定して、サブ行が複数のクエリ条件を同時に満たすように要求します。
NestedQuery nestedQuery = new NestedQuery(); // クエリタイプを NestedQuery に設定します。
nestedQuery.setPath("address"); // ネストされた型カラムのパス、つまりクエリ対象フィールドの親パスを設定します。
nestedQuery.setQuery(boolQuery);
nestedQuery.setScoreMode(ScoreMode.None);
SearchQuery searchQuery = new SearchQuery();
searchQuery.setQuery(nestedQuery);
SearchRequest searchRequest = new SearchRequest("<TABLE_NAME>", "<SEARCH_INDEX_NAME>", searchQuery);
SearchResponse resp = client.search(searchRequest);
System.out.println("Row: " + resp.getRows());
}JSON Object 型のクエリ例
次の例では、`address` フィールドがそのネストされたオブジェクト全体で 2 つの条件を満たす行をクエリします: address.country が "China" であり、かつ address.city が "Seattle" であること。
public static void boolQuery(SyncClient client) {
// 条件 1: address サブ行の country フィールドの値は "China" である必要があります。
TermQuery termQuery1 = new TermQuery();
termQuery1.setFieldName("address.country");
termQuery1.setTerm(ColumnValue.fromString("China"));
// 条件 2: address サブ行の city フィールドの値は "Seattle" である必要があります。
TermQuery termQuery2 = new TermQuery();
termQuery2.setFieldName("address.city");
termQuery2.setTerm(ColumnValue.fromString("Seattle"));
// BoolQuery の AND 条件を使用して、両方の条件を満たすサブ行をクエリします。
List<Query> mustQueries = new ArrayList<>();
mustQueries.add(termQuery1);
mustQueries.add(termQuery2);
BoolQuery boolQuery = new BoolQuery();
boolQuery.setMustQueries(mustQueries);
SearchQuery searchQuery = new SearchQuery();
searchQuery.setQuery(boolQuery);
SearchRequest searchRequest = new SearchRequest("<TABLE_NAME>", "<SEARCH_INDEX_NAME>", searchQuery);
SearchResponse resp = client.search(searchRequest);
System.out.println("Row: " + resp.getRows());
}JSON 型の適用例
Object 型または Nested 型のどちらの JSON 型を選択するかにかかわらず、スキーマ構成は単一層構造と多層構造の両方で同じです。唯一の違いは JSON 型の設定です。この一貫性により、型間の柔軟な切り替えと統一された管理が可能になります。
単一層 JSON の例
この Java コード例では、tags という名前の単一層 JSON フィールドを作成します。必要に応じて JSON 型を設定できます。このフィールドには、次の 3 つのサブフィールドが含まれます。
tagName: KEYWORD (文字列) 型で、タグ名の完全一致検索と集計に使用します。
score: DOUBLE (浮動小数点数) 型で、数値計算やタグの重みによるソートに使用します。
time: DATE 型で、ミリ秒単位の UNIX タイムスタンプ形式を使用します。時間範囲クエリや時系列分析に使用されます。
データは配列形式または非配列形式で書き込むことができます。データ構造に合った形式を選択してください。
// 配列形式
[{"tagName":"tag1", "score":0.8,"time": 1730690237000 }, {"tagName":"tag2", "score":0.2,"time": 1730691557000}]
// 非配列形式
{"tagName":"tag1", "score":0.8,"time": 1730690237000 }完全なスキーマ構成コードは次のとおりです。
List<FieldSchema> subFieldSchemas = new ArrayList<FieldSchema>();
subFieldSchemas.add(new FieldSchema("tagName", FieldType.KEYWORD)
.setIndex(true).setEnableSortAndAgg(true));
subFieldSchemas.add(new FieldSchema("score", FieldType.DOUBLE)
.setIndex(true).setEnableSortAndAgg(true));
subFieldSchemas.add(new FieldSchema("time", FieldType.DATE)
.setDateFormats(Arrays.asList("epoch_millis")));
FieldSchema nestedFieldSchema = new FieldSchema("tags", FieldType.Json)
.setJsonType(JsonType.OBJECT) // 必要に応じて JsonType.NESTED に置き換えます
.setSubFieldSchemas(subFieldSchemas);多層 JSON の例
この Java コード例では、user という名前の多層 JSON フィールドを作成します。必要に応じて `JsonType` を設定できます。このフィールドは、基本的なユーザー情報とネストされた住所情報を含む完全なデータ構造を構築します。多層 JSON 構造では、Nested 型と Object 型を混在させて使用できます。
基本フィールド: `name` (名前の完全一致クエリ用の KEYWORD 型)、`age` (年齢範囲フィルタリング用の LONG 型)、`birth` (誕生日クエリ用の `yyyy-MM-dd HH:mm:ss.SSS` 形式の DATE 型)、および `phone` (連絡先情報マッチング用の KEYWORD 型)。
ネストされたフィールド:
address(必要に応じて `JsonType` を設定可能)。これには、province、city、streetという 3 つの住所レベルのフィールドが含まれます。すべて KEYWORD 型であり、地理的位置の階層クエリをサポートします。
以下は、ユーザーデータを書き込む典型的な例です。
{
"name": "Zhang San",
"age": 20,
"birth": "2014-10-10 12:00:00.000",
"phone": "1390000****",
"address": {
"province": "Zhejiang Province",
"city": "Hangzhou City",
"street": "No. 1201, Xingfu Community, Yangguang Avenue"
}
}
多層 JSON の完全なスキーマ構成は次のとおりです。
// address サブフィールドのスキーマ (パス: user.address)
List<FieldSchema> addressSubFiledSchemas = new ArrayList<>();
addressSubFiledSchemas.add(new FieldSchema("province",FieldType.KEYWORD));
addressSubFiledSchemas.add(new FieldSchema("city",FieldType.KEYWORD));
addressSubFiledSchemas.add(new FieldSchema("street",FieldType.KEYWORD));
// user サブフィールドのスキーマ (パス: user)
List<FieldSchema> subFieldSchemas = new ArrayList<>();
subFieldSchemas.add(new FieldSchema("name",FieldType.KEYWORD));
subFieldSchemas.add(new FieldSchema("age",FieldType.LONG));
subFieldSchemas.add(new FieldSchema("birth",FieldType.DATE)
.setDateFormats(Arrays.asList("yyyy-MM-dd HH:mm:ss.SSS")));
subFieldSchemas.add(new FieldSchema("phone",FieldType.KEYWORD));
subFieldSchemas.add(new FieldSchema("address",FieldType.JSON)
.setJsonType(JsonType.NESTED) // 必要に応じて JsonType.OBJECT に置き換えます
.setSubFieldSchemas(addressSubFiledSchemas));
// 親フィールド user を作成します
List<FieldSchema> fieldSchemas = new ArrayList<>();
fieldSchemas.add(new FieldSchema("user",FieldType.JSON)
.setJsonType(JsonType.OBJECT) // 必要に応じて JsonType.NESTED に置き換えます
.setSubFieldSchemas(subFieldSchemas));割り当てと制限
JSON 型フィールドを使用する際は、次の制限事項と構成要件に注意してください。
ベクター型の制限: ベクター型フィールドを JSON フィールドのサブフィールドにすることはできません。ベクターフィールドには別のインデックスを構成する必要があります。
Nested 型の制限: JSON フィールドの型を Nested に設定した場合、そのフィールドに対してインデックスの事前ソート機能を使用することはできません。フィールドをクエリするには NestedQuery を使用する必要があります。
サブフィールドの配列構成: JSON ではないサブフィールドに配列形式でデータを書き込む場合は、フィールド構成で `IsArray` プロパティを `true` に設定する必要があります。また、データが
`"[a, b, c]"`のような標準的な配列形式で書き込まれていることを確認する必要があります。これらの要件を満たさない場合、インデックスはサブフィールドのデータを正しく同期または取得できません。
開発者向けインテグレーション
JSON 型は、Java SDK、Go SDK、および Python SDK で使用できます。