mirror of
https://github.com/elastic/kibana.git
synced 2025-04-23 17:28:26 -04:00
Change default query to match_all (#13047)
* Change default query to match_all * Move files around and add tests * Fix dashboard isQueryFilter * Add secondary describe to separate tests * Fix dashboard test * Change default discover query * Fix functional test
This commit is contained in:
parent
a3768c0d4e
commit
858f6fbbd3
12 changed files with 128 additions and 222 deletions
|
@ -1,4 +1,5 @@
|
|||
import _ from 'lodash';
|
||||
import { getDefaultQuery } from 'ui/parse_query';
|
||||
|
||||
/**
|
||||
* @typedef {Object} QueryFilter
|
||||
|
@ -14,7 +15,7 @@ export class FilterUtils {
|
|||
* (e.g. goes in the query input bar), false otherwise (e.g. is in the filter bar).
|
||||
*/
|
||||
static isQueryFilter(filter) {
|
||||
return filter.query && filter.query.query_string && !filter.meta;
|
||||
return filter.query && !filter.meta;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -33,10 +34,9 @@ export class FilterUtils {
|
|||
* @returns {QueryFilter}
|
||||
*/
|
||||
static getQueryFilterForDashboard(dashboard) {
|
||||
const defaultQueryFilter = { query_string: { query: '*' } };
|
||||
const dashboardFilters = this.getDashboardFilters(dashboard);
|
||||
const dashboardQueryFilter = _.find(dashboardFilters, this.isQueryFilter);
|
||||
return dashboardQueryFilter ? dashboardQueryFilter.query : defaultQueryFilter;
|
||||
return dashboardQueryFilter ? dashboardQueryFilter.query : getDefaultQuery();
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -29,6 +29,7 @@ import indexTemplate from 'plugins/kibana/discover/index.html';
|
|||
import { StateProvider } from 'ui/state_management/state';
|
||||
import { documentationLinks } from 'ui/documentation_links/documentation_links';
|
||||
import { SavedObjectsClientProvider } from 'ui/saved_objects';
|
||||
import { getDefaultQuery } from 'ui/parse_query';
|
||||
|
||||
const app = uiModules.get('apps/discover', [
|
||||
'kibana/notify',
|
||||
|
@ -168,7 +169,7 @@ function discoverController($scope, config, courier, $route, $window, Notifier,
|
|||
|
||||
function getStateDefaults() {
|
||||
return {
|
||||
query: $scope.searchSource.get('query') || '',
|
||||
query: $scope.searchSource.get('query') || getDefaultQuery(),
|
||||
sort: getSort.array(savedSearch.sort, $scope.indexPattern),
|
||||
columns: savedSearch.columns.length > 0 ? savedSearch.columns : config.get('defaultColumns').slice(),
|
||||
index: $scope.indexPattern.id,
|
||||
|
|
|
@ -20,6 +20,7 @@ import editorTemplate from 'plugins/kibana/visualize/editor/editor.html';
|
|||
import { DashboardConstants } from 'plugins/kibana/dashboard/dashboard_constants';
|
||||
import { VisualizeConstants } from '../visualize_constants';
|
||||
import { documentationLinks } from 'ui/documentation_links/documentation_links';
|
||||
import { getDefaultQuery } from 'ui/parse_query';
|
||||
|
||||
uiRoutes
|
||||
.when(VisualizeConstants.CREATE_PATH, {
|
||||
|
@ -135,7 +136,7 @@ function VisEditor($rootScope, $scope, $route, timefilter, AppState, $window, kb
|
|||
const stateDefaults = {
|
||||
uiState: savedVis.uiStateJSON ? JSON.parse(savedVis.uiStateJSON) : {},
|
||||
linked: !!savedVis.savedSearchId,
|
||||
query: searchSource.getOwn('query') || { query_string: { query: '*' } },
|
||||
query: searchSource.getOwn('query') || getDefaultQuery(),
|
||||
filters: searchSource.getOwn('filter') || [],
|
||||
vis: savedVisState
|
||||
};
|
||||
|
|
|
@ -1,9 +1,8 @@
|
|||
import _ from 'lodash';
|
||||
import angular from 'angular';
|
||||
|
||||
import { AggTypesBucketsBucketAggTypeProvider } from 'ui/agg_types/buckets/_bucket_agg_type';
|
||||
import { AggTypesBucketsCreateFilterFiltersProvider } from 'ui/agg_types/buckets/create_filter/filters';
|
||||
import { DecorateQueryProvider } from 'ui/courier/data_source/_decorate_query';
|
||||
import { formatQuery } from 'ui/parse_query';
|
||||
import filtersTemplate from 'ui/agg_types/controls/filters.html';
|
||||
|
||||
export function AggTypesBucketsFiltersProvider(Private, Notifier) {
|
||||
|
@ -35,7 +34,7 @@ export function AggTypesBucketsFiltersProvider(Private, Notifier) {
|
|||
|
||||
decorateQuery(query);
|
||||
|
||||
const label = filter.label || _.get(query, 'query_string.query') || angular.toJson(query);
|
||||
const label = filter.label || formatQuery(query) || '*';
|
||||
filters[label] = input;
|
||||
}, {});
|
||||
|
||||
|
|
|
@ -1,126 +0,0 @@
|
|||
import angular from 'angular';
|
||||
import expect from 'expect.js';
|
||||
import ngMock from 'ng_mock';
|
||||
|
||||
// Load the kibana app dependencies.
|
||||
|
||||
let $rootScope;
|
||||
let $compile;
|
||||
let Private;
|
||||
let config;
|
||||
let $elemScope;
|
||||
let $elem;
|
||||
|
||||
let cycleIndex = 0;
|
||||
const markup = '<input ng-model="mockModel" parse-query input-focus type="text">';
|
||||
let fromUser;
|
||||
import { toUser } from 'ui/parse_query/lib/to_user';
|
||||
import 'ui/parse_query';
|
||||
import { ParseQueryLibFromUserProvider } from 'ui/parse_query/lib/from_user';
|
||||
|
||||
const init = function () {
|
||||
// Load the application
|
||||
ngMock.module('kibana');
|
||||
|
||||
// Create the scope
|
||||
ngMock.inject(function ($injector, _$rootScope_, _$compile_, _$timeout_, _Private_, _config_) {
|
||||
$compile = _$compile_;
|
||||
Private = _Private_;
|
||||
config = _config_;
|
||||
|
||||
// Give us a scope
|
||||
$rootScope = _$rootScope_;
|
||||
});
|
||||
};
|
||||
|
||||
const compile = function () {
|
||||
$rootScope.mockModel = 'cycle' + cycleIndex++;
|
||||
$rootScope.mockQueryInput = undefined;
|
||||
|
||||
$elem = angular.element(markup);
|
||||
$compile($elem)($rootScope);
|
||||
$elemScope = $elem.isolateScope();
|
||||
$rootScope.$digest();
|
||||
};
|
||||
|
||||
describe('parse-query directive', function () {
|
||||
describe('initialization', function () {
|
||||
beforeEach(function () {
|
||||
init();
|
||||
compile();
|
||||
});
|
||||
|
||||
it('should use the model', function () {
|
||||
expect($elemScope).to.have.property('ngModel');
|
||||
});
|
||||
});
|
||||
|
||||
describe('user input parser', function () {
|
||||
|
||||
beforeEach(function () {
|
||||
fromUser = Private(ParseQueryLibFromUserProvider);
|
||||
config.set('query:queryString:options', {});
|
||||
});
|
||||
|
||||
it('should return the input if passed an object', function () {
|
||||
expect(fromUser({ foo: 'bar' })).to.eql({ foo: 'bar' });
|
||||
});
|
||||
|
||||
it('unless the object is empty, that implies a *', function () {
|
||||
expect(fromUser({})).to.eql({ query_string: { query: '*' } });
|
||||
});
|
||||
|
||||
it('should treat an empty string as a *', function () {
|
||||
expect(fromUser('')).to.eql({ query_string: { query: '*' } });
|
||||
});
|
||||
|
||||
it('should merge in the query string options', function () {
|
||||
config.set('query:queryString:options', { analyze_wildcard: true });
|
||||
expect(fromUser('foo')).to.eql({ query_string: { query: 'foo', analyze_wildcard: true } });
|
||||
expect(fromUser('')).to.eql({ query_string: { query: '*', analyze_wildcard: true } });
|
||||
});
|
||||
|
||||
it('should treat input that does not start with { as a query string', function () {
|
||||
expect(fromUser('foo')).to.eql({ query_string: { query: 'foo' } });
|
||||
expect(fromUser('400')).to.eql({ query_string: { query: '400' } });
|
||||
expect(fromUser('true')).to.eql({ query_string: { query: 'true' } });
|
||||
});
|
||||
|
||||
it('should parse valid JSON', function () {
|
||||
expect(fromUser('{}')).to.eql({});
|
||||
expect(fromUser('{a:b}')).to.eql({ query_string: { query: '{a:b}' } });
|
||||
});
|
||||
});
|
||||
|
||||
describe('model presentation formatter', function () {
|
||||
it('should present undefined as empty string', function () {
|
||||
let notDefined;
|
||||
expect(toUser(notDefined)).to.be('');
|
||||
});
|
||||
|
||||
it('should present null as empty string', function () {
|
||||
expect(toUser(null)).to.be('');
|
||||
});
|
||||
|
||||
it('should present objects as strings', function () {
|
||||
expect(toUser({ foo: 'bar' })).to.be('{"foo":"bar"}');
|
||||
});
|
||||
|
||||
it('should present query_string queries as strings', function () {
|
||||
expect(toUser({ query_string: { query: 'lucene query string' } })).to.be('lucene query string');
|
||||
});
|
||||
|
||||
it('should present query_string queries without a query as an empty string', function () {
|
||||
expect(toUser({ query_string: {} })).to.be('');
|
||||
});
|
||||
|
||||
it('should present string as strings', function () {
|
||||
expect(toUser('foo')).to.be('foo');
|
||||
});
|
||||
|
||||
it('should present numbers as strings', function () {
|
||||
expect(toUser(400)).to.be('400');
|
||||
});
|
||||
});
|
||||
|
||||
});
|
18
src/ui/public/directives/parse_query.js
Normal file
18
src/ui/public/directives/parse_query.js
Normal file
|
@ -0,0 +1,18 @@
|
|||
import { parseQuery, formatQuery } from 'ui/parse_query';
|
||||
|
||||
import { uiModules } from 'ui/modules';
|
||||
uiModules
|
||||
.get('kibana')
|
||||
.directive('parseQuery', function () {
|
||||
return {
|
||||
restrict: 'A',
|
||||
require: 'ngModel',
|
||||
scope: {
|
||||
'ngModel': '='
|
||||
},
|
||||
link: function ($scope, elem, attr, ngModel) {
|
||||
ngModel.$parsers.push(parseQuery);
|
||||
ngModel.$formatters.push(formatQuery);
|
||||
}
|
||||
};
|
||||
});
|
52
src/ui/public/parse_query/__tests__/parse_query.js
Normal file
52
src/ui/public/parse_query/__tests__/parse_query.js
Normal file
|
@ -0,0 +1,52 @@
|
|||
import expect from 'expect.js';
|
||||
import { formatQuery, parseQuery } from 'ui/parse_query';
|
||||
|
||||
describe('Query parsing', function () {
|
||||
describe('parseQuery', function () {
|
||||
it('should treat an empty string as a match_all', function () {
|
||||
expect(parseQuery('')).to.eql({ match_all: {} });
|
||||
});
|
||||
|
||||
it('should treat input that does not start with { as a query string', function () {
|
||||
expect(parseQuery('foo')).to.eql({ query_string: { query: 'foo' } });
|
||||
expect(parseQuery('400')).to.eql({ query_string: { query: '400' } });
|
||||
expect(parseQuery('true')).to.eql({ query_string: { query: 'true' } });
|
||||
});
|
||||
|
||||
it('should parse valid JSON', function () {
|
||||
expect(parseQuery('{}')).to.eql({});
|
||||
expect(parseQuery('{a:b}')).to.eql({ query_string: { query: '{a:b}' } });
|
||||
});
|
||||
});
|
||||
|
||||
describe('formatQuery', function () {
|
||||
it('should present undefined as empty string', function () {
|
||||
let notDefined;
|
||||
expect(formatQuery(notDefined)).to.be('');
|
||||
});
|
||||
|
||||
it('should present null as empty string', function () {
|
||||
expect(formatQuery(null)).to.be('');
|
||||
});
|
||||
|
||||
it('should present objects as strings', function () {
|
||||
expect(formatQuery({ foo: 'bar' })).to.be('{"foo":"bar"}');
|
||||
});
|
||||
|
||||
it('should present query_string queries as strings', function () {
|
||||
expect(formatQuery({ query_string: { query: 'lucene query string' } })).to.be('lucene query string');
|
||||
});
|
||||
|
||||
it('should present query_string queries without a query as an empty string', function () {
|
||||
expect(formatQuery({ query_string: {} })).to.be('');
|
||||
});
|
||||
|
||||
it('should present string as strings', function () {
|
||||
expect(formatQuery('foo')).to.be('foo');
|
||||
});
|
||||
|
||||
it('should present numbers as strings', function () {
|
||||
expect(formatQuery(400)).to.be('400');
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,43 +0,0 @@
|
|||
import _ from 'lodash';
|
||||
import { DecorateQueryProvider } from 'ui/courier/data_source/_decorate_query';
|
||||
|
||||
export function ParseQueryLibFromUserProvider(es, Private) {
|
||||
const decorateQuery = Private(DecorateQueryProvider);
|
||||
|
||||
/**
|
||||
* Take text from the user and make it into a query object
|
||||
* @param {text} user's query input
|
||||
* @returns {object}
|
||||
*/
|
||||
return function (text) {
|
||||
function getQueryStringQuery(text) {
|
||||
return decorateQuery({ query_string: { query: text } });
|
||||
}
|
||||
|
||||
const matchAll = getQueryStringQuery('*');
|
||||
|
||||
// If we get an empty object, treat it as a *
|
||||
if (_.isObject(text)) {
|
||||
if (Object.keys(text).length) {
|
||||
return text;
|
||||
} else {
|
||||
return matchAll;
|
||||
}
|
||||
}
|
||||
|
||||
// Nope, not an object.
|
||||
text = (text || '').trim();
|
||||
if (text.length === 0) return matchAll;
|
||||
|
||||
if (text[0] === '{') {
|
||||
try {
|
||||
return JSON.parse(text);
|
||||
} catch (e) {
|
||||
return getQueryStringQuery(text);
|
||||
}
|
||||
} else {
|
||||
return getQueryStringQuery(text);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
@ -1,19 +0,0 @@
|
|||
import _ from 'lodash';
|
||||
import angular from 'angular';
|
||||
|
||||
/**
|
||||
* Take text from the model and present it to the user as a string
|
||||
* @param {text} model value
|
||||
* @returns {string}
|
||||
*/
|
||||
export function toUser(text) {
|
||||
if (text == null) return '';
|
||||
if (_.isObject(text)) {
|
||||
if (text.query_string) return toUser(text.query_string.query);
|
||||
return angular.toJson(text);
|
||||
}
|
||||
if (text === '*') {
|
||||
return '';
|
||||
}
|
||||
return '' + text;
|
||||
}
|
|
@ -1,27 +1,51 @@
|
|||
import { toUser } from 'ui/parse_query/lib/to_user';
|
||||
import { ParseQueryLibFromUserProvider } from 'ui/parse_query/lib/from_user';
|
||||
import _ from 'lodash';
|
||||
import angular from 'angular';
|
||||
|
||||
import { uiModules } from 'ui/modules';
|
||||
uiModules
|
||||
.get('kibana')
|
||||
.directive('parseQuery', function (Private) {
|
||||
const fromUser = Private(ParseQueryLibFromUserProvider);
|
||||
export function getDefaultQuery() {
|
||||
return { match_all: {} };
|
||||
}
|
||||
|
||||
return {
|
||||
restrict: 'A',
|
||||
require: 'ngModel',
|
||||
scope: {
|
||||
'ngModel': '='
|
||||
},
|
||||
link: function ($scope, elem, attr, ngModel) {
|
||||
const init = function () {
|
||||
$scope.ngModel = fromUser($scope.ngModel);
|
||||
};
|
||||
export function isDefaultQuery(query) {
|
||||
return _.isEqual(query, getDefaultQuery());
|
||||
}
|
||||
|
||||
ngModel.$parsers.push(fromUser);
|
||||
ngModel.$formatters.push(toUser);
|
||||
export function getTextQuery(query) {
|
||||
return {
|
||||
query_string: { query }
|
||||
};
|
||||
}
|
||||
|
||||
init();
|
||||
}
|
||||
};
|
||||
});
|
||||
export function isTextQuery(query) {
|
||||
return _.has(query, 'query_string');
|
||||
}
|
||||
|
||||
export function getQueryText(query) {
|
||||
return _.get(query, ['query_string', 'query']) || '';
|
||||
}
|
||||
|
||||
export function parseQuery(query) {
|
||||
if (!_.isString(query) || query.trim() === '') {
|
||||
return getDefaultQuery();
|
||||
}
|
||||
|
||||
try {
|
||||
const parsedQuery = JSON.parse(query);
|
||||
if (_.isObject(parsedQuery)) {
|
||||
return parsedQuery;
|
||||
}
|
||||
return getTextQuery(query);
|
||||
} catch (e) {
|
||||
return getTextQuery(query);
|
||||
}
|
||||
}
|
||||
|
||||
export function formatQuery(query) {
|
||||
if (query == null || isDefaultQuery(query)) {
|
||||
return '';
|
||||
} else if (isTextQuery(query)) {
|
||||
return getQueryText(query);
|
||||
} else if (_.isObject(query)) {
|
||||
return angular.toJson(query);
|
||||
}
|
||||
return '' + query;
|
||||
}
|
||||
|
|
|
@ -139,7 +139,7 @@ export default function ({ getService, getPageObjects }) {
|
|||
const currentQuery = await PageObjects.dashboard.getQuery();
|
||||
expect(currentQuery).to.equal('');
|
||||
const currentUrl = await remote.getCurrentUrl();
|
||||
const newUrl = currentUrl.replace('query:%27*%27', 'query:%27hi%27');
|
||||
const newUrl = currentUrl.replace('match_all:()', 'query_string:(query:hi)');
|
||||
// Don't add the timestamp to the url or it will cause a hard refresh and we want to test a
|
||||
// soft refresh.
|
||||
await remote.get(newUrl.toString(), false);
|
||||
|
|
|
@ -75,8 +75,7 @@ export default function ({ getService, getPageObjects }) {
|
|||
+ '/discover?_g=(refreshInterval:(display:Off,pause:!f,value:0),time'
|
||||
+ ':(from:\'2015-09-19T06:31:44.000Z\',mode:absolute,to:\'2015-09'
|
||||
+ '-23T18:31:44.000Z\'))&_a=(columns:!(_source),index:\'logstash-'
|
||||
+ '*\',interval:auto,query:(query_string:(analyze_wildcard:!t,query'
|
||||
+ ':\'*\')),sort:!(\'@timestamp\',desc))';
|
||||
+ '*\',interval:auto,query:(match_all:()),sort:!(\'@timestamp\',desc))';
|
||||
return PageObjects.discover.getSharedUrl()
|
||||
.then(function (actualUrl) {
|
||||
// strip the timestamp out of each URL
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue