package meilisearch import ( "crypto/tls" "encoding/json" "testing" "github.com/stretchr/testify/require" ) func TestIndex_SearchWithContentEncoding(t *testing.T) { tests := []struct { Name string ContentEncoding ContentEncoding Query string Request *SearchRequest FacetRequest *FacetSearchRequest Response *SearchResponse FacetResponse *FacetSearchResponse }{ { Name: "SearchResultWithGzipEncoding", ContentEncoding: GzipEncoding, Query: "prince", Request: &SearchRequest{ IndexUID: "indexUID", Limit: 20, Offset: 0, }, FacetRequest: &FacetSearchRequest{ FacetName: "tag", FacetQuery: "Novel", }, FacetResponse: &FacetSearchResponse{ FacetHits: []interface{}{ map[string]interface{}{ "value": "Novel", "count": float64(5), }, }, FacetQuery: "Novel", }, Response: &SearchResponse{ Hits: []interface{}{ map[string]interface{}{ "book_id": float64(456), "title": "Le Petit Prince", }, map[string]interface{}{ "Tag": "Epic fantasy", "book_id": float64(4), "title": "Harry Potter and the Half-Blood Prince", }, }, EstimatedTotalHits: 2, Offset: 0, Limit: 20, }, }, { Name: "SearchResultWithDeflateEncoding", ContentEncoding: DeflateEncoding, Query: "prince", Request: &SearchRequest{ IndexUID: "indexUID", Limit: 20, Offset: 0, }, Response: &SearchResponse{ Hits: []interface{}{ map[string]interface{}{ "book_id": float64(456), "title": "Le Petit Prince", }, map[string]interface{}{ "Tag": "Epic fantasy", "book_id": float64(4), "title": "Harry Potter and the Half-Blood Prince", }, }, EstimatedTotalHits: 2, Offset: 0, Limit: 20, }, FacetRequest: &FacetSearchRequest{ FacetName: "tag", FacetQuery: "Novel", }, FacetResponse: &FacetSearchResponse{ FacetHits: []interface{}{ map[string]interface{}{ "value": "Novel", "count": float64(5), }, }, FacetQuery: "Novel", }, }, { Name: "SearchResultWithBrotliEncoding", ContentEncoding: BrotliEncoding, Query: "prince", Request: &SearchRequest{ IndexUID: "indexUID", Limit: 20, Offset: 0, }, Response: &SearchResponse{ Hits: []interface{}{ map[string]interface{}{ "book_id": float64(456), "title": "Le Petit Prince", }, map[string]interface{}{ "Tag": "Epic fantasy", "book_id": float64(4), "title": "Harry Potter and the Half-Blood Prince", }, }, EstimatedTotalHits: 2, Offset: 0, Limit: 20, }, FacetRequest: &FacetSearchRequest{ FacetName: "tag", FacetQuery: "Novel", }, FacetResponse: &FacetSearchResponse{ FacetHits: []interface{}{ map[string]interface{}{ "value": "Novel", "count": float64(5), }, }, FacetQuery: "Novel", }, }, } for _, tt := range tests { t.Run(tt.Name, func(t *testing.T) { sv := setup(t, "", WithContentEncoding(tt.ContentEncoding, DefaultCompression)) setUpIndexForFaceting(sv) i := sv.Index(tt.Request.IndexUID) t.Cleanup(cleanup(sv)) got, err := i.Search(tt.Query, tt.Request) require.NoError(t, err) require.Equal(t, len(tt.Response.Hits), len(got.Hits)) gotJson, err := i.SearchRaw(tt.Query, tt.Request) require.NoError(t, err) var resp SearchResponse err = json.Unmarshal(*gotJson, &resp) require.NoError(t, err, "error unmarshalling raw got SearchResponse") require.Equal(t, len(tt.Response.Hits), len(resp.Hits)) filterableAttrs := []string{"tag"} task, err := i.UpdateFilterableAttributes(&filterableAttrs) require.NoError(t, err) testWaitForTask(t, i, task) gotJson, err = i.FacetSearch(tt.FacetRequest) require.NoError(t, err) var gotFacet FacetSearchResponse err = json.Unmarshal(*gotJson, &gotFacet) require.NoError(t, err, "error unmarshalling raw got SearchResponse") require.NoError(t, err) require.Equal(t, len(gotFacet.FacetHits), len(tt.FacetResponse.FacetHits)) }) } } func TestIndex_SearchRaw(t *testing.T) { sv := setup(t, "") type args struct { UID string PrimaryKey string client ServiceManager query string request *SearchRequest } tests := []struct { name string args args want *SearchResponse wantErr bool }{ { name: "TestIndexBasicSearch", args: args{ UID: "indexUID", client: sv, query: "prince", request: &SearchRequest{ IndexUID: "foobar", }, }, want: &SearchResponse{ Hits: []interface{}{ map[string]interface{}{ "book_id": float64(456), "title": "Le Petit Prince", }, map[string]interface{}{ "Tag": "Epic fantasy", "book_id": float64(4), "title": "Harry Potter and the Half-Blood Prince", }, }, EstimatedTotalHits: 2, Offset: 0, Limit: 20, }, wantErr: false, }, { name: "TestNullRequestInSearchRow", args: args{ UID: "indexUID", client: sv, query: "prince", request: nil, }, want: nil, wantErr: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { setUpIndexForFaceting(tt.args.client) c := tt.args.client i := c.Index(tt.args.UID) t.Cleanup(cleanup(c)) gotRaw, err := i.SearchRaw(tt.args.query, tt.args.request) if tt.wantErr { require.Error(t, err) require.Nil(t, tt.want) return } require.NoError(t, err) // Unmarshall the raw response from SearchRaw into a SearchResponse var got SearchResponse err = json.Unmarshal(*gotRaw, &got) require.NoError(t, err, "error unmarshalling raw got SearchResponse") require.Equal(t, len(tt.want.Hits), len(got.Hits)) for len := range got.Hits { require.Equal(t, tt.want.Hits[len].(map[string]interface{})["title"], got.Hits[len].(map[string]interface{})["title"]) require.Equal(t, tt.want.Hits[len].(map[string]interface{})["book_id"], got.Hits[len].(map[string]interface{})["book_id"]) } if tt.want.Hits[0].(map[string]interface{})["_formatted"] != nil { require.Equal(t, tt.want.Hits[0].(map[string]interface{})["_formatted"], got.Hits[0].(map[string]interface{})["_formatted"]) } require.Equal(t, tt.want.EstimatedTotalHits, got.EstimatedTotalHits) require.Equal(t, tt.want.Offset, got.Offset) require.Equal(t, tt.want.Limit, got.Limit) require.Equal(t, tt.want.FacetDistribution, got.FacetDistribution) }) } } func TestIndex_Search(t *testing.T) { sv := setup(t, "") customSv := setup(t, "", WithCustomClientWithTLS(&tls.Config{ InsecureSkipVerify: true, })) type args struct { UID string PrimaryKey string client ServiceManager query string request *SearchRequest } tests := []struct { name string args args want *SearchResponse wantErr bool }{ { name: "TestIndexSearchWithEmptyRequest", args: args{ UID: "indexUID", client: sv, query: "prince", request: nil, }, want: nil, wantErr: true, }, { name: "TestIndexBasicSearch", args: args{ UID: "indexUID", client: sv, query: "prince", request: &SearchRequest{}, }, want: &SearchResponse{ Hits: []interface{}{ map[string]interface{}{ "book_id": float64(456), "title": "Le Petit Prince", }, map[string]interface{}{ "Tag": "Epic fantasy", "book_id": float64(4), "title": "Harry Potter and the Half-Blood Prince", }, }, EstimatedTotalHits: 2, Offset: 0, Limit: 20, }, wantErr: false, }, { name: "TestIndexBasicSearchWithIndexUIDInRequest", args: args{ UID: "indexUID", client: sv, query: "prince", request: &SearchRequest{ IndexUID: "foobar", }, }, want: &SearchResponse{ Hits: []interface{}{ map[string]interface{}{ "book_id": float64(456), "title": "Le Petit Prince", }, map[string]interface{}{ "Tag": "Epic fantasy", "book_id": float64(4), "title": "Harry Potter and the Half-Blood Prince", }, }, EstimatedTotalHits: 2, Offset: 0, Limit: 20, }, wantErr: false, }, { name: "TestIndexSearchWithCustomClient", args: args{ UID: "indexUID", client: customSv, query: "prince", request: &SearchRequest{}, }, want: &SearchResponse{ Hits: []interface{}{ map[string]interface{}{ "book_id": float64(456), "title": "Le Petit Prince", }, map[string]interface{}{ "Tag": "Epic fantasy", "book_id": float64(4), "title": "Harry Potter and the Half-Blood Prince", }, }, EstimatedTotalHits: 2, Offset: 0, Limit: 20, }, wantErr: false, }, { name: "TestIndexSearchWithLimit", args: args{ UID: "indexUID", client: sv, query: "prince", request: &SearchRequest{ Limit: 1, }, }, want: &SearchResponse{ Hits: []interface{}{ map[string]interface{}{ "book_id": float64(456), "title": "Le Petit Prince", }, }, EstimatedTotalHits: 2, Offset: 0, Limit: 1, }, wantErr: false, }, { name: "TestIndexSearchWithPlaceholderSearch", args: args{ UID: "indexUID", client: sv, request: &SearchRequest{ Limit: 1, }, }, want: &SearchResponse{ Hits: []interface{}{ map[string]interface{}{ "book_id": float64(123), "title": "Pride and Prejudice", }, }, EstimatedTotalHits: 22, Offset: 0, Limit: 1, }, wantErr: false, }, { name: "TestIndexSearchWithOffset", args: args{ UID: "indexUID", client: sv, query: "prince", request: &SearchRequest{ Offset: 1, }, }, want: &SearchResponse{ Hits: []interface{}{ map[string]interface{}{ "book_id": float64(4), "title": "Harry Potter and the Half-Blood Prince", }, }, EstimatedTotalHits: 2, Offset: 1, Limit: 20, }, wantErr: false, }, { name: "TestIndexSearchWithAttributeToRetrieve", args: args{ UID: "indexUID", client: sv, query: "prince", request: &SearchRequest{ AttributesToRetrieve: []string{"book_id", "title"}, }, }, want: &SearchResponse{ Hits: []interface{}{ map[string]interface{}{ "book_id": float64(456), "title": "Le Petit Prince", }, map[string]interface{}{ "book_id": float64(4), "title": "Harry Potter and the Half-Blood Prince", }, }, EstimatedTotalHits: 2, Offset: 0, Limit: 20, }, wantErr: false, }, { name: "TestIndexSearchWithAttributeToSearchOn", args: args{ UID: "indexUID", client: sv, query: "prince", request: &SearchRequest{ AttributesToSearchOn: []string{"title"}, }, }, want: &SearchResponse{ Hits: []interface{}{ map[string]interface{}{ "book_id": float64(456), "title": "Le Petit Prince", }, map[string]interface{}{ "book_id": float64(4), "title": "Harry Potter and the Half-Blood Prince", }, }, EstimatedTotalHits: 2, Offset: 0, Limit: 20, }, wantErr: false, }, { name: "TestIndexSearchWithAttributesToCrop", args: args{ UID: "indexUID", client: sv, query: "to", request: &SearchRequest{ AttributesToCrop: []string{"title"}, CropLength: 2, }, }, want: &SearchResponse{ Hits: []interface{}{ map[string]interface{}{ "book_id": float64(42), "title": "The Hitchhiker's Guide to the Galaxy", "_formatted": map[string]interface{}{ "book_id": "42", "tag": "Epic fantasy", "title": "…Guide to…", "year": "1978", }, }, }, EstimatedTotalHits: 1, Offset: 0, Limit: 20, }, wantErr: false, }, { name: "TestIndexSearchWithAttributesToCropAndCustomCropMarker", args: args{ UID: "indexUID", client: sv, query: "to", request: &SearchRequest{ AttributesToCrop: []string{"title"}, CropLength: 2, CropMarker: "(ꈍᴗꈍ)", }, }, want: &SearchResponse{ Hits: []interface{}{ map[string]interface{}{ "book_id": float64(42), "title": "The Hitchhiker's Guide to the Galaxy", "_formatted": map[string]interface{}{ "book_id": "42", "tag": "Epic fantasy", "title": "(ꈍᴗꈍ)Guide to(ꈍᴗꈍ)", "year": "1978", }, }, }, EstimatedTotalHits: 1, Offset: 0, Limit: 20, }, wantErr: false, }, { name: "TestIndexSearchWithAttributeToHighlight", args: args{ UID: "indexUID", client: sv, query: "prince", request: &SearchRequest{ Limit: 1, AttributesToHighlight: []string{"*"}, }, }, want: &SearchResponse{ Hits: []interface{}{ map[string]interface{}{ "book_id": float64(456), "title": "Le Petit Prince", "_formatted": map[string]interface{}{ "book_id": "456", "tag": "Tale", "title": "Le Petit Prince", "year": "1943", }, }, }, EstimatedTotalHits: 2, Offset: 0, Limit: 1, }, wantErr: false, }, { name: "TestIndexSearchWithCustomPreAndPostHighlightTags", args: args{ UID: "indexUID", client: sv, query: "prince", request: &SearchRequest{ Limit: 1, AttributesToHighlight: []string{"*"}, HighlightPreTag: "(⊃。•́‿•̀。)⊃ ", HighlightPostTag: " ⊂(´• ω •`⊂)", }, }, want: &SearchResponse{ Hits: []interface{}{ map[string]interface{}{ "book_id": float64(456), "title": "Le Petit Prince", "_formatted": map[string]interface{}{ "book_id": "456", "tag": "Tale", "title": "Le Petit (⊃。•́‿•̀。)⊃ Prince ⊂(´• ω •`⊂)", "year": "1943", }, }, }, EstimatedTotalHits: 2, Offset: 0, Limit: 1, }, wantErr: false, }, { name: "TestIndexSearchWithShowMatchesPosition", args: args{ UID: "indexUID", client: sv, query: "and", request: &SearchRequest{ ShowMatchesPosition: true, }, }, want: &SearchResponse{ Hits: []interface{}{ map[string]interface{}{ "book_id": float64(123), "title": "Pride and Prejudice", }, map[string]interface{}{ "book_id": float64(730), "title": "War and Peace", }, map[string]interface{}{ "book_id": float64(1032), "title": "Crime and Punishment", }, map[string]interface{}{ "book_id": float64(4), "title": "Harry Potter and the Half-Blood Prince", }, }, EstimatedTotalHits: 4, Offset: 0, Limit: 20, }, wantErr: false, }, { name: "TestIndexSearchWithQuoteInQUery", args: args{ UID: "indexUID", client: sv, query: "and \"harry\"", request: &SearchRequest{}, }, want: &SearchResponse{ Hits: []interface{}{ map[string]interface{}{ "book_id": float64(4), "title": "Harry Potter and the Half-Blood Prince", }, }, EstimatedTotalHits: 1, Offset: 0, Limit: 20, }, wantErr: false, }, { name: "TestIndexSearchWithCustomMatchingStrategyAll", args: args{ UID: "indexUID", client: sv, query: "le prince", request: &SearchRequest{ Limit: 10, AttributesToRetrieve: []string{"book_id", "title"}, MatchingStrategy: "all", }, }, want: &SearchResponse{ Hits: []interface{}{ map[string]interface{}{ "book_id": float64(456), "title": "Le Petit Prince", }, }, EstimatedTotalHits: 1, Offset: 0, Limit: 10, }, wantErr: false, }, { name: "TestIndexSearchWithCustomMatchingStrategyLast", args: args{ UID: "indexUID", client: sv, query: "prince", request: &SearchRequest{ Limit: 10, AttributesToRetrieve: []string{"book_id", "title"}, MatchingStrategy: "last", }, }, want: &SearchResponse{ Hits: []interface{}{ map[string]interface{}{ "book_id": float64(456), "title": "Le Petit Prince", }, map[string]interface{}{ "book_id": float64(4), "title": "Harry Potter and the Half-Blood Prince", }, }, EstimatedTotalHits: 2, Offset: 0, Limit: 10, }, wantErr: false, }, { name: "TestIndexSearchWithRankingScoreThreshold", args: args{ UID: "indexUID", client: sv, query: "pri", request: &SearchRequest{ Limit: 10, AttributesToRetrieve: []string{"book_id", "title"}, RankingScoreThreshold: 0.2, }, }, want: &SearchResponse{ Hits: []interface{}{ map[string]interface{}{ "book_id": float64(123), "title": "Pride and Prejudice", }, map[string]interface{}{ "book_id": float64(456), "title": "Le Petit Prince", }, map[string]interface{}{ "book_id": float64(4), "title": "Harry Potter and the Half-Blood Prince", }, }, EstimatedTotalHits: 3, Offset: 0, Limit: 10, }, wantErr: false, }, { name: "TestIndexSearchWithMatchStrategyFrequency", args: args{ UID: "indexUID", client: sv, query: "white shirt", request: &SearchRequest{ MatchingStrategy: Frequency, }, }, want: &SearchResponse{ Hits: []interface{}{ map[string]interface{}{ "book_id": float64(1039), "title": "The Girl in the white shirt", }, }, EstimatedTotalHits: 1, Offset: 0, Limit: 20, }, wantErr: false, }, { name: "TestIndexSearchWithInvalidIndex", args: args{ UID: "invalidIndex", client: sv, query: "pri", request: &SearchRequest{ Limit: 10, AttributesToRetrieve: []string{"book_id", "title"}, RankingScoreThreshold: 0.2, }, }, want: nil, wantErr: true, }, { name: "TestIndexSearchWithLocate", args: args{ UID: "indexUID", client: sv, query: "王子", request: &SearchRequest{ Locates: []string{"jpn"}, }, }, want: &SearchResponse{ Hits: []interface{}{ map[string]interface{}{ "book_id": float64(1050), "title": "星の王子さま", }, }, EstimatedTotalHits: 1, Offset: 0, Limit: 20, }, wantErr: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { setUpIndexForFaceting(tt.args.client) c := tt.args.client i := c.Index(tt.args.UID) t.Cleanup(cleanup(c)) got, err := i.Search(tt.args.query, tt.args.request) if tt.wantErr { require.Error(t, err) require.Nil(t, tt.want) return } require.NoError(t, err) require.Equal(t, len(tt.want.Hits), len(got.Hits)) for len := range got.Hits { require.Equal(t, tt.want.Hits[len].(map[string]interface{})["title"], got.Hits[len].(map[string]interface{})["title"]) require.Equal(t, tt.want.Hits[len].(map[string]interface{})["book_id"], got.Hits[len].(map[string]interface{})["book_id"]) } if tt.want.Hits[0].(map[string]interface{})["_formatted"] != nil { require.Equal(t, tt.want.Hits[0].(map[string]interface{})["_formatted"], got.Hits[0].(map[string]interface{})["_formatted"]) } require.Equal(t, tt.want.EstimatedTotalHits, got.EstimatedTotalHits) require.Equal(t, tt.want.Offset, got.Offset) require.Equal(t, tt.want.Limit, got.Limit) require.Equal(t, tt.want.FacetDistribution, got.FacetDistribution) }) } } func TestIndex_SearchFacets(t *testing.T) { sv := setup(t, "") customSv := setup(t, "", WithCustomClientWithTLS(&tls.Config{ InsecureSkipVerify: true, })) type args struct { UID string PrimaryKey string client ServiceManager query string request *SearchRequest filterableAttributes []string } tests := []struct { name string args args want *SearchResponse wantErr bool }{ { name: "TestIndexSearchWithEmptyRequest", args: args{ UID: "indexUID", client: sv, query: "prince", request: nil, }, want: nil, wantErr: true, }, { name: "TestIndexSearchWithFacets", args: args{ UID: "indexUID", client: sv, query: "prince", request: &SearchRequest{ Facets: []string{"*"}, }, filterableAttributes: []string{"tag"}, }, want: &SearchResponse{ Hits: []interface{}{ map[string]interface{}{ "book_id": float64(456), "title": "Le Petit Prince", }, map[string]interface{}{ "book_id": float64(4), "title": "Harry Potter and the Half-Blood Prince", }, }, EstimatedTotalHits: 2, Offset: 0, Limit: 20, FacetDistribution: map[string]interface{}{ "tag": map[string]interface{}{ "Epic fantasy": float64(1), "Tale": float64(1), }, }, }, wantErr: false, }, { name: "TestIndexSearchWithFacetsWithCustomClient", args: args{ UID: "indexUID", client: customSv, query: "prince", request: &SearchRequest{ Facets: []string{"*"}, }, filterableAttributes: []string{"tag"}, }, want: &SearchResponse{ Hits: []interface{}{ map[string]interface{}{ "book_id": float64(456), "title": "Le Petit Prince", }, map[string]interface{}{ "book_id": float64(4), "title": "Harry Potter and the Half-Blood Prince", }, }, EstimatedTotalHits: 2, Offset: 0, Limit: 20, FacetDistribution: map[string]interface{}{ "tag": map[string]interface{}{ "Epic fantasy": float64(1), "Tale": float64(1), }, }, }, wantErr: false, }, { name: "TestIndexSearchWithFacetsAndFacetsStats", args: args{ UID: "indexUID", client: sv, query: "prince", request: &SearchRequest{ Facets: []string{"book_id"}, }, filterableAttributes: []string{"book_id"}, }, want: &SearchResponse{ Hits: []interface{}{ map[string]interface{}{ "book_id": float64(456), "title": "Le Petit Prince", }, map[string]interface{}{ "book_id": float64(4), "title": "Harry Potter and the Half-Blood Prince", }, }, EstimatedTotalHits: 2, Offset: 0, Limit: 20, FacetDistribution: map[string]interface{}{ "book_id": map[string]interface{}{ "4": float64(1), "456": float64(1), }, }, FacetStats: map[string]interface{}{ "book_id": map[string]interface{}{ "max": float64(456), "min": float64(4), }, }, }, wantErr: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { setUpIndexForFaceting(tt.args.client) c := tt.args.client i := c.Index(tt.args.UID) t.Cleanup(cleanup(c)) updateFilter, err := i.UpdateFilterableAttributes(&tt.args.filterableAttributes) require.NoError(t, err) testWaitForTask(t, i, updateFilter) got, err := i.Search(tt.args.query, tt.args.request) if tt.wantErr { require.Error(t, err) require.Nil(t, tt.want) return } require.NoError(t, err) require.Equal(t, len(tt.want.Hits), len(got.Hits)) for len := range got.Hits { require.Equal(t, tt.want.Hits[len].(map[string]interface{})["title"], got.Hits[len].(map[string]interface{})["title"]) require.Equal(t, tt.want.Hits[len].(map[string]interface{})["book_id"], got.Hits[len].(map[string]interface{})["book_id"]) } require.Equal(t, tt.want.EstimatedTotalHits, got.EstimatedTotalHits) require.Equal(t, tt.want.Offset, got.Offset) require.Equal(t, tt.want.Limit, got.Limit) require.Equal(t, tt.want.FacetDistribution, got.FacetDistribution) if tt.want.FacetStats != nil { require.Equal(t, tt.want.FacetStats, got.FacetStats) } }) } } func TestIndex_SearchWithFilters(t *testing.T) { sv := setup(t, "") type args struct { UID string PrimaryKey string client ServiceManager query string filterableAttributes []string request *SearchRequest } tests := []struct { name string args args want *SearchResponse wantErr bool }{ { name: "TestIndexBasicSearchWithFilter", args: args{ UID: "indexUID", client: sv, query: "and", filterableAttributes: []string{ "tag", }, request: &SearchRequest{ Filter: "tag = romance", }, }, want: &SearchResponse{ Hits: []interface{}{ map[string]interface{}{ "book_id": float64(123), "title": "Pride and Prejudice", }, }, EstimatedTotalHits: 1, Offset: 0, Limit: 20, }, wantErr: false, }, { name: "TestIndexSearchWithFilterInInt", args: args{ UID: "indexUID", client: sv, query: "and", filterableAttributes: []string{ "year", }, request: &SearchRequest{ Filter: "year = 2005", }, }, want: &SearchResponse{ Hits: []interface{}{ map[string]interface{}{ "book_id": float64(4), "title": "Harry Potter and the Half-Blood Prince", }, }, EstimatedTotalHits: 1, Offset: 0, Limit: 20, }, wantErr: false, }, { name: "TestIndexSearchWithFilterArray", args: args{ UID: "indexUID", client: sv, query: "and", filterableAttributes: []string{ "year", }, request: &SearchRequest{ Filter: []string{ "year = 2005", }, }, }, want: &SearchResponse{ Hits: []interface{}{ map[string]interface{}{ "book_id": float64(4), "title": "Harry Potter and the Half-Blood Prince", }, }, EstimatedTotalHits: 1, Offset: 0, Limit: 20, }, wantErr: false, }, { name: "TestIndexSearchWithFilterMultipleArray", args: args{ UID: "indexUID", client: sv, query: "and", filterableAttributes: []string{ "year", "tag", }, request: &SearchRequest{ Filter: [][]string{ {"year < 1850"}, {"tag = romance"}, }, }, }, want: &SearchResponse{ Hits: []interface{}{ map[string]interface{}{ "book_id": float64(123), "title": "Pride and Prejudice", }, }, EstimatedTotalHits: 1, Offset: 0, Limit: 20, }, wantErr: false, }, { name: "TestIndexSearchWithMultipleFilter", args: args{ UID: "indexUID", client: sv, query: "prince", filterableAttributes: []string{ "tag", "year", }, request: &SearchRequest{ Filter: "year > 1930", }, }, want: &SearchResponse{ Hits: []interface{}{ map[string]interface{}{ "book_id": float64(456), "title": "Le Petit Prince", }, map[string]interface{}{ "book_id": float64(4), "title": "Harry Potter and the Half-Blood Prince", }, }, EstimatedTotalHits: 2, Offset: 0, Limit: 20, }, wantErr: false, }, { name: "TestIndexSearchWithOneFilterAnd", args: args{ UID: "indexUID", client: sv, query: "", filterableAttributes: []string{ "year", }, request: &SearchRequest{ Filter: "year < 1930 AND year > 1910", }, }, want: &SearchResponse{ Hits: []interface{}{ map[string]interface{}{ "book_id": float64(742), "title": "The Great Gatsby", }, map[string]interface{}{ "book_id": float64(17), "title": "In Search of Lost Time", }, map[string]interface{}{ "book_id": float64(204), "title": "Ulysses", }, }, EstimatedTotalHits: 3, Offset: 0, Limit: 20, }, wantErr: false, }, { name: "TestIndexSearchWithMultipleFilterAnd", args: args{ UID: "indexUID", client: sv, query: "", filterableAttributes: []string{ "tag", "year", }, request: &SearchRequest{ Filter: "year < 1930 AND tag = Tale", }, }, want: &SearchResponse{ Hits: []interface{}{ map[string]interface{}{ "book_id": float64(1), "title": "Alice In Wonderland", }, }, EstimatedTotalHits: 1, Offset: 0, Limit: 20, }, wantErr: false, }, { name: "TestIndexSearchWithFilterOr", args: args{ UID: "indexUID", client: sv, query: "", filterableAttributes: []string{ "year", "tag", }, request: &SearchRequest{ Filter: "year > 2000 OR tag = Tale", }, }, want: &SearchResponse{ Hits: []interface{}{ map[string]interface{}{ "book_id": float64(456), "title": "Le Petit Prince", }, map[string]interface{}{ "book_id": float64(1), "title": "Alice In Wonderland", }, map[string]interface{}{ "book_id": float64(4), "title": "Harry Potter and the Half-Blood Prince", }, }, EstimatedTotalHits: 3, Offset: 0, Limit: 20, }, wantErr: false, }, { name: "TestIndexSearchWithAttributeToHighlight", args: args{ UID: "indexUID", client: sv, query: "prince", filterableAttributes: []string{ "book_id", }, request: &SearchRequest{ AttributesToHighlight: []string{"*"}, Filter: "book_id > 10", }, }, want: &SearchResponse{ Hits: []interface{}{ map[string]interface{}{ "book_id": float64(456), "title": "Le Petit Prince", }, }, EstimatedTotalHits: 1, Offset: 0, Limit: 20, }, wantErr: false, }, { name: "TestIndexSearchWithFilterContainingSpaces", args: args{ UID: "indexUID", client: sv, query: "and", filterableAttributes: []string{ "tag", }, request: &SearchRequest{ Filter: "tag = 'Crime fiction'", }, }, want: &SearchResponse{ Hits: []interface{}{ map[string]interface{}{ "book_id": float64(1032), "title": "Crime and Punishment", }, }, EstimatedTotalHits: 1, Offset: 0, Limit: 20, }, wantErr: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { setUpIndexForFaceting(tt.args.client) c := tt.args.client i := c.Index(tt.args.UID) t.Cleanup(cleanup(c)) updateFilter, err := i.UpdateFilterableAttributes(&tt.args.filterableAttributes) require.NoError(t, err) testWaitForTask(t, i, updateFilter) got, err := i.Search(tt.args.query, tt.args.request) if tt.wantErr { require.Error(t, err) require.Nil(t, tt.want) return } require.NoError(t, err) require.Equal(t, len(tt.want.Hits), len(got.Hits)) for len := range got.Hits { require.Equal(t, tt.want.Hits[len].(map[string]interface{})["title"], got.Hits[len].(map[string]interface{})["title"]) require.Equal(t, tt.want.Hits[len].(map[string]interface{})["book_id"], got.Hits[len].(map[string]interface{})["book_id"]) } require.Equal(t, tt.args.query, got.Query) require.Equal(t, tt.want.EstimatedTotalHits, got.EstimatedTotalHits) require.Equal(t, tt.want.Offset, got.Offset) require.Equal(t, tt.want.Limit, got.Limit) require.Equal(t, tt.want.FacetDistribution, got.FacetDistribution) }) } } func TestIndex_SearchWithSort(t *testing.T) { sv := setup(t, "") type args struct { UID string PrimaryKey string client ServiceManager query string sortableAttributes []string request *SearchRequest } tests := []struct { name string args args want *SearchResponse wantErr bool }{ { name: "TestIndexBasicSearchWithSortIntParameter", args: args{ UID: "indexUID", client: sv, query: "and", sortableAttributes: []string{ "year", }, request: &SearchRequest{ Sort: []string{ "year:asc", }, }, }, want: &SearchResponse{ Hits: []interface{}{ map[string]interface{}{ "book_id": float64(123), "title": "Pride and Prejudice", }, map[string]interface{}{ "book_id": float64(730), "title": "War and Peace", }, map[string]interface{}{ "book_id": float64(1032), "title": "Crime and Punishment", }, map[string]interface{}{ "book_id": float64(4), "title": "Harry Potter and the Half-Blood Prince", }, }, EstimatedTotalHits: 4, Offset: 0, Limit: 20, }, wantErr: false, }, { name: "TestIndexBasicSearchWithSortStringParameter", args: args{ UID: "indexUID", client: sv, query: "and", sortableAttributes: []string{ "title", }, request: &SearchRequest{ Sort: []string{ "title:asc", }, }, }, want: &SearchResponse{ Hits: []interface{}{ map[string]interface{}{ "book_id": float64(1032), "title": "Crime and Punishment", }, map[string]interface{}{ "book_id": float64(123), "title": "Pride and Prejudice", }, map[string]interface{}{ "book_id": float64(730), "title": "War and Peace", }, map[string]interface{}{ "book_id": float64(4), "title": "Harry Potter and the Half-Blood Prince", }, }, EstimatedTotalHits: 4, Offset: 0, Limit: 20, }, wantErr: false, }, { name: "TestIndexBasicSearchWithSortMultipleParameter", args: args{ UID: "indexUID", client: sv, query: "and", sortableAttributes: []string{ "title", "year", }, request: &SearchRequest{ Sort: []string{ "title:asc", "year:asc", }, }, }, want: &SearchResponse{ Hits: []interface{}{ map[string]interface{}{ "book_id": float64(1032), "title": "Crime and Punishment", }, map[string]interface{}{ "book_id": float64(123), "title": "Pride and Prejudice", }, map[string]interface{}{ "book_id": float64(730), "title": "War and Peace", }, map[string]interface{}{ "book_id": float64(4), "title": "Harry Potter and the Half-Blood Prince", }, }, EstimatedTotalHits: 4, Offset: 0, Limit: 20, }, wantErr: false, }, { name: "TestIndexBasicSearchWithSortMultipleParameterReverse", args: args{ UID: "indexUID", client: sv, query: "and", sortableAttributes: []string{ "title", "year", }, request: &SearchRequest{ Sort: []string{ "year:asc", "title:asc", }, }, }, want: &SearchResponse{ Hits: []interface{}{ map[string]interface{}{ "book_id": float64(123), "title": "Pride and Prejudice", }, map[string]interface{}{ "book_id": float64(730), "title": "War and Peace", }, map[string]interface{}{ "book_id": float64(1032), "title": "Crime and Punishment", }, map[string]interface{}{ "book_id": float64(4), "title": "Harry Potter and the Half-Blood Prince", }, }, EstimatedTotalHits: 4, Offset: 0, Limit: 20, }, wantErr: false, }, { name: "TestIndexBasicSearchWithSortMultipleParameterPlaceHolder", args: args{ UID: "indexUID", client: sv, query: "", sortableAttributes: []string{ "title", "year", }, request: &SearchRequest{ Sort: []string{ "year:asc", "title:asc", }, Limit: 4, }, }, want: &SearchResponse{ Hits: []interface{}{ map[string]interface{}{ "book_id": float64(56), "title": "The Divine Comedy", }, map[string]interface{}{ "book_id": float64(32), "title": "The Odyssey", }, map[string]interface{}{ "book_id": float64(69), "title": "Hamlet", }, map[string]interface{}{ "book_id": float64(7), "title": "Don Quixote", }, }, EstimatedTotalHits: 22, Offset: 0, Limit: 4, }, wantErr: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { setUpIndexForFaceting(tt.args.client) c := tt.args.client i := c.Index(tt.args.UID) t.Cleanup(cleanup(c)) updateFilter, err := i.UpdateSortableAttributes(&tt.args.sortableAttributes) require.NoError(t, err) testWaitForTask(t, i, updateFilter) got, err := i.Search(tt.args.query, tt.args.request) if tt.wantErr { require.Error(t, err) require.Nil(t, tt.want) return } require.NoError(t, err) require.Equal(t, len(tt.want.Hits), len(got.Hits)) for len := range got.Hits { require.Equal(t, tt.want.Hits[len].(map[string]interface{})["title"], got.Hits[len].(map[string]interface{})["title"]) require.Equal(t, tt.want.Hits[len].(map[string]interface{})["book_id"], got.Hits[len].(map[string]interface{})["book_id"]) } require.Equal(t, tt.want.EstimatedTotalHits, got.EstimatedTotalHits) require.Equal(t, tt.want.Offset, got.Offset) require.Equal(t, tt.want.Limit, got.Limit) require.Equal(t, tt.want.FacetDistribution, got.FacetDistribution) }) } } func TestIndex_SearchOnNestedFileds(t *testing.T) { sv := setup(t, "") customSv := setup(t, "", WithCustomClientWithTLS(&tls.Config{ InsecureSkipVerify: true, })) type args struct { UID string PrimaryKey string client ServiceManager query string request *SearchRequest searchableAttribute []string sortableAttribute []string } tests := []struct { name string args args want *SearchResponse wantErr bool }{ { name: "TestIndexBasicSearchOnNestedFields", args: args{ UID: "TestIndexBasicSearchOnNestedFields", client: sv, query: "An awesome", request: &SearchRequest{}, }, want: &SearchResponse{ Hits: []interface{}{ map[string]interface{}{ "id": float64(5), "title": "The Hobbit", "info": map[string]interface{}{ "comment": "An awesome book", "reviewNb": float64(900), }, }, }, EstimatedTotalHits: 1, Offset: 0, Limit: 20, }, wantErr: false, }, { name: "TestIndexBasicSearchOnNestedFieldsWithCustomClient", args: args{ UID: "TestIndexBasicSearchOnNestedFieldsWithCustomClient", client: customSv, query: "An awesome", request: &SearchRequest{}, }, want: &SearchResponse{ Hits: []interface{}{ map[string]interface{}{ "id": float64(5), "title": "The Hobbit", "info": map[string]interface{}{ "comment": "An awesome book", "reviewNb": float64(900), }, }, }, EstimatedTotalHits: 1, Offset: 0, Limit: 20, }, wantErr: false, }, { name: "TestIndexSearchOnMultipleNestedFields", args: args{ UID: "TestIndexSearchOnMultipleNestedFields", client: sv, query: "french", request: &SearchRequest{}, }, want: &SearchResponse{ Hits: []interface{}{ map[string]interface{}{ "id": float64(2), "title": "Le Petit Prince", "info": map[string]interface{}{ "comment": "A french book", "reviewNb": float64(600), }, }, map[string]interface{}{ "id": float64(3), "title": "Le Rouge et le Noir", "info": map[string]interface{}{ "comment": "Another french book", "reviewNb": float64(700), }, }, }, EstimatedTotalHits: 2, Offset: 0, Limit: 20, }, }, { name: "TestIndexSearchOnNestedFieldsWithSearchableAttribute", args: args{ UID: "TestIndexSearchOnNestedFieldsWithSearchableAttribute", client: sv, query: "An awesome", request: &SearchRequest{}, searchableAttribute: []string{ "title", "info.comment", }, }, want: &SearchResponse{ Hits: []interface{}{ map[string]interface{}{ "id": float64(5), "title": "The Hobbit", "info": map[string]interface{}{ "comment": "An awesome book", "reviewNb": float64(900), }, }, }, EstimatedTotalHits: 1, Offset: 0, Limit: 20, }, wantErr: false, }, { name: "TestIndexSearchOnNestedFieldsWithSortableAttribute", args: args{ UID: "TestIndexSearchOnNestedFieldsWithSortableAttribute", client: sv, query: "An awesome", request: &SearchRequest{ Sort: []string{ "info.reviewNb:desc", }, }, searchableAttribute: []string{ "title", "info.comment", }, sortableAttribute: []string{ "info.reviewNb", }, }, want: &SearchResponse{ Hits: []interface{}{ map[string]interface{}{ "id": float64(5), "title": "The Hobbit", "info": map[string]interface{}{ "comment": "An awesome book", "reviewNb": float64(900), }, }, }, EstimatedTotalHits: 1, Offset: 0, Limit: 20, }, wantErr: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { setUpIndexWithNestedFields(tt.args.client, tt.args.UID) c := tt.args.client i := c.Index(tt.args.UID) t.Cleanup(cleanup(c)) if tt.args.searchableAttribute != nil { gotTask, err := i.UpdateSearchableAttributes(&tt.args.searchableAttribute) require.NoError(t, err) testWaitForTask(t, i, gotTask) } if tt.args.sortableAttribute != nil { gotTask, err := i.UpdateSortableAttributes(&tt.args.sortableAttribute) require.NoError(t, err) testWaitForTask(t, i, gotTask) } got, err := i.Search(tt.args.query, tt.args.request) if tt.wantErr { require.Error(t, err) require.Nil(t, tt.want) return } require.NoError(t, err) require.Equal(t, len(tt.want.Hits), len(got.Hits)) for len := range got.Hits { require.Equal(t, tt.want.Hits[len], got.Hits[len]) } require.Equal(t, tt.want.EstimatedTotalHits, got.EstimatedTotalHits) require.Equal(t, tt.want.Offset, got.Offset) require.Equal(t, tt.want.Limit, got.Limit) require.Equal(t, tt.want.FacetDistribution, got.FacetDistribution) }) } } func TestIndex_SearchWithPagination(t *testing.T) { sv := setup(t, "") type args struct { UID string PrimaryKey string client ServiceManager query string request *SearchRequest } tests := []struct { name string args args want *SearchResponse wantErr bool }{ { name: "TestIndexBasicSearchWithHitsPerPage", args: args{ UID: "indexUID", client: sv, query: "and", request: &SearchRequest{ HitsPerPage: 10, }, }, want: &SearchResponse{ Hits: []interface{}{ map[string]interface{}{ "book_id": float64(123), "title": "Pride and Prejudice", }, map[string]interface{}{ "book_id": float64(730), "title": "War and Peace", }, map[string]interface{}{ "book_id": float64(1032), "title": "Crime and Punishment", }, map[string]interface{}{ "book_id": float64(4), "title": "Harry Potter and the Half-Blood Prince", }, }, HitsPerPage: 10, Page: 1, TotalHits: 4, TotalPages: 1, }, wantErr: false, }, { name: "TestIndexBasicSearchWithPage", args: args{ UID: "indexUID", client: sv, query: "and", request: &SearchRequest{ Page: 1, }, }, want: &SearchResponse{ Hits: []interface{}{ map[string]interface{}{ "book_id": float64(123), "title": "Pride and Prejudice", }, map[string]interface{}{ "book_id": float64(730), "title": "War and Peace", }, map[string]interface{}{ "book_id": float64(1032), "title": "Crime and Punishment", }, map[string]interface{}{ "book_id": float64(4), "title": "Harry Potter and the Half-Blood Prince", }, }, HitsPerPage: 20, Page: 1, TotalHits: 4, TotalPages: 1, }, wantErr: false, }, { name: "TestIndexBasicSearchWithPageAndHitsPerPage", args: args{ UID: "indexUID", client: sv, query: "and", request: &SearchRequest{ HitsPerPage: 10, Page: 1, }, }, want: &SearchResponse{ Hits: []interface{}{ map[string]interface{}{ "book_id": float64(123), "title": "Pride and Prejudice", }, map[string]interface{}{ "book_id": float64(730), "title": "War and Peace", }, map[string]interface{}{ "book_id": float64(1032), "title": "Crime and Punishment", }, map[string]interface{}{ "book_id": float64(4), "title": "Harry Potter and the Half-Blood Prince", }, }, HitsPerPage: 10, Page: 1, TotalHits: 4, TotalPages: 1, }, wantErr: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { setUpIndexForFaceting(tt.args.client) c := tt.args.client i := c.Index(tt.args.UID) t.Cleanup(cleanup(c)) got, err := i.Search(tt.args.query, tt.args.request) require.NoError(t, err) require.Equal(t, len(tt.want.Hits), len(got.Hits)) for len := range got.Hits { require.Equal(t, tt.want.Hits[len].(map[string]interface{})["title"], got.Hits[len].(map[string]interface{})["title"]) require.Equal(t, tt.want.Hits[len].(map[string]interface{})["book_id"], got.Hits[len].(map[string]interface{})["book_id"]) } require.Equal(t, tt.args.query, got.Query) require.Equal(t, tt.want.HitsPerPage, got.HitsPerPage) require.Equal(t, tt.want.Page, got.Page) require.Equal(t, tt.want.TotalHits, got.TotalHits) require.Equal(t, tt.want.TotalPages, got.TotalPages) }) } } func TestIndex_SearchWithShowRankingScore(t *testing.T) { sv := setup(t, "") type args struct { UID string PrimaryKey string client ServiceManager query string request SearchRequest } testArg := args{ UID: "indexUID", client: sv, query: "and", request: SearchRequest{ ShowRankingScore: true, }, } setUpIndexForFaceting(testArg.client) c := testArg.client i := c.Index(testArg.UID) t.Cleanup(cleanup(c)) got, err := i.Search(testArg.query, &testArg.request) require.NoError(t, err) require.NotNil(t, got.Hits[0].(map[string]interface{})["_rankingScore"]) } func TestIndex_SearchWithShowRankingScoreDetails(t *testing.T) { sv := setup(t, "") type args struct { UID string PrimaryKey string client ServiceManager query string request SearchRequest } testArg := args{ UID: "indexUID", client: sv, query: "and", request: SearchRequest{ ShowRankingScoreDetails: true, }, } setUpIndexForFaceting(testArg.client) c := testArg.client i := c.Index(testArg.UID) t.Cleanup(cleanup(c)) got, err := i.Search(testArg.query, &testArg.request) require.NoError(t, err) require.NotNil(t, got.Hits[0].(map[string]interface{})["_rankingScoreDetails"]) } func TestIndex_SearchWithVectorStore(t *testing.T) { sv := setup(t, "") tests := []struct { name string UID string PrimaryKey string client ServiceManager query string request SearchRequest }{ { name: "basic hybrid test", UID: "indexUID", client: sv, query: "Pride and Prejudice", request: SearchRequest{ Hybrid: &SearchRequestHybrid{ SemanticRatio: 0.5, Embedder: "default", }, RetrieveVectors: true, }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { i, err := setUpIndexWithVector(tt.client.(*meilisearch), tt.UID) if err != nil { t.Fatal(err) } c := tt.client t.Cleanup(cleanup(c)) got, err := i.Search(tt.query, &tt.request) require.NoError(t, err) for _, hit := range got.Hits { hit := hit.(map[string]interface{}) require.NotNil(t, hit["_vectors"]) } }) } } func TestIndex_SearchWithDistinct(t *testing.T) { sv := setup(t, "") tests := []struct { UID string PrimaryKey string client ServiceManager query string request SearchRequest }{ { UID: "indexUID", client: sv, query: "white shirt", request: SearchRequest{ Distinct: "sku", }, }, } for _, tt := range tests { t.Run(tt.UID, func(t *testing.T) { setUpDistinctIndex(tt.client, tt.UID) c := tt.client t.Cleanup(cleanup(c)) i := c.Index(tt.UID) got, err := i.Search(tt.query, &tt.request) require.NoError(t, err) require.NotNil(t, got.Hits) }) } } func TestIndex_SearchSimilarDocuments(t *testing.T) { sv := setup(t, "") tests := []struct { UID string PrimaryKey string client ServiceManager request *SimilarDocumentQuery resp *SimilarDocumentResult wantErr bool }{ { UID: "indexUID", client: sv, request: &SimilarDocumentQuery{ Id: "123", Embedder: "default", }, resp: new(SimilarDocumentResult), wantErr: false, }, { UID: "indexUID", client: sv, request: &SimilarDocumentQuery{ Embedder: "default", }, resp: new(SimilarDocumentResult), wantErr: true, }, } for _, tt := range tests { t.Run(tt.UID, func(t *testing.T) { i, err := setUpIndexWithVector(tt.client.(*meilisearch), tt.UID) require.NoError(t, err) c := tt.client t.Cleanup(cleanup(c)) err = i.SearchSimilarDocuments(tt.request, tt.resp) if tt.wantErr { require.Error(t, err) return } require.NoError(t, err) require.NotNil(t, tt.resp) }) } } func TestIndex_FacetSearch(t *testing.T) { sv := setup(t, "") type args struct { UID string PrimaryKey string client ServiceManager request *FacetSearchRequest filterableAttributes []string } tests := []struct { name string args args want *FacetSearchResponse wantErr bool }{ { name: "TestIndexBasicFacetSearch", args: args{ UID: "indexUID", client: sv, request: &FacetSearchRequest{ FacetName: "tag", FacetQuery: "Novel", }, filterableAttributes: []string{"tag"}, }, want: &FacetSearchResponse{ FacetHits: []interface{}{ map[string]interface{}{ "value": "Novel", "count": float64(5), }, }, FacetQuery: "Novel", }, wantErr: false, }, { name: "TestIndexFacetSearchWithFilter", args: args{ UID: "indexUID", client: sv, request: &FacetSearchRequest{ FacetName: "tag", FacetQuery: "Novel", Filter: "tag = 'Novel'", }, filterableAttributes: []string{"tag"}, }, want: &FacetSearchResponse{ FacetHits: []interface{}{ map[string]interface{}{ "value": "Novel", "count": float64(5), }, }, FacetQuery: "Novel", }, wantErr: false, }, { name: "TestIndexFacetSearchWithMatchingStrategy", args: args{ UID: "indexUID", client: sv, request: &FacetSearchRequest{ FacetName: "tag", FacetQuery: "Novel", MatchingStrategy: "frequency", }, filterableAttributes: []string{"tag"}, }, want: &FacetSearchResponse{ FacetHits: []interface{}{ map[string]interface{}{ "value": "Novel", "count": float64(5), }, }, FacetQuery: "Novel", }, wantErr: false, }, { name: "TestIndexFacetSearchWithAttributesToSearchOn", args: args{ UID: "indexUID", client: sv, request: &FacetSearchRequest{ FacetName: "tag", FacetQuery: "Novel", AttributesToSearchOn: []string{"tag"}, }, filterableAttributes: []string{"tag"}, }, want: &FacetSearchResponse{ FacetHits: []interface{}{ map[string]interface{}{ "value": "Novel", "count": float64(5), }, }, FacetQuery: "Novel", }, wantErr: false, }, { name: "TestIndexFacetSearchWithNoFacetSearchRequest", args: args{ UID: "indexUID", client: sv, request: nil, }, want: nil, wantErr: true, }, { name: "TestIndexFacetSearchWithNoFacetName", args: args{ UID: "indexUID", client: sv, request: &FacetSearchRequest{ FacetQuery: "Novel", }, }, want: nil, wantErr: true, }, { name: "TestIndexFacetSearchWithNoFacetQuery", args: args{ UID: "indexUID", client: sv, request: &FacetSearchRequest{ FacetName: "tag", }, }, want: nil, wantErr: true, }, { name: "TestIndexFacetSearchWithNoFilterableAttributes", args: args{ UID: "indexUID", client: sv, request: &FacetSearchRequest{ FacetName: "tag", FacetQuery: "Novel", }, }, want: nil, wantErr: true, }, { name: "TestIndexFacetSearchWithQ", args: args{ UID: "indexUID", client: sv, request: &FacetSearchRequest{ Q: "query", FacetName: "tag", }, filterableAttributes: []string{"tag"}, }, want: &FacetSearchResponse{ FacetHits: []interface{}{}, FacetQuery: "", }, wantErr: false, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { setUpIndexForFaceting(tt.args.client) c := tt.args.client i := c.Index(tt.args.UID) t.Cleanup(cleanup(c)) if len(tt.args.filterableAttributes) > 0 { updateFilter, err := i.UpdateFilterableAttributes(&tt.args.filterableAttributes) require.NoError(t, err) testWaitForTask(t, i, updateFilter) } gotRaw, err := i.FacetSearch(tt.args.request) if tt.wantErr { require.Error(t, err) require.Nil(t, gotRaw) return } require.NoError(t, err) // Unmarshall the raw response from FacetSearch into a FacetSearchResponse var got FacetSearchResponse err = json.Unmarshal(*gotRaw, &got) require.NoError(t, err, "error unmarshalling raw got FacetSearchResponse") require.Equal(t, len(tt.want.FacetHits), len(got.FacetHits)) for len := range got.FacetHits { require.Equal(t, tt.want.FacetHits[len].(map[string]interface{})["value"], got.FacetHits[len].(map[string]interface{})["value"]) require.Equal(t, tt.want.FacetHits[len].(map[string]interface{})["count"], got.FacetHits[len].(map[string]interface{})["count"]) } require.Equal(t, tt.want.FacetQuery, got.FacetQuery) }) } }