diff --git a/CHANGELOG.md b/CHANGELOG.md index 9e3b01c8..6bac2863 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,8 @@ Inspired from [Keep a Changelog](https://keepachangelog.com/en/1.0.0/) ## [Unreleased] ### Dependencies ### Added +- Adds `InnerHits` field to `SearchResp` ([#672](https://github.com/opensearch-project/opensearch-go/pull/672)) + ### Changed ### Deprecated ### Removed diff --git a/opensearchapi/api_search.go b/opensearchapi/api_search.go index e0465143..fb0b27d7 100644 --- a/opensearchapi/api_search.go +++ b/opensearchapi/api_search.go @@ -63,17 +63,10 @@ func (r SearchReq) GetRequest() (*http.Request, error) { // SearchResp represents the returned struct of the /_search response type SearchResp struct { - Took int `json:"took"` - Timeout bool `json:"timed_out"` - Shards ResponseShards `json:"_shards"` - Hits struct { - Total struct { - Value int `json:"value"` - Relation string `json:"relation"` - } `json:"total"` - MaxScore float32 `json:"max_score"` - Hits []SearchHit `json:"hits"` - } `json:"hits"` + Took int `json:"took"` + Timeout bool `json:"timed_out"` + Shards ResponseShards `json:"_shards"` + Hits SearchHits `json:"hits"` Errors bool `json:"errors"` Aggregations json.RawMessage `json:"aggregations"` ScrollID *string `json:"_scroll_id,omitempty"` @@ -86,14 +79,27 @@ func (r SearchResp) Inspect() Inspect { return Inspect{Response: r.response} } +// SearchHits is a list of SearchHit with Total and MaxScore fields +type SearchHits struct { + Total struct { + Value int `json:"value"` + Relation string `json:"relation"` + } `json:"total"` + MaxScore float32 `json:"max_score"` + Hits []SearchHit `json:"hits"` +} + // SearchHit is a sub type of SearchResp containing information of the search hit with an unparsed Source field type SearchHit struct { - Index string `json:"_index"` - ID string `json:"_id"` - Routing string `json:"_routing"` - Score float32 `json:"_score"` - Source json.RawMessage `json:"_source"` - Fields json.RawMessage `json:"fields"` + Index string `json:"_index"` + ID string `json:"_id"` + Routing string `json:"_routing"` + Score float32 `json:"_score"` + Source json.RawMessage `json:"_source"` + Fields json.RawMessage `json:"fields"` + InnerHits map[string]struct { + Hits SearchHits `json:"hits"` + } `json:"inner_hits"` Type string `json:"_type"` // Deprecated field Sort []any `json:"sort"` Explanation *DocumentExplainDetails `json:"_explanation"` diff --git a/opensearchapi/api_search_test.go b/opensearchapi/api_search_test.go index d214cec2..ea8297fe 100644 --- a/opensearchapi/api_search_test.go +++ b/opensearchapi/api_search_test.go @@ -27,12 +27,28 @@ func TestSearch(t *testing.T) { index := "test-index-search" + _, err = client.Indices.Create( + nil, + opensearchapi.IndicesCreateReq{ + Index: index, + Body: strings.NewReader(`{ + "mappings": { + "properties": { + "baz": { + "type": "nested" + } + } + } + }`), + }, + ) + require.Nil(t, err) _, err = client.Index( nil, opensearchapi.IndexReq{ DocumentID: "foo", Index: index, - Body: strings.NewReader(`{"foo": "bar"}`), + Body: strings.NewReader(`{"foo": "bar", "baz": [{"foo": "test"}]}`), Params: opensearchapi.IndexParams{Refresh: "true", Routing: "foo"}, }, ) @@ -227,4 +243,31 @@ func TestSearch(t *testing.T) { assert.NotEmpty(t, resp.Hits.Hits) assert.Equal(t, []string{"test"}, resp.Hits.Hits[0].MatchedQueries) }) + + t.Run("request with inner hits", func(t *testing.T) { + resp, err := client.Search( + nil, + &opensearchapi.SearchReq{ + Indices: []string{index}, + Body: strings.NewReader(`{ + "query": { + "nested": { + "path": "baz", + "query": { + "match": { + "baz.foo": "test" + } + }, + "inner_hits": {} + } + } + }`), + }, + ) + require.Nil(t, err) + assert.NotEmpty(t, resp.Hits.Hits) + assert.NotEmpty(t, resp.Hits.Hits[0].InnerHits) + assert.NotNil(t, resp.Hits.Hits[0].InnerHits["baz"]) + assert.NotEmpty(t, resp.Hits.Hits[0].InnerHits["baz"].Hits.Hits) + }) }