search: add OR filter for kinds (#95915)

search: add OR filter for kinds
This commit is contained in:
Scott Lepper 2024-11-05 17:02:13 -05:00 committed by GitHub
parent cd9fcd08aa
commit 1feaf6df99
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 69 additions and 17 deletions

View File

@ -5,6 +5,7 @@ import (
"net/http"
"net/url"
"strconv"
"strings"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
@ -106,7 +107,7 @@ func (b *SearchAPIBuilder) GetAPIRoutes() *builder.APIRoutes {
searchRequest := &resource.SearchRequest{
Tenant: tenant,
Kind: queryParams.Get("kind"),
Kind: strings.Split(queryParams.Get("kind"), ","),
QueryType: queryParams.Get("queryType"),
Query: queryParams.Get("query"),
Limit: int64(limit),

View File

@ -165,7 +165,7 @@ func (s *StandardSearchService) doSearchQuery(ctx context.Context, qry Query, _
// will use stack id for cloud and org id for on-prem
tenantId := request.GetNamespaceMapper(s.cfg)(orgID)
req := &resource.SearchRequest{Tenant: tenantId, Query: qry.Query, Limit: int64(qry.Limit), Offset: int64(qry.From)}
req := &resource.SearchRequest{Tenant: tenantId, Query: qry.Query, Limit: int64(qry.Limit), Offset: int64(qry.From), Kind: qry.Kind}
res, err := s.resourceClient.Search(ctx, req)
if err != nil {
s.logger.Error("Failed to search resources", "error", err)

View File

@ -269,7 +269,19 @@ func (i *Index) Search(ctx context.Context, request *SearchRequest) (*IndexResul
request.Limit = 10
}
query := bleve.NewQueryStringQuery(request.Query)
textQuery := bleve.NewQueryStringQuery(request.Query)
query := bleve.NewConjunctionQuery(textQuery)
if len(request.Kind) > 0 {
// apply OR condition filter for each kind ( dashboard, folder, etc )
orQuery := bleve.NewDisjunctionQuery()
for _, term := range request.Kind {
termQuery := bleve.NewTermQuery(term)
orQuery.AddQuery(termQuery)
}
query.AddQuery(orQuery)
}
req := bleve.NewSearchRequest(query)
for _, group := range request.GroupBy {
@ -387,6 +399,12 @@ func fetchResourceTypes() []*ListOptions {
Group: "folder.grafana.app",
Resource: "folders",
},
})
},
&ListOptions{
Key: &ResourceKey{
Group: "dashboard.grafana.app",
Resource: "dashboards",
},
})
return items
}

View File

@ -29,7 +29,7 @@ func TestIndexDashboard(t *testing.T) {
require.NoError(t, err)
assertCountEquals(t, index, 1)
assertSearchCountEquals(t, index, "*", 1)
assertSearchCountEquals(t, index, "*", nil, 1)
}
func TestIndexFolder(t *testing.T) {
@ -41,7 +41,7 @@ func TestIndexFolder(t *testing.T) {
require.NoError(t, err)
assertCountEquals(t, index, 1)
assertSearchCountEquals(t, index, "*", 1)
assertSearchCountEquals(t, index, "*", nil, 1)
}
func TestSearchFolder(t *testing.T) {
@ -54,7 +54,21 @@ func TestSearchFolder(t *testing.T) {
require.NoError(t, err)
assertCountEquals(t, index, 2)
assertSearchCountEquals(t, index, "Kind:Folder", 1)
assertSearchCountEquals(t, index, "*", []string{"folder"}, 1)
}
func TestSearchDashboardsAndFoldersOnly(t *testing.T) {
dashboard := readTestData(t, "dashboard-resource.json")
folder := readTestData(t, "folder-resource.json")
playlist := readTestData(t, "playlist-resource.json")
list := &ListResponse{Items: []*ResourceWrapper{{Value: dashboard}, {Value: folder}, {Value: playlist}}}
index := newTestIndex(t, 1)
err := index.writeBatch(testContext, list)
require.NoError(t, err)
assertCountEquals(t, index, 3)
assertSearchCountEquals(t, index, "*", []string{"dashboard", "folder"}, 2)
}
func TestLookupNames(t *testing.T) {
@ -75,7 +89,7 @@ func TestLookupNames(t *testing.T) {
for _, id := range chunk {
query += `"` + id + `" `
}
assertSearchCountEquals(t, index, query, int64(len(chunk)))
assertSearchCountEquals(t, index, query, nil, int64(len(chunk)))
}
func TestIndexDashboardWithTags(t *testing.T) {
@ -88,8 +102,8 @@ func TestIndexDashboardWithTags(t *testing.T) {
require.NoError(t, err)
assertCountEquals(t, index, 2)
assertSearchCountEquals(t, index, "tag1", 2)
assertSearchCountEquals(t, index, "tag4", 1)
assertSearchCountEquals(t, index, "tag1", nil, 2)
assertSearchCountEquals(t, index, "tag4", nil, 1)
assertSearchGroupCountEquals(t, index, "*", "tags", 4)
assertSearchGroupCountEquals(t, index, "tag4", "tags", 3)
}
@ -187,8 +201,8 @@ func assertCountEquals(t *testing.T, index *Index, expected uint64) {
assert.Equal(t, expected, total)
}
func assertSearchCountEquals(t *testing.T, index *Index, search string, expected int64) {
req := &SearchRequest{Query: search, Tenant: testTenant, Limit: expected + 1, Offset: 0}
func assertSearchCountEquals(t *testing.T, index *Index, search string, kind []string, expected int64) {
req := &SearchRequest{Query: search, Tenant: testTenant, Limit: expected + 1, Offset: 0, Kind: kind}
start := time.Now()
results, err := index.Search(testContext, req)
require.NoError(t, err)

View File

@ -1633,7 +1633,7 @@ type SearchRequest struct {
QueryType string `protobuf:"bytes,2,opt,name=queryType,proto3" json:"queryType,omitempty"`
Tenant string `protobuf:"bytes,3,opt,name=tenant,proto3" json:"tenant,omitempty"`
// resource kind (playlists, dashboards, etc)
Kind string `protobuf:"bytes,4,opt,name=kind,proto3" json:"kind,omitempty"`
Kind []string `protobuf:"bytes,4,rep,name=kind,proto3" json:"kind,omitempty"`
// pagination support
Limit int64 `protobuf:"varint,5,opt,name=limit,proto3" json:"limit,omitempty"`
Offset int64 `protobuf:"varint,6,opt,name=offset,proto3" json:"offset,omitempty"`
@ -1692,11 +1692,11 @@ func (x *SearchRequest) GetTenant() string {
return ""
}
func (x *SearchRequest) GetKind() string {
func (x *SearchRequest) GetKind() []string {
if x != nil {
return x.Kind
}
return ""
return nil
}
func (x *SearchRequest) GetLimit() int64 {
@ -2916,7 +2916,7 @@ var file_resource_proto_rawDesc = []byte{
0x79, 0x70, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x71, 0x75, 0x65, 0x72, 0x79,
0x54, 0x79, 0x70, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x18, 0x03,
0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x74, 0x65, 0x6e, 0x61, 0x6e, 0x74, 0x12, 0x12, 0x0a, 0x04,
0x6b, 0x69, 0x6e, 0x64, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6b, 0x69, 0x6e, 0x64,
0x6b, 0x69, 0x6e, 0x64, 0x18, 0x04, 0x20, 0x03, 0x28, 0x09, 0x52, 0x04, 0x6b, 0x69, 0x6e, 0x64,
0x12, 0x14, 0x0a, 0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x03, 0x52,
0x05, 0x6c, 0x69, 0x6d, 0x69, 0x74, 0x12, 0x16, 0x0a, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74,
0x18, 0x06, 0x20, 0x01, 0x28, 0x03, 0x52, 0x06, 0x6f, 0x66, 0x66, 0x73, 0x65, 0x74, 0x12, 0x2b,

View File

@ -330,7 +330,7 @@ message SearchRequest {
string queryType = 2;
string tenant = 3;
// resource kind (playlists, dashboards, etc)
string kind = 4;
repeated string kind = 4;
// pagination support
int64 limit = 5;
int64 offset = 6;

View File

@ -0,0 +1,19 @@
{
"kind": "Playlist",
"apiVersion": "playlist.grafana.app/v0alpha1",
"metadata": {
"name": "ae2ntrqxefvnke",
"namespace": "default",
"uid": "playlist-1",
"creationTimestamp": "2024-11-01T19:42:22Z",
"annotations": {
"grafana.app/createdBy": "user:1",
"grafana.app/originName": "SQL",
"grafana.app/originPath": "15",
"grafana.app/originTimestamp": "2024-11-01T19:42:22Z"
}
},
"spec": {
"title": "test-us-playlist"
}
}