github.com/opensearch-project/opensearch-go/v2@v2.3.0/opensearchapi/api.bulk.go (about) 1 // SPDX-License-Identifier: Apache-2.0 2 // 3 // The OpenSearch Contributors require contributions made to 4 // this file be licensed under the Apache-2.0 license or a 5 // compatible open source license. 6 // 7 // Modifications Copyright OpenSearch Contributors. See 8 // GitHub history for details. 9 10 // Licensed to Elasticsearch B.V. under one or more contributor 11 // license agreements. See the NOTICE file distributed with 12 // this work for additional information regarding copyright 13 // ownership. Elasticsearch B.V. licenses this file to you under 14 // the Apache License, Version 2.0 (the "License"); you may 15 // not use this file except in compliance with the License. 16 // You may obtain a copy of the License at 17 // 18 // http://www.apache.org/licenses/LICENSE-2.0 19 // 20 // Unless required by applicable law or agreed to in writing, 21 // software distributed under the License is distributed on an 22 // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 23 // KIND, either express or implied. See the License for the 24 // specific language governing permissions and limitations 25 // under the License. 26 27 package opensearchapi 28 29 import ( 30 "context" 31 "io" 32 "net/http" 33 "strconv" 34 "strings" 35 "time" 36 ) 37 38 func newBulkFunc(t Transport) Bulk { 39 return func(body io.Reader, o ...func(*BulkRequest)) (*Response, error) { 40 var r = BulkRequest{Body: body} 41 for _, f := range o { 42 f(&r) 43 } 44 return r.Do(r.ctx, t) 45 } 46 } 47 48 // ----- API Definition ------------------------------------------------------- 49 50 // Bulk allows to perform multiple index/update/delete operations in a single request. 51 // 52 // 53 type Bulk func(body io.Reader, o ...func(*BulkRequest)) (*Response, error) 54 55 // BulkRequest configures the Bulk API request. 56 // 57 type BulkRequest struct { 58 Index string 59 60 Body io.Reader 61 62 Pipeline string 63 Refresh string 64 RequireAlias *bool 65 Routing string 66 Source interface{} 67 SourceExcludes []string 68 SourceIncludes []string 69 Timeout time.Duration 70 WaitForActiveShards string 71 72 Pretty bool 73 Human bool 74 ErrorTrace bool 75 FilterPath []string 76 77 Header http.Header 78 79 ctx context.Context 80 } 81 82 // Do executes the request and returns response or error. 83 // 84 func (r BulkRequest) Do(ctx context.Context, transport Transport) (*Response, error) { 85 var ( 86 method string 87 path strings.Builder 88 params map[string]string 89 ) 90 91 method = "POST" 92 93 path.Grow(1 + len(r.Index) + 1 + len("_bulk")) 94 if r.Index != "" { 95 path.WriteString("/") 96 path.WriteString(r.Index) 97 } 98 path.WriteString("/") 99 path.WriteString("_bulk") 100 101 params = make(map[string]string) 102 103 if r.Pipeline != "" { 104 params["pipeline"] = r.Pipeline 105 } 106 107 if r.Refresh != "" { 108 params["refresh"] = r.Refresh 109 } 110 111 if r.RequireAlias != nil { 112 params["require_alias"] = strconv.FormatBool(*r.RequireAlias) 113 } 114 115 if r.Routing != "" { 116 params["routing"] = r.Routing 117 } 118 119 if source, ok := r.Source.(bool); ok { 120 params["_source"] = strconv.FormatBool(source) 121 } else if source, ok := r.Source.(string); ok && source != "" { 122 params["_source"] = source 123 } else if sources, ok := r.Source.([]string); ok && len(sources) > 0 { 124 params["_source"] = strings.Join(sources, ",") 125 } 126 127 if len(r.SourceExcludes) > 0 { 128 params["_source_excludes"] = strings.Join(r.SourceExcludes, ",") 129 } 130 131 if len(r.SourceIncludes) > 0 { 132 params["_source_includes"] = strings.Join(r.SourceIncludes, ",") 133 } 134 135 if r.Timeout != 0 { 136 params["timeout"] = formatDuration(r.Timeout) 137 } 138 139 if r.WaitForActiveShards != "" { 140 params["wait_for_active_shards"] = r.WaitForActiveShards 141 } 142 143 if r.Pretty { 144 params["pretty"] = "true" 145 } 146 147 if r.Human { 148 params["human"] = "true" 149 } 150 151 if r.ErrorTrace { 152 params["error_trace"] = "true" 153 } 154 155 if len(r.FilterPath) > 0 { 156 params["filter_path"] = strings.Join(r.FilterPath, ",") 157 } 158 159 req, err := newRequest(method, path.String(), r.Body) 160 if err != nil { 161 return nil, err 162 } 163 164 if len(params) > 0 { 165 q := req.URL.Query() 166 for k, v := range params { 167 q.Set(k, v) 168 } 169 req.URL.RawQuery = q.Encode() 170 } 171 172 if r.Body != nil { 173 req.Header[headerContentType] = headerContentTypeJSON 174 } 175 176 if len(r.Header) > 0 { 177 if len(req.Header) == 0 { 178 req.Header = r.Header 179 } else { 180 for k, vv := range r.Header { 181 for _, v := range vv { 182 req.Header.Add(k, v) 183 } 184 } 185 } 186 } 187 188 if ctx != nil { 189 req = req.WithContext(ctx) 190 } 191 192 res, err := transport.Perform(req) 193 if err != nil { 194 return nil, err 195 } 196 197 response := Response{ 198 StatusCode: res.StatusCode, 199 Body: res.Body, 200 Header: res.Header, 201 } 202 203 return &response, nil 204 } 205 206 // WithContext sets the request context. 207 // 208 func (f Bulk) WithContext(v context.Context) func(*BulkRequest) { 209 return func(r *BulkRequest) { 210 r.ctx = v 211 } 212 } 213 214 // WithIndex - default index for items which don't provide one. 215 // 216 func (f Bulk) WithIndex(v string) func(*BulkRequest) { 217 return func(r *BulkRequest) { 218 r.Index = v 219 } 220 } 221 222 // WithPipeline - the pipeline ID to preprocess incoming documents with. 223 // 224 func (f Bulk) WithPipeline(v string) func(*BulkRequest) { 225 return func(r *BulkRequest) { 226 r.Pipeline = v 227 } 228 } 229 230 // WithRefresh - if `true` then refresh the affected shards to make this operation visible to search, if `wait_for` then wait for a refresh to make this operation visible to search, if `false` (the default) then do nothing with refreshes.. 231 // 232 func (f Bulk) WithRefresh(v string) func(*BulkRequest) { 233 return func(r *BulkRequest) { 234 r.Refresh = v 235 } 236 } 237 238 // WithRequireAlias - sets require_alias for all incoming documents. defaults to unset (false). 239 // 240 func (f Bulk) WithRequireAlias(v bool) func(*BulkRequest) { 241 return func(r *BulkRequest) { 242 r.RequireAlias = &v 243 } 244 } 245 246 // WithRouting - specific routing value. 247 // 248 func (f Bulk) WithRouting(v string) func(*BulkRequest) { 249 return func(r *BulkRequest) { 250 r.Routing = v 251 } 252 } 253 254 // WithSource - true or false to return the _source field or not, or default list of fields to return, can be overridden on each sub-request. 255 // 256 func (f Bulk) WithSource(v interface{}) func(*BulkRequest) { 257 return func(r *BulkRequest) { 258 r.Source = v 259 } 260 } 261 262 // WithSourceExcludes - default list of fields to exclude from the returned _source field, can be overridden on each sub-request. 263 // 264 func (f Bulk) WithSourceExcludes(v ...string) func(*BulkRequest) { 265 return func(r *BulkRequest) { 266 r.SourceExcludes = v 267 } 268 } 269 270 // WithSourceIncludes - default list of fields to extract and return from the _source field, can be overridden on each sub-request. 271 // 272 func (f Bulk) WithSourceIncludes(v ...string) func(*BulkRequest) { 273 return func(r *BulkRequest) { 274 r.SourceIncludes = v 275 } 276 } 277 278 // WithTimeout - explicit operation timeout. 279 // 280 func (f Bulk) WithTimeout(v time.Duration) func(*BulkRequest) { 281 return func(r *BulkRequest) { 282 r.Timeout = v 283 } 284 } 285 286 // WithWaitForActiveShards - sets the number of shard copies that must be active before proceeding with the bulk operation. defaults to 1, meaning the primary shard only. set to `all` for all shard copies, otherwise set to any non-negative value less than or equal to the total number of copies for the shard (number of replicas + 1). 287 // 288 func (f Bulk) WithWaitForActiveShards(v string) func(*BulkRequest) { 289 return func(r *BulkRequest) { 290 r.WaitForActiveShards = v 291 } 292 } 293 294 // WithPretty makes the response body pretty-printed. 295 // 296 func (f Bulk) WithPretty() func(*BulkRequest) { 297 return func(r *BulkRequest) { 298 r.Pretty = true 299 } 300 } 301 302 // WithHuman makes statistical values human-readable. 303 // 304 func (f Bulk) WithHuman() func(*BulkRequest) { 305 return func(r *BulkRequest) { 306 r.Human = true 307 } 308 } 309 310 // WithErrorTrace includes the stack trace for errors in the response body. 311 // 312 func (f Bulk) WithErrorTrace() func(*BulkRequest) { 313 return func(r *BulkRequest) { 314 r.ErrorTrace = true 315 } 316 } 317 318 // WithFilterPath filters the properties of the response body. 319 // 320 func (f Bulk) WithFilterPath(v ...string) func(*BulkRequest) { 321 return func(r *BulkRequest) { 322 r.FilterPath = v 323 } 324 } 325 326 // WithHeader adds the headers to the HTTP request. 327 // 328 func (f Bulk) WithHeader(h map[string]string) func(*BulkRequest) { 329 return func(r *BulkRequest) { 330 if r.Header == nil { 331 r.Header = make(http.Header) 332 } 333 for k, v := range h { 334 r.Header.Add(k, v) 335 } 336 } 337 } 338 339 // WithOpaqueID adds the X-Opaque-Id header to the HTTP request. 340 // 341 func (f Bulk) WithOpaqueID(s string) func(*BulkRequest) { 342 return func(r *BulkRequest) { 343 if r.Header == nil { 344 r.Header = make(http.Header) 345 } 346 r.Header.Set("X-Opaque-Id", s) 347 } 348 }