Commit 448a8b8d by Torkel Ödegaard

Major refactorings around searching, moved to seperate package, trying to move…

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
parent c8146e75
......@@ -216,7 +216,6 @@ exchange = grafana_events
#################################### Dashboard JSON files ##########################
[dashboards.json]
enabled = false
path = dashboards
orgs = *
path = /var/lib/grafana/dashboards
......@@ -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
......@@ -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"
......
......@@ -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"
)
......
......@@ -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) {
......
......@@ -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
}
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
}
......@@ -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
......
......@@ -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,
......
......@@ -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()
......
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
}
......@@ -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)
......
......@@ -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)
......
......@@ -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');
};
......
......@@ -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
......
<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>
......
......@@ -62,7 +62,7 @@ function (angular, app, _, config, PanelMeta) {
}
return backendSrv.search(params).then(function(result) {
$scope.dashList = result.dashboards;
$scope.dashList = result;
});
};
......
......@@ -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">
......
......@@ -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 {
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment