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 "encoding/json" 10 "fmt" 11 "net/http" 12 "net/url" 13 "strings" 14 15 "github.com/olivere/elastic/v7/uritemplates" 16) 17 18// GetService allows to get a typed JSON document from the index based 19// on its id. 20// 21// See https://www.elastic.co/guide/en/elasticsearch/reference/7.0/docs-get.html 22// for details. 23type GetService struct { 24 client *Client 25 26 pretty *bool // pretty format the returned JSON response 27 human *bool // return human readable values for statistics 28 errorTrace *bool // include the stack trace of returned errors 29 filterPath []string // list of filters used to reduce the response 30 headers http.Header // custom request-level HTTP headers 31 32 index string 33 typ string 34 id string 35 routing string 36 preference string 37 storedFields []string 38 refresh string 39 realtime *bool 40 fsc *FetchSourceContext 41 version interface{} 42 versionType string 43 parent string 44 ignoreErrorsOnGeneratedFields *bool 45} 46 47// NewGetService creates a new GetService. 48func NewGetService(client *Client) *GetService { 49 return &GetService{ 50 client: client, 51 typ: "_doc", 52 } 53} 54 55// Pretty tells Elasticsearch whether to return a formatted JSON response. 56func (s *GetService) Pretty(pretty bool) *GetService { 57 s.pretty = &pretty 58 return s 59} 60 61// Human specifies whether human readable values should be returned in 62// the JSON response, e.g. "7.5mb". 63func (s *GetService) Human(human bool) *GetService { 64 s.human = &human 65 return s 66} 67 68// ErrorTrace specifies whether to include the stack trace of returned errors. 69func (s *GetService) ErrorTrace(errorTrace bool) *GetService { 70 s.errorTrace = &errorTrace 71 return s 72} 73 74// FilterPath specifies a list of filters used to reduce the response. 75func (s *GetService) FilterPath(filterPath ...string) *GetService { 76 s.filterPath = filterPath 77 return s 78} 79 80// Header adds a header to the request. 81func (s *GetService) Header(name string, value string) *GetService { 82 if s.headers == nil { 83 s.headers = http.Header{} 84 } 85 s.headers.Add(name, value) 86 return s 87} 88 89// Headers specifies the headers of the request. 90func (s *GetService) Headers(headers http.Header) *GetService { 91 s.headers = headers 92 return s 93} 94 95// Index is the name of the index. 96func (s *GetService) Index(index string) *GetService { 97 s.index = index 98 return s 99} 100 101// Type is the type of the document 102// 103// Deprecated: Types are in the process of being removed. 104func (s *GetService) Type(typ string) *GetService { 105 s.typ = typ 106 return s 107} 108 109// Id is the document ID. 110func (s *GetService) Id(id string) *GetService { 111 s.id = id 112 return s 113} 114 115// Parent is the ID of the parent document. 116func (s *GetService) Parent(parent string) *GetService { 117 s.parent = parent 118 return s 119} 120 121// Routing is the specific routing value. 122func (s *GetService) Routing(routing string) *GetService { 123 s.routing = routing 124 return s 125} 126 127// Preference specifies the node or shard the operation should be performed on (default: random). 128func (s *GetService) Preference(preference string) *GetService { 129 s.preference = preference 130 return s 131} 132 133// StoredFields is a list of fields to return in the response. 134func (s *GetService) StoredFields(storedFields ...string) *GetService { 135 s.storedFields = append(s.storedFields, storedFields...) 136 return s 137} 138 139func (s *GetService) FetchSource(fetchSource bool) *GetService { 140 if s.fsc == nil { 141 s.fsc = NewFetchSourceContext(fetchSource) 142 } else { 143 s.fsc.SetFetchSource(fetchSource) 144 } 145 return s 146} 147 148func (s *GetService) FetchSourceContext(fetchSourceContext *FetchSourceContext) *GetService { 149 s.fsc = fetchSourceContext 150 return s 151} 152 153// Refresh the shard containing the document before performing the operation. 154// 155// See https://www.elastic.co/guide/en/elasticsearch/reference/7.0/docs-refresh.html 156// for details. 157func (s *GetService) Refresh(refresh string) *GetService { 158 s.refresh = refresh 159 return s 160} 161 162// Realtime specifies whether to perform the operation in realtime or search mode. 163func (s *GetService) Realtime(realtime bool) *GetService { 164 s.realtime = &realtime 165 return s 166} 167 168// VersionType is the specific version type. 169func (s *GetService) VersionType(versionType string) *GetService { 170 s.versionType = versionType 171 return s 172} 173 174// Version is an explicit version number for concurrency control. 175func (s *GetService) Version(version interface{}) *GetService { 176 s.version = version 177 return s 178} 179 180// IgnoreErrorsOnGeneratedFields indicates whether to ignore fields that 181// are generated if the transaction log is accessed. 182func (s *GetService) IgnoreErrorsOnGeneratedFields(ignore bool) *GetService { 183 s.ignoreErrorsOnGeneratedFields = &ignore 184 return s 185} 186 187// Validate checks if the operation is valid. 188func (s *GetService) Validate() error { 189 var invalid []string 190 if s.id == "" { 191 invalid = append(invalid, "Id") 192 } 193 if s.index == "" { 194 invalid = append(invalid, "Index") 195 } 196 if s.typ == "" { 197 invalid = append(invalid, "Type") 198 } 199 if len(invalid) > 0 { 200 return fmt.Errorf("missing required fields: %v", invalid) 201 } 202 return nil 203} 204 205// buildURL builds the URL for the operation. 206func (s *GetService) buildURL() (string, url.Values, error) { 207 // Build URL 208 path, err := uritemplates.Expand("/{index}/{type}/{id}", map[string]string{ 209 "id": s.id, 210 "index": s.index, 211 "type": s.typ, 212 }) 213 if err != nil { 214 return "", url.Values{}, err 215 } 216 217 // Add query string parameters 218 params := url.Values{} 219 if v := s.pretty; v != nil { 220 params.Set("pretty", fmt.Sprint(*v)) 221 } 222 if v := s.human; v != nil { 223 params.Set("human", fmt.Sprint(*v)) 224 } 225 if v := s.errorTrace; v != nil { 226 params.Set("error_trace", fmt.Sprint(*v)) 227 } 228 if len(s.filterPath) > 0 { 229 params.Set("filter_path", strings.Join(s.filterPath, ",")) 230 } 231 if s.routing != "" { 232 params.Set("routing", s.routing) 233 } 234 if s.parent != "" { 235 params.Set("parent", s.parent) 236 } 237 if s.preference != "" { 238 params.Set("preference", s.preference) 239 } 240 if len(s.storedFields) > 0 { 241 params.Set("stored_fields", strings.Join(s.storedFields, ",")) 242 } 243 if s.refresh != "" { 244 params.Set("refresh", s.refresh) 245 } 246 if s.version != nil { 247 params.Set("version", fmt.Sprintf("%v", s.version)) 248 } 249 if s.versionType != "" { 250 params.Set("version_type", s.versionType) 251 } 252 if s.realtime != nil { 253 params.Set("realtime", fmt.Sprintf("%v", *s.realtime)) 254 } 255 if s.ignoreErrorsOnGeneratedFields != nil { 256 params.Add("ignore_errors_on_generated_fields", fmt.Sprintf("%v", *s.ignoreErrorsOnGeneratedFields)) 257 } 258 if s.fsc != nil { 259 for k, values := range s.fsc.Query() { 260 params.Add(k, strings.Join(values, ",")) 261 } 262 } 263 return path, params, nil 264} 265 266// Do executes the operation. 267func (s *GetService) Do(ctx context.Context) (*GetResult, error) { 268 // Check pre-conditions 269 if err := s.Validate(); err != nil { 270 return nil, err 271 } 272 273 // Get URL for request 274 path, params, err := s.buildURL() 275 if err != nil { 276 return nil, err 277 } 278 279 // Get HTTP response 280 res, err := s.client.PerformRequest(ctx, PerformRequestOptions{ 281 Method: "GET", 282 Path: path, 283 Params: params, 284 Headers: s.headers, 285 }) 286 if err != nil { 287 return nil, err 288 } 289 290 // Return operation response 291 ret := new(GetResult) 292 if err := s.client.decoder.Decode(res.Body, ret); err != nil { 293 return nil, err 294 } 295 return ret, nil 296} 297 298// -- Result of a get request. 299 300// GetResult is the outcome of GetService.Do. 301type GetResult struct { 302 Index string `json:"_index"` // index meta field 303 Type string `json:"_type"` // type meta field 304 Id string `json:"_id"` // id meta field 305 Uid string `json:"_uid"` // uid meta field (see MapperService.java for all meta fields) 306 Routing string `json:"_routing"` // routing meta field 307 Parent string `json:"_parent"` // parent meta field 308 Version *int64 `json:"_version"` // version number, when Version is set to true in SearchService 309 SeqNo *int64 `json:"_seq_no"` 310 PrimaryTerm *int64 `json:"_primary_term"` 311 Source json.RawMessage `json:"_source,omitempty"` 312 Found bool `json:"found,omitempty"` 313 Fields map[string]interface{} `json:"fields,omitempty"` 314 //Error string `json:"error,omitempty"` // used only in MultiGet 315 // TODO double-check that MultiGet now returns details error information 316 Error *ErrorDetails `json:"error,omitempty"` // only used in MultiGet 317} 318