All Products
Search
Document Center

Tablestore:Sorting and Paging

Last Updated:May 13, 2026

When you query data with a search index, you can control the result order by predefining a sorting method or specifying one at query time. If the result set is large, use paging to navigate to the data you need quickly.

IndexSort

By default, a search index sorts data based on its configured IndexSort. When you query data with the search index, IndexSort determines the default result order.

You can define a custom IndexSort when you create a search index. If you do not specify a custom IndexSort, it defaults to sorting by the primary key.

Important
  • IndexSort supports only PrimaryKeySort (sorts by primary key) and FieldSort (sorts by field value).

  • Search indexes with nested-type fields do not support IndexSort.

  • After you create a search index, you can use the dynamic schema modification feature to change its IndexSort setting.

Sorting at query time

Sorting is supported only for fields for which EnableSortAndAgg is set to true.

You can specify a sorting method for each query. A search index supports four types of sorters. You can also use multiple sorters to sort the results based on a sequence of criteria.

ScoreSort

Sorts results by their relevance score, calculated with the BM25 algorithm. This method is suitable for scenarios that require relevance ranking, such as full-text search.

Important
  • To sort results by relevance score, you must explicitly specify ScoreSort. Otherwise, the results are sorted based on the index's IndexSort setting.

  • When you use ScoreSort, FuzzyKeyword fields are not included in the sorting process, and the weight parameter has no effect on these fields.

searchQuery := search.NewSearchQuery()
searchQuery.SetSort(&search.Sort{
    []search.Sorter{
        &search.ScoreSort{
            Order: search.SortOrder_DESC.Enum(), // Sort in descending order of score.
        },
    },
})

PrimaryKeySort

Sorts results by primary key, which is useful for ordering items by their unique identifiers.

searchQuery := search.NewSearchQuery()
searchQuery.SetSort(&search.Sort{
    []search.Sorter{
        &search.PrimaryKeySort{
            Order: search.SortOrder_ASC.Enum(),
        },
    },
})

FieldSort

Sorts results by the value of a specific field. This method is useful in e-commerce or social media applications where you might want to sort items by attributes like sales volume or page views.

Single-column sorting

Sorts results based on the values in a single field.

// Sort the results based on the Col_Long field in descending order.
searchQuery.SetSort(&search.Sort{
    []search.Sorter{
        &search.FieldSort{
            FieldName: "Col_Long",
            Order:     search.SortOrder_DESC.Enum(),
        },
    },
})

Multi-column sorting

Sorts results first by the values in one field, then by the values in another.

searchQuery.SetSort(&search.Sort{
    []search.Sorter{
        &search.FieldSort{
            FieldName: "col1",
            Order:     search.SortOrder_ASC.Enum(),
        },
        &search.FieldSort{
            FieldName: "col2",
            Order:     search.SortOrder_DESC.Enum(),
        },
    },
})

Missing value sorting

When a sorting field is missing from a document, the MissingValue parameter determines its position in the sorted results.

The sorting behavior is as follows:

  • If you set MissingValue to search.FirstWhenMissing, documents missing the field are placed at the beginning, regardless of whether the sort order is ascending (asc) or descending (desc).

  • If you set MissingValue to search.LastWhenMissing or nil, documents missing the field are placed at the end, regardless of the sort order.

    // Sort the results based on the view_at field in ascending order,
    // and place documents with missing values at the end.
    searchQuery.SetSort(&search.Sort{
        []search.Sorter{
            &search.FieldSort{
                FieldName:    "view_at",
                Order:        search.SortOrder_ASC.Enum(),
                MissingValue: search.LastWhenMissing,
            },
        },
    })

GeoDistanceSort

Sorts results by distance from a geographic point. This is useful for applications in mapping and logistics, such as sorting nearby restaurants by their distance from your current location.

searchQuery.SetSort(&search.Sort{
    []search.Sorter{
        &search.GeoDistanceSort{
            FieldName: "location",   // Specify the name of the GeoPoint field.
            Points:    []string{"40,-70"}, // Specify the center point.
        },
    },
})

Paging methods

To page through results, you can use either the Limit and Offset parameters or a token.

Paging with Limit and Offset

You can use Limit and Offset for paging. The sum of Limit and Offset must be less than or equal to 100,000, and the maximum value for Limit is 100.

Note

To increase the upper limit of the Limit parameter, see How do I increase the limit parameter to 1000 for search index queries?.

If you do not specify these parameters, Limit defaults to 10 and Offset defaults to 0.

searchQuery := search.NewSearchQuery()
searchQuery.SetLimit(10)
searchQuery.SetOffset(10) 

Paging with a token

For deep paging, use a token, as this method has no depth limitations.

If a query returns more results than a single response can hold, the server provides a NextToken. Use this token in a subsequent request to retrieve the next page.

By default, token-based paging only allows you to move forward. However, since a token remains valid for the duration of a query session, you can cache previous tokens to implement backward paging.

Important

If you need to persist the NextToken or send it to a front-end application, use Base64 encoding to convert it into a string for storage and transmission. Because the token is a byte slice, not a string, directly converting it by using string(NextToken) results in data loss.

When using a token, the sorting method from the previous request is automatically applied. Therefore, you cannot specify the Sort or Offset parameters in a token-based request. You can only read data sequentially, one page at a time.

Important

Search indexes with nested-type fields do not support IndexSort. Therefore, you must specify a sorting method in your query to enable paging. Without a specified sort order, the server will not return a NextToken, even if more results exist.

/**
 * Read data page by page by using a token.
 * If a SearchResponse contains a NextToken, you can use it to initiate the next query.
 * A nil NextToken indicates that all matching data has been retrieved.
 */
func QueryRowsWithToken(client *tablestore.TableStoreClient, tableName string, indexName string) {
    querys := []search.Query{
        &search.MatchAllQuery{},
        &search.TermQuery{
            FieldName: "Col_Keyword",
            Term:      "tablestore",
        },
    }
    for _, query := range querys {
        fmt.Printf("Test query: %#v\n", query)
        searchRequest := &tablestore.SearchRequest{}
        searchRequest.SetTableName(tableName)
        searchRequest.SetIndexName(indexName)
        searchQuery := search.NewSearchQuery()
        searchQuery.SetQuery(query)
        searchQuery.SetLimit(10)
        searchQuery.SetGetTotalCount(true)
        searchRequest.SetSearchQuery(searchQuery)
        searchResponse, err := client.Search(searchRequest)
        if err != nil {
            fmt.Printf("%#v", err)
            return
        }
        rows := searchResponse.Rows
        requestCount := 1
        for searchResponse.NextToken != nil {           
            {  
	              // To persist or send the NextToken, Base64-encode it.
	              // The token is a byte slice, not a string; direct conversion causes data loss.
	              tokenAsString := base64.StdEncoding.EncodeToString(searchResponse.NextToken)
	              // Decode the string back into a byte slice.
	              tokenAsByte, err := base64.StdEncoding.DecodeString(tokenAsString)
	              if err != nil {
		                fmt.Printf("len:%d, %#v",len(tokenAsByte), err)
		                return
	              }
            }
            searchQuery.SetToken(searchResponse.NextToken)
            searchResponse, err = client.Search(searchRequest)
            if err != nil {
                fmt.Printf("%#v", err)
                return
            }
            requestCount++
            for _, r := range searchResponse.Rows {
                rows = append(rows, r)
            }
        }
        fmt.Println("IsAllSuccess: ", searchResponse.IsAllSuccess)
        fmt.Println("TotalCount: ", searchResponse.TotalCount)
        fmt.Println("RowsSize: ", len(rows))
        fmt.Println("RequestCount: ", requestCount)
    }
}