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/http" 11 "net/url" 12 13 "gopkg.in/olivere/elastic.v5/uritemplates" 14) 15 16// ExistsService checks for the existence of a document using HEAD. 17// 18// See https://www.elastic.co/guide/en/elasticsearch/reference/5.2/docs-get.html 19// for details. 20type ExistsService struct { 21 client *Client 22 pretty bool 23 id string 24 index string 25 typ string 26 preference string 27 realtime *bool 28 refresh string 29 routing string 30 parent string 31} 32 33// NewExistsService creates a new ExistsService. 34func NewExistsService(client *Client) *ExistsService { 35 return &ExistsService{ 36 client: client, 37 } 38} 39 40// Id is the document ID. 41func (s *ExistsService) Id(id string) *ExistsService { 42 s.id = id 43 return s 44} 45 46// Index is the name of the index. 47func (s *ExistsService) Index(index string) *ExistsService { 48 s.index = index 49 return s 50} 51 52// Type is the type of the document (use `_all` to fetch the first document 53// matching the ID across all types). 54func (s *ExistsService) Type(typ string) *ExistsService { 55 s.typ = typ 56 return s 57} 58 59// Preference specifies the node or shard the operation should be performed on (default: random). 60func (s *ExistsService) Preference(preference string) *ExistsService { 61 s.preference = preference 62 return s 63} 64 65// Realtime specifies whether to perform the operation in realtime or search mode. 66func (s *ExistsService) Realtime(realtime bool) *ExistsService { 67 s.realtime = &realtime 68 return s 69} 70 71// Refresh the shard containing the document before performing the operation. 72func (s *ExistsService) Refresh(refresh string) *ExistsService { 73 s.refresh = refresh 74 return s 75} 76 77// Routing is a specific routing value. 78func (s *ExistsService) Routing(routing string) *ExistsService { 79 s.routing = routing 80 return s 81} 82 83// Parent is the ID of the parent document. 84func (s *ExistsService) Parent(parent string) *ExistsService { 85 s.parent = parent 86 return s 87} 88 89// Pretty indicates that the JSON response be indented and human readable. 90func (s *ExistsService) Pretty(pretty bool) *ExistsService { 91 s.pretty = pretty 92 return s 93} 94 95// buildURL builds the URL for the operation. 96func (s *ExistsService) buildURL() (string, url.Values, error) { 97 // Build URL 98 path, err := uritemplates.Expand("/{index}/{type}/{id}", map[string]string{ 99 "id": s.id, 100 "index": s.index, 101 "type": s.typ, 102 }) 103 if err != nil { 104 return "", url.Values{}, err 105 } 106 107 // Add query string parameters 108 params := url.Values{} 109 if s.pretty { 110 params.Set("pretty", "1") 111 } 112 if s.realtime != nil { 113 params.Set("realtime", fmt.Sprintf("%v", *s.realtime)) 114 } 115 if s.refresh != "" { 116 params.Set("refresh", s.refresh) 117 } 118 if s.routing != "" { 119 params.Set("routing", s.routing) 120 } 121 if s.parent != "" { 122 params.Set("parent", s.parent) 123 } 124 if s.preference != "" { 125 params.Set("preference", s.preference) 126 } 127 return path, params, nil 128} 129 130// Validate checks if the operation is valid. 131func (s *ExistsService) Validate() error { 132 var invalid []string 133 if s.id == "" { 134 invalid = append(invalid, "Id") 135 } 136 if s.index == "" { 137 invalid = append(invalid, "Index") 138 } 139 if s.typ == "" { 140 invalid = append(invalid, "Type") 141 } 142 if len(invalid) > 0 { 143 return fmt.Errorf("missing required fields: %v", invalid) 144 } 145 return nil 146} 147 148// Do executes the operation. 149func (s *ExistsService) Do(ctx context.Context) (bool, error) { 150 // Check pre-conditions 151 if err := s.Validate(); err != nil { 152 return false, err 153 } 154 155 // Get URL for request 156 path, params, err := s.buildURL() 157 if err != nil { 158 return false, err 159 } 160 161 // Get HTTP response 162 res, err := s.client.PerformRequest(ctx, "HEAD", path, params, nil, 404) 163 if err != nil { 164 return false, err 165 } 166 167 // Return operation response 168 switch res.StatusCode { 169 case http.StatusOK: 170 return true, nil 171 case http.StatusNotFound: 172 return false, nil 173 default: 174 return false, fmt.Errorf("elastic: got HTTP code %d when it should have been either 200 or 404", res.StatusCode) 175 } 176} 177