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 12 "gopkg.in/olivere/elastic.v5/uritemplates" 13) 14 15// IndicesAnalyzeService performs the analysis process on a text and returns 16// the tokens breakdown of the text. 17// 18// See https://www.elastic.co/guide/en/elasticsearch/reference/5.2/indices-analyze.html 19// for detail. 20type IndicesAnalyzeService struct { 21 client *Client 22 pretty bool 23 index string 24 request *IndicesAnalyzeRequest 25 format string 26 preferLocal *bool 27 bodyJson interface{} 28 bodyString string 29} 30 31// NewIndicesAnalyzeService creates a new IndicesAnalyzeService. 32func NewIndicesAnalyzeService(client *Client) *IndicesAnalyzeService { 33 return &IndicesAnalyzeService{ 34 client: client, 35 request: new(IndicesAnalyzeRequest), 36 } 37} 38 39// Index is the name of the index to scope the operation. 40func (s *IndicesAnalyzeService) Index(index string) *IndicesAnalyzeService { 41 s.index = index 42 return s 43} 44 45// Format of the output. 46func (s *IndicesAnalyzeService) Format(format string) *IndicesAnalyzeService { 47 s.format = format 48 return s 49} 50 51// PreferLocal, when true, specifies that a local shard should be used 52// if available. When false, a random shard is used (default: true). 53func (s *IndicesAnalyzeService) PreferLocal(preferLocal bool) *IndicesAnalyzeService { 54 s.preferLocal = &preferLocal 55 return s 56} 57 58// Request passes the analyze request to use. 59func (s *IndicesAnalyzeService) Request(request *IndicesAnalyzeRequest) *IndicesAnalyzeService { 60 if request == nil { 61 s.request = new(IndicesAnalyzeRequest) 62 } else { 63 s.request = request 64 } 65 return s 66} 67 68// Analyzer is the name of the analyzer to use. 69func (s *IndicesAnalyzeService) Analyzer(analyzer string) *IndicesAnalyzeService { 70 s.request.Analyzer = analyzer 71 return s 72} 73 74// Attributes is a list of token attributes to output; this parameter works 75// only with explain=true. 76func (s *IndicesAnalyzeService) Attributes(attributes ...string) *IndicesAnalyzeService { 77 s.request.Attributes = attributes 78 return s 79} 80 81// CharFilter is a list of character filters to use for the analysis. 82func (s *IndicesAnalyzeService) CharFilter(charFilter ...string) *IndicesAnalyzeService { 83 s.request.CharFilter = charFilter 84 return s 85} 86 87// Explain, when true, outputs more advanced details (default: false). 88func (s *IndicesAnalyzeService) Explain(explain bool) *IndicesAnalyzeService { 89 s.request.Explain = explain 90 return s 91} 92 93// Field specifies to use a specific analyzer configured for this field (instead of passing the analyzer name). 94func (s *IndicesAnalyzeService) Field(field string) *IndicesAnalyzeService { 95 s.request.Field = field 96 return s 97} 98 99// Filter is a list of filters to use for the analysis. 100func (s *IndicesAnalyzeService) Filter(filter ...string) *IndicesAnalyzeService { 101 s.request.Filter = filter 102 return s 103} 104 105// Text is the text on which the analysis should be performed (when request body is not used). 106func (s *IndicesAnalyzeService) Text(text ...string) *IndicesAnalyzeService { 107 s.request.Text = text 108 return s 109} 110 111// Tokenizer is the name of the tokenizer to use for the analysis. 112func (s *IndicesAnalyzeService) Tokenizer(tokenizer string) *IndicesAnalyzeService { 113 s.request.Tokenizer = tokenizer 114 return s 115} 116 117// Pretty indicates that the JSON response be indented and human readable. 118func (s *IndicesAnalyzeService) Pretty(pretty bool) *IndicesAnalyzeService { 119 s.pretty = pretty 120 return s 121} 122 123// BodyJson is the text on which the analysis should be performed. 124func (s *IndicesAnalyzeService) BodyJson(body interface{}) *IndicesAnalyzeService { 125 s.bodyJson = body 126 return s 127} 128 129// BodyString is the text on which the analysis should be performed. 130func (s *IndicesAnalyzeService) BodyString(body string) *IndicesAnalyzeService { 131 s.bodyString = body 132 return s 133} 134 135// buildURL builds the URL for the operation. 136func (s *IndicesAnalyzeService) buildURL() (string, url.Values, error) { 137 // Build URL 138 var err error 139 var path string 140 141 if s.index == "" { 142 path = "/_analyze" 143 } else { 144 path, err = uritemplates.Expand("/{index}/_analyze", map[string]string{ 145 "index": s.index, 146 }) 147 } 148 if err != nil { 149 return "", url.Values{}, err 150 } 151 152 // Add query string parameters 153 params := url.Values{} 154 if s.pretty { 155 params.Set("pretty", "1") 156 } 157 if s.format != "" { 158 params.Set("format", s.format) 159 } 160 if s.preferLocal != nil { 161 params.Set("prefer_local", fmt.Sprintf("%v", *s.preferLocal)) 162 } 163 164 return path, params, nil 165} 166 167// Do will execute the request with the given context. 168func (s *IndicesAnalyzeService) Do(ctx context.Context) (*IndicesAnalyzeResponse, error) { 169 // Check pre-conditions 170 if err := s.Validate(); err != nil { 171 return nil, err 172 } 173 174 path, params, err := s.buildURL() 175 if err != nil { 176 return nil, err 177 } 178 179 // Setup HTTP request body 180 var body interface{} 181 if s.bodyJson != nil { 182 body = s.bodyJson 183 } else if s.bodyString != "" { 184 body = s.bodyString 185 } else { 186 // Request parameters are deprecated in 5.1.1, and we must use a JSON 187 // structure in the body to pass the parameters. 188 // See https://www.elastic.co/guide/en/elasticsearch/reference/5.2/indices-analyze.html 189 body = s.request 190 } 191 192 res, err := s.client.PerformRequest(ctx, "POST", path, params, body) 193 if err != nil { 194 return nil, err 195 } 196 197 ret := new(IndicesAnalyzeResponse) 198 if err = s.client.decoder.Decode(res.Body, ret); err != nil { 199 return nil, err 200 } 201 202 return ret, nil 203} 204 205func (s *IndicesAnalyzeService) Validate() error { 206 var invalid []string 207 if s.bodyJson == nil && s.bodyString == "" { 208 if len(s.request.Text) == 0 { 209 invalid = append(invalid, "Text") 210 } 211 } 212 if len(invalid) > 0 { 213 return fmt.Errorf("missing required fields: %v", invalid) 214 } 215 return nil 216} 217 218// IndicesAnalyzeRequest specifies the parameters of the analyze request. 219type IndicesAnalyzeRequest struct { 220 Text []string `json:"text,omitempty"` 221 Analyzer string `json:"analyzer,omitempty"` 222 Tokenizer string `json:"tokenizer,omitempty"` 223 Filter []string `json:"filter,omitempty"` 224 CharFilter []string `json:"char_filter,omitempty"` 225 Field string `json:"field,omitempty"` 226 Explain bool `json:"explain,omitempty"` 227 Attributes []string `json:"attributes,omitempty"` 228} 229 230type IndicesAnalyzeResponse struct { 231 Tokens []IndicesAnalyzeResponseToken `json:"tokens"` // json part for normal message 232 Detail IndicesAnalyzeResponseDetail `json:"detail"` // json part for verbose message of explain request 233} 234 235type IndicesAnalyzeResponseToken struct { 236 Token string `json:"token"` 237 StartOffset int `json:"start_offset"` 238 EndOffset int `json:"end_offset"` 239 Type string `json:"type"` 240 Position int `json:"position"` 241} 242 243type IndicesAnalyzeResponseDetail struct { 244 CustomAnalyzer bool `json:"custom_analyzer"` 245 Charfilters []interface{} `json:"charfilters"` 246 Analyzer struct { 247 Name string `json:"name"` 248 Tokens []struct { 249 Token string `json:"token"` 250 StartOffset int `json:"start_offset"` 251 EndOffset int `json:"end_offset"` 252 Type string `json:"type"` 253 Position int `json:"position"` 254 Bytes string `json:"bytes"` 255 PositionLength int `json:"positionLength"` 256 } `json:"tokens"` 257 } `json:"analyzer"` 258 Tokenizer struct { 259 Name string `json:"name"` 260 Tokens []struct { 261 Token string `json:"token"` 262 StartOffset int `json:"start_offset"` 263 EndOffset int `json:"end_offset"` 264 Type string `json:"type"` 265 Position int `json:"position"` 266 } `json:"tokens"` 267 } `json:"tokenizer"` 268 Tokenfilters []struct { 269 Name string `json:"name"` 270 Tokens []struct { 271 Token string `json:"token"` 272 StartOffset int `json:"start_offset"` 273 EndOffset int `json:"end_offset"` 274 Type string `json:"type"` 275 Position int `json:"position"` 276 Keyword bool `json:"keyword"` 277 } `json:"tokens"` 278 } `json:"tokenfilters"` 279} 280