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
5package elastic
6
7import (
8	"context"
9	"fmt"
10	"net/url"
11	"strings"
12
13	"gopkg.in/olivere/elastic.v5/uritemplates"
14)
15
16// AliasesService returns the aliases associated with one or more indices.
17// See http://www.elastic.co/guide/en/elasticsearch/reference/5.2/indices-aliases.html.
18type AliasesService struct {
19	client *Client
20	index  []string
21	pretty bool
22}
23
24// NewAliasesService instantiates a new AliasesService.
25func NewAliasesService(client *Client) *AliasesService {
26	builder := &AliasesService{
27		client: client,
28	}
29	return builder
30}
31
32// Pretty asks Elasticsearch to indent the returned JSON.
33func (s *AliasesService) Pretty(pretty bool) *AliasesService {
34	s.pretty = pretty
35	return s
36}
37
38// Index adds one or more indices.
39func (s *AliasesService) Index(index ...string) *AliasesService {
40	s.index = append(s.index, index...)
41	return s
42}
43
44// buildURL builds the URL for the operation.
45func (s *AliasesService) buildURL() (string, url.Values, error) {
46	var err error
47	var path string
48
49	if len(s.index) > 0 {
50		path, err = uritemplates.Expand("/{index}/_aliases", map[string]string{
51			"index": strings.Join(s.index, ","),
52		})
53	} else {
54		path = "/_aliases"
55	}
56	if err != nil {
57		return "", url.Values{}, err
58	}
59
60	// Add query string parameters
61	params := url.Values{}
62	if s.pretty {
63		params.Set("pretty", fmt.Sprintf("%v", s.pretty))
64	}
65	return path, params, nil
66}
67
68func (s *AliasesService) Do(ctx context.Context) (*AliasesResult, error) {
69	path, params, err := s.buildURL()
70	if err != nil {
71		return nil, err
72	}
73
74	// Get response
75	res, err := s.client.PerformRequest(ctx, "GET", path, params, nil)
76	if err != nil {
77		return nil, err
78	}
79
80	// {
81	//   "indexName" : {
82	//     "aliases" : {
83	//       "alias1" : { },
84	//       "alias2" : { }
85	//     }
86	//   },
87	//   "indexName2" : {
88	//     ...
89	//   },
90	// }
91	indexMap := make(map[string]interface{})
92	if err := s.client.decoder.Decode(res.Body, &indexMap); err != nil {
93		return nil, err
94	}
95
96	// Each (indexName, _)
97	ret := &AliasesResult{
98		Indices: make(map[string]indexResult),
99	}
100	for indexName, indexData := range indexMap {
101		indexOut, found := ret.Indices[indexName]
102		if !found {
103			indexOut = indexResult{Aliases: make([]aliasResult, 0)}
104		}
105
106		// { "aliases" : { ... } }
107		indexDataMap, ok := indexData.(map[string]interface{})
108		if ok {
109			aliasesData, ok := indexDataMap["aliases"].(map[string]interface{})
110			if ok {
111				for aliasName, _ := range aliasesData {
112					aliasRes := aliasResult{AliasName: aliasName}
113					indexOut.Aliases = append(indexOut.Aliases, aliasRes)
114				}
115			}
116		}
117
118		ret.Indices[indexName] = indexOut
119	}
120
121	return ret, nil
122}
123
124// -- Result of an alias request.
125
126type AliasesResult struct {
127	Indices map[string]indexResult
128}
129
130type indexResult struct {
131	Aliases []aliasResult
132}
133
134type aliasResult struct {
135	AliasName string
136}
137
138func (ar AliasesResult) IndicesByAlias(aliasName string) []string {
139	var indices []string
140	for indexName, indexInfo := range ar.Indices {
141		for _, aliasInfo := range indexInfo.Aliases {
142			if aliasInfo.AliasName == aliasName {
143				indices = append(indices, indexName)
144			}
145		}
146	}
147	return indices
148}
149
150func (ir indexResult) HasAlias(aliasName string) bool {
151	for _, alias := range ir.Aliases {
152		if alias.AliasName == aliasName {
153			return true
154		}
155	}
156	return false
157}
158