すべてのプロダクト
Search
ドキュメントセンター

Tablestore:ソートとページング

最終更新日:May 13, 2026

検索インデックスを使用してデータをクエリする際、事前にソート方法を定義するか、クエリ時に指定することで、結果の順序を制御できます。結果セットが大きい場合は、ページングを使用して必要なデータに素早くアクセスできます。

IndexSort

デフォルトでは、検索インデックスは設定された IndexSort に基づいてデータをソートします。検索インデックスを使用してデータをクエリする際、IndexSort がデフォルトの結果順序を決定します。

検索インデックスを作成する際に、カスタムの IndexSort を定義できます。カスタムの IndexSort を指定しない場合、デフォルトでプライマリキーによるソートが適用されます。

重要
  • IndexSort は、PrimaryKeySort (プライマリキーによるソート) と FieldSort (フィールド値によるソート) のみをサポートします。

  • ネスト型フィールドを含む検索インデックスは、IndexSort をサポートしません。

  • 検索インデックスを作成した後、動的スキーマ変更機能を使用して IndexSort 設定を変更できます。

クエリ時のソート

EnableSortAndAggtrue に設定されているフィールドに対してのみサポートされます。

各クエリに対してソート方法を指定できます。検索インデックスは 4 種類のソート方法をサポートしています。また、複数のソート方法を組み合わせて、一連の基準に基づいて結果をソートすることも可能です。

ScoreSort

BM25 アルゴリズムで計算された関連性スコアに基づいて、結果をソートします。この方法は、全文検索など、関連性ランキングが必要なシナリオに適しています。

重要
  • 関連性スコアで結果をソートするには、明示的に ScoreSort を指定する必要があります。指定しない場合、結果はインデックスの IndexSort 設定に基づいてソートされます。

  • ScoreSort を使用する場合、FuzzyKeyword フィールドはソート処理に含まれず、weight パラメータはこれらのフィールドに影響しません。

searchQuery := search.NewSearchQuery()
searchQuery.SetSort(&search.Sort{
    []search.Sorter{
        &search.ScoreSort{
            Order: search.SortOrder_DESC.Enum(), // スコアの降順でソートします
        },
    },
})

PrimaryKeySort

プライマリキー に基づいて結果をソートします。これは、一意の識別子でアイテムを順序付ける場合に便利です。

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

FieldSort

特定の フィールド の値に基づいて結果をソートします。この方法は、販売数やページビューなどの属性でアイテムをソートする必要がある e コマースやソーシャルメディアアプリケーションで便利です。

単一列のソート

単一のフィールドの値に基づいて結果をソートします。

// Col_Long フィールドに基づいて結果を降順にソートします
searchQuery.SetSort(&search.Sort{
    []search.Sorter{
        &search.FieldSort{
            FieldName: "Col_Long",
            Order:     search.SortOrder_DESC.Enum(),
        },
    },
})

複数列のソート

まず 1 つのフィールドの値で結果をソートし、次に別のフィールドの値でソートします。

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

欠損値のソート

ドキュメントにソートフィールドが存在しない場合、MissingValue パラメータがソート結果内での位置を決定します。

ソート動作は以下の通りです:

  • MissingValuesearch.FirstWhenMissing に設定した場合、フィールドが欠損しているドキュメントは、ソート順序が昇順 (asc) か降順 (desc) かに関わらず、先頭に配置されます。

  • MissingValuesearch.LastWhenMissing または nil に設定した場合、フィールドが欠損しているドキュメントは、ソート順序に関わらず、末尾に配置されます。

    // view_at フィールドに基づいて結果を昇順にソートし、
    // 欠損値を持つドキュメントを末尾に配置します
    searchQuery.SetSort(&search.Sort{
        []search.Sorter{
            &search.FieldSort{
                FieldName:    "view_at",
                Order:        search.SortOrder_ASC.Enum(),
                MissingValue: search.LastWhenMissing,
            },
        },
    })

GeoDistanceSort

地理的な地点からの距離に基づいて結果をソートします。これは、現在地から近いレストランを距離順にソートするなど、マッピングやロジスティクスのアプリケーションで便利です。

searchQuery.SetSort(&search.Sort{
    []search.Sorter{
        &search.GeoDistanceSort{
            FieldName: "location",   // GeoPoint フィールドの名前を指定します
            Points:    []string{"40,-70"}, // 中心点を指定します
        },
    },
})

ページング方法

結果をページングするには、Limit および Offset パラメータ、または token を使用できます。

Limit と Offset によるページング

LimitOffset を使用してページングできます。LimitOffset の合計は 100,000 以下である必要があり、Limit の最大値は 100 です。

説明

Limit パラメータの上限を引き上げる方法については、「検索インデックスクエリの limit パラメータを 1000 に増やす方法」をご参照ください。

これらのパラメータを指定しない場合、Limit のデフォルト値は 10、Offset のデフォルト値は 0 です。

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

トークンによるページング

ディープページングの場合は token を使用してください。この方法には深さの制限がありません。

クエリが単一のレスポンスに収まりきらない結果を返す場合、サーバーは NextToken を提供します。このトークンを後続のリクエストで使用して、次のページを取得します。

デフォルトでは、トークンベースのページングは前方へのみ移動できます。ただし、token はクエリセッション中有効であるため、以前のトークンをキャッシュすることで後方へのページングも実装できます。

重要

NextToken を永続化したり、フロントエンドアプリケーションに送信したりする必要がある場合は、Base64 エンコーディングを使用して文字列に変換し、保存および送信してください。トークンはバイトスライスであり、文字列ではないため、string(NextToken) を使用して直接変換すると、データ損失が発生します。

token を使用する場合、前回のリクエストのソート方法が自動的に適用されます。そのため、トークンベースのリクエストでは Sort または Offset パラメータを指定できません。データは順次、1 ページずつのみ読み取ることができます。

重要

ネスト型フィールドを含む検索インデックスは IndexSort をサポートしません。そのため、ページングを有効にするには、クエリでソート方法を指定する必要があります。ソート順序を指定しない場合、後続の結果が存在する場合でも、サーバーは NextToken を返しません。

/**
 * トークンを使用してページごとにデータを読み取ります。
 * SearchResponse に NextToken が含まれている場合、それを使用して次のクエリを開始できます。
 * nil の NextToken は、一致するすべてのデータが取得されたことを示します。
 */
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 {           
            {  
	              // NextToken を永続化または送信するには、Base64 エンコードを使用します。
	              // トークンはバイトスライスであり、文字列ではないため、直接変換するとデータ損失が発生します。
	              tokenAsString := base64.StdEncoding.EncodeToString(searchResponse.NextToken)
	              // 文字列をバイトスライスにデコードします。
	              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)
    }
}