All Products
Search
Document Center

Tablestore:Pengurutan dan paging

Last Updated:May 13, 2026

Saat mengkueri data dari indeks pencarian, Anda dapat mengurutkan hasil dengan menentukan urutan sebelumnya atau saat kueri. Untuk set hasil yang besar, gunakan paging berbasis offset atau paging berbasis token untuk menemukan data yang Anda butuhkan dengan cepat.

Kasus penggunaan

Kategori

Metode

Fitur

Kasus penggunaan

Pengurutan

Tentukan saat pembuatan

Index presorting (IndexSort)

Secara default, Tablestore mengurutkan data dalam indeks pencarian menggunakan metode index presorting (IndexSort) yang dikonfigurasi. Ini menetapkan urutan pengurutan default untuk hasil yang dikembalikan.

Tentukan saat kueri

ScoreSort (relevance score sort)

Mengurutkan hasil kueri berdasarkan skor relevansi, yang dihitung menggunakan algoritma BM25. Metode ini cocok untuk skenario berbasis relevansi, seperti pencarian teks lengkap.

PrimaryKeySort (primary key sort)

Mengurutkan hasil berdasarkan primary key. Ini berguna untuk mengurutkan item berdasarkan pengenal uniknya.

FieldSort (field sort)

Mengurutkan hasil berdasarkan nilai bidang. Ini berguna untuk skenario e-commerce atau media sosial di mana Anda mungkin mengurutkan berdasarkan atribut seperti volume penjualan atau jumlah tampilan halaman.

Untuk bidang bernilai ganda, seperti bidang bertipe array atau nested, Anda dapat menggunakan parameter mode untuk mengontrol elemen mana yang digunakan untuk pengurutan.

GeoDistanceSort (geo-distance sort)

Mengurutkan hasil berdasarkan jarak dari titik geografis. Ini cocok untuk layanan berbasis lokasi seperti peta atau logistik, misalnya, mengurutkan restoran terdekat berdasarkan jarak.

Paging

Tentukan saat kueri

Gunakan offset-based paging

Gunakan offset-based paging ketika jumlah total baris yang akan diambil kurang dari 100.000.

Gunakan token-based paging

Gunakan token-based paging untuk mengambil set hasil besar secara berurutan. Secara default, Anda hanya dapat melakukan paging maju. Namun, karena token tetap valid selama proses kueri, Anda dapat menyimpan cache token sebelumnya untuk melakukan paging mundur.

SDK

Anda dapat menerapkan pengurutan dan paging menggunakan SDK untuk bahasa-bahasa berikut.

Index presorting

Secara default, Tablestore mengurutkan data dalam indeks pencarian berdasarkan urutan pengurutan yang telah ditentukan sebelumnya, yang disebut IndexSort. Saat Anda mengkueri data, IndexSort menentukan urutan default hasil yang dikembalikan.

Saat membuat indeks pencarian, Anda dapat menyesuaikan IndexSort. Jika tidak ditentukan, indeks akan menggunakan PrimaryKeySort sebagai default.

Penting
  • Pengurutan awal indeks hanya mendukung PrimaryKeySort (pengurutan kunci primer) dan FieldSort (pengurutan bidang).

  • IndexSort tidak dapat digunakan untuk indeks pencarian yang berisi bidang bertipe nested.

  • Setelah membuat indeks pencarian, fitur dynamic schema modification memungkinkan Anda memodifikasi pengaturan IndexSort.

Urutkan saat kueri

Pengurutan hanya dapat dilakukan pada bidang yang properti enableSortAndAgg-nya diatur ke true.

Anda dapat menentukan metode pengurutan untuk setiap kueri. Indeks pencarian mendukung empat jenis sorter berikut. Beberapa sorter juga dapat digabungkan untuk mengurutkan hasil berdasarkan urutan kriteria tertentu.

ScoreSort

Mengurutkan hasil kueri berdasarkan skor relevansi, yang dihitung menggunakan algoritma BM25. Metode ini cocok untuk skenario berbasis relevansi, seperti pencarian teks lengkap.

Penting
  • Untuk mengurutkan berdasarkan skor relevansi, Anda harus secara eksplisit menentukan ScoreSort. Jika tidak, Tablestore akan mengurutkan hasil sesuai dengan pengaturan IndexSort pada indeks.

  • Saat menggunakan ScoreSort, bidang bertipe FuzzyKeyword tidak termasuk dalam pengurutan, dan parameter weight tidak berpengaruh pada bidang tersebut.

SearchQuery searchQuery = new SearchQuery();
searchQuery.setSort(new Sort(Arrays.asList(new ScoreSort())));

PrimaryKeySort

Mengurutkan hasil berdasarkan primary key.

SearchQuery searchQuery = new SearchQuery();
searchQuery.setSort(new Sort(Arrays.asList(new PrimaryKeySort()))); // Dalam urutan ascending.
//searchQuery.setSort(new Sort(Arrays.asList(new PrimaryKeySort(SortOrder.DESC)))); // Dalam urutan descending.

FieldSort

Mengurutkan hasil berdasarkan nilai bidang.

Pengurutan satu bidang

Mengurutkan hasil berdasarkan nilai dalam satu bidang.

SearchQuery searchQuery = new SearchQuery();
searchQuery.setSort(new Sort(Arrays.asList(new FieldSort("col", SortOrder.ASC))));

Pengurutan multi-bidang

Mengurutkan hasil terlebih dahulu berdasarkan nilai dalam satu bidang, lalu berdasarkan nilai dalam bidang lainnya.

SearchQuery searchQuery = new SearchQuery();
searchQuery.setSort(new Sort(Arrays.asList(
    new FieldSort("col1", SortOrder.ASC), new FieldSort("col2", SortOrder.ASC))));

Bidang fallback

Saat mengurutkan berdasarkan bidang bertipe Long, Double, atau Date, Anda dapat mengatur parameter missingField untuk menentukan bidang lain dengan tipe yang sama sebagai nilai fallback jika suatu baris tidak memiliki nilai pada bidang utama pengurutan.

/**
* Urutkan hasil dalam urutan descending berdasarkan nilai pada bidang Col_Long. 
* Jika suatu baris tidak memiliki nilai pada bidang Col_Long (tipe Long), 
* nilai dari bidang Col_Long_sec (tipe Long) digunakan sebagai gantinya untuk pengurutan.
*/
SearchQuery searchQuery = new SearchQuery();
FieldSort fieldSort = new FieldSort("Col_Long");
// Tentukan bidang Col_Long_sec sebagai fallback untuk pengurutan saat nilai pada bidang Col_Long tidak tersedia.
fieldSort.setMissingField("Col_Long_sec");
fieldSort.setOrder(SortOrder.DESC); 

Nilai yang hilang

Parameter missingValue menentukan posisi pengurutan untuk dokumen yang tidak memiliki bidang pengurutan. Anda dapat mengatur parameter ini untuk mengontrol di mana dokumen tersebut muncul dalam hasil.

Perilaku pengurutan adalah sebagai berikut:

  • Jika missingValue diatur ke FieldSort.FIRST_WHEN_MISSING, dokumen dengan nilai yang hilang selalu ditempatkan di awal hasil, terlepas dari urutan pengurutan (ascending atau descending).

  • Jika missingValue diatur ke FieldSort.LAST_WHEN_MISSING atau null, dokumen dengan nilai yang hilang selalu ditempatkan di akhir hasil, terlepas dari urutan pengurutan.

    /**
    * Urutkan hasil dalam urutan descending berdasarkan nilai pada bidang Col_Long (tipe Long).
    * Jika suatu baris tidak memiliki nilai pada bidang Col_Long, tempatkan dokumen tersebut di awal hasil.
    */
    SearchQuery searchQuery = new SearchQuery();
    FieldSort fieldSort = new FieldSort("Col_Long");
    // Tempatkan dokumen dengan nilai yang hilang di awal.
    fieldSort.setMissingValue(FieldSort.FIRST_WHEN_MISSING);
    fieldSort.setOrder(SortOrder.DESC);
    searchQuery.setSort(new Sort(Arrays.asList(fieldSort)));

Bidang bernilai ganda

Untuk bidang bernilai ganda, seperti bidang bertipe array atau nested, Anda dapat menggunakan parameter mode untuk menentukan elemen mana yang digunakan untuk pengurutan.

Urutkan berdasarkan nilai tertentu dalam array bernilai ganda.

// Asumsikan Anda memiliki dua baris, doc1 dan doc2. Keduanya memiliki field1 bertipe array.
// Nilai field1 pada doc1 adalah [2,3]. Nilai field1 pada doc2 adalah [1,3,4].
// Anda dapat mengatur parameter mode untuk menentukan nilai mana dalam array yang digunakan untuk pengurutan.
{
    // Ketika mode diatur ke SortMode.MAX, urutan pengurutan adalah doc2 (diurutkan berdasarkan 4), lalu doc1 (diurutkan berdasarkan 3).
    FieldSort fieldSort = new FieldSort("field1", SortOrder.DESC);
    fieldSort.setMode(SortMode.MAX);
}
{
    // Ketika mode diatur ke SortMode.MIN, urutan pengurutan adalah doc1 (diurutkan berdasarkan 2), lalu doc2 (diurutkan berdasarkan 1).
    FieldSort fieldSort = new FieldSort("field1", SortOrder.DESC);
    fieldSort.setMode(SortMode.MIN);
}

Anda juga dapat mengurutkan sub-baris dari bidang bertipe nested.

// Asumsikan Anda memiliki dua baris, doc1 dan doc2. Keduanya memiliki field1 bertipe nested.
// Nilai field1 pada doc1 adalah [{"name":"b", "age":1},{"name":"a", "age":7}].
// Nilai field1 pada doc2 adalah [{"name":"a", "age":1},{"name":"c", "age":1},{"name":"d", "age":5}].

{
    // Urutkan semua sub-baris dan gunakan parameter mode untuk menentukan nilai mana yang digunakan untuk pengurutan.
    // Ketika mode diatur ke SortMode.MAX dan Anda mengurutkan berdasarkan bidang age, hasilnya adalah doc1 (diurutkan berdasarkan 7), lalu doc2 (diurutkan berdasarkan 5).
    FieldSort fieldSort = new FieldSort("field1.age", SortOrder.DESC);
    fieldSort.setMode(SortMode.MAX);
    String path = "field1";
    NestedFilter nestedFilter = new NestedFilter(path, QueryBuilders.matchAll().build());
    fieldSort.setNestedFilter(nestedFilter);
}
{
    // Urutkan hanya sub-baris di mana age=1, dan gunakan parameter mode untuk menentukan nilai mana yang digunakan.
    {
        // Ketika mode diatur ke SortMode.MAX dan Anda mengurutkan berdasarkan bidang name, hasilnya adalah doc2 (diurutkan berdasarkan "c"), lalu doc1 (diurutkan berdasarkan "b").
        FieldSort fieldSort = new FieldSort("field1.name", SortOrder.DESC);
        fieldSort.setMode(SortMode.MAX);
        String path = "field1";
        NestedFilter nestedFilter = new NestedFilter(path, QueryBuilders.term("field1.age",1).build());
        fieldSort.setNestedFilter(nestedFilter);
    }
    {
        // Ketika mode diatur ke SortMode.MIN dan Anda mengurutkan berdasarkan bidang name, hasilnya adalah doc1 (diurutkan berdasarkan "b"), lalu doc2 (diurutkan berdasarkan "a").
        FieldSort fieldSort = new FieldSort("field1.name", SortOrder.DESC);
        fieldSort.setMode(SortMode.MIN);
        String path = "field1";
        NestedFilter nestedFilter = new NestedFilter(path, QueryBuilders.term("field1.age",1).build());
        fieldSort.setNestedFilter(nestedFilter);
    }
}

GeoDistanceSort

Mengurutkan hasil berdasarkan jarak dari titik geografis.

SearchQuery searchQuery = new SearchQuery();
// Bidang 'geo' bertipe GeoPoint. Urutkan hasil berdasarkan jarak
// dari nilai dalam bidang ini ke titik "0,0".
Sort.Sorter sorter = new GeoDistanceSort("geo", Arrays.asList("0, 0"));
searchQuery.setSort(new Sort(Arrays.asList(sorter)));

Metode paging

Untuk melakukan paging pada hasil kueri, Anda dapat menggunakan parameter limit dan offset atau token.

Offset-based paging

Gunakan offset-based paging ketika jumlah total baris yang akan diambil kurang dari 100.000. Jumlah dari limit dan offset harus kurang dari atau sama dengan 100.000, dan nilai maksimum untuk limit adalah 100.

Catatan

Untuk meningkatkan ambang batas limit, lihat Bagaimana cara meningkatkan limit API Pencarian menjadi 1.000?.

Jika parameter limit dan offset tidak diatur, limit akan default ke 10 dan offset akan default ke 0.

SearchQuery searchQuery = new SearchQuery();
searchQuery.setQuery(new MatchAllQuery());
searchQuery.setLimit(100);
searchQuery.setOffset(100);

Token-based paging

Token-based paging direkomendasikan untuk deep paging karena tidak memiliki batas kedalaman.

Jika respons tidak berisi semua data yang cocok, server akan mengembalikan nextToken.

Secara default, token-based paging hanya memungkinkan Anda bergerak maju melalui hasil. Namun, karena token tetap valid selama proses kueri, Anda dapat menyimpan cache token sebelumnya untuk melakukan paging mundur.

Penting

Jika Anda perlu menyimpan nextToken secara persisten atau mengirimkannya ke aplikasi front-end, encode-kan token tersebut ke Base64 menjadi string. Token merupakan array byte, bukan string. Mengonversinya langsung ke string dengan new String(nextToken) menyebabkan kehilangan data.

Saat melakukan paging dengan token, urutan pengurutan mengikuti permintaan sebelumnya, baik menggunakan IndexSort default maupun pengurutan kustom. Oleh karena itu, Anda tidak dapat mengatur parameter Sort saat menggunakan token. Anda juga tidak dapat mengatur offset, karena data hanya dapat dibaca secara berurutan.

Penting

Indeks pencarian yang berisi bidang bertipe nested tidak mendukung IndexSort. Jika Anda perlu melakukan paging pada hasil dari indeks semacam itu, Anda harus menentukan urutan pengurutan dalam kueri. Jika tidak, server tidak akan mengembalikan nextToken meskipun masih ada data tambahan.

private static void readMoreRowsWithToken(SyncClient client) {
    SearchQuery searchQuery = new SearchQuery();
    searchQuery.setQuery(new MatchAllQuery());
    searchQuery.setGetTotalCount(true);// Atur agar mengembalikan jumlah total baris yang cocok.
    // Tentukan nama tabel data (misalnya, sampleTable) dan nama indeks pencarian (misalnya, sampleSearchIndex). 
    // Anda dapat menemukan nama indeks pencarian di tab Manajemen Indeks untuk tabel di Konsol Tablestore, atau dengan mencantumkan indeks pencarian menggunakan SDK.
    SearchRequest searchRequest = new SearchRequest("sampleTable", "sampleSearchIndex", searchQuery);

    SearchResponse resp = client.search(searchRequest);
    if (!resp.isAllSuccess()) {
        throw new RuntimeException("not all success");
    }
    List<Row> rows = resp.getRows();
    while (resp.getNextToken()!=null) { // nextToken null berarti semua data telah dibaca.
        // Dapatkan nextToken.
        byte[] nextToken = resp.getNextToken();

        {
            // Jika Anda perlu menyimpan nextToken secara persisten atau mengirimkannya ke aplikasi front-end, 
            // gunakan Base64 untuk mengencode nextToken menjadi string untuk penyimpanan dan transfer.
            // Token merupakan array byte. Menggunakan new String(nextToken) secara langsung akan menyebabkan kehilangan data.
            String tokenAsString = Base64.toBase64String(nextToken);
            // Decode string kembali menjadi array byte.
            byte[] tokenAsByte = Base64.fromBase64String(tokenAsString);
        }

        // Atur token untuk permintaan berikutnya.
        searchRequest.getSearchQuery().setToken(nextToken);
        resp = client.search(searchRequest);
        if (!resp.isAllSuccess()) {
            throw new RuntimeException("not all success");
        }
        rows.addAll(resp.getRows());
    }
    System.out.println("RowSize: " + rows.size());
    System.out.println("TotalCount: " + resp.getTotalCount());// Mencetak jumlah total baris yang cocok, bukan jumlah baris yang dikembalikan.
}