Search Index mendukung bidang tipe JSON untuk menyimpan data semi-terstruktur secara efisien dan melakukan kueri secara fleksibel. Fitur ini memungkinkan Anda mengambil dan menganalisis struktur data bersarang dalam skenario bisnis kompleks, seperti analisis log, pencatatan perilaku pengguna, dan manajemen konfigurasi. Penggunaan bidang tipe JSON secara signifikan meningkatkan efisiensi kueri dan akurasi pemrosesan data.
Untuk menggunakan tipe JSON di Search Index, silakan dan hubungi dukungan teknis Tablestore untuk mengaktifkan fitur ini.
Cara kerja
Anda dapat memetakan bidang tipe String dari tabel data ke bidang tipe JSON di indeks pencarian. Hal ini memungkinkan Anda memproses data semi-terstruktur yang memiliki struktur hirarkis. Untuk memenuhi berbagai kebutuhan kueri dan performa, Tablestore menyediakan dua mode penyimpanan JSON: Object type dan Nested type.
Object type: Meratakan struktur bersarang menjadi bidang-bidang terpisah. Tipe ini cocok untuk skenario yang melibatkan pengambilan bidang sederhana dan memerlukan performa kueri tinggi.
Nested type: Mempertahankan independensi objek bersarang dan hubungan antar bidangnya. Tipe ini cocok untuk skenario kueri kompleks yang memerlukan kecocokan eksak untuk kombinasi bidang dalam satu objek.
Perbedaan utama
Kedua tipe JSON tersebut memiliki perbedaan signifikan dalam metode pemrosesan data, sintaks kueri, hubungan bidang, dan karakteristik performa:
Metode pemrosesan data: Object type menyimpan data bersarang dalam struktur datar. Nested type menyimpan setiap objek bersarang sebagai dokumen terpisah.
Metode kueri: Object type menggunakan kueri dasar. Nested type harus menggunakan NestedQuery.
Hubungan bidang: Object type memungkinkan pencocokan silang antar bidang dari objek bersarang yang berbeda. Nested type memastikan bahwa bidang dicocokkan secara independen dalam setiap objek bersarang.
Performa: Kueri tipe Object ringan, efisien, dan mengonsumsi sedikit resource. Kueri tipe Nested kuat tetapi mengonsumsi lebih banyak resource.
Prinsip pemilihan
Pilih Nested type jika kueri Anda harus secara ketat mempertahankan hubungan dan akurasi bidang dalam objek bersarang. Pilih Object type jika Anda memerlukan performa kueri tinggi dan tidak memiliki persyaratan ketat terhadap hubungan bidang.
Konfigurasi dan penggunaan indeks tipe JSON
Bagian ini menjelaskan alur konfigurasi lengkap untuk indeks tipe JSON, yang membantu Anda membangun sistem pengambilan data yang efisien dan andal untuk data semi-terstruktur Anda. Proses ini mencakup pemilihan tipe, pembuatan indeks, dan validasi kueri.
Baik Anda memilih tipe JSON Object atau Nested, Anda harus secara eksplisit menentukan tipe bidang untuk semua sub-bidang. Sistem pengindeksan mengabaikan bidang apa pun yang tidak memiliki tipe yang ditentukan. Bidang tersebut tidak dapat dimasukkan dalam kueri.
Langkah 1: Pilih tipe JSON dan format data
Pilih tipe JSON dan format data yang paling sesuai berdasarkan kebutuhan kueri spesifik, persyaratan performa, dan struktur data Anda.
Kebijakan pemilihan tipe JSON
Object type: Cocok untuk kueri independen pada bidang sederhana. Memberikan performa kueri unggul dan konsumsi resource rendah.
Nested type: Cocok untuk kueri presisi yang harus mempertahankan hubungan bidang. Ini memastikan akurasi hasil kueri.
Penggunaan campuran: Anda dapat menggabungkan tipe Object dan Nested dalam indeks yang sama untuk memenuhi kebutuhan skenario bisnis kompleks.
Spesifikasi format penulisan data
Bidang JSON mendukung format data array maupun non-array. Pilih format yang sesuai dengan struktur data Anda:
// Format array
[{ "country": "China", "city": "hangzhou" }, { "country": "usa", "city": "Seattle" }]
// Format non-array
{ "country": "China", "city": "hangzhou" }Contoh penulisan data
private static void putRow(SyncClient client) {
// Konstruksi primary key.
PrimaryKeyBuilder primaryKeyBuilder = PrimaryKeyBuilder.createPrimaryKeyBuilder();
primaryKeyBuilder.addPrimaryKeyColumn("id", PrimaryKeyValue.fromString("10001"));
PrimaryKey primaryKey = primaryKeyBuilder.build();
// Tetapkan nama tabel data.
RowPutChange rowPutChange = new RowPutChange("<TABLE_NAME>", primaryKey);
// Bangun data JSON mentah.
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));
}Langkah 2: Konfigurasi struktur bidang dan buat indeks
Contoh ini menunjukkan proses konfigurasi indeks lengkap untuk objek JSON tingkat tunggal dengan dua sub-bidang. Menentukan tipe bidang dan mengatur properti dengan benar memastikan integritas indeks dan mengoptimalkan performa kueri.
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) // Setel ke JsonType.OBJECT atau JsonType.NESTED
.setSubFieldSchemas(subFieldSchemas);Langkah 3: Kueri data
Anda dapat menguji kueri untuk memverifikasi konfigurasi indeks. Bandingkan perilaku dan hasil kueri untuk tipe JSON Object dan Nested menggunakan data dan kondisi kueri yang sama.
Object type: Meratakan data bersarang. Untuk melakukan kueri, gunakan path yang menghubungkan nama bidang induk dan anak dengan titik (.). Struktur datar ini memungkinkan nilai bidang dari objek bersarang berbeda untuk dicocokkan silang.
Nested type: Mempertahankan independensi dan integritas setiap objek bersarang. Anda harus membungkus kondisi kueri dalam NestedQuery. Ini memastikan bahwa bidang hanya dicocokkan dalam objek bersarang yang sama.
Format setelah pengindeksan
Asumsikan data untuk bidang `address` adalah `[{ "country": "China", "city": "Hangzhou" }, { "country": "USA", "city": "Seattle" }]`.
Format JSON tipe Object setelah pengindeksan:
{"address.country": ["China", "USA"], "address.city": ["Hangzhou","Seattle"]}Format JSON tipe Nested setelah pengindeksan: Dokumen terpisah
{ "country": "China", "city": "Hangzhou" }dan{ "country": "USA", "city": "Seattle" }
Berdasarkan contoh sebelumnya, jika kondisi kueri adalah address.country ="China" dan address.city="Seattle", kueri tipe Object mengembalikan kecocokan, tetapi kueri tipe Nested tidak. Jika kondisi kueri adalah address.country ="China" dan address.city="Hangzhou", kueri pada kedua tipe Object dan Nested mengembalikan kecocokan.
Contoh kueri
Contoh kueri tipe JSON Nested
Contoh berikut melakukan kueri untuk baris yang memenuhi dua kondisi dalam objek bersarang yang sama pada bidang `address`: address.country bernilai "China" dan address.city bernilai "Seattle".
public static void nestedQuery(SyncClient client) {
// Kondisi 1: Nilai bidang country dalam sub-baris address harus "China".
TermQuery termQuery1 = new TermQuery();
termQuery1.setFieldName("address.country");
termQuery1.setTerm(ColumnValue.fromString("China"));
// Kondisi 2: Nilai bidang city dalam sub-baris address harus "Seattle".
TermQuery termQuery2 = new TermQuery();
termQuery2.setFieldName("address.city");
termQuery2.setTerm(ColumnValue.fromString("Seattle"));
// Gunakan kondisi AND dari BoolQuery untuk mencari sub-baris yang memenuhi kedua kondisi.
List<Query> mustQueries = new ArrayList<>();
mustQueries.add(termQuery1);
mustQueries.add(termQuery2);
BoolQuery boolQuery = new BoolQuery();
boolQuery.setMustQueries(mustQueries);
// Tetapkan BoolQuery dalam NestedQuery agar sub-baris memenuhi beberapa kondisi kueri sekaligus.
NestedQuery nestedQuery = new NestedQuery(); // Tetapkan tipe kueri ke NestedQuery.
nestedQuery.setPath("address"); // Tetapkan path kolom tipe nested, yaitu path induk dari bidang yang akan dikueri.
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());
}Contoh kueri tipe JSON Object
Contoh berikut melakukan kueri untuk baris di mana bidang `address` memenuhi dua kondisi di seluruh objek bersarangnya: address.country bernilai "China" dan address.city bernilai "Seattle".
public static void boolQuery(SyncClient client) {
// Kondisi 1: Nilai bidang country dalam sub-baris address harus "China".
TermQuery termQuery1 = new TermQuery();
termQuery1.setFieldName("address.country");
termQuery1.setTerm(ColumnValue.fromString("China"));
// Kondisi 2: Nilai bidang city dalam sub-baris address harus "Seattle".
TermQuery termQuery2 = new TermQuery();
termQuery2.setFieldName("address.city");
termQuery2.setTerm(ColumnValue.fromString("Seattle"));
// Gunakan kondisi AND dari BoolQuery untuk mencari sub-baris yang memenuhi kedua kondisi.
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());
}Contoh penerapan tipe JSON
Baik Anda memilih tipe JSON Object atau Nested, konfigurasi skema untuk struktur tingkat tunggal maupun multi-level sama. Satu-satunya perbedaan adalah pengaturan tipe JSON. Konsistensi ini memungkinkan peralihan fleksibel antar tipe dan manajemen terpadu.
Contoh JSON tingkat tunggal
Contoh kode Java ini membuat bidang JSON tingkat tunggal bernama tags. Anda dapat menyetel tipe JSON sesuai kebutuhan. Bidang ini mencakup tiga sub-bidang berikut:
tagName: Tipe Keyword (string) untuk pencocokan eksak dan agregasi nama tag.
score: Tipe Double (bilangan titik mengambang) untuk perhitungan numerik dan pengurutan berdasarkan bobot tag.
time: Tipe Date yang menggunakan format stempel waktu UNIX milidetik. Digunakan untuk kueri rentang waktu dan analisis deret waktu.
Data dapat ditulis dalam format array atau non-array. Pilih format yang sesuai dengan struktur data Anda:
// Format array
[{"tagName":"tag1", "score":0.8,"time": 1730690237000 }, {"tagName":"tag2", "score":0.2,"time": 1730691557000}]
// Format non-array
{"tagName":"tag1", "score":0.8,"time": 1730690237000 }Kode konfigurasi skema lengkap adalah sebagai berikut:
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) // Ganti dengan JsonType.NESTED sesuai kebutuhan
.setSubFieldSchemas(subFieldSchemas);Contoh JSON multi-level
Contoh kode Java ini membuat bidang JSON multi-level bernama user. Anda dapat menyetel `JsonType` sesuai kebutuhan. Bidang ini membangun struktur data lengkap yang berisi informasi pengguna dasar dan informasi alamat bersarang. Anda dapat menggunakan kombinasi tipe Nested dan Object dalam struktur JSON multi-level.
Bidang dasar: `name` (tipe Keyword untuk kueri nama eksak), `age` (tipe Long untuk filter rentang usia), `birth` (tipe Date dalam format `yyyy-MM-dd HH:mm:ss.SSS` untuk kueri tanggal lahir), dan `phone` (tipe Keyword untuk pencocokan informasi kontak).
Bidang bersarang:
address(Anda dapat menyetel `JsonType` sesuai kebutuhan). Berisi tiga bidang tingkat alamat:province,city, danstreet. Semuanya bertipe Keyword yang mendukung kueri hierarkis untuk lokasi geografis.
Berikut adalah contoh khas penulisan data pengguna:
{
"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"
}
}
Konfigurasi skema lengkap untuk JSON multi-level adalah sebagai berikut:
// Skema untuk sub-bidang address (path: 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));
// Skema untuk sub-bidang user (path: 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) // Ganti dengan JsonType.OBJECT sesuai kebutuhan
.setSubFieldSchemas(addressSubFiledSchemas));
// Buat bidang induk user
List<FieldSchema> fieldSchemas = new ArrayList<>();
fieldSchemas.add(new FieldSchema("user",FieldType.JSON)
.setJsonType(JsonType.OBJECT) // Ganti dengan JsonType.NESTED sesuai kebutuhan
.setSubFieldSchemas(subFieldSchemas));Kuota dan batasan
Perhatikan batasan dan persyaratan konfigurasi berikut saat menggunakan bidang tipe JSON:
Batasan tipe vektor: Bidang tipe vektor tidak dapat menjadi sub-bidang dari bidang JSON. Anda harus mengonfigurasi indeks terpisah untuk bidang vektor.
Batasan tipe Nested: Saat tipe bidang JSON diatur ke Nested, Anda tidak dapat menggunakan fitur pre-sorting indeks untuk bidang tersebut. Anda harus menggunakan NestedQuery untuk mengkueri bidang tersebut.
Konfigurasi array sub-bidang: Jika Anda menulis data ke sub-bidang non-JSON dalam format array, Anda harus menyetel properti `IsArray` ke `true` dalam konfigurasi bidang. Anda juga harus memastikan data ditulis dalam format array standar, seperti
`"[a, b, c]"`. Jika persyaratan ini tidak dipenuhi, indeks tidak dapat menyinkronkan atau mengambil data sub-bidang secara benar.
Integrasi pengembang
Anda dapat menggunakan tipe JSON dengan Java SDK, Go SDK, dan Python SDK.