diff --git a/CHANGELOG.md b/CHANGELOG.md
index 0b25f025d1d..eb1cb5c55ab 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -10,6 +10,7 @@
* **InfluxDB**: Add spread function, closes [#5211](https://github.com/grafana/grafana/issues/5211)
* **Scripts**: Use restart instead of start for deb package script, closes [#5282](https://github.com/grafana/grafana/pull/5282)
* **Logging**: Moved to structured logging lib, and moved to component specific level filters via config file, closes [#4590](https://github.com/grafana/grafana/issues/4590)
+* **Search**: Add search limit query parameter, closes [#5292](https://github.com/grafana/grafana/pull/5292)
## Breaking changes
* **Logging** : Changed default logging output format (now structured into message, and key value pairs, with logger key acting as component). You can also no change in config to json log ouput.
diff --git a/pkg/services/search/handlers.go b/pkg/services/search/handlers.go
index a4905d6fa58..a8344ba05a5 100644
--- a/pkg/services/search/handlers.go
+++ b/pkg/services/search/handlers.go
@@ -44,6 +44,7 @@ func searchHandler(query *Query) error {
IsStarred: query.IsStarred,
OrgId: query.OrgId,
DashboardIds: query.DashboardIds,
+ Limit: query.Limit,
}
if err := bus.Dispatch(&dashQuery); err != nil {
diff --git a/pkg/services/search/models.go b/pkg/services/search/models.go
index 159637013f5..ada3e1ccdfa 100644
--- a/pkg/services/search/models.go
+++ b/pkg/services/search/models.go
@@ -42,6 +42,7 @@ type FindPersistedDashboardsQuery struct {
UserId int64
IsStarred bool
DashboardIds []int
+ Limit int
Result HitList
}
diff --git a/pkg/services/sqlstore/dashboard.go b/pkg/services/sqlstore/dashboard.go
index fbf245a951b..3fac1672dff 100644
--- a/pkg/services/sqlstore/dashboard.go
+++ b/pkg/services/sqlstore/dashboard.go
@@ -123,6 +123,11 @@ type DashboardSearchProjection struct {
}
func SearchDashboards(query *search.FindPersistedDashboardsQuery) error {
+ limit := query.Limit
+ if limit == 0 {
+ limit = 1000
+ }
+
var sql bytes.Buffer
params := make([]interface{}, 0)
@@ -165,7 +170,8 @@ func SearchDashboards(query *search.FindPersistedDashboardsQuery) error {
params = append(params, "%"+query.Title+"%")
}
- sql.WriteString(fmt.Sprintf(" ORDER BY dashboard.title ASC LIMIT 1000"))
+ sql.WriteString(fmt.Sprintf(" ORDER BY dashboard.title ASC LIMIT ?"))
+ params = append(params, limit)
var res []DashboardSearchProjection
diff --git a/public/app/core/components/query_part/query_part.ts b/public/app/core/components/query_part/query_part.ts
new file mode 100644
index 00000000000..90724f65d2d
--- /dev/null
+++ b/public/app/core/components/query_part/query_part.ts
@@ -0,0 +1,123 @@
+///
+
+import _ from 'lodash';
+
+export class QueryPartDef {
+ type: string;
+ params: any[];
+ defaultParams: any[];
+ renderer: any;
+ category: any;
+ addStrategy: any;
+
+ constructor(options: any) {
+ this.type = options.type;
+ this.params = options.params;
+ this.defaultParams = options.defaultParams;
+ this.renderer = options.renderer;
+ this.category = options.category;
+ this.addStrategy = options.addStrategy;
+ }
+}
+
+export class QueryPart {
+ part: any;
+ def: QueryPartDef;
+ params: any[];
+ text: string;
+
+ constructor(part: any, def: any) {
+ this.part = part;
+ this.def = def;
+ if (!this.def) {
+ throw {message: 'Could not find query part ' + part.type};
+ }
+
+ part.params = part.params || _.clone(this.def.defaultParams);
+ this.params = part.params;
+ this.updateText();
+ }
+
+ render(innerExpr: string) {
+ return this.def.renderer(this, innerExpr);
+ }
+
+ hasMultipleParamsInString (strValue, index) {
+ if (strValue.indexOf(',') === -1) {
+ return false;
+ }
+
+ return this.def.params[index + 1] && this.def.params[index + 1].optional;
+ }
+
+ updateParam (strValue, index) {
+ // handle optional parameters
+ // if string contains ',' and next param is optional, split and update both
+ if (this.hasMultipleParamsInString(strValue, index)) {
+ _.each(strValue.split(','), function(partVal: string, idx) {
+ this.updateParam(partVal.trim(), idx);
+ }, this);
+ return;
+ }
+
+ if (strValue === '' && this.def.params[index].optional) {
+ this.params.splice(index, 1);
+ } else {
+ this.params[index] = strValue;
+ }
+
+ this.part.params = this.params;
+ this.updateText();
+ }
+
+ updateText() {
+ if (this.params.length === 0) {
+ this.text = this.def.type + '()';
+ return;
+ }
+
+ var text = this.def.type + '(';
+ text += this.params.join(', ');
+ text += ')';
+ this.text = text;
+ }
+}
+
+export function functionRenderer(part, innerExpr) {
+ var str = part.def.type + '(';
+ var parameters = _.map(part.params, (value, index) => {
+ var paramType = part.def.params[index];
+ if (paramType.type === 'time') {
+ if (value === 'auto') {
+ value = '$interval';
+ }
+ }
+ if (paramType.quote === 'single') {
+ return "'" + value + "'";
+ } else if (paramType.quote === 'double') {
+ return '"' + value + '"';
+ }
+
+ return value;
+ });
+
+ if (innerExpr) {
+ parameters.unshift(innerExpr);
+ }
+ return str + parameters.join(', ') + ')';
+}
+
+
+export function suffixRenderer(part, innerExpr) {
+ return innerExpr + ' ' + part.params[0];
+}
+
+export function identityRenderer(part, innerExpr) {
+ return part.params[0];
+}
+
+export function quotedIdentityRenderer(part, innerExpr) {
+ return '"' + part.params[0] + '"';
+}
+
+
diff --git a/public/app/core/components/query_part/query_part_editor.ts b/public/app/core/components/query_part/query_part_editor.ts
new file mode 100644
index 00000000000..f9122ee283b
--- /dev/null
+++ b/public/app/core/components/query_part/query_part_editor.ts
@@ -0,0 +1,183 @@
+///
+
+import _ from 'lodash';
+import $ from 'jquery';
+import coreModule from 'app/core/core_module';
+
+var template = `
+
diff --git a/public/app/plugins/datasource/influxdb/partials/query_part.html b/public/app/plugins/datasource/influxdb/partials/query_part.html
deleted file mode 100644
index 478edfe5c29..00000000000
--- a/public/app/plugins/datasource/influxdb/partials/query_part.html
+++ /dev/null
@@ -1,5 +0,0 @@
-
-
-
-
-
{{part.def.type}}()
diff --git a/public/app/plugins/datasource/influxdb/query_ctrl.ts b/public/app/plugins/datasource/influxdb/query_ctrl.ts
index 8d6a03fc4a1..69895e53c25 100644
--- a/public/app/plugins/datasource/influxdb/query_ctrl.ts
+++ b/public/app/plugins/datasource/influxdb/query_ctrl.ts
@@ -1,8 +1,5 @@
///
-import './query_part_editor';
-import './query_part_editor';
-
import angular from 'angular';
import _ from 'lodash';
import InfluxQueryBuilder from './query_builder';
diff --git a/public/app/plugins/datasource/influxdb/query_part.ts b/public/app/plugins/datasource/influxdb/query_part.ts
index 130174c3084..0081481437d 100644
--- a/public/app/plugins/datasource/influxdb/query_part.ts
+++ b/public/app/plugins/datasource/influxdb/query_part.ts
@@ -1,6 +1,14 @@
///
import _ from 'lodash';
+import {
+ QueryPartDef,
+ QueryPart,
+ functionRenderer,
+ suffixRenderer,
+ identityRenderer,
+ quotedIdentityRenderer,
+} from 'app/core/components/query_part/query_part';
var index = [];
var categories = {
@@ -12,71 +20,26 @@ var categories = {
Fields: [],
};
+function createPart(part): any {
+ var def = index[part.type];
+ if (!def) {
+ throw {message: 'Could not find query part ' + part.type};
+ }
+
+ return new QueryPart(part, def);
+};
+
+function register(options: any) {
+ index[options.type] = new QueryPartDef(options);
+ options.category.push(index[options.type]);
+}
+
var groupByTimeFunctions = [];
-class QueryPartDef {
- type: string;
- params: any[];
- defaultParams: any[];
- renderer: any;
- category: any;
- addStrategy: any;
-
- constructor(options: any) {
- this.type = options.type;
- this.params = options.params;
- this.defaultParams = options.defaultParams;
- this.renderer = options.renderer;
- this.category = options.category;
- this.addStrategy = options.addStrategy;
- }
-
- static register(options: any) {
- index[options.type] = new QueryPartDef(options);
- options.category.push(index[options.type]);
- }
-}
-
-function functionRenderer(part, innerExpr) {
- var str = part.def.type + '(';
- var parameters = _.map(part.params, (value, index) => {
- var paramType = part.def.params[index];
- if (paramType.type === 'time') {
- if (value === 'auto') {
- value = '$interval';
- }
- }
- if (paramType.quote === 'single') {
- return "'" + value + "'";
- } else if (paramType.quote === 'double') {
- return '"' + value + '"';
- }
-
- return value;
- });
-
- if (innerExpr) {
- parameters.unshift(innerExpr);
- }
- return str + parameters.join(', ') + ')';
-}
-
function aliasRenderer(part, innerExpr) {
return innerExpr + ' AS ' + '"' + part.params[0] + '"';
}
-function suffixRenderer(part, innerExpr) {
- return innerExpr + ' ' + part.params[0];
-}
-
-function identityRenderer(part, innerExpr) {
- return part.params[0];
-}
-
-function quotedIdentityRenderer(part, innerExpr) {
- return '"' + part.params[0] + '"';
-}
-
function fieldRenderer(part, innerExpr) {
if (part.params[0] === '*') {
return '*';
@@ -149,13 +112,13 @@ function addAliasStrategy(selectParts, partModel) {
function addFieldStrategy(selectParts, partModel, query) {
// copy all parts
var parts = _.map(selectParts, function(part: any) {
- return new QueryPart({type: part.def.type, params: _.clone(part.params)});
+ return createPart({type: part.def.type, params: _.clone(part.params)});
});
query.selectModels.push(parts);
}
-QueryPartDef.register({
+register({
type: 'field',
addStrategy: addFieldStrategy,
category: categories.Fields,
@@ -165,7 +128,7 @@ QueryPartDef.register({
});
// Aggregations
-QueryPartDef.register({
+register({
type: 'count',
addStrategy: replaceAggregationAddStrategy,
category: categories.Aggregations,
@@ -174,7 +137,7 @@ QueryPartDef.register({
renderer: functionRenderer,
});
-QueryPartDef.register({
+register({
type: 'distinct',
addStrategy: replaceAggregationAddStrategy,
category: categories.Aggregations,
@@ -183,7 +146,7 @@ QueryPartDef.register({
renderer: functionRenderer,
});
-QueryPartDef.register({
+register({
type: 'integral',
addStrategy: replaceAggregationAddStrategy,
category: categories.Aggregations,
@@ -192,7 +155,7 @@ QueryPartDef.register({
renderer: functionRenderer,
});
-QueryPartDef.register({
+register({
type: 'mean',
addStrategy: replaceAggregationAddStrategy,
category: categories.Aggregations,
@@ -201,7 +164,7 @@ QueryPartDef.register({
renderer: functionRenderer,
});
-QueryPartDef.register({
+register({
type: 'median',
addStrategy: replaceAggregationAddStrategy,
category: categories.Aggregations,
@@ -210,7 +173,7 @@ QueryPartDef.register({
renderer: functionRenderer,
});
-QueryPartDef.register({
+register({
type: 'sum',
addStrategy: replaceAggregationAddStrategy,
category: categories.Aggregations,
@@ -221,7 +184,7 @@ QueryPartDef.register({
// transformations
-QueryPartDef.register({
+register({
type: 'derivative',
addStrategy: addTransformationStrategy,
category: categories.Transformations,
@@ -230,7 +193,7 @@ QueryPartDef.register({
renderer: functionRenderer,
});
-QueryPartDef.register({
+register({
type: 'spread',
addStrategy: addTransformationStrategy,
category: categories.Transformations,
@@ -239,7 +202,7 @@ QueryPartDef.register({
renderer: functionRenderer,
});
-QueryPartDef.register({
+register({
type: 'non_negative_derivative',
addStrategy: addTransformationStrategy,
category: categories.Transformations,
@@ -248,7 +211,7 @@ QueryPartDef.register({
renderer: functionRenderer,
});
-QueryPartDef.register({
+register({
type: 'difference',
addStrategy: addTransformationStrategy,
category: categories.Transformations,
@@ -257,7 +220,7 @@ QueryPartDef.register({
renderer: functionRenderer,
});
-QueryPartDef.register({
+register({
type: 'moving_average',
addStrategy: addTransformationStrategy,
category: categories.Transformations,
@@ -266,7 +229,7 @@ QueryPartDef.register({
renderer: functionRenderer,
});
-QueryPartDef.register({
+register({
type: 'stddev',
addStrategy: addTransformationStrategy,
category: categories.Transformations,
@@ -275,7 +238,7 @@ QueryPartDef.register({
renderer: functionRenderer,
});
-QueryPartDef.register({
+register({
type: 'time',
category: groupByTimeFunctions,
params: [{ name: "interval", type: "time", options: ['auto', '1s', '10s', '1m', '5m', '10m', '15m', '1h'] }],
@@ -283,7 +246,7 @@ QueryPartDef.register({
renderer: functionRenderer,
});
-QueryPartDef.register({
+register({
type: 'fill',
category: groupByTimeFunctions,
params: [{ name: "fill", type: "string", options: ['none', 'null', '0', 'previous'] }],
@@ -292,7 +255,7 @@ QueryPartDef.register({
});
// Selectors
-QueryPartDef.register({
+register({
type: 'bottom',
addStrategy: replaceAggregationAddStrategy,
category: categories.Selectors,
@@ -301,7 +264,7 @@ QueryPartDef.register({
renderer: functionRenderer,
});
-QueryPartDef.register({
+register({
type: 'first',
addStrategy: replaceAggregationAddStrategy,
category: categories.Selectors,
@@ -310,7 +273,7 @@ QueryPartDef.register({
renderer: functionRenderer,
});
-QueryPartDef.register({
+register({
type: 'last',
addStrategy: replaceAggregationAddStrategy,
category: categories.Selectors,
@@ -319,7 +282,7 @@ QueryPartDef.register({
renderer: functionRenderer,
});
-QueryPartDef.register({
+register({
type: 'max',
addStrategy: replaceAggregationAddStrategy,
category: categories.Selectors,
@@ -328,7 +291,7 @@ QueryPartDef.register({
renderer: functionRenderer,
});
-QueryPartDef.register({
+register({
type: 'min',
addStrategy: replaceAggregationAddStrategy,
category: categories.Selectors,
@@ -337,7 +300,7 @@ QueryPartDef.register({
renderer: functionRenderer,
});
-QueryPartDef.register({
+register({
type: 'percentile',
addStrategy: replaceAggregationAddStrategy,
category: categories.Selectors,
@@ -346,7 +309,7 @@ QueryPartDef.register({
renderer: functionRenderer,
});
-QueryPartDef.register({
+register({
type: 'top',
addStrategy: replaceAggregationAddStrategy,
category: categories.Selectors,
@@ -355,7 +318,7 @@ QueryPartDef.register({
renderer: functionRenderer,
});
-QueryPartDef.register({
+register({
type: 'tag',
category: groupByTimeFunctions,
params: [{name: 'tag', type: 'string', dynamicLookup: true}],
@@ -363,7 +326,7 @@ QueryPartDef.register({
renderer: fieldRenderer,
});
-QueryPartDef.register({
+register({
type: 'math',
addStrategy: addMathStrategy,
category: categories.Math,
@@ -372,7 +335,7 @@ QueryPartDef.register({
renderer: suffixRenderer,
});
-QueryPartDef.register({
+register({
type: 'alias',
addStrategy: addAliasStrategy,
category: categories.Aliasing,
@@ -382,74 +345,9 @@ QueryPartDef.register({
renderer: aliasRenderer,
});
-class QueryPart {
- part: any;
- def: QueryPartDef;
- params: any[];
- text: string;
-
- constructor(part: any) {
- this.part = part;
- this.def = index[part.type];
- if (!this.def) {
- throw {message: 'Could not find query part ' + part.type};
- }
-
- part.params = part.params || _.clone(this.def.defaultParams);
- this.params = part.params;
- this.updateText();
- }
-
- render(innerExpr: string) {
- return this.def.renderer(this, innerExpr);
- }
-
- hasMultipleParamsInString (strValue, index) {
- if (strValue.indexOf(',') === -1) {
- return false;
- }
-
- return this.def.params[index + 1] && this.def.params[index + 1].optional;
- }
-
- updateParam (strValue, index) {
- // handle optional parameters
- // if string contains ',' and next param is optional, split and update both
- if (this.hasMultipleParamsInString(strValue, index)) {
- _.each(strValue.split(','), function(partVal: string, idx) {
- this.updateParam(partVal.trim(), idx);
- }, this);
- return;
- }
-
- if (strValue === '' && this.def.params[index].optional) {
- this.params.splice(index, 1);
- } else {
- this.params[index] = strValue;
- }
-
- this.part.params = this.params;
- this.updateText();
- }
-
- updateText() {
- if (this.params.length === 0) {
- this.text = this.def.type + '()';
- return;
- }
-
- var text = this.def.type + '(';
- text += this.params.join(', ');
- text += ')';
- this.text = text;
- }
-}
export default {
- create: function(part): any {
- return new QueryPart(part);
- },
-
+ create: createPart,
getCategories: function() {
return categories;
}
diff --git a/public/app/plugins/datasource/influxdb/query_part_editor.js b/public/app/plugins/datasource/influxdb/query_part_editor.js
deleted file mode 100644
index 4e044eca304..00000000000
--- a/public/app/plugins/datasource/influxdb/query_part_editor.js
+++ /dev/null
@@ -1,178 +0,0 @@
-define([
- 'angular',
- 'lodash',
- 'jquery',
-],
-function (angular, _, $) {
- 'use strict';
-
- angular
- .module('grafana.directives')
- .directive('influxQueryPartEditor', function($compile, templateSrv) {
-
- var paramTemplate = '
';
- return {
- restrict: 'E',
- templateUrl: 'public/app/plugins/datasource/influxdb/partials/query_part.html',
- scope: {
- part: "=",
- removeAction: "&",
- partUpdated: "&",
- getOptions: "&",
- },
- link: function postLink($scope, elem) {
- var part = $scope.part;
- var partDef = part.def;
- var $paramsContainer = elem.find('.query-part-parameters');
- var $controlsContainer = elem.find('.tight-form-func-controls');
-
- function clickFuncParam(paramIndex) {
- /*jshint validthis:true */
- var $link = $(this);
- var $input = $link.next();
-
- $input.val(part.params[paramIndex]);
- $input.css('width', ($link.width() + 16) + 'px');
-
- $link.hide();
- $input.show();
- $input.focus();
- $input.select();
-
- var typeahead = $input.data('typeahead');
- if (typeahead) {
- $input.val('');
- typeahead.lookup();
- }
- }
-
- function inputBlur(paramIndex) {
- /*jshint validthis:true */
- var $input = $(this);
- var $link = $input.prev();
- var newValue = $input.val();
-
- if (newValue !== '' || part.def.params[paramIndex].optional) {
- $link.html(templateSrv.highlightVariablesAsHtml(newValue));
-
- part.updateParam($input.val(), paramIndex);
- $scope.$apply($scope.partUpdated);
- }
-
- $input.hide();
- $link.show();
- }
-
- function inputKeyPress(paramIndex, e) {
- /*jshint validthis:true */
- if(e.which === 13) {
- inputBlur.call(this, paramIndex);
- }
- }
-
- function inputKeyDown() {
- /*jshint validthis:true */
- this.style.width = (3 + this.value.length) * 8 + 'px';
- }
-
- function addTypeahead($input, param, paramIndex) {
- if (!param.options && !param.dynamicLookup) {
- return;
- }
-
- var typeaheadSource = function (query, callback) {
- if (param.options) { return param.options; }
-
- $scope.$apply(function() {
- $scope.getOptions().then(function(result) {
- var dynamicOptions = _.map(result, function(op) { return op.value; });
- callback(dynamicOptions);
- });
- });
- };
-
- $input.attr('data-provide', 'typeahead');
- var options = param.options;
- if (param.type === 'int') {
- options = _.map(options, function(val) { return val.toString(); });
- }
-
- $input.typeahead({
- source: typeaheadSource,
- minLength: 0,
- items: 1000,
- updater: function (value) {
- setTimeout(function() {
- inputBlur.call($input[0], paramIndex);
- }, 0);
- return value;
- }
- });
-
- var typeahead = $input.data('typeahead');
- typeahead.lookup = function () {
- this.query = this.$element.val() || '';
- var items = this.source(this.query, $.proxy(this.process, this));
- return items ? this.process(items) : items;
- };
- }
-
- $scope.toggleControls = function() {
- var targetDiv = elem.closest('.tight-form');
-
- if (elem.hasClass('show-function-controls')) {
- elem.removeClass('show-function-controls');
- targetDiv.removeClass('has-open-function');
- $controlsContainer.hide();
- return;
- }
-
- elem.addClass('show-function-controls');
- targetDiv.addClass('has-open-function');
- $controlsContainer.show();
- };
-
- $scope.removeActionInternal = function() {
- $scope.toggleControls();
- $scope.removeAction();
- };
-
- function addElementsAndCompile() {
- _.each(partDef.params, function(param, index) {
- if (param.optional && part.params.length <= index) {
- return;
- }
-
- if (index > 0) {
- $('
, ').appendTo($paramsContainer);
- }
-
- var paramValue = templateSrv.highlightVariablesAsHtml(part.params[index]);
- var $paramLink = $('
' + paramValue + '');
- var $input = $(paramTemplate);
-
- $paramLink.appendTo($paramsContainer);
- $input.appendTo($paramsContainer);
-
- $input.blur(_.partial(inputBlur, index));
- $input.keyup(inputKeyDown);
- $input.keypress(_.partial(inputKeyPress, index));
- $paramLink.click(_.partial(clickFuncParam, index));
-
- addTypeahead($input, param, index);
- });
- }
-
- function relink() {
- $paramsContainer.empty();
- addElementsAndCompile();
- }
-
- relink();
- }
- };
-
- });
-
-});
diff --git a/public/app/plugins/datasource/opentsdb/datasource.js b/public/app/plugins/datasource/opentsdb/datasource.js
index b683aa02068..1d3db75658c 100644
--- a/public/app/plugins/datasource/opentsdb/datasource.js
+++ b/public/app/plugins/datasource/opentsdb/datasource.js
@@ -403,10 +403,7 @@ function (angular, _, dateMath) {
} else {
return _.findIndex(options.targets, function(target) {
if (target.filters && target.filters.length > 0) {
- return target.metric === metricData.metric &&
- _.all(target.filters, function(filter) {
- return filter.tagk === interpolatedTagValue === "*";
- });
+ return target.metric === metricData.metric;
} else {
return target.metric === metricData.metric &&
_.all(target.tags, function(tagV, tagK) {