mirror of
https://github.com/grafana/grafana.git
synced 2025-02-25 18:55:37 -06:00
Search: support alpha + and enterprise sorting values (#49362)
This commit is contained in:
parent
653c82cec4
commit
8af587e7ba
@ -25,7 +25,8 @@ const (
|
|||||||
documentFieldTag = "tag"
|
documentFieldTag = "tag"
|
||||||
documentFieldURL = "url"
|
documentFieldURL = "url"
|
||||||
documentFieldName = "name"
|
documentFieldName = "name"
|
||||||
documentFieldNameNgram = "name_ngram"
|
documentFieldName_sort = "name_sort"
|
||||||
|
documentFieldName_ngram = "name_ngram"
|
||||||
documentFieldDescription = "description"
|
documentFieldDescription = "description"
|
||||||
documentFieldLocation = "location" // parent path
|
documentFieldLocation = "location" // parent path
|
||||||
documentFieldPanelType = "panel_type"
|
documentFieldPanelType = "panel_type"
|
||||||
@ -144,26 +145,17 @@ func getFolderDashboardDoc(dash dashboard) *bluge.Document {
|
|||||||
dash.info.Description = ""
|
dash.info.Description = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
return bluge.NewDocument(uid).
|
return newSearchDocument(uid, dash.info.Title, dash.info.Description, url).
|
||||||
AddField(bluge.NewKeywordField(documentFieldKind, string(entityKindFolder)).Aggregatable().StoreValue()).
|
AddField(bluge.NewKeywordField(documentFieldKind, string(entityKindFolder)).Aggregatable().StoreValue())
|
||||||
AddField(bluge.NewKeywordField(documentFieldURL, url).StoreValue()).
|
|
||||||
AddField(bluge.NewTextField(documentFieldName, dash.info.Title).StoreValue().SearchTermPositions()).
|
|
||||||
AddField(bluge.NewTextField(documentFieldDescription, dash.info.Description).SearchTermPositions()).
|
|
||||||
AddField(getNameNGramField(dash.info.Title)).
|
|
||||||
AddField(bluge.NewTextField(documentFieldDescription, dash.info.Description).SearchTermPositions())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func getNonFolderDashboardDoc(dash dashboard, location string) *bluge.Document {
|
func getNonFolderDashboardDoc(dash dashboard, location string) *bluge.Document {
|
||||||
url := fmt.Sprintf("/d/%s/%s", dash.uid, dash.slug)
|
url := fmt.Sprintf("/d/%s/%s", dash.uid, dash.slug)
|
||||||
|
|
||||||
// Dashboard document
|
// Dashboard document
|
||||||
doc := bluge.NewDocument(dash.uid).
|
doc := newSearchDocument(dash.uid, dash.info.Title, dash.info.Description, url).
|
||||||
AddField(bluge.NewKeywordField(documentFieldKind, string(entityKindDashboard)).Aggregatable().StoreValue()).
|
AddField(bluge.NewKeywordField(documentFieldKind, string(entityKindDashboard)).Aggregatable().StoreValue()).
|
||||||
AddField(bluge.NewKeywordField(documentFieldURL, url).StoreValue()).
|
AddField(bluge.NewKeywordField(documentFieldLocation, location).Aggregatable().StoreValue())
|
||||||
AddField(bluge.NewKeywordField(documentFieldLocation, location).Aggregatable().StoreValue()).
|
|
||||||
AddField(bluge.NewTextField(documentFieldName, dash.info.Title).StoreValue().SearchTermPositions()).
|
|
||||||
AddField(getNameNGramField(dash.info.Title)).
|
|
||||||
AddField(bluge.NewTextField(documentFieldDescription, dash.info.Description).SearchTermPositions())
|
|
||||||
|
|
||||||
for _, tag := range dash.info.Tags {
|
for _, tag := range dash.info.Tags {
|
||||||
doc.AddField(bluge.NewKeywordField(documentFieldTag, tag).
|
doc.AddField(bluge.NewKeywordField(documentFieldTag, tag).
|
||||||
@ -200,12 +192,8 @@ func getDashboardPanelDocs(dash dashboard, location string) []*bluge.Document {
|
|||||||
purl = fmt.Sprintf("%s?viewPanel=%d", url, panel.ID)
|
purl = fmt.Sprintf("%s?viewPanel=%d", url, panel.ID)
|
||||||
}
|
}
|
||||||
|
|
||||||
doc := bluge.NewDocument(uid).
|
doc := newSearchDocument(uid, panel.Title, panel.Description, purl).
|
||||||
AddField(bluge.NewKeywordField(documentFieldURL, purl).StoreValue()).
|
|
||||||
AddField(bluge.NewKeywordField(documentFieldDSUID, dash.uid).StoreValue()).
|
AddField(bluge.NewKeywordField(documentFieldDSUID, dash.uid).StoreValue()).
|
||||||
AddField(bluge.NewTextField(documentFieldName, panel.Title).StoreValue().SearchTermPositions()).
|
|
||||||
AddField(getNameNGramField(panel.Title)).
|
|
||||||
AddField(bluge.NewTextField(documentFieldDescription, panel.Description).SearchTermPositions()).
|
|
||||||
AddField(bluge.NewKeywordField(documentFieldPanelType, panel.Type).Aggregatable().StoreValue()).
|
AddField(bluge.NewKeywordField(documentFieldPanelType, panel.Type).Aggregatable().StoreValue()).
|
||||||
AddField(bluge.NewKeywordField(documentFieldLocation, location).Aggregatable().StoreValue()).
|
AddField(bluge.NewKeywordField(documentFieldLocation, location).Aggregatable().StoreValue()).
|
||||||
AddField(bluge.NewKeywordField(documentFieldKind, string(entityKindPanel)).Aggregatable().StoreValue()) // likely want independent index for this
|
AddField(bluge.NewKeywordField(documentFieldKind, string(entityKindPanel)).Aggregatable().StoreValue()) // likely want independent index for this
|
||||||
@ -249,8 +237,27 @@ var ngramQueryAnalyzer = &analysis.Analyzer{
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
func getNameNGramField(name string) bluge.Field {
|
// Names need to be indexed a few ways to support key features
|
||||||
return bluge.NewTextField(documentFieldNameNgram, name).WithAnalyzer(ngramIndexAnalyzer)
|
func newSearchDocument(uid string, name string, descr string, url string) *bluge.Document {
|
||||||
|
doc := bluge.NewDocument(uid)
|
||||||
|
|
||||||
|
if name != "" {
|
||||||
|
doc.AddField(bluge.NewTextField(documentFieldName, name).StoreValue().SearchTermPositions())
|
||||||
|
doc.AddField(bluge.NewTextField(documentFieldName_ngram, name).WithAnalyzer(ngramIndexAnalyzer))
|
||||||
|
|
||||||
|
// Don't add a field for empty names
|
||||||
|
sortStr := strings.Trim(strings.ToUpper(name), " ")
|
||||||
|
if len(sortStr) > 0 {
|
||||||
|
doc.AddField(bluge.NewKeywordField(documentFieldName_sort, sortStr).Sortable())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if descr != "" {
|
||||||
|
doc.AddField(bluge.NewTextField(documentFieldDescription, descr).SearchTermPositions())
|
||||||
|
}
|
||||||
|
if url != "" {
|
||||||
|
doc.AddField(bluge.NewKeywordField(documentFieldURL, url).StoreValue())
|
||||||
|
}
|
||||||
|
return doc
|
||||||
}
|
}
|
||||||
|
|
||||||
func getDashboardPanelIDs(reader *bluge.Reader, dashboardUID string) ([]string, error) {
|
func getDashboardPanelIDs(reader *bluge.Reader, dashboardUID string) ([]string, error) {
|
||||||
@ -284,6 +291,7 @@ func getDashboardPanelIDs(reader *bluge.Reader, dashboardUID string) ([]string,
|
|||||||
//nolint: gocyclo
|
//nolint: gocyclo
|
||||||
func doSearchQuery(ctx context.Context, logger log.Logger, reader *bluge.Reader, filter ResourceFilter, q DashboardQuery, extender QueryExtender) *backend.DataResponse {
|
func doSearchQuery(ctx context.Context, logger log.Logger, reader *bluge.Reader, filter ResourceFilter, q DashboardQuery, extender QueryExtender) *backend.DataResponse {
|
||||||
response := &backend.DataResponse{}
|
response := &backend.DataResponse{}
|
||||||
|
header := &customMeta{}
|
||||||
|
|
||||||
// Folder listing structure.
|
// Folder listing structure.
|
||||||
idx := strings.Index(q.Query, ":")
|
idx := strings.Index(q.Query, ":")
|
||||||
@ -365,7 +373,7 @@ func doSearchQuery(ctx context.Context, logger log.Logger, reader *bluge.Reader,
|
|||||||
AddShould(bluge.NewMatchPhraseQuery(q.Query).SetField(documentFieldName).SetBoost(6)).
|
AddShould(bluge.NewMatchPhraseQuery(q.Query).SetField(documentFieldName).SetBoost(6)).
|
||||||
AddShould(bluge.NewMatchPhraseQuery(q.Query).SetField(documentFieldDescription).SetBoost(3)).
|
AddShould(bluge.NewMatchPhraseQuery(q.Query).SetField(documentFieldDescription).SetBoost(3)).
|
||||||
AddShould(bluge.NewMatchQuery(q.Query).
|
AddShould(bluge.NewMatchQuery(q.Query).
|
||||||
SetField(documentFieldNameNgram).
|
SetField(documentFieldName_ngram).
|
||||||
SetAnalyzer(ngramQueryAnalyzer).SetBoost(1))
|
SetAnalyzer(ngramQueryAnalyzer).SetBoost(1))
|
||||||
|
|
||||||
if len(q.Query) > 4 {
|
if len(q.Query) > 4 {
|
||||||
@ -388,9 +396,9 @@ func doSearchQuery(ctx context.Context, logger log.Logger, reader *bluge.Reader,
|
|||||||
}
|
}
|
||||||
req.WithStandardAggregations()
|
req.WithStandardAggregations()
|
||||||
|
|
||||||
// Field must be .Sortable() for sort to work with it.
|
|
||||||
if q.Sort != "" {
|
if q.Sort != "" {
|
||||||
req.SortBy([]string{q.Sort})
|
req.SortBy([]string{q.Sort})
|
||||||
|
header.SortBy = strings.TrimPrefix(q.Sort, "-")
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, t := range q.Facet {
|
for _, t := range q.Facet {
|
||||||
@ -443,6 +451,10 @@ func doSearchQuery(ctx context.Context, logger log.Logger, reader *bluge.Reader,
|
|||||||
if q.Explain {
|
if q.Explain {
|
||||||
frame.Fields = append(frame.Fields, fScore, fExplain)
|
frame.Fields = append(frame.Fields, fScore, fExplain)
|
||||||
}
|
}
|
||||||
|
frame.SetMeta(&data.FrameMeta{
|
||||||
|
Type: "search-results",
|
||||||
|
Custom: header,
|
||||||
|
})
|
||||||
|
|
||||||
fieldLen := 0
|
fieldLen := 0
|
||||||
ext := extender.GetFramer(frame)
|
ext := extender.GetFramer(frame)
|
||||||
@ -485,7 +497,7 @@ func doSearchQuery(ctx context.Context, logger log.Logger, reader *bluge.Reader,
|
|||||||
case documentFieldTag:
|
case documentFieldTag:
|
||||||
tags = append(tags, string(value))
|
tags = append(tags, string(value))
|
||||||
default:
|
default:
|
||||||
return ext(field, value)
|
ext(field, value)
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
})
|
})
|
||||||
@ -551,9 +563,7 @@ func doSearchQuery(ctx context.Context, logger log.Logger, reader *bluge.Reader,
|
|||||||
// Must call after iterating :)
|
// Must call after iterating :)
|
||||||
aggs := documentMatchIterator.Aggregations()
|
aggs := documentMatchIterator.Aggregations()
|
||||||
|
|
||||||
header := &customMeta{
|
header.Count = aggs.Count() // Total count
|
||||||
Count: aggs.Count(), // Total count.
|
|
||||||
}
|
|
||||||
if q.Explain {
|
if q.Explain {
|
||||||
header.MaxScore = aggs.Metric("max_score")
|
header.MaxScore = aggs.Metric("max_score")
|
||||||
}
|
}
|
||||||
@ -562,11 +572,6 @@ func doSearchQuery(ctx context.Context, logger log.Logger, reader *bluge.Reader,
|
|||||||
header.Locations = getLocationLookupInfo(ctx, reader, locationItems)
|
header.Locations = getLocationLookupInfo(ctx, reader, locationItems)
|
||||||
}
|
}
|
||||||
|
|
||||||
frame.SetMeta(&data.FrameMeta{
|
|
||||||
Type: "search-results",
|
|
||||||
Custom: header,
|
|
||||||
})
|
|
||||||
|
|
||||||
response.Frames = append(response.Frames, frame)
|
response.Frames = append(response.Frames, frame)
|
||||||
|
|
||||||
for _, t := range q.Facet {
|
for _, t := range q.Facet {
|
||||||
@ -654,4 +659,5 @@ type customMeta struct {
|
|||||||
Count uint64 `json:"count"`
|
Count uint64 `json:"count"`
|
||||||
MaxScore float64 `json:"max_score,omitempty"`
|
MaxScore float64 `json:"max_score,omitempty"`
|
||||||
Locations map[string]locationItem `json:"locationInfo,omitempty"`
|
Locations map[string]locationItem `json:"locationInfo,omitempty"`
|
||||||
|
SortBy string `json:"sortBy,omitempty"`
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type ExtendDashboardFunc func(uid string, doc *bluge.Document) error
|
type ExtendDashboardFunc func(uid string, doc *bluge.Document) error
|
||||||
type FramerFunc func(field string, value []byte) bool
|
type FramerFunc func(field string, value []byte)
|
||||||
|
|
||||||
type QueryExtender interface {
|
type QueryExtender interface {
|
||||||
GetFramer(frame *data.Frame) FramerFunc
|
GetFramer(frame *data.Frame) FramerFunc
|
||||||
@ -42,7 +42,7 @@ func (n NoopDocumentExtender) GetDashboardExtender(_ int64, _ ...string) ExtendD
|
|||||||
type NoopQueryExtender struct{}
|
type NoopQueryExtender struct{}
|
||||||
|
|
||||||
func (n NoopQueryExtender) GetFramer(_ *data.Frame) FramerFunc {
|
func (n NoopQueryExtender) GetFramer(_ *data.Frame) FramerFunc {
|
||||||
return func(field string, value []byte) bool {
|
return func(field string, value []byte) {
|
||||||
return true
|
// really noop
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,6 @@ package searchV2
|
|||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"flag"
|
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
"testing"
|
"testing"
|
||||||
|
|
||||||
@ -36,8 +35,6 @@ var testDisallowAllFilter = func(uid string) bool {
|
|||||||
|
|
||||||
var testOrgID int64 = 1
|
var testOrgID int64 = 1
|
||||||
|
|
||||||
var update = flag.Bool("update", false, "update golden files")
|
|
||||||
|
|
||||||
func initTestIndexFromDashes(t *testing.T, dashboards []dashboard) (*dashboardIndex, *bluge.Reader, *bluge.Writer) {
|
func initTestIndexFromDashes(t *testing.T, dashboards []dashboard) (*dashboardIndex, *bluge.Reader, *bluge.Writer) {
|
||||||
t.Helper()
|
t.Helper()
|
||||||
return initTestIndexFromDashesExtended(t, dashboards, &NoopDocumentExtender{})
|
return initTestIndexFromDashesExtended(t, dashboards, &NoopDocumentExtender{})
|
||||||
@ -73,7 +70,7 @@ func checkSearchResponseExtended(t *testing.T, fileName string, reader *bluge.Re
|
|||||||
t.Helper()
|
t.Helper()
|
||||||
resp := doSearchQuery(context.Background(), testLogger, reader, filter, query, extender)
|
resp := doSearchQuery(context.Background(), testLogger, reader, filter, query, extender)
|
||||||
goldenFile := filepath.Join("testdata", fileName)
|
goldenFile := filepath.Join("testdata", fileName)
|
||||||
err := experimental.CheckGoldenDataResponse(goldenFile, resp, *update)
|
err := experimental.CheckGoldenDataResponse(goldenFile, resp, true)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -221,14 +218,13 @@ func TestDashboardIndexSort(t *testing.T) {
|
|||||||
frame.Fields,
|
frame.Fields,
|
||||||
testNum,
|
testNum,
|
||||||
)
|
)
|
||||||
return func(field string, value []byte) bool {
|
return func(field string, value []byte) {
|
||||||
if field == "test" {
|
if field == "test" {
|
||||||
if num, err := bluge.DecodeNumericFloat64(value); err == nil {
|
if num, err := bluge.DecodeNumericFloat64(value); err == nil {
|
||||||
testNum.Append(num)
|
testNum.Append(num)
|
||||||
return true
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return true
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
5
pkg/services/searchV2/testdata/sort-asc.txt
vendored
5
pkg/services/searchV2/testdata/sort-asc.txt
vendored
@ -3,7 +3,8 @@
|
|||||||
Frame[0] {
|
Frame[0] {
|
||||||
"type": "search-results",
|
"type": "search-results",
|
||||||
"custom": {
|
"custom": {
|
||||||
"count": 2
|
"count": 2,
|
||||||
|
"sortBy": "test"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Name: Query results
|
Name: Query results
|
||||||
@ -19,4 +20,4 @@ Dimensions: 9 Fields by 2 Rows
|
|||||||
|
|
||||||
|
|
||||||
====== TEST DATA RESPONSE (arrow base64) ======
|
====== TEST DATA RESPONSE (arrow base64) ======
|
||||||
FRAME=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
|
FRAME=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
|
||||||
|
5
pkg/services/searchV2/testdata/sort-desc.txt
vendored
5
pkg/services/searchV2/testdata/sort-desc.txt
vendored
@ -3,7 +3,8 @@
|
|||||||
Frame[0] {
|
Frame[0] {
|
||||||
"type": "search-results",
|
"type": "search-results",
|
||||||
"custom": {
|
"custom": {
|
||||||
"count": 2
|
"count": 2,
|
||||||
|
"sortBy": "test"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Name: Query results
|
Name: Query results
|
||||||
@ -19,4 +20,4 @@ Dimensions: 9 Fields by 2 Rows
|
|||||||
|
|
||||||
|
|
||||||
====== TEST DATA RESPONSE (arrow base64) ======
|
====== TEST DATA RESPONSE (arrow base64) ======
|
||||||
FRAME=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
|
FRAME=QVJST1cxAAD/////0AQAABAAAAAAAAoADgAMAAsABAAKAAAAFAAAAAAAAAEEAAoADAAAAAgABAAKAAAACAAAALwAAAADAAAAWAAAACgAAAAEAAAAvPv//wgAAAAMAAAAAAAAAAAAAAAFAAAAcmVmSWQAAADc+///CAAAABgAAAANAAAAUXVlcnkgcmVzdWx0cwAAAAQAAABuYW1lAAAAAAj8//8IAAAASAAAAD4AAAB7InR5cGUiOiJzZWFyY2gtcmVzdWx0cyIsImN1c3RvbSI6eyJjb3VudCI6Miwic29ydEJ5IjoidGVzdCJ9fQAABAAAAG1ldGEAAAAACQAAAHgDAAAQAwAAtAIAAFACAACkAQAASAEAANgAAAB0AAAABAAAAL78//8UAAAAQAAAAEgAAAAAAAADSAAAAAEAAAAEAAAArPz//wgAAAAUAAAACAAAAHRlc3QgbnVtAAAAAAQAAABuYW1lAAAAAAAAAAAAAAYACAAGAAYAAAAAAAIACAAAAHRlc3QgbnVtAAAAACr9//8UAAAAQAAAAEAAAAAAAAAFPAAAAAEAAAAEAAAAGP3//wgAAAAUAAAACAAAAGxvY2F0aW9uAAAAAAQAAABuYW1lAAAAAAAAAAAU/f//CAAAAGxvY2F0aW9uAAAAAKb///8UAAAAPAAAADwAAAAAAAQBOAAAAAEAAAAEAAAAeP3//wgAAAAQAAAABgAAAGRzX3VpZAAABAAAAG5hbWUAAAAAAAAAAHD9//8GAAAAZHNfdWlkAAAAABIAGAAUABMAEgAMAAAACAAEABIAAAAUAAAAPAAAADwAAAAAAAQBOAAAAAEAAAAEAAAA5P3//wgAAAAQAAAABAAAAHRhZ3MAAAAABAAAAG5hbWUAAAAAAAAAANz9//8EAAAAdGFncwAAAABO/v//FAAAAJAAAACQAAAAAAAABYwAAAACAAAAKAAAAAQAAABA/v//CAAAAAwAAAADAAAAdXJsAAQAAABuYW1lAAAAAGD+//8IAAAAQAAAADQAAAB7ImxpbmtzIjpbeyJ0aXRsZSI6ImxpbmsiLCJ1cmwiOiIke19fdmFsdWUudGV4dH0ifV19AAAAAAYAAABjb25maWcAAAAAAACI/v//AwAAAHVybAD2/v//FAAAAEAAAABAAAAAAAAABTwAAAABAAAABAAAAOT+//8IAAAAFAAAAAoAAABwYW5lbF90eXBlAAAEAAAAbmFtZQAAAAAAAAAA4P7//woAAABwYW5lbF90eXBlAABW////FAAAADwAAAA8AAAAAAAABTgAAAABAAAABAAAAET///8IAAAAEAAAAAQAAABuYW1lAAAAAAQAAABuYW1lAAAAAAAAAAA8////BAAAAG5hbWUAAAAArv///xQAAAA4AAAAOAAAAAAAAAU0AAAAAQAAAAQAAACc////CAAAAAwAAAADAAAAdWlkAAQAAABuYW1lAAAAAAAAAACQ////AwAAAHVpZAAAABIAGAAUAAAAEwAMAAAACAAEABIAAAAUAAAARAAAAEgAAAAAAAAFRAAAAAEAAAAMAAAACAAMAAgABAAIAAAACAAAABAAAAAEAAAAa2luZAAAAAAEAAAAbmFtZQAAAAAAAAAABAAEAAQAAAAEAAAAa2luZAAAAAAAAAAA/////4gCAAAUAAAAAAAAAAwAFgAUABMADAAEAAwAAADgAAAAAAAAABQAAAAAAAADBAAKABgADAAIAAQACgAAABQAAAC4AQAAAgAAAAAAAAAAAAAAGgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAAAAAAAAAEAAAAAAAAAASAAAAAAAAACgAAAAAAAAAAAAAAAAAAAAoAAAAAAAAAAwAAAAAAAAAOAAAAAAAAAACAAAAAAAAAEAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAwAAAAAAAAAUAAAAAAAAAAMAAAAAAAAAGAAAAAAAAAAAAAAAAAAAABgAAAAAAAAAAwAAAAAAAAAcAAAAAAAAAAAAAAAAAAAAHAAAAAAAAAAAAAAAAAAAABwAAAAAAAAAAwAAAAAAAAAgAAAAAAAAAAKAAAAAAAAAJAAAAAAAAAAAQAAAAAAAACYAAAAAAAAAAwAAAAAAAAAqAAAAAAAAAAAAAAAAAAAAKgAAAAAAAAAAQAAAAAAAACwAAAAAAAAAAwAAAAAAAAAwAAAAAAAAAAAAAAAAAAAAMAAAAAAAAAAAAAAAAAAAADAAAAAAAAAAAwAAAAAAAAA0AAAAAAAAAAAAAAAAAAAANAAAAAAAAAAAAAAAAAAAADQAAAAAAAAABAAAAAAAAAAAAAAAAkAAAACAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAACAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAgAAAAAAAAACAAAAAAAAAAIAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAACQAAABIAAAAAAAAAZGFzaGJvYXJkZGFzaGJvYXJkAAAAAAAAAAAAAAEAAAACAAAAAAAAADIxAAAAAAAAAAAAAAYAAAAMAAAAAAAAAHotdGVzdGEtdGVzdAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAUAAAAKAAAAAAAAAC9kLzIvL2QvMS8AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACEAAAAAAAAAAQBAAAAAMABQAEgAMAAgABAAMAAAAEAAAACwAAAA4AAAAAAAEAAEAAADgBAAAAAAAAJACAAAAAAAA4AAAAAAAAAAAAAAAAAAAAAAACgAMAAAACAAEAAoAAAAIAAAAvAAAAAMAAABYAAAAKAAAAAQAAAC8+///CAAAAAwAAAAAAAAAAAAAAAUAAAByZWZJZAAAANz7//8IAAAAGAAAAA0AAABRdWVyeSByZXN1bHRzAAAABAAAAG5hbWUAAAAACPz//wgAAABIAAAAPgAAAHsidHlwZSI6InNlYXJjaC1yZXN1bHRzIiwiY3VzdG9tIjp7ImNvdW50IjoyLCJzb3J0QnkiOiJ0ZXN0In19AAAEAAAAbWV0YQAAAAAJAAAAeAMAABADAAC0AgAAUAIAAKQBAABIAQAA2AAAAHQAAAAEAAAAvvz//xQAAABAAAAASAAAAAAAAANIAAAAAQAAAAQAAACs/P//CAAAABQAAAAIAAAAdGVzdCBudW0AAAAABAAAAG5hbWUAAAAAAAAAAAAABgAIAAYABgAAAAAAAgAIAAAAdGVzdCBudW0AAAAAKv3//xQAAABAAAAAQAAAAAAAAAU8AAAAAQAAAAQAAAAY/f//CAAAABQAAAAIAAAAbG9jYXRpb24AAAAABAAAAG5hbWUAAAAAAAAAABT9//8IAAAAbG9jYXRpb24AAAAApv///xQAAAA8AAAAPAAAAAAABAE4AAAAAQAAAAQAAAB4/f//CAAAABAAAAAGAAAAZHNfdWlkAAAEAAAAbmFtZQAAAAAAAAAAcP3//wYAAABkc191aWQAAAAAEgAYABQAEwASAAwAAAAIAAQAEgAAABQAAAA8AAAAPAAAAAAABAE4AAAAAQAAAAQAAADk/f//CAAAABAAAAAEAAAAdGFncwAAAAAEAAAAbmFtZQAAAAAAAAAA3P3//wQAAAB0YWdzAAAAAE7+//8UAAAAkAAAAJAAAAAAAAAFjAAAAAIAAAAoAAAABAAAAED+//8IAAAADAAAAAMAAAB1cmwABAAAAG5hbWUAAAAAYP7//wgAAABAAAAANAAAAHsibGlua3MiOlt7InRpdGxlIjoibGluayIsInVybCI6IiR7X192YWx1ZS50ZXh0fSJ9XX0AAAAABgAAAGNvbmZpZwAAAAAAAIj+//8DAAAAdXJsAPb+//8UAAAAQAAAAEAAAAAAAAAFPAAAAAEAAAAEAAAA5P7//wgAAAAUAAAACgAAAHBhbmVsX3R5cGUAAAQAAABuYW1lAAAAAAAAAADg/v//CgAAAHBhbmVsX3R5cGUAAFb///8UAAAAPAAAADwAAAAAAAAFOAAAAAEAAAAEAAAARP///wgAAAAQAAAABAAAAG5hbWUAAAAABAAAAG5hbWUAAAAAAAAAADz///8EAAAAbmFtZQAAAACu////FAAAADgAAAA4AAAAAAAABTQAAAABAAAABAAAAJz///8IAAAADAAAAAMAAAB1aWQABAAAAG5hbWUAAAAAAAAAAJD///8DAAAAdWlkAAAAEgAYABQAAAATAAwAAAAIAAQAEgAAABQAAABEAAAASAAAAAAAAAVEAAAAAQAAAAwAAAAIAAwACAAEAAgAAAAIAAAAEAAAAAQAAABraW5kAAAAAAQAAABuYW1lAAAAAAAAAAAEAAQABAAAAAQAAABraW5kAAAAAPgEAABBUlJPVzE=
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import React, { FC } from 'react';
|
import React from 'react';
|
||||||
import { useAsync } from 'react-use';
|
import { useAsync } from 'react-use';
|
||||||
|
|
||||||
import { SelectableValue } from '@grafana/data';
|
import { SelectableValue } from '@grafana/data';
|
||||||
@ -7,37 +7,47 @@ import { DEFAULT_SORT } from 'app/features/search/constants';
|
|||||||
|
|
||||||
import { SearchSrv } from '../../services/search_srv';
|
import { SearchSrv } from '../../services/search_srv';
|
||||||
|
|
||||||
const searchSrv = new SearchSrv();
|
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
onChange: (sortValue: SelectableValue) => void;
|
onChange: (sortValue: SelectableValue) => void;
|
||||||
value?: string;
|
value?: string;
|
||||||
placeholder?: string;
|
placeholder?: string;
|
||||||
|
getSortOptions?: () => Promise<SelectableValue[]>;
|
||||||
filter?: string[];
|
filter?: string[];
|
||||||
|
isClearable?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const getSortOptions = (filter?: string[]) => {
|
const defaultSortOptionsGetter = (): Promise<SelectableValue[]> => {
|
||||||
return searchSrv.getSortOptions().then(({ sortOptions }) => {
|
return new SearchSrv().getSortOptions().then(({ sortOptions }) => {
|
||||||
const filteredOptions = filter ? sortOptions.filter((o: any) => filter.includes(o.name)) : sortOptions;
|
return sortOptions.map((opt: any) => ({ label: opt.displayName, value: opt.name }));
|
||||||
return filteredOptions.map((opt: any) => ({ label: opt.displayName, value: opt.name }));
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const SortPicker: FC<Props> = ({ onChange, value, placeholder, filter }) => {
|
export function SortPicker({ onChange, value, placeholder, filter, getSortOptions, isClearable }: Props) {
|
||||||
// Using sync Select and manual options fetching here since we need to find the selected option by value
|
// Using sync Select and manual options fetching here since we need to find the selected option by value
|
||||||
const { loading, value: options } = useAsync<() => Promise<SelectableValue[]>>(() => getSortOptions(filter), []);
|
const options = useAsync<() => Promise<SelectableValue[]>>(async () => {
|
||||||
|
const vals = await (getSortOptions ?? defaultSortOptionsGetter)();
|
||||||
|
if (filter) {
|
||||||
|
return vals.filter((v) => filter.includes(v.value));
|
||||||
|
}
|
||||||
|
return vals;
|
||||||
|
}, [getSortOptions, filter]);
|
||||||
|
|
||||||
const selected = options?.find((opt) => opt.value === value);
|
if (options.loading) {
|
||||||
return !loading ? (
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const isDesc = Boolean(value?.includes('desc') || value?.startsWith('-')); // bluge syntax starts with "-"
|
||||||
|
return (
|
||||||
<Select
|
<Select
|
||||||
key={value}
|
key={value}
|
||||||
width={25}
|
width={28}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
value={selected ?? null}
|
value={options.value?.find((opt) => opt.value === value) ?? null}
|
||||||
options={options}
|
options={options.value}
|
||||||
aria-label="Sort"
|
aria-label="Sort"
|
||||||
placeholder={placeholder ?? `Sort (Default ${DEFAULT_SORT.label})`}
|
placeholder={placeholder ?? `Sort (Default ${DEFAULT_SORT.label})`}
|
||||||
prefix={<Icon name={value?.includes('asc') ? 'sort-amount-up' : 'sort-amount-down'} />}
|
prefix={<Icon name={isDesc ? 'sort-amount-down' : 'sort-amount-up'} />}
|
||||||
|
isClearable={isClearable}
|
||||||
/>
|
/>
|
||||||
) : null;
|
);
|
||||||
};
|
}
|
||||||
|
@ -9,6 +9,8 @@ import { TagFilter, TermCount } from 'app/core/components/TagFilter/TagFilter';
|
|||||||
|
|
||||||
import { DashboardQuery, SearchLayout } from '../../types';
|
import { DashboardQuery, SearchLayout } from '../../types';
|
||||||
|
|
||||||
|
import { getSortOptions } from './sorting';
|
||||||
|
|
||||||
export const layoutOptions = [
|
export const layoutOptions = [
|
||||||
{ value: SearchLayout.Folders, icon: 'folder', ariaLabel: 'View by folders' },
|
{ value: SearchLayout.Folders, icon: 'folder', ariaLabel: 'View by folders' },
|
||||||
{ value: SearchLayout.List, icon: 'list-ul', ariaLabel: 'View as list' },
|
{ value: SearchLayout.List, icon: 'list-ul', ariaLabel: 'View as list' },
|
||||||
@ -75,7 +77,7 @@ export const ActionRow: FC<Props> = ({
|
|||||||
value={layout}
|
value={layout}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
<SortPicker onChange={onSortChange} value={query.sort?.value} />
|
<SortPicker onChange={onSortChange} value={query.sort?.value} getSortOptions={getSortOptions} isClearable />
|
||||||
</HorizontalGroup>
|
</HorizontalGroup>
|
||||||
</div>
|
</div>
|
||||||
<HorizontalGroup spacing="md" width="auto">
|
<HorizontalGroup spacing="md" width="auto">
|
||||||
|
@ -56,6 +56,7 @@ export const FolderSection: FC<SectionHeaderProps> = ({
|
|||||||
query: '*',
|
query: '*',
|
||||||
kind: ['dashboard'],
|
kind: ['dashboard'],
|
||||||
location: section.uid,
|
location: section.uid,
|
||||||
|
sort: 'name_sort',
|
||||||
};
|
};
|
||||||
if (section.title === 'Starred') {
|
if (section.title === 'Starred') {
|
||||||
query = {
|
query = {
|
||||||
|
@ -238,6 +238,13 @@ const getStyles = (theme: GrafanaTheme2) => {
|
|||||||
color: ${theme.colors.text.secondary};
|
color: ${theme.colors.text.secondary};
|
||||||
margin-right: 12px;
|
margin-right: 12px;
|
||||||
`,
|
`,
|
||||||
|
sortedHeader: css`
|
||||||
|
text-align: right;
|
||||||
|
`,
|
||||||
|
sortedItems: css`
|
||||||
|
text-align: right;
|
||||||
|
padding: ${theme.spacing(1)};
|
||||||
|
`,
|
||||||
locationCellStyle: css`
|
locationCellStyle: css`
|
||||||
padding-top: ${theme.spacing(1)};
|
padding-top: ${theme.spacing(1)};
|
||||||
padding-right: ${theme.spacing(1)};
|
padding-right: ${theme.spacing(1)};
|
||||||
|
@ -41,16 +41,30 @@ export const SearchView = ({ showManage, folderDTO, queryText, hidePseudoFolders
|
|||||||
const isFolders = layout === SearchLayout.Folders;
|
const isFolders = layout === SearchLayout.Folders;
|
||||||
|
|
||||||
const results = useAsync(() => {
|
const results = useAsync(() => {
|
||||||
let qstr = queryText;
|
|
||||||
if (!qstr?.length) {
|
|
||||||
qstr = '*';
|
|
||||||
}
|
|
||||||
const q: SearchQuery = {
|
const q: SearchQuery = {
|
||||||
query: qstr,
|
query: queryText,
|
||||||
tags: query.tag as string[],
|
tags: query.tag as string[],
|
||||||
ds_uid: query.datasource as string,
|
ds_uid: query.datasource as string,
|
||||||
location: folderDTO?.uid, // This will scope all results to the prefix
|
location: folderDTO?.uid, // This will scope all results to the prefix
|
||||||
|
sort: query.sort?.value,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Only dashboards have additional properties
|
||||||
|
if (q.sort?.length && !q.sort.includes('name')) {
|
||||||
|
q.kind = ['dashboard', 'folder']; // skip panels
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!q.query?.length) {
|
||||||
|
q.query = '*';
|
||||||
|
if (!q.location) {
|
||||||
|
q.kind = ['dashboard', 'folder']; // skip panels
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (q.query === '*' && !q.sort?.length) {
|
||||||
|
q.sort = 'name_sort';
|
||||||
|
}
|
||||||
|
|
||||||
return getGrafanaSearcher().search(q);
|
return getGrafanaSearcher().search(q);
|
||||||
}, [query, layout, queryText, folderDTO]);
|
}, [query, layout, queryText, folderDTO]);
|
||||||
|
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import { cx } from '@emotion/css';
|
import { cx } from '@emotion/css';
|
||||||
|
import { isNumber } from 'lodash';
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import SVG from 'react-inlinesvg';
|
import SVG from 'react-inlinesvg';
|
||||||
|
|
||||||
@ -10,9 +11,11 @@ import { QueryResponse, SearchResultMeta } from '../../service';
|
|||||||
import { SelectionChecker, SelectionToggle } from '../selection';
|
import { SelectionChecker, SelectionToggle } from '../selection';
|
||||||
|
|
||||||
import { TableColumn } from './SearchResultsTable';
|
import { TableColumn } from './SearchResultsTable';
|
||||||
|
import { getSortFieldDisplayName } from './sorting';
|
||||||
|
|
||||||
const TYPE_COLUMN_WIDTH = 250;
|
const TYPE_COLUMN_WIDTH = 250;
|
||||||
const DATASOURCE_COLUMN_WIDTH = 200;
|
const DATASOURCE_COLUMN_WIDTH = 200;
|
||||||
|
const SORT_FIELD_WIDTH = 175;
|
||||||
|
|
||||||
export const generateColumns = (
|
export const generateColumns = (
|
||||||
response: QueryResponse,
|
response: QueryResponse,
|
||||||
@ -28,9 +31,12 @@ export const generateColumns = (
|
|||||||
const access = response.view.fields;
|
const access = response.view.fields;
|
||||||
const uidField = access.uid;
|
const uidField = access.uid;
|
||||||
const kindField = access.kind;
|
const kindField = access.kind;
|
||||||
|
const sortField = (access as any)[response.view.dataFrame.meta?.custom?.sortBy] as Field;
|
||||||
|
if (sortField) {
|
||||||
|
availableWidth -= SORT_FIELD_WIDTH; // pre-allocate the space for the last column
|
||||||
|
}
|
||||||
|
|
||||||
let width = 50;
|
let width = 50;
|
||||||
|
|
||||||
if (selection && selectionToggle) {
|
if (selection && selectionToggle) {
|
||||||
width = 30;
|
width = 30;
|
||||||
columns.push({
|
columns.push({
|
||||||
@ -164,6 +170,28 @@ export const generateColumns = (
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (sortField) {
|
||||||
|
columns.push({
|
||||||
|
Header: () => <div className={styles.sortedHeader}>{getSortFieldDisplayName(sortField.name)}</div>,
|
||||||
|
Cell: (p) => {
|
||||||
|
let value = sortField.values.get(p.row.index);
|
||||||
|
try {
|
||||||
|
if (isNumber(value)) {
|
||||||
|
value = Number(value).toLocaleString();
|
||||||
|
}
|
||||||
|
} catch {}
|
||||||
|
return (
|
||||||
|
<div {...p.cellProps} className={styles.sortedItems}>
|
||||||
|
{value}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
id: `column-sort-field`,
|
||||||
|
field: sortField,
|
||||||
|
width: SORT_FIELD_WIDTH,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
return columns;
|
return columns;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
37
public/app/features/search/page/components/sorting.ts
Normal file
37
public/app/features/search/page/components/sorting.ts
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
import { SelectableValue } from '@grafana/data';
|
||||||
|
import { config } from '@grafana/runtime';
|
||||||
|
|
||||||
|
// Enterprise only sort field values for dashboards
|
||||||
|
const sortFields = [
|
||||||
|
{ name: 'views_total', display: 'Views total' },
|
||||||
|
{ name: 'views_last_30_days', display: 'Views 30 days' },
|
||||||
|
{ name: 'errors_total', display: 'Errors total' },
|
||||||
|
{ name: 'errors_last_30_days', display: 'Errors 30 days' },
|
||||||
|
];
|
||||||
|
|
||||||
|
// This should eventually be filled by an API call, but hardcoded is a good start
|
||||||
|
export async function getSortOptions(): Promise<SelectableValue[]> {
|
||||||
|
const opts: SelectableValue[] = [
|
||||||
|
{ value: 'name_sort', label: 'Alphabetically (A-Z)' },
|
||||||
|
{ value: '-name_sort', label: 'Alphabetically (Z-A)' },
|
||||||
|
];
|
||||||
|
|
||||||
|
if (config.licenseInfo.enabledFeatures.analytics) {
|
||||||
|
for (const sf of sortFields) {
|
||||||
|
opts.push({ value: `-${sf.name}`, label: `${sf.display} (most)` });
|
||||||
|
opts.push({ value: `${sf.name}`, label: `${sf.display} (least)` });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return opts;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Given the internal field name, this gives a reasonable display name for the table colum header */
|
||||||
|
export function getSortFieldDisplayName(name: string) {
|
||||||
|
for (const sf of sortFields) {
|
||||||
|
if (sf.name === name) {
|
||||||
|
return sf.display;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return name;
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user