検索インデックスを使用してデータをクエリする際、事前にソート方法を定義するか、クエリ時に指定することで、結果の順序を制御できます。結果セットが大きい場合は、ページングを使用して必要なデータに素早くアクセスできます。
IndexSort
デフォルトでは、検索インデックスは設定された IndexSort に基づいてデータをソートします。検索インデックスを使用してデータをクエリする際、IndexSort がデフォルトの結果順序を決定します。
検索インデックスを作成する際に、カスタムの IndexSort を定義できます。カスタムの IndexSort を指定しない場合、デフォルトでプライマリキーによるソートが適用されます。
IndexSortは、PrimaryKeySort(プライマリキーによるソート) とFieldSort(フィールド値によるソート) のみをサポートします。ネスト型フィールドを含む検索インデックスは、
IndexSortをサポートしません。検索インデックスを作成した後、動的スキーマ変更機能を使用して
IndexSort設定を変更できます。
クエリ時のソート
EnableSortAndAgg が true に設定されているフィールドに対してのみサポートされます。
各クエリに対してソート方法を指定できます。検索インデックスは 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 パラメータがソート結果内での位置を決定します。
ソート動作は以下の通りです:
MissingValueをsearch.FirstWhenMissingに設定した場合、フィールドが欠損しているドキュメントは、ソート順序が昇順 (asc) か降順 (desc) かに関わらず、先頭に配置されます。MissingValueをsearch.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 によるページング
Limit と Offset を使用してページングできます。Limit と Offset の合計は 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)
}
}