Skip to content

Commit eaba086

Browse files
committed
Add Indices Segments API
Close #679
1 parent 25a0282 commit eaba086

File tree

4 files changed

+329
-1
lines changed

4 files changed

+329
-1
lines changed

README.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,7 @@ See the [wiki](https://github.com/olivere/elastic/wiki) for more details.
237237
- [x] Index Templates
238238
- [ ] Shadow Replica Indices
239239
- [x] Indices Stats
240-
- [ ] Indices Segments
240+
- [x] Indices Segments
241241
- [ ] Indices Recovery
242242
- [ ] Indices Shard Stores
243243
- [ ] Clear Cache

client.go

+5
Original file line numberDiff line numberDiff line change
@@ -1518,6 +1518,11 @@ func (c *Client) IndexPutSettings(indices ...string) *IndicesPutSettingsService
15181518
return NewIndicesPutSettingsService(c).Index(indices...)
15191519
}
15201520

1521+
// IndexSegments retrieves low level segment information for all, one or more indices.
1522+
func (c *Client) IndexSegments(indices ...string) *IndicesSegmentsService {
1523+
return NewIndicesSegmentsService(c).Index(indices...)
1524+
}
1525+
15211526
// IndexAnalyze performs the analysis process on a text and returns the
15221527
// token breakdown of the text.
15231528
func (c *Client) IndexAnalyze() *IndicesAnalyzeService {

indices_segments.go

+237
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,237 @@
1+
// Copyright 2012-present Oliver Eilhard. All rights reserved.
2+
// Use of this source code is governed by a MIT-license.
3+
// See http://olivere.mit-license.org/license.txt for details.
4+
5+
package elastic
6+
7+
import (
8+
"context"
9+
"encoding/json"
10+
"fmt"
11+
"net/url"
12+
"strings"
13+
14+
"github.com/olivere/elastic/uritemplates"
15+
)
16+
17+
// IndicesSegmentsService provides low level segments information that a
18+
// Lucene index (shard level) is built with. Allows to be used to provide
19+
// more information on the state of a shard and an index, possibly
20+
// optimization information, data "wasted" on deletes, and so on.
21+
//
22+
// Find further documentation at
23+
// https://www.elastic.co/guide/en/elasticsearch/reference/6.1/indices-segments.html.
24+
type IndicesSegmentsService struct {
25+
client *Client
26+
pretty bool
27+
index []string
28+
allowNoIndices *bool
29+
expandWildcards string
30+
ignoreUnavailable *bool
31+
human *bool
32+
operationThreading interface{}
33+
verbose *bool
34+
}
35+
36+
// NewIndicesSegmentsService creates a new IndicesSegmentsService.
37+
func NewIndicesSegmentsService(client *Client) *IndicesSegmentsService {
38+
return &IndicesSegmentsService{
39+
client: client,
40+
}
41+
}
42+
43+
// Index is a comma-separated list of index names; use `_all` or empty string
44+
// to perform the operation on all indices.
45+
func (s *IndicesSegmentsService) Index(indices ...string) *IndicesSegmentsService {
46+
s.index = append(s.index, indices...)
47+
return s
48+
}
49+
50+
// AllowNoIndices indicates whether to ignore if a wildcard indices expression
51+
// resolves into no concrete indices. (This includes `_all` string or when
52+
// no indices have been specified).
53+
func (s *IndicesSegmentsService) AllowNoIndices(allowNoIndices bool) *IndicesSegmentsService {
54+
s.allowNoIndices = &allowNoIndices
55+
return s
56+
}
57+
58+
// ExpandWildcards indicates whether to expand wildcard expression to concrete indices
59+
// that are open, closed or both.
60+
func (s *IndicesSegmentsService) ExpandWildcards(expandWildcards string) *IndicesSegmentsService {
61+
s.expandWildcards = expandWildcards
62+
return s
63+
}
64+
65+
// IgnoreUnavailable indicates whether specified concrete indices should be
66+
// ignored when unavailable (missing or closed).
67+
func (s *IndicesSegmentsService) IgnoreUnavailable(ignoreUnavailable bool) *IndicesSegmentsService {
68+
s.ignoreUnavailable = &ignoreUnavailable
69+
return s
70+
}
71+
72+
// Human, when set to true, returns time and byte-values in human-readable format.
73+
func (s *IndicesSegmentsService) Human(human bool) *IndicesSegmentsService {
74+
s.human = &human
75+
return s
76+
}
77+
78+
// OperationThreading is undocumented in Elasticsearch as of now.
79+
func (s *IndicesSegmentsService) OperationThreading(operationThreading interface{}) *IndicesSegmentsService {
80+
s.operationThreading = operationThreading
81+
return s
82+
}
83+
84+
// Verbose, when set to true, includes detailed memory usage by Lucene.
85+
func (s *IndicesSegmentsService) Verbose(verbose bool) *IndicesSegmentsService {
86+
s.verbose = &verbose
87+
return s
88+
}
89+
90+
// Pretty indicates that the JSON response be indented and human readable.
91+
func (s *IndicesSegmentsService) Pretty(pretty bool) *IndicesSegmentsService {
92+
s.pretty = pretty
93+
return s
94+
}
95+
96+
// buildURL builds the URL for the operation.
97+
func (s *IndicesSegmentsService) buildURL() (string, url.Values, error) {
98+
var err error
99+
var path string
100+
101+
if len(s.index) > 0 {
102+
path, err = uritemplates.Expand("/{index}/_segments", map[string]string{
103+
"index": strings.Join(s.index, ","),
104+
})
105+
} else {
106+
path = "/_segments"
107+
}
108+
if err != nil {
109+
return "", url.Values{}, err
110+
}
111+
112+
// Add query string parameters
113+
params := url.Values{}
114+
if s.pretty {
115+
params.Set("pretty", "true")
116+
}
117+
if s.allowNoIndices != nil {
118+
params.Set("allow_no_indices", fmt.Sprintf("%v", *s.allowNoIndices))
119+
}
120+
if s.expandWildcards != "" {
121+
params.Set("expand_wildcards", s.expandWildcards)
122+
}
123+
if s.ignoreUnavailable != nil {
124+
params.Set("ignore_unavailable", fmt.Sprintf("%v", *s.ignoreUnavailable))
125+
}
126+
if s.human != nil {
127+
params.Set("human", fmt.Sprintf("%v", *s.human))
128+
}
129+
if s.operationThreading != nil {
130+
params.Set("operation_threading", fmt.Sprintf("%v", s.operationThreading))
131+
}
132+
if s.verbose != nil {
133+
params.Set("verbose", fmt.Sprintf("%v", *s.verbose))
134+
}
135+
return path, params, nil
136+
}
137+
138+
// Validate checks if the operation is valid.
139+
func (s *IndicesSegmentsService) Validate() error {
140+
return nil
141+
}
142+
143+
// Do executes the operation.
144+
func (s *IndicesSegmentsService) Do(ctx context.Context) (*IndicesSegmentsResponse, error) {
145+
// Check pre-conditions
146+
if err := s.Validate(); err != nil {
147+
return nil, err
148+
}
149+
150+
// Get URL for request
151+
path, params, err := s.buildURL()
152+
if err != nil {
153+
return nil, err
154+
}
155+
156+
// Get HTTP response
157+
res, err := s.client.PerformRequest(ctx, PerformRequestOptions{
158+
Method: "GET",
159+
Path: path,
160+
Params: params,
161+
})
162+
if err != nil {
163+
return nil, err
164+
}
165+
166+
// Return operation response
167+
ret := new(IndicesSegmentsResponse)
168+
if err := json.Unmarshal(res.Body, ret); err != nil {
169+
return nil, err
170+
}
171+
return ret, nil
172+
}
173+
174+
// IndicesSegmentsResponse is the response of IndicesSegmentsService.Do.
175+
type IndicesSegmentsResponse struct {
176+
// Shards provides information returned from shards.
177+
Shards shardsInfo `json:"_shards"`
178+
179+
// Indices provides a map into the stats of an index.
180+
// The key of the map is the index name.
181+
Indices map[string]*IndexSegments `json:"indices,omitempty"`
182+
}
183+
184+
type IndexSegments struct {
185+
// Shards provides a map into the shard related information of an index.
186+
// The key of the map is the number of a specific shard.
187+
Shards map[string][]*IndexSegmentsShards `json:"shards,omitempty"`
188+
}
189+
190+
type IndexSegmentsShards struct {
191+
Routing *IndexSegmentsRouting `json:"routing,omitempty"`
192+
NumCommittedSegments int64 `json:"num_committed_segments,omitempty"`
193+
NumSearchSegments int64 `json:"num_search_segments"`
194+
195+
// Segments provides a map into the segment related information of a shard.
196+
// The key of the map is the specific lucene segment id.
197+
Segments map[string]*IndexSegmentsDetails `json:"segments,omitempty"`
198+
}
199+
200+
type IndexSegmentsRouting struct {
201+
State string `json:"state,omitempty"`
202+
Primary bool `json:"primary,omitempty"`
203+
Node string `json:"node,omitempty"`
204+
RelocatingNode string `json:"relocating_node,omitempty"`
205+
}
206+
207+
type IndexSegmentsDetails struct {
208+
Generation int64 `json:"generation,omitempty"`
209+
NumDocs int64 `json:"num_docs,omitempty"`
210+
DeletedDocs int64 `json:"deleted_docs,omitempty"`
211+
Size string `json:"size,omitempty"`
212+
SizeInBytes int64 `json:"size_in_bytes,omitempty"`
213+
Memory string `json:"memory,omitempty"`
214+
MemoryInBytes int64 `json:"memory_in_bytes,omitempty"`
215+
Committed bool `json:"committed,omitempty"`
216+
Search bool `json:"search,omitempty"`
217+
Version string `json:"version,omitempty"`
218+
Compound bool `json:"compound,omitempty"`
219+
MergeId string `json:"merge_id,omitempty"`
220+
Sort []*IndexSegmentsSort `json:"sort,omitempty"`
221+
RAMTree []*IndexSegmentsRamTree `json:"ram_tree,omitempty"`
222+
Attributes map[string]string `json:"attributes,omitempty"`
223+
}
224+
225+
type IndexSegmentsSort struct {
226+
Field string `json:"field,omitempty"`
227+
Mode string `json:"mode,omitempty"`
228+
Missing interface{} `json:"missing,omitempty"`
229+
Reverse bool `json:"reverse,omitempty"`
230+
}
231+
232+
type IndexSegmentsRamTree struct {
233+
Description string `json:"description,omitempty"`
234+
Size string `json:"size,omitempty"`
235+
SizeInBytes int64 `json:"size_in_bytes,omitempty"`
236+
Children []*IndexSegmentsRamTree `json:"children,omitempty"`
237+
}

indices_segments_test.go

+86
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
// Copyright 2012-present Oliver Eilhard. All rights reserved.
2+
// Use of this source code is governed by a MIT-license.
3+
// See http://olivere.mit-license.org/license.txt for details.
4+
5+
package elastic
6+
7+
import (
8+
"context"
9+
"testing"
10+
)
11+
12+
func TestIndicesSegments(t *testing.T) {
13+
client := setupTestClientAndCreateIndex(t)
14+
15+
tests := []struct {
16+
Indices []string
17+
Expected string
18+
}{
19+
{
20+
[]string{},
21+
"/_segments",
22+
},
23+
{
24+
[]string{"index1"},
25+
"/index1/_segments",
26+
},
27+
{
28+
[]string{"index1", "index2"},
29+
"/index1%2Cindex2/_segments",
30+
},
31+
}
32+
33+
for i, test := range tests {
34+
path, _, err := client.IndexSegments().Index(test.Indices...).buildURL()
35+
if err != nil {
36+
t.Errorf("case #%d: %v", i+1, err)
37+
}
38+
if path != test.Expected {
39+
t.Errorf("case #%d: expected %q; got: %q", i+1, test.Expected, path)
40+
}
41+
}
42+
}
43+
44+
func TestIndexSegments(t *testing.T) {
45+
client := setupTestClientAndCreateIndexAndAddDocs(t)
46+
//client := setupTestClientAndCreateIndexAndAddDocs(t, SetTraceLog(log.New(os.Stdout, "", 0)))
47+
48+
segments, err := client.IndexSegments(testIndexName).Pretty(true).Human(true).Do(context.TODO())
49+
if err != nil {
50+
t.Fatalf("expected no error; got: %v", err)
51+
}
52+
if segments == nil {
53+
t.Fatalf("expected response; got: %v", segments)
54+
}
55+
indices, found := segments.Indices[testIndexName]
56+
if !found {
57+
t.Fatalf("expected index information about index %v; got: %v", testIndexName, found)
58+
}
59+
shards, found := indices.Shards["0"]
60+
if !found {
61+
t.Fatalf("expected shard information about index %v", testIndexName)
62+
}
63+
if shards == nil {
64+
t.Fatalf("expected shard information to be != nil for index %v", testIndexName)
65+
}
66+
shard := shards[0]
67+
if shard == nil {
68+
t.Fatalf("expected shard information to be != nil for shard 0 in index %v", testIndexName)
69+
}
70+
if shard.Routing == nil {
71+
t.Fatalf("expected shard routing information to be != nil for index %v", testIndexName)
72+
}
73+
segmentDetail, found := shard.Segments["_0"]
74+
if !found {
75+
t.Fatalf("expected segment detail to be != nil for index %v", testIndexName)
76+
}
77+
if segmentDetail == nil {
78+
t.Fatalf("expected segment detail to be != nil for index %v", testIndexName)
79+
}
80+
if segmentDetail.NumDocs == 0 {
81+
t.Fatal("expected segment to contain >= 1 docs")
82+
}
83+
if len(segmentDetail.Attributes) == 0 {
84+
t.Fatalf("expected segment attributes map to contain at least one key, value pair for index %v", testIndexName)
85+
}
86+
}

0 commit comments

Comments
 (0)