浏览代码

Dashboard search by tag, and tag cloud now works, god dam I hate SQL

Torkel Ödegaard 11 年之前
父节点
当前提交
1ae52d2472
共有 6 个文件被更改,包括 126 次插入40 次删除
  1. 1 1
      grafana
  2. 10 4
      pkg/api/dashboard.go
  3. 23 20
      pkg/models/dashboards.go
  4. 61 10
      pkg/stores/sqlstore/dashboards.go
  5. 24 4
      pkg/stores/sqlstore/dashboards_test.go
  6. 7 1
      pkg/stores/sqlstore/sqlstore.go

+ 1 - 1
grafana

@@ -1 +1 @@
-Subproject commit d03949a735fd6ee486e278feb3b87f252be5ce96
+Subproject commit 37ba2511d5aef8034b3f275e581e0c4206823854

+ 10 - 4
pkg/api/dashboard.go

@@ -1,6 +1,7 @@
 package api
 
 import (
+	"regexp"
 	"strings"
 
 	"github.com/torkelo/grafana-pro/pkg/bus"
@@ -49,8 +50,8 @@ func DeleteDashboard(c *middleware.Context) {
 func Search(c *middleware.Context) {
 	queryText := c.Query("q")
 	result := m.SearchResult{
-		Dashboards: []m.DashboardSearchHit{},
-		Tags:       []m.DashboardTagCloudItem{},
+		Dashboards: []*m.DashboardSearchHit{},
+		Tags:       []*m.DashboardTagCloudItem{},
 	}
 
 	if strings.HasPrefix(queryText, "tags!:") {
@@ -63,8 +64,13 @@ func Search(c *middleware.Context) {
 		result.Tags = query.Result
 		result.TagsOnly = true
 	} else {
-		queryText := strings.TrimPrefix(queryText, "title:")
-		query := m.SearchDashboardsQuery{Query: queryText, AccountId: c.GetAccountId()}
+		searchQueryRegEx, _ := regexp.Compile(`(tags:(\w*)\sAND\s)?(?:title:)?(.*)?`)
+		matches := searchQueryRegEx.FindStringSubmatch(queryText)
+		query := m.SearchDashboardsQuery{
+			Title:     matches[3],
+			Tag:       matches[2],
+			AccountId: c.GetAccountId(),
+		}
 		err := bus.Dispatch(&query)
 		if err != nil {
 			c.JsonApiErr(500, "Search failed", err)

+ 23 - 20
pkg/models/dashboards.go

@@ -18,22 +18,20 @@ type Dashboard struct {
 	Slug      string `xorm:"index(IX_AccountIdSlug)"`
 	AccountId int64  `xorm:"index(IX_AccountIdSlug)"`
 
-	Created time.Time `xorm:"CREATED"`
-	Updated time.Time `xorm:"UPDATED"`
+	Created time.Time
+	Updated time.Time
 
 	Title string
-	Tags  []string
 	Data  map[string]interface{}
 }
 
 type SearchResult struct {
-	Dashboards []DashboardSearchHit    `json:"dashboards"`
-	Tags       []DashboardTagCloudItem `json:"tags"`
-	TagsOnly   bool                    `json:"tagsOnly"`
+	Dashboards []*DashboardSearchHit    `json:"dashboards"`
+	Tags       []*DashboardTagCloudItem `json:"tags"`
+	TagsOnly   bool                     `json:"tagsOnly"`
 }
 
 type DashboardSearchHit struct {
-	Id    string   `json:"id"`
 	Title string   `json:"title"`
 	Slug  string   `json:"slug"`
 	Tags  []string `json:"tags"`
@@ -45,15 +43,16 @@ type DashboardTagCloudItem struct {
 }
 
 type SearchDashboardsQuery struct {
-	Query     string
+	Title     string
+	Tag       string
 	AccountId int64
 
-	Result []DashboardSearchHit
+	Result []*DashboardSearchHit
 }
 
 type GetDashboardTagsQuery struct {
 	AccountId int64
-	Result    []DashboardTagCloudItem
+	Result    []*DashboardTagCloudItem
 }
 
 type SaveDashboardCommand struct {
@@ -75,15 +74,6 @@ type GetDashboardQuery struct {
 	Result *Dashboard
 }
 
-func convertToStringArray(arr []interface{}) []string {
-	b := make([]string, len(arr))
-	for i := range arr {
-		b[i] = arr[i].(string)
-	}
-
-	return b
-}
-
 func NewDashboard(title string) *Dashboard {
 	dash := &Dashboard{}
 	dash.Data = make(map[string]interface{})
@@ -93,12 +83,25 @@ func NewDashboard(title string) *Dashboard {
 	return dash
 }
 
+func (dash *Dashboard) GetTags() []string {
+	jsonTags := dash.Data["tags"]
+	if jsonTags == nil {
+		return []string{}
+	}
+
+	arr := jsonTags.([]interface{})
+	b := make([]string, len(arr))
+	for i := range arr {
+		b[i] = arr[i].(string)
+	}
+	return b
+}
+
 func (cmd *SaveDashboardCommand) GetDashboardModel() *Dashboard {
 	dash := &Dashboard{}
 	dash.Data = cmd.Dashboard
 	dash.Title = dash.Data["title"].(string)
 	dash.AccountId = cmd.AccountId
-	dash.Tags = convertToStringArray(dash.Data["tags"].([]interface{}))
 	dash.UpdateSlug()
 
 	if dash.Data["id"] != nil {

+ 61 - 10
pkg/stores/sqlstore/dashboards.go

@@ -35,6 +35,22 @@ func SaveDashboard(cmd *m.SaveDashboardCommand) error {
 			_, err = sess.Id(dash.Id).Update(dash)
 		}
 
+		// delete existing tabs
+		_, err = sess.Exec("DELETE FROM dashboard_tag WHERE dashboard_id=?", dash.Id)
+		if err != nil {
+			return err
+		}
+
+		// insert new tags
+		tags := dash.GetTags()
+		if len(tags) > 0 {
+			tagRows := make([]DashboardTag, len(tags))
+			for _, tag := range tags {
+				tagRows = append(tagRows, DashboardTag{Term: tag, DashboardId: dash.Id})
+			}
+			sess.InsertMulti(&tagRows)
+		}
+
 		cmd.Result = dash
 
 		return err
@@ -55,24 +71,59 @@ func GetDashboard(query *m.GetDashboardQuery) error {
 	return nil
 }
 
+type DashboardSearchProjection struct {
+	Id    int64
+	Title string
+	Slug  string
+	Term  string
+}
+
 func SearchDashboards(query *m.SearchDashboardsQuery) error {
-	titleQuery := "%" + query.Query + "%"
+	titleQuery := "%" + query.Title + "%"
 
-	sess := x.Limit(100, 0).Where("account_id=? AND title LIKE ?", query.AccountId, titleQuery)
-	sess.Table("Dashboard")
+	sess := x.Table("dashboard")
+	sess.Join("LEFT OUTER", "dashboard_tag", "dashboard.id=dashboard_tag.dashboard_id")
+	sess.Where("account_id=? AND title LIKE ?", query.AccountId, titleQuery)
+	sess.Cols("dashboard.id", "dashboard.title", "dashboard.slug", "dashboard_tag.term")
+	sess.Limit(100, 0)
 
-	query.Result = make([]m.DashboardSearchHit, 0)
-	err := sess.Find(&query.Result)
+	if len(query.Tag) > 0 {
+		sess.And("dashboard_tag.term=?", query.Tag)
+	}
+
+	var res []DashboardSearchProjection
+	err := sess.Find(&res)
+	if err != nil {
+		return err
+	}
+
+	query.Result = make([]*m.DashboardSearchHit, 0)
+	hits := make(map[int64]*m.DashboardSearchHit)
+
+	for _, item := range res {
+		hit, exists := hits[item.Id]
+		if !exists {
+			hit = &m.DashboardSearchHit{
+				Title: item.Title,
+				Slug:  item.Slug,
+				Tags:  []string{},
+			}
+			query.Result = append(query.Result, hit)
+			hits[item.Id] = hit
+		}
+		if len(item.Term) > 0 {
+			hit.Tags = append(hit.Tags, item.Term)
+		}
+	}
 
 	return err
 }
 
 func GetDashboardTags(query *m.GetDashboardTagsQuery) error {
-	query.Result = []m.DashboardTagCloudItem{
-		m.DashboardTagCloudItem{Term: "test", Count: 10},
-		m.DashboardTagCloudItem{Term: "prod", Count: 20},
-	}
-	return nil
+	sess := x.Sql("select count() as count, term from dashboard_tag group by term")
+
+	err := sess.Find(&query.Result)
+	return err
 }
 
 func DeleteDashboard(cmd *m.DeleteDashboardCommand) error {

+ 24 - 4
pkg/stores/sqlstore/dashboards_test.go

@@ -51,7 +51,7 @@ func TestDashboardDataAccess(t *testing.T) {
 
 			Convey("Should be able to search for dashboard", func() {
 				query := m.SearchDashboardsQuery{
-					Query:     "%test%",
+					Title:     "%test%",
 					AccountId: 1,
 				}
 
@@ -59,6 +59,20 @@ func TestDashboardDataAccess(t *testing.T) {
 				So(err, ShouldBeNil)
 
 				So(len(query.Result), ShouldEqual, 1)
+				hit := query.Result[0]
+				So(len(hit.Tags), ShouldEqual, 2)
+			})
+
+			Convey("Should be able to search for dashboards using tags", func() {
+				query1 := m.SearchDashboardsQuery{Tag: "webapp", AccountId: 1}
+				query2 := m.SearchDashboardsQuery{Tag: "tagdoesnotexist", AccountId: 1}
+
+				err := SearchDashboards(&query1)
+				err = SearchDashboards(&query2)
+				So(err, ShouldBeNil)
+
+				So(len(query1.Result), ShouldEqual, 1)
+				So(len(query2.Result), ShouldEqual, 0)
 			})
 
 			Convey("Should not be able to save dashboard with same name", func() {
@@ -67,7 +81,7 @@ func TestDashboardDataAccess(t *testing.T) {
 					Dashboard: map[string]interface{}{
 						"id":    nil,
 						"title": "test dash 23",
-						"tags":  make([]interface{}, 0),
+						"tags":  []interface{}{},
 					},
 				}
 
@@ -75,8 +89,14 @@ func TestDashboardDataAccess(t *testing.T) {
 				So(err, ShouldNotBeNil)
 			})
 
-		})
+			Convey("Should be able to get dashboard tags", func() {
+				query := m.GetDashboardTagsQuery{}
 
-	})
+				err := GetDashboardTags(&query)
+				So(err, ShouldBeNil)
 
+				So(len(query.Result), ShouldEqual, 3)
+			})
+		})
+	})
 }

+ 7 - 1
pkg/stores/sqlstore/sqlstore.go

@@ -27,11 +27,17 @@ var (
 	UseSQLite3 bool
 )
 
+type DashboardTag struct {
+	Id          int64
+	DashboardId int64
+	Term        string
+}
+
 func init() {
 	tables = make([]interface{}, 0)
 
 	tables = append(tables, new(m.Account), new(m.Dashboard),
-		new(m.Collaborator), new(m.DataSource))
+		new(m.Collaborator), new(m.DataSource), new(DashboardTag))
 }
 
 func Init() {