فهرست منبع

enable partial tag matches for annotations

bergquist 7 سال پیش
والد
کامیت
1638c6bea1

+ 11 - 10
pkg/api/annotations.go

@@ -14,16 +14,17 @@ import (
 func GetAnnotations(c *m.ReqContext) Response {
 func GetAnnotations(c *m.ReqContext) Response {
 
 
 	query := &annotations.ItemQuery{
 	query := &annotations.ItemQuery{
-		From:        c.QueryInt64("from"),
-		To:          c.QueryInt64("to"),
-		OrgId:       c.OrgId,
-		UserId:      c.QueryInt64("userId"),
-		AlertId:     c.QueryInt64("alertId"),
-		DashboardId: c.QueryInt64("dashboardId"),
-		PanelId:     c.QueryInt64("panelId"),
-		Limit:       c.QueryInt64("limit"),
-		Tags:        c.QueryStrings("tags"),
-		Type:        c.Query("type"),
+		From:         c.QueryInt64("from"),
+		To:           c.QueryInt64("to"),
+		OrgId:        c.OrgId,
+		UserId:       c.QueryInt64("userId"),
+		AlertId:      c.QueryInt64("alertId"),
+		DashboardId:  c.QueryInt64("dashboardId"),
+		PanelId:      c.QueryInt64("panelId"),
+		Limit:        c.QueryInt64("limit"),
+		Tags:         c.QueryStrings("tags"),
+		Type:         c.Query("type"),
+		PartialMatch: c.QueryBool("partialMatch"),
 	}
 	}
 
 
 	repo := annotations.GetRepository()
 	repo := annotations.GetRepository()

+ 1 - 0
pkg/services/annotations/annotations.go

@@ -21,6 +21,7 @@ type ItemQuery struct {
 	RegionId     int64    `json:"regionId"`
 	RegionId     int64    `json:"regionId"`
 	Tags         []string `json:"tags"`
 	Tags         []string `json:"tags"`
 	Type         string   `json:"type"`
 	Type         string   `json:"type"`
+	PartialMatch bool     `json:"partialMatch"`
 
 
 	Limit int64 `json:"limit"`
 	Limit int64 `json:"limit"`
 }
 }

+ 6 - 1
pkg/services/sqlstore/annotation.go

@@ -211,7 +211,12 @@ func (r *SqlAnnotationRepo) Find(query *annotations.ItemQuery) ([]*annotations.I
             )
             )
       `, strings.Join(keyValueFilters, " OR "))
       `, strings.Join(keyValueFilters, " OR "))
 
 
-			sql.WriteString(fmt.Sprintf(" AND (%s) = %d ", tagsSubQuery, len(tags)))
+			if query.PartialMatch {
+				sql.WriteString(fmt.Sprintf(" AND (%s) > 0 ", tagsSubQuery))
+			} else {
+				sql.WriteString(fmt.Sprintf(" AND (%s) = %d ", tagsSubQuery, len(tags)))
+			}
+
 		}
 		}
 	}
 	}
 
 

+ 39 - 2
pkg/services/sqlstore/annotation_test.go

@@ -78,7 +78,31 @@ func TestAnnotations(t *testing.T) {
 			So(err, ShouldBeNil)
 			So(err, ShouldBeNil)
 			So(annotation2.Id, ShouldBeGreaterThan, 0)
 			So(annotation2.Id, ShouldBeGreaterThan, 0)
 
 
-			Convey("Can query for annotation", func() {
+			globalAnnotation1 := &annotations.Item{
+				OrgId:  1,
+				UserId: 1,
+				Text:   "deploy",
+				Type:   "",
+				Epoch:  15,
+				Tags:   []string{"deploy"},
+			}
+			err = repo.Save(globalAnnotation1)
+			So(err, ShouldBeNil)
+			So(globalAnnotation1.Id, ShouldBeGreaterThan, 0)
+
+			globalAnnotation2 := &annotations.Item{
+				OrgId:  1,
+				UserId: 1,
+				Text:   "rollback",
+				Type:   "",
+				Epoch:  17,
+				Tags:   []string{"rollback"},
+			}
+			err = repo.Save(globalAnnotation2)
+			So(err, ShouldBeNil)
+			So(globalAnnotation2.Id, ShouldBeGreaterThan, 0)
+
+			Convey("Can query for annotation by dashboard id", func() {
 				items, err := repo.Find(&annotations.ItemQuery{
 				items, err := repo.Find(&annotations.ItemQuery{
 					OrgId:       1,
 					OrgId:       1,
 					DashboardId: 1,
 					DashboardId: 1,
@@ -165,7 +189,7 @@ func TestAnnotations(t *testing.T) {
 					OrgId:       1,
 					OrgId:       1,
 					DashboardId: 1,
 					DashboardId: 1,
 					From:        1,
 					From:        1,
-					To:          15,
+					To:          15, //this will exclude the second test annotation
 					Tags:        []string{"outage", "error"},
 					Tags:        []string{"outage", "error"},
 				})
 				})
 
 
@@ -173,6 +197,19 @@ func TestAnnotations(t *testing.T) {
 				So(items, ShouldHaveLength, 1)
 				So(items, ShouldHaveLength, 1)
 			})
 			})
 
 
+			Convey("Should find two annotations using partial match", func() {
+				items, err := repo.Find(&annotations.ItemQuery{
+					OrgId:        1,
+					From:         1,
+					To:           25,
+					PartialMatch: true,
+					Tags:         []string{"rollback", "deploy"},
+				})
+
+				So(err, ShouldBeNil)
+				So(items, ShouldHaveLength, 2)
+			})
+
 			Convey("Should find one when all key value tag filters does match", func() {
 			Convey("Should find one when all key value tag filters does match", func() {
 				items, err := repo.Find(&annotations.ItemQuery{
 				items, err := repo.Find(&annotations.ItemQuery{
 					OrgId:       1,
 					OrgId:       1,

+ 1 - 0
public/app/plugins/datasource/grafana/datasource.ts

@@ -40,6 +40,7 @@ class GrafanaDatasource {
       to: options.range.to.valueOf(),
       to: options.range.to.valueOf(),
       limit: options.annotation.limit,
       limit: options.annotation.limit,
       tags: options.annotation.tags,
       tags: options.annotation.tags,
+      partialMatch: options.annotation.partialMatch,
     };
     };
 
 
     if (options.annotation.type === 'dashboard') {
     if (options.annotation.type === 'dashboard') {

+ 18 - 9
public/app/plugins/datasource/grafana/partials/annotations.editor.html

@@ -2,7 +2,7 @@
 <div class="gf-form-group">
 <div class="gf-form-group">
 	<div class="gf-form-inline">
 	<div class="gf-form-inline">
 		<div class="gf-form">
 		<div class="gf-form">
-			<span class="gf-form-label width-8">
+			<span class="gf-form-label width-9">
 				Filter by
 				Filter by
 				<info-popover mode="right-normal">
 				<info-popover mode="right-normal">
 					<ul>
 					<ul>
@@ -11,18 +11,11 @@
 					</ul>
 					</ul>
 				</info-popover>
 				</info-popover>
 			</span>
 			</span>
-			<div class="gf-form-select-wrapper width-9">
+			<div class="gf-form-select-wrapper width-8">
 				<select class="gf-form-input" ng-model="ctrl.annotation.type" ng-options="f.value as f.text for f in ctrl.types">
 				<select class="gf-form-input" ng-model="ctrl.annotation.type" ng-options="f.value as f.text for f in ctrl.types">
 				</select>
 				</select>
 			</div>
 			</div>
 		</div>
 		</div>
-
-		<div class="gf-form" ng-if="ctrl.annotation.type === 'tags'">
-			<span class="gf-form-label">Tags</span>
-			<bootstrap-tagsinput ng-model="ctrl.annotation.tags" tagclass="label label-tag" placeholder="add tags">
-			</bootstrap-tagsinput>
-		</div>
-
 		<div class="gf-form">
 		<div class="gf-form">
 			<span class="gf-form-label">Max limit</span>
 			<span class="gf-form-label">Max limit</span>
 			<div class="gf-form-select-wrapper">
 			<div class="gf-form-select-wrapper">
@@ -31,6 +24,22 @@
 			</div>
 			</div>
 		</div>
 		</div>
 	</div>
 	</div>
+	<div class="gf-form-inline">
+		<div class="gf-form" ng-if="ctrl.annotation.type === 'tags'">
+			<gf-form-switch 
+				class="gf-form" 
+				label="Partial match" 
+				label-class="width-9" 
+				checked="ctrl.annotation.partialMatch" 
+				on-change="ctrl.refresh()"
+				tooltip="By default Grafana will only show annotation that matches all tags in the query. Enabling this will make Grafana return any annotation with the tags you specify."></gf-form-switch>
+		</div>
+		<div class="gf-form" ng-if="ctrl.annotation.type === 'tags'">
+			<span class="gf-form-label">Tags</span>
+			<bootstrap-tagsinput ng-model="ctrl.annotation.tags" tagclass="label label-tag" placeholder="add tags">
+			</bootstrap-tagsinput>
+		</div>
+	</div>
 </div>
 </div>