Repository: opensearch-project/opensearch-go
Pull request #532 (2024-04-18 16:23:39)

Add missing `search_pipeline` field in `opensearchapi.SearchParams`
### Description
Added `search_pipeline` field in SearchParams struct.

_Note: I didn't add any tests as I can't see any others there for existing `SearchParam` fields._

#### Context:

With the addition of hybrid search and search pipelines in Opensearch version `2.11`, a new `search_pipeline` parameter exists on the `_search` endpoint ([docs here](https://opensearch.org/docs/latest/search-plugins/search-pipelines/using-search-pipeline/)).

### Issues Resolved
This pull request supports the new search pipelines functionality by allowing users of the library to set the `search_pipeline` parameter via the `SearchParams` struct.

By submitting this pull request, I confirm that my contribution is made under the terms of the Apache 2.0 license.
For more information on following Developer Certificate of Origin and signing off your commits, please check [here](https://github.com/opensearch-project/OpenSearch/blob/main/CONTRIBUTING.md#developer-certificate-of-origin).


codecov[bot]: ## [Codecov](https://app.codecov.io/gh/opensearch-project/opensearch-go/pull/532?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=opensearch-project) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Project coverage is 81.64%. Comparing base [(`06a6dc8`)](https://app.codecov.io/gh/opensearch-project/opensearch-go/commit/06a6dc8cbaeb8d1e3657487661edd4900e4f31df?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=opensearch-project) to head [(`9978925`)](https://app.codecov.io/gh/opensearch-project/opensearch-go/pull/532?dropdown=coverage&src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=opensearch-project).
> Report is 15 commits behind head on main.

> :exclamation: Current head 9978925 differs from pull request most recent head 9250b81. Consider uploading reports for the commit 9250b81 to get more accurate results

<details><summary>Additional details and impacted files</summary>


```diff
@@             Coverage Diff             @@
##             main     #532       +/-   ##
===========================================
+ Coverage   57.29%   81.64%   +24.34%     
===========================================
  Files         315       14      -301     
  Lines        9823     1329     -8494     
===========================================
- Hits         5628     1085     -4543     
+ Misses       2902      165     -2737     
+ Partials     1293       79     -1214     
```

| [Flag](https://app.codecov.io/gh/opensearch-project/opensearch-go/pull/532/flags?src=pr&el=flags&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=opensearch-project) | Coverage Δ | |
|---|---|---|
| [integration](https://app.codecov.io/gh/opensearch-project/opensearch-go/pull/532/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=opensearch-project) | `?` | |
| [unit](https://app.codecov.io/gh/opensearch-project/opensearch-go/pull/532/flags?src=pr&el=flag&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=opensearch-project) | `81.64% <ø> (+68.79%)` | :arrow_up: |

Flags with carried forward coverage won't be shown. [Click here](https://docs.codecov.io/docs/carryforward-flags?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=opensearch-project#carryforward-flags-in-the-pull-request-comment) to find out more.

[see 314 files with indirect coverage changes](https://app.codecov.io/gh/opensearch-project/opensearch-go/pull/532/indirect-changes?src=pr&el=tree-more&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=opensearch-project)

</details>
dblock: We do need tests for existing and new code, can you please add something simple that exercises params, please?
jackh-ncl: Thank you 😃 

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index b4f8e12..ea54d47 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,6 +6,8 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/)
 
 ### Added
 
+- Adds the `SearchPipelines` field to `SearchParams` ([#532](https://github.com/opensearch-project/opensearch-go/pull/532))
+
 ### Changed
 
 ### Deprecated
diff --git a/opensearchapi/api_search-params.go b/opensearchapi/api_search-params.go
index 3dcff95..b1e2b63 100644
--- a/opensearchapi/api_search-params.go
+++ b/opensearchapi/api_search-params.go
@@ -59,6 +59,7 @@ type SearchParams struct {
 	RestTotalHitsAsInt         *bool
 	Routing                    []string
 	Scroll                     time.Duration
+	SearchPipeline             string
 	SearchType                 string
 	SeqNoPrimaryTerm           *bool
 	Size                       *int
@@ -183,6 +184,10 @@ func (r SearchParams) get() map[string]string {
 		params["scroll"] = formatDuration(r.Scroll)
 	}
 
+	if r.SearchPipeline != "" {
+		params["search_pipeline"] = r.SearchPipeline
+	}
+
 	if r.SearchType != "" {
 		params["search_type"] = r.SearchType
 	}


Test patch:
diff --git a/opensearchapi/api_search-params_test.go b/opensearchapi/api_search-params_test.go
new file mode 100644
index 0000000..33b616d
--- /dev/null
+++ b/opensearchapi/api_search-params_test.go
@@ -0,0 +1,235 @@
+// SPDX-License-Identifier: Apache-2.0
+//
+// The OpenSearch Contributors require contributions made to
+// this file be licensed under the Apache-2.0 license or a
+// compatible open source license.
+//
+// Modifications Copyright OpenSearch Contributors. See
+// GitHub history for details.
+//
+//go:build !integration
+
+//nolint:testpackage // to test unexported get() method
+package opensearchapi
+
+import (
+	"testing"
+	"time"
+
+	"github.com/stretchr/testify/assert"
+)
+
+func TestSearchParams_get(t *testing.T) {
+	type fields struct {
+		AllowNoIndices             *bool
+		AllowPartialSearchResults  *bool
+		Analyzer                   string
+		AnalyzeWildcard            *bool
+		BatchedReduceSize          *int
+		CcsMinimizeRoundtrips      *bool
+		DefaultOperator            string
+		Df                         string
+		DocvalueFields             []string
+		ExpandWildcards            string
+		Explain                    *bool
+		From                       *int
+		IgnoreThrottled            *bool
+		IgnoreUnavailable          *bool
+		Lenient                    *bool
+		MaxConcurrentShardRequests *int
+		MinCompatibleShardNode     string
+		Preference                 string
+		PreFilterShardSize         *int
+		Query                      string
+		RequestCache               *bool
+		RestTotalHitsAsInt         *bool
+		Routing                    []string
+		Scroll                     time.Duration
+		SearchPipeline             string
+		SearchType                 string
+		SeqNoPrimaryTerm           *bool
+		Size                       *int
+		Sort                       []string
+		Source                     interface{}
+		SourceExcludes             []string
+		SourceIncludes             []string
+		Stats                      []string
+		StoredFields               []string
+		SuggestField               string
+		SuggestMode                string
+		SuggestSize                *int
+		SuggestText                string
+		TerminateAfter             *int
+		Timeout                    time.Duration
+		TrackScores                *bool
+		TrackTotalHits             interface{}
+		TypedKeys                  *bool
+		Version                    *bool
+		Pretty                     bool
+		Human                      bool
+		ErrorTrace                 bool
+	}
+	tests := []struct {
+		name   string
+		fields fields
+		want   map[string]string
+	}{
+		{
+			name:   "Not specifying any parameter should result in an empty parameter map",
+			fields: fields{},
+			want:   map[string]string{},
+		},
+		{
+			name: "Test search params are assigned and in correct format",
+			fields: fields{
+				AllowNoIndices:             ToPointer(true),
+				AllowPartialSearchResults:  ToPointer(true),
+				AnalyzeWildcard:            ToPointer(true),
+				Analyzer:                   "default",
+				BatchedReduceSize:          ToPointer(30),
+				CcsMinimizeRoundtrips:      ToPointer(true),
+				DefaultOperator:            "OR",
+				Df:                         "cake",
+				DocvalueFields:             []string{"title", "date"},
+				ErrorTrace:                 false,
+				ExpandWildcards:            "open,hidden",
+				Explain:                    ToPointer(true),
+				From:                       ToPointer(30),
+				Human:                      false,
+				IgnoreThrottled:            ToPointer(true),
+				IgnoreUnavailable:          ToPointer(true),
+				Lenient:                    ToPointer(true),
+				MaxConcurrentShardRequests: ToPointer(100),
+				MinCompatibleShardNode:     "one",
+				PreFilterShardSize:         ToPointer(128),
+				Preference:                 "_local",
+				Pretty:                     false,
+				Query:                      "{ \"match_all\": {} }",
+				RequestCache:               ToPointer(true),
+				RestTotalHitsAsInt:         ToPointer(true),
+				Routing:                    []string{"route1", "route2"},
+				Scroll:                     10 * time.Second,
+				SearchPipeline:             "balanced",
+				SearchType:                 "query_then_fetch",
+				SeqNoPrimaryTerm:           ToPointer(true),
+				Size:                       ToPointer(100),
+				Sort:                       []string{"title:asc", "date:desc"},
+				Source:                     []string{"title", "date"},
+				SourceExcludes:             []string{"description"},
+				SourceIncludes:             []string{"image"},
+				Stats:                      []string{"fielddata"},
+				StoredFields:               []string{"embedding"},
+				SuggestField:               "title",
+				SuggestMode:                "missing",
+				SuggestSize:                ToPointer(10),
+				SuggestText:                "title",
+				TerminateAfter:             ToPointer(30),
+				Timeout:                    30 * time.Second,
+				TrackScores:                ToPointer(true),
+				TrackTotalHits:             1000,
+				TypedKeys:                  ToPointer(true),
+				Version:                    ToPointer(true),
+			},
+			want: map[string]string{
+				"_source":                       "title,date",
+				"_source_excludes":              "description",
+				"_source_includes":              "image",
+				"allow_no_indices":              "true",
+				"allow_partial_search_results":  "true",
+				"analyze_wildcard":              "true",
+				"analyzer":                      "default",
+				"batched_reduce_size":           "30",
+				"ccs_minimize_roundtrips":       "true",
+				"default_operator":              "OR",
+				"df":                            "cake",
+				"docvalue_fields":               "title,date",
+				"expand_wildcards":              "open,hidden",
+				"explain":                       "true",
+				"from":                          "30",
+				"ignore_throttled":              "true",
+				"ignore_unavailable":            "true",
+				"lenient":                       "true",
+				"max_concurrent_shard_requests": "100",
+				"min_compatible_shard_node":     "one",
+				"pre_filter_shard_size":         "128",
+				"preference":                    "_local",
+				"q":                             "{ \"match_all\": {} }",
+				"request_cache":                 "true",
+				"rest_total_hits_as_int":        "true",
+				"routing":                       "route1,route2",
+				"scroll":                        "10000ms",
+				"search_pipeline":               "balanced",
+				"search_type":                   "query_then_fetch",
+				"seq_no_primary_term":           "true",
+				"size":                          "100",
+				"sort":                          "title:asc,date:desc",
+				"stats":                         "fielddata",
+				"stored_fields":                 "embedding",
+				"suggest_field":                 "title",
+				"suggest_mode":                  "missing",
+				"suggest_size":                  "10",
+				"suggest_text":                  "title",
+				"terminate_after":               "30",
+				"timeout":                       "30000ms",
+				"track_scores":                  "true",
+				"track_total_hits":              "1000",
+				"typed_keys":                    "true",
+				"version":                       "true",
+			},
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			r := SearchParams{
+				AllowNoIndices:             tt.fields.AllowNoIndices,
+				AllowPartialSearchResults:  tt.fields.AllowPartialSearchResults,
+				Analyzer:                   tt.fields.Analyzer,
+				AnalyzeWildcard:            tt.fields.AnalyzeWildcard,
+				BatchedReduceSize:          tt.fields.BatchedReduceSize,
+				CcsMinimizeRoundtrips:      tt.fields.CcsMinimizeRoundtrips,
+				DefaultOperator:            tt.fields.DefaultOperator,
+				Df:                         tt.fields.Df,
+				DocvalueFields:             tt.fields.DocvalueFields,
+				ExpandWildcards:            tt.fields.ExpandWildcards,
+				Explain:                    tt.fields.Explain,
+				From:                       tt.fields.From,
+				IgnoreThrottled:            tt.fields.IgnoreThrottled,
+				IgnoreUnavailable:          tt.fields.IgnoreUnavailable,
+				Lenient:                    tt.fields.Lenient,
+				MaxConcurrentShardRequests: tt.fields.MaxConcurrentShardRequests,
+				MinCompatibleShardNode:     tt.fields.MinCompatibleShardNode,
+				Preference:                 tt.fields.Preference,
+				PreFilterShardSize:         tt.fields.PreFilterShardSize,
+				Query:                      tt.fields.Query,
+				RequestCache:               tt.fields.RequestCache,
+				RestTotalHitsAsInt:         tt.fields.RestTotalHitsAsInt,
+				Routing:                    tt.fields.Routing,
+				Scroll:                     tt.fields.Scroll,
+				SearchPipeline:             tt.fields.SearchPipeline,
+				SearchType:                 tt.fields.SearchType,
+				SeqNoPrimaryTerm:           tt.fields.SeqNoPrimaryTerm,
+				Size:                       tt.fields.Size,
+				Sort:                       tt.fields.Sort,
+				Source:                     tt.fields.Source,
+				SourceExcludes:             tt.fields.SourceExcludes,
+				SourceIncludes:             tt.fields.SourceIncludes,
+				Stats:                      tt.fields.Stats,
+				StoredFields:               tt.fields.StoredFields,
+				SuggestField:               tt.fields.SuggestField,
+				SuggestMode:                tt.fields.SuggestMode,
+				SuggestSize:                tt.fields.SuggestSize,
+				SuggestText:                tt.fields.SuggestText,
+				TerminateAfter:             tt.fields.TerminateAfter,
+				Timeout:                    tt.fields.Timeout,
+				TrackScores:                tt.fields.TrackScores,
+				TrackTotalHits:             tt.fields.TrackTotalHits,
+				TypedKeys:                  tt.fields.TypedKeys,
+				Version:                    tt.fields.Version,
+				Pretty:                     tt.fields.Pretty,
+				Human:                      tt.fields.Human,
+				ErrorTrace:                 tt.fields.ErrorTrace,
+			}
+			assert.Equalf(t, tt.want, r.get(), "get()")
+		})
+	}
+}