多次元インデックスは JSON フィールドタイプをサポートしています。データテーブル内の String 型列を多次元インデックス内の JSON フィールドにマッピングすることで、半構造化データに対する構造化検索と分析を実行できます。JSON フィールドタイプには、Object と Nested という 2 つのストレージモードがあり、それぞれ独立したフィールドクエリと相関するネストされたオブジェクトクエリに適しています。
仕組み
String 型列を多次元インデックス内の JSON フィールドにマッピングすると、Tablestore は JSON 構造を解析し、個々のサブフィールドにインデックスを作成してクエリが可能になります。Object と Nested という 2 つのストレージモードが用意されており、それぞれ異なるクエリパターンに最適化されています。
Object:ネストされた構造をトップレベルのフィールドにフラット化します。高いパフォーマンスを必要とするシンプルなフィールドクエリに最適です。
Nested:各ネストされたオブジェクトの独立性とフィールド間の相関を保持します。単一のネストされたオブジェクト内での完全一致を必要とするクエリに最適です。
主な違い
|
項目 |
Object |
Nested |
|
データ処理 |
ネストされたデータをトップレベルのフィールドにフラット化します |
各ネストされたオブジェクトを独立したドキュメントとして格納します |
|
クエリ方法 |
標準クエリ |
NestedQuery が必要 |
|
フィールド間の相関 |
異なるネストされたオブジェクトのフィールド間でクロスオブジェクトマッチングが可能です |
同一のネストされたオブジェクト内でのみフィールドが一致します |
|
パフォーマンス |
リソース消費が少ない |
リソース消費が多く、相関フィールドクエリをサポートします |
選択ガイドライン
ネストされたオブジェクト内のフィールド間の相関を厳密に保持する必要があるクエリの場合は、Nested を選択してください。高いクエリパフォーマンスが優先され、厳密なフィールド間の相関が不要な場合は、Object を選択してください。
JSON フィールドインデックスの設定と使用
JSON フィールドの多次元インデックスを作成するには、次の 3 つのステップを実行します。JSON タイプの選択、インデックスの設定、クエリの実行です。
Object と Nested のどちらを選択する場合でも、各サブフィールドのフィールドタイプを明示的に定義する必要があります。タイプが定義されていないサブフィールドはインデックスによって無視され、クエリの対象になりません。
ステップ 1: JSON タイプとデータ形式の選択
以下のセクションでは、Object タイプと Nested タイプを比較し、サポートされるデータ形式を示し、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:各ネストされたオブジェクトの独立性を保持します。同一のネストされたオブジェクト内でのみフィールドマッチングが行われるように、クエリ条件を NestedQuery でラップします。
インデックス形式
address フィールドに次のデータが含まれていると仮定します。[{ "country": "China", "city": "Hangzhou" }, { "country": "USA", "city": "Seattle" }]
Object のインデックス形式:
{"address.country": ["China", "USA"], "address.city": ["Hangzhou","Seattle"]}Nested のインデックス形式:独立したドキュメント
{ "country": "China", "city": "Hangzhou" }と{ "country": "USA", "city": "Seattle" }
条件 address.country = "China" AND address.city = "Seattle" の場合、Object は一致します (異なるオブジェクトのフィールド値が結合される) が、Nested は一致しません (両方を持つ単一のオブジェクトが存在しない)。条件 address.country = "China" AND address.city = "Hangzhou" の場合、両方のタイプが一致します。
クエリの例
JSON ネスト型のクエリ例
次の例では、`address` フィールドの同じネストされたオブジェクト内で、address.country が "China" で、address.city が "Seattle" という 2 つの条件を満たす行をクエリします。
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 オブジェクト型のクエリ例
次の例では、address フィールドが、ネストされたオブジェクトを横断して、address.country が "China" であり、かつ address.city が "Seattle" であるという 2 つの条件を満たす行をクエリします。
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 の例
単一レベルおよび複数レベルの JSON フィールドのスキーマ設定は、Object と Nested の両方で同じ構造です。異なるのは型設定のみです。
単一レベル JSON
次の Java の例では、3 つのサブフィールドを持つ単一レベルの JSON フィールド tags (要件に応じて JSON 型を選択) を作成します:
tagName:Keyword タイプ。タグ名の完全一致と集約に使用されます。
score:Double タイプ。タグの重みの数値計算とソートに使用されます。
time:epoch_millis 形式の Date タイプ。時間範囲クエリと時系列分析に使用されます。
データの書き込みには、配列形式と非配列形式の両方がサポートされています。
// 配列形式
[{"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 の例では、基本的なユーザー情報とネストされた住所データを含む多階層 JSON フィールド user を作成します(要件に応じて JSON 型を選択してください)。多階層 JSON フィールドは、Nested 型と Object 型の組み合わせをサポートします。
基本フィールド:name (Keyword、正確な名前クエリ用)、age (Long、年齢範囲フィルタリング用)、birth (Date、形式 yyyy-MM-dd HH:mm:ss.SSS、誕生日クエリ用)、phone (Keyword、連絡先マッチング用)。
ネストされたフィールド:
address(要件に基づいて JSON 型を選択) は、階層的な位置情報クエリのためにprovince、city、およびstreet(すべてキーワード型) を含みます。
ユーザーデータのサンプル:
{
"name": "田中太郎",
"age": 20,
"birth": "2014-10-10 12:00:00.000",
"phone": "1390000****",
"address": {
"province": "Zhejiang",
"city": "Hangzhou",
"street": "1201, Sunshine 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 フィールドのサブフィールドとして使用できません。ベクトルフィールドのインデックスは独立して設定してください。
Nested タイプ:Nested タイプの JSON フィールドはインデックスの事前ソートをサポートしていません。Nested フィールドをクエリするには NestedQuery を使用してください。
配列サブフィールド: 配列形式のデータを持つ非 JSON サブフィールドについては、IsArray プロパティを true に設定します。データは、標準の配列形式
"[a, b, c]"で記述します。そうしない場合、インデックスでサブフィールドの同期またはクエリを実行できなくなります。
関連ドキュメント
JSON フィールドタイプは現在、Tablestore SDK for Java、Tablestore SDK for Go、Tablestore SDK for Python を通じてサポートされています。