在这个例子中,我们将使用Elasticsearch的Search API来查询索引,并在Golang中分页显示结果集。我们将使用经典的from 和size 参数,因为from+size 总是小于或等于10000 。然而,如果你不能保证这一点,你应该坚持使用Search after功能。这个功能最好的地方是它使用了时间点API(PIT)。当刷新发生在分页请求之间时,结果的顺序可能会改变,这将导致不同页面之间的不一致。时间点API保留了当前的索引状态以防止这种问题。
elasticsearch.go
package elasticsearch
...
// postsResponse represents list of posts in Search API response body.
type postsResponse struct {
Hits struct {
Total struct {
Value int `json:"value"`
} `json:"total"`
Hits []struct {
Source *storage.Post `json:"_source"`
} `json:"hits"`
} `json:"hits"`
}
post_storer.go
package storage
...
type PostStorer interface {
...
// The returned `int` represents all the found docs, not the ones in the current page!
ListAll(ctx context.Context, from, size int) (int, []*Post, error)
}
elasticsearch.go
package elasticsearch
...
// Validation
// from >= 0
// size >= 0
// from+size <= 10000
func (p PostStorage) ListAll(ctx context.Context, from, size int) (int, []*storage.Post, error) {
// res, err := p.elastic.client.Search()
req := esapi.SearchRequest{
Index: []string{p.elastic.alias},
From: &from,
Size: &size,
}
ctx, cancel := context.WithTimeout(ctx, p.timeout)
defer cancel()
res, err := req.Do(ctx, p.elastic.client)
if err != nil {
return 0, nil, fmt.Errorf("list all: request: %w", err)
}
defer res.Body.Close()
if res.IsError() {
return 0, nil, fmt.Errorf("list all: response: %s", res.String())
}
var body postsResponse
if err := json.NewDecoder(res.Body).Decode(&body); err != nil {
return 0, nil, fmt.Errorf("list all: decode: %w", err)
}
posts := make([]*storage.Post, len(body.Hits.Hits))
for i, v := range body.Hits.Hits {
posts[i] = v.Source
}
return body.Hits.Total.Value, posts, nil
}