github.com/opensearch-project/opensearch-go/v2@v2.3.0/opensearchapi/api.search.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 "fmt" 32 "io" 33 "net/http" 34 "strconv" 35 "strings" 36 "time" 37 ) 38 39 func newSearchFunc(t Transport) Search { 40 return func(o ...func(*SearchRequest)) (*Response, error) { 41 var r = SearchRequest{} 42 for _, f := range o { 43 f(&r) 44 } 45 return r.Do(r.ctx, t) 46 } 47 } 48 49 // ----- API Definition ------------------------------------------------------- 50 51 // Search returns results matching a query. 52 // 53 // 54 type Search func(o ...func(*SearchRequest)) (*Response, error) 55 56 // SearchRequest configures the Search API request. 57 // 58 type SearchRequest struct { 59 Index []string 60 61 Body io.Reader 62 63 AllowNoIndices *bool 64 AllowPartialSearchResults *bool 65 Analyzer string 66 AnalyzeWildcard *bool 67 BatchedReduceSize *int 68 CcsMinimizeRoundtrips *bool 69 DefaultOperator string 70 Df string 71 DocvalueFields []string 72 ExpandWildcards string 73 Explain *bool 74 From *int 75 IgnoreThrottled *bool 76 IgnoreUnavailable *bool 77 Lenient *bool 78 MaxConcurrentShardRequests *int 79 MinCompatibleShardNode string 80 Preference string 81 PreFilterShardSize *int 82 Query string 83 RequestCache *bool 84 RestTotalHitsAsInt *bool 85 Routing []string 86 Scroll time.Duration 87 SearchType string 88 SeqNoPrimaryTerm *bool 89 Size *int 90 Sort []string 91 Source interface{} 92 SourceExcludes []string 93 SourceIncludes []string 94 Stats []string 95 StoredFields []string 96 SuggestField string 97 SuggestMode string 98 SuggestSize *int 99 SuggestText string 100 TerminateAfter *int 101 Timeout time.Duration 102 TrackScores *bool 103 TrackTotalHits interface{} 104 TypedKeys *bool 105 Version *bool 106 107 Pretty bool 108 Human bool 109 ErrorTrace bool 110 FilterPath []string 111 112 Header http.Header 113 114 ctx context.Context 115 } 116 117 // Do executes the request and returns response or error. 118 // 119 func (r SearchRequest) Do(ctx context.Context, transport Transport) (*Response, error) { 120 var ( 121 method string 122 path strings.Builder 123 params map[string]string 124 ) 125 126 method = "POST" 127 128 path.Grow(1 + len(strings.Join(r.Index, ",")) + 1 + len("_search")) 129 if len(r.Index) > 0 { 130 path.WriteString("/") 131 path.WriteString(strings.Join(r.Index, ",")) 132 } 133 path.WriteString("/") 134 path.WriteString("_search") 135 136 params = make(map[string]string) 137 138 if r.AllowNoIndices != nil { 139 params["allow_no_indices"] = strconv.FormatBool(*r.AllowNoIndices) 140 } 141 142 if r.AllowPartialSearchResults != nil { 143 params["allow_partial_search_results"] = strconv.FormatBool(*r.AllowPartialSearchResults) 144 } 145 146 if r.Analyzer != "" { 147 params["analyzer"] = r.Analyzer 148 } 149 150 if r.AnalyzeWildcard != nil { 151 params["analyze_wildcard"] = strconv.FormatBool(*r.AnalyzeWildcard) 152 } 153 154 if r.BatchedReduceSize != nil { 155 params["batched_reduce_size"] = strconv.FormatInt(int64(*r.BatchedReduceSize), 10) 156 } 157 158 if r.CcsMinimizeRoundtrips != nil { 159 params["ccs_minimize_roundtrips"] = strconv.FormatBool(*r.CcsMinimizeRoundtrips) 160 } 161 162 if r.DefaultOperator != "" { 163 params["default_operator"] = r.DefaultOperator 164 } 165 166 if r.Df != "" { 167 params["df"] = r.Df 168 } 169 170 if len(r.DocvalueFields) > 0 { 171 params["docvalue_fields"] = strings.Join(r.DocvalueFields, ",") 172 } 173 174 if r.ExpandWildcards != "" { 175 params["expand_wildcards"] = r.ExpandWildcards 176 } 177 178 if r.Explain != nil { 179 params["explain"] = strconv.FormatBool(*r.Explain) 180 } 181 182 if r.From != nil { 183 params["from"] = strconv.FormatInt(int64(*r.From), 10) 184 } 185 186 if r.IgnoreThrottled != nil { 187 params["ignore_throttled"] = strconv.FormatBool(*r.IgnoreThrottled) 188 } 189 190 if r.IgnoreUnavailable != nil { 191 params["ignore_unavailable"] = strconv.FormatBool(*r.IgnoreUnavailable) 192 } 193 194 if r.Lenient != nil { 195 params["lenient"] = strconv.FormatBool(*r.Lenient) 196 } 197 198 if r.MaxConcurrentShardRequests != nil { 199 params["max_concurrent_shard_requests"] = strconv.FormatInt(int64(*r.MaxConcurrentShardRequests), 10) 200 } 201 202 if r.MinCompatibleShardNode != "" { 203 params["min_compatible_shard_node"] = r.MinCompatibleShardNode 204 } 205 206 if r.Preference != "" { 207 params["preference"] = r.Preference 208 } 209 210 if r.PreFilterShardSize != nil { 211 params["pre_filter_shard_size"] = strconv.FormatInt(int64(*r.PreFilterShardSize), 10) 212 } 213 214 if r.Query != "" { 215 params["q"] = r.Query 216 } 217 218 if r.RequestCache != nil { 219 params["request_cache"] = strconv.FormatBool(*r.RequestCache) 220 } 221 222 if r.RestTotalHitsAsInt != nil { 223 params["rest_total_hits_as_int"] = strconv.FormatBool(*r.RestTotalHitsAsInt) 224 } 225 226 if len(r.Routing) > 0 { 227 params["routing"] = strings.Join(r.Routing, ",") 228 } 229 230 if r.Scroll != 0 { 231 params["scroll"] = formatDuration(r.Scroll) 232 } 233 234 if r.SearchType != "" { 235 params["search_type"] = r.SearchType 236 } 237 238 if r.SeqNoPrimaryTerm != nil { 239 params["seq_no_primary_term"] = strconv.FormatBool(*r.SeqNoPrimaryTerm) 240 } 241 242 if r.Size != nil { 243 params["size"] = strconv.FormatInt(int64(*r.Size), 10) 244 } 245 246 if len(r.Sort) > 0 { 247 params["sort"] = strings.Join(r.Sort, ",") 248 } 249 250 if source, ok := r.Source.(bool); ok { 251 params["_source"] = strconv.FormatBool(source) 252 } else if source, ok := r.Source.(string); ok && source != "" { 253 params["_source"] = source 254 } else if sources, ok := r.Source.([]string); ok && len(sources) > 0 { 255 params["_source"] = strings.Join(sources, ",") 256 } 257 258 if len(r.SourceExcludes) > 0 { 259 params["_source_excludes"] = strings.Join(r.SourceExcludes, ",") 260 } 261 262 if len(r.SourceIncludes) > 0 { 263 params["_source_includes"] = strings.Join(r.SourceIncludes, ",") 264 } 265 266 if len(r.Stats) > 0 { 267 params["stats"] = strings.Join(r.Stats, ",") 268 } 269 270 if len(r.StoredFields) > 0 { 271 params["stored_fields"] = strings.Join(r.StoredFields, ",") 272 } 273 274 if r.SuggestField != "" { 275 params["suggest_field"] = r.SuggestField 276 } 277 278 if r.SuggestMode != "" { 279 params["suggest_mode"] = r.SuggestMode 280 } 281 282 if r.SuggestSize != nil { 283 params["suggest_size"] = strconv.FormatInt(int64(*r.SuggestSize), 10) 284 } 285 286 if r.SuggestText != "" { 287 params["suggest_text"] = r.SuggestText 288 } 289 290 if r.TerminateAfter != nil { 291 params["terminate_after"] = strconv.FormatInt(int64(*r.TerminateAfter), 10) 292 } 293 294 if r.Timeout != 0 { 295 params["timeout"] = formatDuration(r.Timeout) 296 } 297 298 if r.TrackScores != nil { 299 params["track_scores"] = strconv.FormatBool(*r.TrackScores) 300 } 301 302 if r.TrackTotalHits != nil { 303 params["track_total_hits"] = fmt.Sprintf("%v", r.TrackTotalHits) 304 } 305 306 if r.TypedKeys != nil { 307 params["typed_keys"] = strconv.FormatBool(*r.TypedKeys) 308 } 309 310 if r.Version != nil { 311 params["version"] = strconv.FormatBool(*r.Version) 312 } 313 314 if r.Pretty { 315 params["pretty"] = "true" 316 } 317 318 if r.Human { 319 params["human"] = "true" 320 } 321 322 if r.ErrorTrace { 323 params["error_trace"] = "true" 324 } 325 326 if len(r.FilterPath) > 0 { 327 params["filter_path"] = strings.Join(r.FilterPath, ",") 328 } 329 330 req, err := newRequest(method, path.String(), r.Body) 331 if err != nil { 332 return nil, err 333 } 334 335 if len(params) > 0 { 336 q := req.URL.Query() 337 for k, v := range params { 338 q.Set(k, v) 339 } 340 req.URL.RawQuery = q.Encode() 341 } 342 343 if r.Body != nil { 344 req.Header[headerContentType] = headerContentTypeJSON 345 } 346 347 if len(r.Header) > 0 { 348 if len(req.Header) == 0 { 349 req.Header = r.Header 350 } else { 351 for k, vv := range r.Header { 352 for _, v := range vv { 353 req.Header.Add(k, v) 354 } 355 } 356 } 357 } 358 359 if ctx != nil { 360 req = req.WithContext(ctx) 361 } 362 363 res, err := transport.Perform(req) 364 if err != nil { 365 return nil, err 366 } 367 368 response := Response{ 369 StatusCode: res.StatusCode, 370 Body: res.Body, 371 Header: res.Header, 372 } 373 374 return &response, nil 375 } 376 377 // WithContext sets the request context. 378 // 379 func (f Search) WithContext(v context.Context) func(*SearchRequest) { 380 return func(r *SearchRequest) { 381 r.ctx = v 382 } 383 } 384 385 // WithBody - The search definition using the Query DSL. 386 // 387 func (f Search) WithBody(v io.Reader) func(*SearchRequest) { 388 return func(r *SearchRequest) { 389 r.Body = v 390 } 391 } 392 393 // WithIndex - a list of index names to search; use _all to perform the operation on all indices. 394 // 395 func (f Search) WithIndex(v ...string) func(*SearchRequest) { 396 return func(r *SearchRequest) { 397 r.Index = v 398 } 399 } 400 401 // WithAllowNoIndices - whether to ignore if a wildcard indices expression resolves into no concrete indices. (this includes `_all` string or when no indices have been specified). 402 // 403 func (f Search) WithAllowNoIndices(v bool) func(*SearchRequest) { 404 return func(r *SearchRequest) { 405 r.AllowNoIndices = &v 406 } 407 } 408 409 // WithAllowPartialSearchResults - indicate if an error should be returned if there is a partial search failure or timeout. 410 // 411 func (f Search) WithAllowPartialSearchResults(v bool) func(*SearchRequest) { 412 return func(r *SearchRequest) { 413 r.AllowPartialSearchResults = &v 414 } 415 } 416 417 // WithAnalyzer - the analyzer to use for the query string. 418 // 419 func (f Search) WithAnalyzer(v string) func(*SearchRequest) { 420 return func(r *SearchRequest) { 421 r.Analyzer = v 422 } 423 } 424 425 // WithAnalyzeWildcard - specify whether wildcard and prefix queries should be analyzed (default: false). 426 // 427 func (f Search) WithAnalyzeWildcard(v bool) func(*SearchRequest) { 428 return func(r *SearchRequest) { 429 r.AnalyzeWildcard = &v 430 } 431 } 432 433 // WithBatchedReduceSize - the number of shard results that should be reduced at once on the coordinating node. this value should be used as a protection mechanism to reduce the memory overhead per search request if the potential number of shards in the request can be large.. 434 // 435 func (f Search) WithBatchedReduceSize(v int) func(*SearchRequest) { 436 return func(r *SearchRequest) { 437 r.BatchedReduceSize = &v 438 } 439 } 440 441 // WithCcsMinimizeRoundtrips - indicates whether network round-trips should be minimized as part of cross-cluster search requests execution. 442 // 443 func (f Search) WithCcsMinimizeRoundtrips(v bool) func(*SearchRequest) { 444 return func(r *SearchRequest) { 445 r.CcsMinimizeRoundtrips = &v 446 } 447 } 448 449 // WithDefaultOperator - the default operator for query string query (and or or). 450 // 451 func (f Search) WithDefaultOperator(v string) func(*SearchRequest) { 452 return func(r *SearchRequest) { 453 r.DefaultOperator = v 454 } 455 } 456 457 // WithDf - the field to use as default where no field prefix is given in the query string. 458 // 459 func (f Search) WithDf(v string) func(*SearchRequest) { 460 return func(r *SearchRequest) { 461 r.Df = v 462 } 463 } 464 465 // WithDocvalueFields - a list of fields to return as the docvalue representation of a field for each hit. 466 // 467 func (f Search) WithDocvalueFields(v ...string) func(*SearchRequest) { 468 return func(r *SearchRequest) { 469 r.DocvalueFields = v 470 } 471 } 472 473 // WithExpandWildcards - whether to expand wildcard expression to concrete indices that are open, closed or both.. 474 // 475 func (f Search) WithExpandWildcards(v string) func(*SearchRequest) { 476 return func(r *SearchRequest) { 477 r.ExpandWildcards = v 478 } 479 } 480 481 // WithExplain - specify whether to return detailed information about score computation as part of a hit. 482 // 483 func (f Search) WithExplain(v bool) func(*SearchRequest) { 484 return func(r *SearchRequest) { 485 r.Explain = &v 486 } 487 } 488 489 // WithFrom - starting offset (default: 0). 490 // 491 func (f Search) WithFrom(v int) func(*SearchRequest) { 492 return func(r *SearchRequest) { 493 r.From = &v 494 } 495 } 496 497 // WithIgnoreThrottled - whether specified concrete, expanded or aliased indices should be ignored when throttled. 498 // 499 func (f Search) WithIgnoreThrottled(v bool) func(*SearchRequest) { 500 return func(r *SearchRequest) { 501 r.IgnoreThrottled = &v 502 } 503 } 504 505 // WithIgnoreUnavailable - whether specified concrete indices should be ignored when unavailable (missing or closed). 506 // 507 func (f Search) WithIgnoreUnavailable(v bool) func(*SearchRequest) { 508 return func(r *SearchRequest) { 509 r.IgnoreUnavailable = &v 510 } 511 } 512 513 // WithLenient - specify whether format-based query failures (such as providing text to a numeric field) should be ignored. 514 // 515 func (f Search) WithLenient(v bool) func(*SearchRequest) { 516 return func(r *SearchRequest) { 517 r.Lenient = &v 518 } 519 } 520 521 // WithMaxConcurrentShardRequests - the number of concurrent shard requests per node this search executes concurrently. this value should be used to limit the impact of the search on the cluster in order to limit the number of concurrent shard requests. 522 // 523 func (f Search) WithMaxConcurrentShardRequests(v int) func(*SearchRequest) { 524 return func(r *SearchRequest) { 525 r.MaxConcurrentShardRequests = &v 526 } 527 } 528 529 // WithMinCompatibleShardNode - the minimum compatible version that all shards involved in search should have for this request to be successful. 530 // 531 func (f Search) WithMinCompatibleShardNode(v string) func(*SearchRequest) { 532 return func(r *SearchRequest) { 533 r.MinCompatibleShardNode = v 534 } 535 } 536 537 // WithPreference - specify the node or shard the operation should be performed on (default: random). 538 // 539 func (f Search) WithPreference(v string) func(*SearchRequest) { 540 return func(r *SearchRequest) { 541 r.Preference = v 542 } 543 } 544 545 // WithPreFilterShardSize - a threshold that enforces a pre-filter roundtrip to prefilter search shards based on query rewriting if the number of shards the search request expands to exceeds the threshold. this filter roundtrip can limit the number of shards significantly if for instance a shard can not match any documents based on its rewrite method ie. if date filters are mandatory to match but the shard bounds and the query are disjoint.. 546 // 547 func (f Search) WithPreFilterShardSize(v int) func(*SearchRequest) { 548 return func(r *SearchRequest) { 549 r.PreFilterShardSize = &v 550 } 551 } 552 553 // WithQuery - query in the lucene query string syntax. 554 // 555 func (f Search) WithQuery(v string) func(*SearchRequest) { 556 return func(r *SearchRequest) { 557 r.Query = v 558 } 559 } 560 561 // WithRequestCache - specify if request cache should be used for this request or not, defaults to index level setting. 562 // 563 func (f Search) WithRequestCache(v bool) func(*SearchRequest) { 564 return func(r *SearchRequest) { 565 r.RequestCache = &v 566 } 567 } 568 569 // WithRestTotalHitsAsInt - indicates whether hits.total should be rendered as an integer or an object in the rest search response. 570 // 571 func (f Search) WithRestTotalHitsAsInt(v bool) func(*SearchRequest) { 572 return func(r *SearchRequest) { 573 r.RestTotalHitsAsInt = &v 574 } 575 } 576 577 // WithRouting - a list of specific routing values. 578 // 579 func (f Search) WithRouting(v ...string) func(*SearchRequest) { 580 return func(r *SearchRequest) { 581 r.Routing = v 582 } 583 } 584 585 // WithScroll - specify how long a consistent view of the index should be maintained for scrolled search. 586 // 587 func (f Search) WithScroll(v time.Duration) func(*SearchRequest) { 588 return func(r *SearchRequest) { 589 r.Scroll = v 590 } 591 } 592 593 // WithSearchType - search operation type. 594 // 595 func (f Search) WithSearchType(v string) func(*SearchRequest) { 596 return func(r *SearchRequest) { 597 r.SearchType = v 598 } 599 } 600 601 // WithSeqNoPrimaryTerm - specify whether to return sequence number and primary term of the last modification of each hit. 602 // 603 func (f Search) WithSeqNoPrimaryTerm(v bool) func(*SearchRequest) { 604 return func(r *SearchRequest) { 605 r.SeqNoPrimaryTerm = &v 606 } 607 } 608 609 // WithSize - number of hits to return (default: 10). 610 // 611 func (f Search) WithSize(v int) func(*SearchRequest) { 612 return func(r *SearchRequest) { 613 r.Size = &v 614 } 615 } 616 617 // WithSort - a list of <field>:<direction> pairs. 618 // 619 func (f Search) WithSort(v ...string) func(*SearchRequest) { 620 return func(r *SearchRequest) { 621 r.Sort = v 622 } 623 } 624 625 // WithSource - true or false to return the _source field or not, or a list of fields to return. 626 // 627 func (f Search) WithSource(v interface{}) func(*SearchRequest) { 628 return func(r *SearchRequest) { 629 r.Source = v 630 } 631 } 632 633 // WithSourceExcludes - a list of fields to exclude from the returned _source field. 634 // 635 func (f Search) WithSourceExcludes(v ...string) func(*SearchRequest) { 636 return func(r *SearchRequest) { 637 r.SourceExcludes = v 638 } 639 } 640 641 // WithSourceIncludes - a list of fields to extract and return from the _source field. 642 // 643 func (f Search) WithSourceIncludes(v ...string) func(*SearchRequest) { 644 return func(r *SearchRequest) { 645 r.SourceIncludes = v 646 } 647 } 648 649 // WithStats - specific 'tag' of the request for logging and statistical purposes. 650 // 651 func (f Search) WithStats(v ...string) func(*SearchRequest) { 652 return func(r *SearchRequest) { 653 r.Stats = v 654 } 655 } 656 657 // WithStoredFields - a list of stored fields to return as part of a hit. 658 // 659 func (f Search) WithStoredFields(v ...string) func(*SearchRequest) { 660 return func(r *SearchRequest) { 661 r.StoredFields = v 662 } 663 } 664 665 // WithSuggestField - specify which field to use for suggestions. 666 // 667 func (f Search) WithSuggestField(v string) func(*SearchRequest) { 668 return func(r *SearchRequest) { 669 r.SuggestField = v 670 } 671 } 672 673 // WithSuggestMode - specify suggest mode. 674 // 675 func (f Search) WithSuggestMode(v string) func(*SearchRequest) { 676 return func(r *SearchRequest) { 677 r.SuggestMode = v 678 } 679 } 680 681 // WithSuggestSize - how many suggestions to return in response. 682 // 683 func (f Search) WithSuggestSize(v int) func(*SearchRequest) { 684 return func(r *SearchRequest) { 685 r.SuggestSize = &v 686 } 687 } 688 689 // WithSuggestText - the source text for which the suggestions should be returned. 690 // 691 func (f Search) WithSuggestText(v string) func(*SearchRequest) { 692 return func(r *SearchRequest) { 693 r.SuggestText = v 694 } 695 } 696 697 // WithTerminateAfter - the maximum number of documents to collect for each shard, upon reaching which the query execution will terminate early.. 698 // 699 func (f Search) WithTerminateAfter(v int) func(*SearchRequest) { 700 return func(r *SearchRequest) { 701 r.TerminateAfter = &v 702 } 703 } 704 705 // WithTimeout - explicit operation timeout. 706 // 707 func (f Search) WithTimeout(v time.Duration) func(*SearchRequest) { 708 return func(r *SearchRequest) { 709 r.Timeout = v 710 } 711 } 712 713 // WithTrackScores - whether to calculate and return scores even if they are not used for sorting. 714 // 715 func (f Search) WithTrackScores(v bool) func(*SearchRequest) { 716 return func(r *SearchRequest) { 717 r.TrackScores = &v 718 } 719 } 720 721 // WithTrackTotalHits - indicate if the number of documents that match the query should be tracked. 722 // 723 func (f Search) WithTrackTotalHits(v interface{}) func(*SearchRequest) { 724 return func(r *SearchRequest) { 725 r.TrackTotalHits = v 726 } 727 } 728 729 // WithTypedKeys - specify whether aggregation and suggester names should be prefixed by their respective types in the response. 730 // 731 func (f Search) WithTypedKeys(v bool) func(*SearchRequest) { 732 return func(r *SearchRequest) { 733 r.TypedKeys = &v 734 } 735 } 736 737 // WithVersion - specify whether to return document version as part of a hit. 738 // 739 func (f Search) WithVersion(v bool) func(*SearchRequest) { 740 return func(r *SearchRequest) { 741 r.Version = &v 742 } 743 } 744 745 // WithPretty makes the response body pretty-printed. 746 // 747 func (f Search) WithPretty() func(*SearchRequest) { 748 return func(r *SearchRequest) { 749 r.Pretty = true 750 } 751 } 752 753 // WithHuman makes statistical values human-readable. 754 // 755 func (f Search) WithHuman() func(*SearchRequest) { 756 return func(r *SearchRequest) { 757 r.Human = true 758 } 759 } 760 761 // WithErrorTrace includes the stack trace for errors in the response body. 762 // 763 func (f Search) WithErrorTrace() func(*SearchRequest) { 764 return func(r *SearchRequest) { 765 r.ErrorTrace = true 766 } 767 } 768 769 // WithFilterPath filters the properties of the response body. 770 // 771 func (f Search) WithFilterPath(v ...string) func(*SearchRequest) { 772 return func(r *SearchRequest) { 773 r.FilterPath = v 774 } 775 } 776 777 // WithHeader adds the headers to the HTTP request. 778 // 779 func (f Search) WithHeader(h map[string]string) func(*SearchRequest) { 780 return func(r *SearchRequest) { 781 if r.Header == nil { 782 r.Header = make(http.Header) 783 } 784 for k, v := range h { 785 r.Header.Add(k, v) 786 } 787 } 788 } 789 790 // WithOpaqueID adds the X-Opaque-Id header to the HTTP request. 791 // 792 func (f Search) WithOpaqueID(s string) func(*SearchRequest) { 793 return func(r *SearchRequest) { 794 if r.Header == nil { 795 r.Header = make(http.Header) 796 } 797 r.Header.Set("X-Opaque-Id", s) 798 } 799 }