1
0
Fork 0
golang-github-meilisearch-m.../index_search_test.go
Daniel Baumann 5d4914ed7f
Adding upstream version 0.31.1.
Signed-off-by: Daniel Baumann <daniel@debian.org>
2025-05-18 21:42:39 +02:00

2267 lines
54 KiB
Go

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 <em>Prince</em>", "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)
})
}
}