Forráskód Böngészése

Major refactorings around searching, moved to seperate package, trying to move stuff out of models package, extend search support searching different types of entities and different types of dashboards, #960

Torkel Ödegaard 10 éve
szülő
commit
448a8b8d1c

+ 1 - 2
conf/defaults.ini

@@ -216,7 +216,6 @@ exchange = grafana_events
 #################################### Dashboard JSON files ##########################
 [dashboards.json]
 enabled = false
-path = dashboards
-orgs = *
+path = /var/lib/grafana/dashboards
 
 

+ 8 - 0
conf/sample.ini

@@ -211,3 +211,11 @@
 ;enabled = false
 ;rabbitmq_url = amqp://localhost/
 ;exchange = grafana_events
+
+;#################################### Dashboard JSON files ##########################
+[dashboards.json]
+;enabled = false
+;path = /var/lib/grafana/dashboards
+
+
+

+ 1 - 1
main.go

@@ -14,8 +14,8 @@ import (
 	"github.com/grafana/grafana/pkg/log"
 	"github.com/grafana/grafana/pkg/metrics"
 	"github.com/grafana/grafana/pkg/plugins"
+	"github.com/grafana/grafana/pkg/search"
 	"github.com/grafana/grafana/pkg/services/eventpublisher"
-	"github.com/grafana/grafana/pkg/services/search"
 	"github.com/grafana/grafana/pkg/services/sqlstore"
 	"github.com/grafana/grafana/pkg/setting"
 	"github.com/grafana/grafana/pkg/social"

+ 1 - 1
pkg/api/dashboard.go

@@ -10,7 +10,7 @@ import (
 	"github.com/grafana/grafana/pkg/metrics"
 	"github.com/grafana/grafana/pkg/middleware"
 	m "github.com/grafana/grafana/pkg/models"
-	"github.com/grafana/grafana/pkg/services/search"
+	"github.com/grafana/grafana/pkg/search"
 	"github.com/grafana/grafana/pkg/setting"
 	"github.com/grafana/grafana/pkg/util"
 )

+ 1 - 1
pkg/api/search.go

@@ -3,7 +3,7 @@ package api
 import (
 	"github.com/grafana/grafana/pkg/bus"
 	"github.com/grafana/grafana/pkg/middleware"
-	"github.com/grafana/grafana/pkg/services/search"
+	"github.com/grafana/grafana/pkg/search"
 )
 
 func Search(c *middleware.Context) {

+ 10 - 0
pkg/models/dashboards.go

@@ -126,3 +126,13 @@ type GetDashboardQuery struct {
 
 	Result *Dashboard
 }
+
+type DashboardTagCloudItem struct {
+	Term  string `json:"term"`
+	Count int    `json:"count"`
+}
+
+type GetDashboardTagsQuery struct {
+	OrgId  int64
+	Result []*DashboardTagCloudItem
+}

+ 1 - 28
pkg/models/search.go

@@ -1,12 +1,6 @@
 package models
 
-type SearchResult struct {
-	Dashboards []*DashboardSearchHit    `json:"dashboards"`
-	Tags       []*DashboardTagCloudItem `json:"tags"`
-	TagsOnly   bool                     `json:"tagsOnly"`
-}
-
-type DashboardSearchHit struct {
+type SearchHit struct {
 	Id        int64    `json:"id"`
 	Title     string   `json:"title"`
 	Uri       string   `json:"uri"`
@@ -14,24 +8,3 @@ type DashboardSearchHit struct {
 	Tags      []string `json:"tags"`
 	IsStarred bool     `json:"isStarred"`
 }
-
-type DashboardTagCloudItem struct {
-	Term  string `json:"term"`
-	Count int    `json:"count"`
-}
-
-type SearchDashboardsQuery struct {
-	Title     string
-	Tag       string
-	OrgId     int64
-	UserId    int64
-	Limit     int
-	IsStarred bool
-
-	Result []*DashboardSearchHit
-}
-
-type GetDashboardTagsQuery struct {
-	OrgId  int64
-	Result []*DashboardTagCloudItem
-}

+ 7 - 16
pkg/services/search/search.go → pkg/search/handlers.go

@@ -2,23 +2,13 @@ package search
 
 import (
 	"path/filepath"
+	"sort"
 
 	"github.com/grafana/grafana/pkg/bus"
 	m "github.com/grafana/grafana/pkg/models"
 	"github.com/grafana/grafana/pkg/setting"
 )
 
-type Query struct {
-	Title     string
-	Tag       string
-	OrgId     int64
-	UserId    int64
-	Limit     int
-	IsStarred bool
-
-	Result []*m.DashboardSearchHit
-}
-
 var jsonDashIndex *JsonDashIndex
 
 func Init() {
@@ -33,15 +23,14 @@ func Init() {
 			jsonFilesPath = filepath.Join(setting.HomePath, jsonFilesPath)
 		}
 
-		orgIds := jsonIndexCfg.Key("org_ids").String()
-		jsonDashIndex = NewJsonDashIndex(jsonFilesPath, orgIds)
+		jsonDashIndex = NewJsonDashIndex(jsonFilesPath)
 	}
 }
 
 func searchHandler(query *Query) error {
-	hits := make([]*m.DashboardSearchHit, 0)
+	hits := make(HitList, 0)
 
-	dashQuery := m.SearchDashboardsQuery{
+	dashQuery := FindPersistedDashboardsQuery{
 		Title:     query.Title,
 		Tag:       query.Tag,
 		UserId:    query.UserId,
@@ -65,6 +54,8 @@ func searchHandler(query *Query) error {
 		hits = append(hits, jsonHits...)
 	}
 
+	sort.Sort(hits)
+
 	if err := setIsStarredFlagOnSearchResults(query.UserId, hits); err != nil {
 		return err
 	}
@@ -73,7 +64,7 @@ func searchHandler(query *Query) error {
 	return nil
 }
 
-func setIsStarredFlagOnSearchResults(userId int64, hits []*m.DashboardSearchHit) error {
+func setIsStarredFlagOnSearchResults(userId int64, hits []*Hit) error {
 	query := m.GetUserStarsQuery{UserId: userId}
 	if err := bus.Dispatch(&query); err != nil {
 		return err

+ 7 - 8
pkg/services/search/json_index.go → pkg/search/json_index.go

@@ -11,9 +11,8 @@ import (
 )
 
 type JsonDashIndex struct {
-	path    string
-	orgsIds []int64
-	items   []*JsonDashIndexItem
+	path  string
+	items []*JsonDashIndexItem
 }
 
 type JsonDashIndexItem struct {
@@ -23,7 +22,7 @@ type JsonDashIndexItem struct {
 	Dashboard  *m.Dashboard
 }
 
-func NewJsonDashIndex(path string, orgIds string) *JsonDashIndex {
+func NewJsonDashIndex(path string) *JsonDashIndex {
 	log.Info("Creating json dashboard index for path: ", path)
 
 	index := JsonDashIndex{}
@@ -32,8 +31,8 @@ func NewJsonDashIndex(path string, orgIds string) *JsonDashIndex {
 	return &index
 }
 
-func (index *JsonDashIndex) Search(query *Query) ([]*m.DashboardSearchHit, error) {
-	results := make([]*m.DashboardSearchHit, 0)
+func (index *JsonDashIndex) Search(query *Query) ([]*Hit, error) {
+	results := make([]*Hit, 0)
 
 	for _, item := range index.items {
 		if len(results) > query.Limit {
@@ -49,8 +48,8 @@ func (index *JsonDashIndex) Search(query *Query) ([]*m.DashboardSearchHit, error
 
 		// add results with matchig title filter
 		if strings.Contains(item.TitleLower, query.Title) {
-			results = append(results, &m.DashboardSearchHit{
-				Type:  m.DashTypeJson,
+			results = append(results, &Hit{
+				Type:  DashHitJson,
 				Title: item.Dashboard.Title,
 				Tags:  item.Dashboard.GetTags(),
 				Uri:   "file/" + item.Path,

+ 1 - 1
pkg/services/search/json_index_test.go → pkg/search/json_index_test.go

@@ -9,7 +9,7 @@ import (
 func TestJsonDashIndex(t *testing.T) {
 
 	Convey("Given the json dash index", t, func() {
-		index := NewJsonDashIndex("../../../public/dashboards/", "*")
+		index := NewJsonDashIndex("../../public/dashboards/", "*")
 
 		Convey("Should be able to update index", func() {
 			err := index.updateIndex()

+ 47 - 0
pkg/search/models.go

@@ -0,0 +1,47 @@
+package search
+
+type HitType string
+
+const (
+	DashHitDB       HitType = "dash-db"
+	DashHitHome     HitType = "dash-home"
+	DashHitJson     HitType = "dash-json"
+	DashHitScripted HitType = "dash-scripted"
+)
+
+type Hit struct {
+	Id        int64    `json:"id"`
+	Title     string   `json:"title"`
+	Uri       string   `json:"uri"`
+	Type      HitType  `json:"type"`
+	Tags      []string `json:"tags"`
+	IsStarred bool     `json:"isStarred"`
+}
+
+type HitList []*Hit
+
+func (s HitList) Len() int           { return len(s) }
+func (s HitList) Swap(i, j int)      { s[i], s[j] = s[j], s[i] }
+func (s HitList) Less(i, j int) bool { return s[i].Title < s[j].Title }
+
+type Query struct {
+	Title     string
+	Tag       string
+	OrgId     int64
+	UserId    int64
+	Limit     int
+	IsStarred bool
+
+	Result HitList
+}
+
+type FindPersistedDashboardsQuery struct {
+	Title     string
+	Tag       string
+	OrgId     int64
+	UserId    int64
+	Limit     int
+	IsStarred bool
+
+	Result HitList
+}

+ 6 - 5
pkg/services/sqlstore/dashboard.go

@@ -8,6 +8,7 @@ import (
 	"github.com/grafana/grafana/pkg/bus"
 	"github.com/grafana/grafana/pkg/metrics"
 	m "github.com/grafana/grafana/pkg/models"
+	"github.com/grafana/grafana/pkg/search"
 )
 
 func init() {
@@ -119,7 +120,7 @@ type DashboardSearchProjection struct {
 	Term  string
 }
 
-func SearchDashboards(query *m.SearchDashboardsQuery) error {
+func SearchDashboards(query *search.FindPersistedDashboardsQuery) error {
 	var sql bytes.Buffer
 	params := make([]interface{}, 0)
 
@@ -166,17 +167,17 @@ func SearchDashboards(query *m.SearchDashboardsQuery) error {
 		return err
 	}
 
-	query.Result = make([]*m.DashboardSearchHit, 0)
-	hits := make(map[int64]*m.DashboardSearchHit)
+	query.Result = make([]*search.Hit, 0)
+	hits := make(map[int64]*search.Hit)
 
 	for _, item := range res {
 		hit, exists := hits[item.Id]
 		if !exists {
-			hit = &m.DashboardSearchHit{
+			hit = &search.Hit{
 				Id:    item.Id,
 				Title: item.Title,
 				Uri:   "db/" + item.Slug,
-				Type:  m.DashTypeDB,
+				Type:  search.DashHitDB,
 				Tags:  []string{},
 			}
 			query.Result = append(query.Result, hit)

+ 5 - 4
pkg/services/sqlstore/dashboard_test.go

@@ -6,6 +6,7 @@ import (
 	. "github.com/smartystreets/goconvey/convey"
 
 	m "github.com/grafana/grafana/pkg/models"
+	"github.com/grafana/grafana/pkg/search"
 )
 
 func insertTestDashboard(title string, orgId int64, tags ...interface{}) *m.Dashboard {
@@ -85,7 +86,7 @@ func TestDashboardDataAccess(t *testing.T) {
 			})
 
 			Convey("Should be able to search for dashboard", func() {
-				query := m.SearchDashboardsQuery{
+				query := search.FindPersistedDashboardsQuery{
 					Title: "test",
 					OrgId: 1,
 				}
@@ -99,8 +100,8 @@ func TestDashboardDataAccess(t *testing.T) {
 			})
 
 			Convey("Should be able to search for dashboards using tags", func() {
-				query1 := m.SearchDashboardsQuery{Tag: "webapp", OrgId: 1}
-				query2 := m.SearchDashboardsQuery{Tag: "tagdoesnotexist", OrgId: 1}
+				query1 := search.FindPersistedDashboardsQuery{Tag: "webapp", OrgId: 1}
+				query2 := search.FindPersistedDashboardsQuery{Tag: "tagdoesnotexist", OrgId: 1}
 
 				err := SearchDashboards(&query1)
 				err = SearchDashboards(&query2)
@@ -146,7 +147,7 @@ func TestDashboardDataAccess(t *testing.T) {
 				})
 
 				Convey("Should be able to search for starred dashboards", func() {
-					query := m.SearchDashboardsQuery{OrgId: 1, UserId: 10, IsStarred: true}
+					query := search.FindPersistedDashboardsQuery{OrgId: 1, UserId: 10, IsStarred: true}
 					err := SearchDashboards(&query)
 
 					So(err, ShouldBeNil)

+ 9 - 28
public/app/controllers/search.js

@@ -40,15 +40,15 @@ function (angular, _, config) {
         $scope.moveSelection(-1);
       }
       if (evt.keyCode === 13) {
-        if ($scope.query.tagcloud) {
-          var tag = $scope.results.tags[$scope.selectedIndex];
+        if ($scope.tagMode) {
+          var tag = $scope.results[$scope.selectedIndex];
           if (tag) {
             $scope.filterByTag(tag.term);
           }
           return;
         }
 
-        var selectedDash = $scope.results.dashboards[$scope.selectedIndex];
+        var selectedDash = $scope.results[$scope.selectedIndex];
         if (selectedDash) {
           $location.search({});
           $location.path(selectedDash.url);
@@ -57,7 +57,9 @@ function (angular, _, config) {
     };
 
     $scope.moveSelection = function(direction) {
-      $scope.selectedIndex = Math.max(Math.min($scope.selectedIndex + direction, $scope.resultCount - 1), 0);
+      var max = ($scope.results || []).length;
+      var newIndex = $scope.selectedIndex + direction;
+      $scope.selectedIndex = ((newIndex %= max) < 0) ? newIndex + max : newIndex;
     };
 
     $scope.searchDashboards = function() {
@@ -68,14 +70,13 @@ function (angular, _, config) {
       return backendSrv.search($scope.query).then(function(results) {
         if (localSearchId < $scope.currentSearchId) { return; }
 
-        $scope.resultCount = results.length;
         $scope.results = _.map(results, function(dash) {
           dash.url = 'dashboard/' + dash.uri;
           return dash;
         });
 
         if ($scope.queryHasNoFilters()) {
-          $scope.results.unshift({ title: 'Home', url: config.appSubUrl + '/', isHome: true });
+          $scope.results.unshift({ title: 'Home', url: config.appSubUrl + '/', type: 'dash-home' });
         }
       });
     };
@@ -97,10 +98,10 @@ function (angular, _, config) {
     };
 
     $scope.getTags = function() {
-      $scope.tagsMode = true;
       return backendSrv.get('/api/dashboards/tags').then(function(results) {
-        $scope.resultCount = results.length;
+        $scope.tagsMode = true;
         $scope.results = results;
+        $scope.giveSearchFocus = $scope.giveSearchFocus + 1;
       });
     };
 
@@ -116,26 +117,6 @@ function (angular, _, config) {
       $scope.searchDashboards();
     };
 
-    $scope.addMetricToCurrentDashboard = function (metricId) {
-      $scope.dashboard.rows.push({
-        title: '',
-        height: '250px',
-        editable: true,
-        panels: [
-      {
-        type: 'graphite',
-        title: 'test',
-        span: 12,
-        targets: [{ target: metricId }]
-      }
-      ]
-      });
-    };
-
-    $scope.toggleImport = function () {
-      $scope.showImport = !$scope.showImport;
-    };
-
     $scope.newDashboard = function() {
       $location.url('dashboard/new');
     };

+ 2 - 2
public/app/features/dashlinks/module.js

@@ -133,12 +133,12 @@ function (angular, _) {
 
     $scope.searchDashboards = function(link) {
       return backendSrv.search({tag: link.tag}).then(function(results) {
-        return _.reduce(results.dashboards, function(memo, dash) {
+        return _.reduce(results, function(memo, dash) {
           // do not add current dashboard
           if (dash.id !== currentDashId) {
             memo.push({
               title: dash.title,
-              url: 'dashboard/db/'+ dash.slug,
+              url: 'dashboard/' + dash.uri,
               icon: 'fa fa-th-large',
               keepTime: link.keepTime,
               includeVars: link.includeVars

+ 1 - 1
public/app/panels/dashlist/module.html

@@ -1,7 +1,7 @@
 <grafana-panel>
 	<div class="dashlist">
 	<div class="dashlist-item" ng-repeat="dash in dashList">
-		<a class="dashlist-link" href="dashboard/{{dash.uri}}">
+		<a class="dashlist-link dashlist-link-{{dash.type}}" href="dashboard/{{dash.uri}}">
 			<span class="dashlist-title">
 				{{dash.title}}
 			</span>

+ 1 - 1
public/app/panels/dashlist/module.js

@@ -62,7 +62,7 @@ function (angular, app, _, config, PanelMeta) {
       }
 
       return backendSrv.search(params).then(function(result) {
-        $scope.dashList = result.dashboards;
+        $scope.dashList = result;
       });
     };
 

+ 25 - 27
public/app/partials/search.html

@@ -24,41 +24,39 @@
 		</div>
 	</div>
 
-	<div ng-if="!showImport">
-		<div class="search-results-container" ng-if="tagsMode">
-			<div class="row">
-				<div class="span6 offset1">
-					<div ng-repeat="tag in results" class="pointer" style="width: 180px; float: left;"
-						ng-class="{'selected': $index === selectedIndex }"
-						ng-click="filterByTag(tag.term, $event)">
-						<a class="search-result-tag label label-tag" tag-color-from-name tag="tag.term">
-							<i class="fa fa-tag"></i>
-							<span>{{tag.term}} &nbsp;({{tag.count}})</span>
-						</a>
-					</div>
+	<div class="search-results-container" ng-if="tagsMode">
+		<div class="row">
+			<div class="span6 offset1">
+				<div ng-repeat="tag in results" class="pointer" style="width: 180px; float: left;"
+					ng-class="{'selected': $index === selectedIndex }"
+					ng-click="filterByTag(tag.term, $event)">
+					<a class="search-result-tag label label-tag" tag-color-from-name tag="tag.term">
+						<i class="fa fa-tag"></i>
+						<span>{{tag.term}} &nbsp;({{tag.count}})</span>
+					</a>
 				</div>
 			</div>
 		</div>
+	</div>
 
-		<div class="search-results-container" ng-if="!tagsMode">
-			<h6 ng-hide="results.length">No dashboards matching your query were found.</h6>
+	<div class="search-results-container" ng-if="!tagsMode">
+		<h6 ng-hide="results.length">No dashboards matching your query were found.</h6>
 
-			<a class="search-result-item pointer search-result-item-{{row.type}}" bindonce ng-repeat="row in results"
-				ng-class="{'selected': $index == selectedIndex}" ng-href="{{row.url}}">
+		<a class="search-item pointer search-item-{{row.type}}" bindonce ng-repeat="row in results"
+			ng-class="{'selected': $index == selectedIndex}" ng-href="{{row.url}}">
 
-				<span class="search-result-tags">
-					<span ng-click="filterByTag(tag, $event)" ng-repeat="tag in row.tags" tag-color-from-name tag="tag"  class="label label-tag">
-						{{tag}}
-					</span>
-					<i class="fa" ng-class="{'fa-star': row.isStarred, 'fa-star-o': !row.isStarred}"></i>
+			<span class="search-result-tags">
+				<span ng-click="filterByTag(tag, $event)" ng-repeat="tag in row.tags" tag-color-from-name tag="tag"  class="label label-tag">
+					{{tag}}
 				</span>
+				<i class="fa" ng-class="{'fa-star': row.isStarred, 'fa-star-o': !row.isStarred}"></i>
+			</span>
 
-				<span class="search-result-link">
-					<i class="search-result-icon"></i>
-					<span bo-text="row.title"></span>
-				</span>
-			</a>
-		</div>
+			<span class="search-result-link">
+				<i class="fa search-result-icon"></i>
+				<span bo-text="row.title"></span>
+			</span>
+		</a>
 	</div>
 
 	<div class="search-button-row">

+ 9 - 2
public/css/less/search.less

@@ -41,7 +41,7 @@
   display: block;
   line-height: 28px;
 
-  .search-result-item:hover, .search-result-item.selected {
+  .search-item:hover, .search-item.selected {
     background-color: @grafanaListHighlight;
   }
 
@@ -67,12 +67,19 @@
     }
   }
 
-  .search-result-item {
+  .search-item {
     display: block;
     padding: 3px 10px;
     white-space: nowrap;
     background-color: @grafanaListBackground;
     margin-bottom: 4px;
+    .search-result-icon:before {
+      content: "\f009";
+    }
+
+    &.search-item-dash-home .search-result-icon:before {
+      content: "\f015";
+    }
   }
 
   .search-result-tags {