2016-11-23 21:29:44 +00:00
|
|
|
require('ui/doc_table/doc_table.js');
|
|
|
|
require('ui/styles/sidebar.less');
|
|
|
|
require('ui/styles/table.less');
|
|
|
|
require('ui/doc_viewer/doc_viewer.js');
|
|
|
|
require('ui/doc_title/doc_title.js');
|
|
|
|
require('ui/style_compile/style_compile.js');
|
|
|
|
require('ui/registry/doc_views.js');
|
|
|
|
require('plugins/kbn_doc_views/kbn_doc_views.js');
|
|
|
|
require('ui/tooltip/tooltip.js');
|
|
|
|
import 'plugins/kibana/discover/components/field_chooser';
|
|
|
|
import _ from 'lodash';
|
|
|
|
import moment from 'moment';
|
2017-07-10 20:05:48 +00:00
|
|
|
import { getSort } from 'ui/doc_table/lib/get_sort';
|
2016-11-23 21:29:44 +00:00
|
|
|
import rison from 'rison-node';
|
|
|
|
import dateMath from '@elastic/datemath';
|
|
|
|
import 'ui/doc_table';
|
|
|
|
import 'ui/visualize';
|
|
|
|
import 'ui/notify';
|
|
|
|
import 'ui/fixed_scroll';
|
|
|
|
import 'ui/directives/validate_json';
|
|
|
|
import 'ui/filters/moment';
|
|
|
|
import 'ui/courier';
|
|
|
|
import 'ui/index_patterns';
|
|
|
|
import 'ui/state_management/app_state';
|
|
|
|
import 'ui/timefilter';
|
2017-05-10 17:20:22 +00:00
|
|
|
import 'ui/pager_control';
|
|
|
|
import 'ui/pager';
|
2016-11-23 21:29:44 +00:00
|
|
|
import 'ui/highlight/highlight_tags';
|
|
|
|
import 'ui/share';
|
2017-07-10 20:05:48 +00:00
|
|
|
import { VisProvider } from 'ui/vis';
|
|
|
|
import { DocTitleProvider } from 'ui/doc_title';
|
|
|
|
import { UtilsBrushEventProvider } from 'ui/utils/brush_event';
|
2016-11-23 21:29:44 +00:00
|
|
|
import PluginsKibanaDiscoverHitSortFnProvider from 'plugins/kibana/discover/_hit_sort_fn';
|
2017-07-10 20:05:48 +00:00
|
|
|
import { FilterBarQueryFilterProvider } from 'ui/filter_bar/query_filter';
|
|
|
|
import { FilterManagerProvider } from 'ui/filter_manager';
|
|
|
|
import { AggTypesBucketsIntervalOptionsProvider } from 'ui/agg_types/buckets/_interval_options';
|
2017-07-20 08:32:59 +00:00
|
|
|
import { stateMonitorFactory } from 'ui/state_management/state_monitor_factory';
|
|
|
|
import uiRoutes from 'ui/routes';
|
2017-07-10 20:05:48 +00:00
|
|
|
import { uiModules } from 'ui/modules';
|
2017-07-20 08:32:59 +00:00
|
|
|
import indexTemplate from 'plugins/wazuh/templates/directives/dis-template.html';
|
2017-07-10 20:05:48 +00:00
|
|
|
import { StateProvider } from 'ui/state_management/state';
|
2017-07-20 08:32:59 +00:00
|
|
|
import { documentationLinks } from 'ui/documentation_links/documentation_links';
|
2017-05-29 15:05:47 +00:00
|
|
|
import * as columnActions from 'ui/doc_table/actions/columns';
|
2017-02-07 16:05:53 +00:00
|
|
|
import 'ui/debounce';
|
2016-11-23 21:29:44 +00:00
|
|
|
import 'plugins/kibana/discover/saved_searches/saved_searches';
|
|
|
|
import 'plugins/kibana/discover/directives/no_results';
|
|
|
|
import 'plugins/kibana/discover/directives/timechart';
|
|
|
|
import 'ui/collapsible_sidebar';
|
|
|
|
import 'plugins/kibana/discover/components/field_chooser/field_chooser';
|
2017-07-20 08:32:59 +00:00
|
|
|
import 'plugins/kibana/discover/controllers/discover';
|
2016-11-23 21:29:44 +00:00
|
|
|
import 'plugins/kibana/discover/styles/main.less';
|
|
|
|
import 'ui/doc_table/components/table_row';
|
2017-07-10 19:18:51 +00:00
|
|
|
import { SavedObjectRegistryProvider } from 'ui/saved_objects/saved_object_registry';
|
2017-07-20 08:32:59 +00:00
|
|
|
import { savedSearchProvider } from 'plugins/kibana/discover/saved_searches/saved_search_register';
|
2017-07-10 19:18:51 +00:00
|
|
|
|
2017-07-20 08:32:59 +00:00
|
|
|
SavedObjectRegistryProvider.register(savedSearchProvider);
|
2016-11-23 21:29:44 +00:00
|
|
|
|
2017-07-20 08:32:59 +00:00
|
|
|
|
|
|
|
var app = uiModules.get('app/wazuh', [])
|
2017-07-21 11:51:13 +00:00
|
|
|
.directive('kbnDis', [function() {
|
2017-01-03 11:00:59 +00:00
|
|
|
return {
|
|
|
|
restrict: 'E',
|
|
|
|
scope: {
|
|
|
|
disA: '@disA',
|
|
|
|
disG: '@disG',
|
|
|
|
disFilter: '@disFilter',
|
|
|
|
tableHeight: '@tableHeight',
|
|
|
|
infiniteScroll: '@infiniteScroll',
|
|
|
|
indexSelector: '@indexSelector'
|
|
|
|
},
|
|
|
|
template: require('../templates/directives/dis-template.html')
|
|
|
|
}
|
|
|
|
}]);
|
|
|
|
|
2017-07-20 08:32:59 +00:00
|
|
|
var app = uiModules.get('app/wazuh', [])
|
2017-01-03 11:00:59 +00:00
|
|
|
.directive('kbnDisfull', [function() {
|
|
|
|
return {
|
|
|
|
restrict: 'E',
|
|
|
|
scope: {
|
|
|
|
disA: '@disA',
|
|
|
|
disG: '@disG',
|
|
|
|
disFilter: '@disFilter',
|
|
|
|
tableHeight: '@tableHeight',
|
|
|
|
infiniteScroll: '@infiniteScroll'
|
|
|
|
},
|
|
|
|
template: require('../templates/directives/dis-full-template.html')
|
|
|
|
}
|
|
|
|
}]);
|
|
|
|
|
|
|
|
|
|
|
|
|
2017-07-20 08:32:59 +00:00
|
|
|
uiModules.get('app/wazuh', []).controller('discoverW', function($scope, config, courier, $route, $window, Notifier,
|
2017-03-29 07:34:48 +00:00
|
|
|
AppState, timefilter, Promise, Private, kbnUrl, $location, savedSearches, appState, $rootScope, getAppState) {
|
2017-01-03 11:00:59 +00:00
|
|
|
|
|
|
|
$scope.defaultManagerName = appState.getDefaultManager().name;
|
|
|
|
$scope.stateQuery = $scope.disFilter;
|
|
|
|
$scope.chrome = {};
|
2017-05-29 15:05:47 +00:00
|
|
|
$scope.removeColumn = function removeColumn(columnName) {
|
|
|
|
$scope.indexPattern.popularizeField(columnName, 1);
|
|
|
|
columnActions.removeColumn($scope.state.columns, columnName);
|
|
|
|
};
|
|
|
|
$scope.addColumn = function addColumn(columnName) {
|
|
|
|
$scope.indexPattern.popularizeField(columnName, 1);
|
|
|
|
columnActions.addColumn($scope.state.columns, columnName);
|
|
|
|
};
|
2017-01-03 11:00:59 +00:00
|
|
|
$scope.chrome.getVisible = function() {
|
|
|
|
return true;
|
2016-11-23 21:29:44 +00:00
|
|
|
}
|
|
|
|
|
2017-01-03 11:00:59 +00:00
|
|
|
const notify = new Notifier({
|
|
|
|
location: '*'
|
|
|
|
});
|
2016-11-23 21:29:44 +00:00
|
|
|
|
2017-01-03 11:00:59 +00:00
|
|
|
if (typeof $rootScope.visCounter === "undefined")
|
|
|
|
$rootScope.visCounter = 0;
|
|
|
|
|
|
|
|
$rootScope.visCounter++;
|
|
|
|
|
|
|
|
const State = Private(StateProvider);
|
|
|
|
courier.indexPatterns.getIds()
|
|
|
|
.then(function(list) {
|
|
|
|
|
|
|
|
// Decode discover settings from directive
|
|
|
|
var disDecoded = rison.decode($scope.disA);
|
|
|
|
const state = disDecoded;
|
|
|
|
|
|
|
|
const specified = !!state.index;
|
|
|
|
const exists = _.contains(list, state.index);
|
|
|
|
const id = exists ? state.index : config.get('defaultIndex');
|
|
|
|
|
|
|
|
Promise.props({
|
|
|
|
list: list,
|
|
|
|
loaded: courier.indexPatterns.get(id),
|
|
|
|
stateVal: state.index,
|
|
|
|
stateValFound: specified && exists
|
|
|
|
}).then(function(result) {
|
|
|
|
$scope._ip = result;
|
|
|
|
savedSearches.get().then(function(result) {
|
|
|
|
$scope._savedSearch = result;
|
|
|
|
|
|
|
|
const Vis = Private(VisProvider);
|
|
|
|
const docTitle = Private(DocTitleProvider);
|
|
|
|
const brushEvent = Private(UtilsBrushEventProvider);
|
|
|
|
const HitSortFn = Private(PluginsKibanaDiscoverHitSortFnProvider);
|
|
|
|
const queryFilter = Private(FilterBarQueryFilterProvider);
|
|
|
|
const filterManager = Private(FilterManagerProvider);
|
|
|
|
|
2017-07-20 08:32:59 +00:00
|
|
|
$scope.queryDocLinks = documentationLinks.query;
|
2017-01-03 11:00:59 +00:00
|
|
|
$scope.intervalOptions = Private(AggTypesBucketsIntervalOptionsProvider);
|
|
|
|
$scope.showInterval = false;
|
2016-11-23 21:29:44 +00:00
|
|
|
|
2017-01-03 11:00:59 +00:00
|
|
|
$scope.intervalEnabled = function(interval) {
|
|
|
|
return interval.val !== 'custom';
|
|
|
|
};
|
2016-11-23 21:29:44 +00:00
|
|
|
|
2017-01-03 11:00:59 +00:00
|
|
|
$scope.toggleInterval = function() {
|
|
|
|
$scope.showInterval = !$scope.showInterval;
|
|
|
|
};
|
2016-11-23 21:29:44 +00:00
|
|
|
|
2017-01-03 11:00:59 +00:00
|
|
|
$scope.timefilter = timefilter;
|
2016-11-23 21:29:44 +00:00
|
|
|
|
2017-01-03 11:00:59 +00:00
|
|
|
// Set default time
|
2017-05-30 10:37:38 +00:00
|
|
|
var gParameter;
|
2017-06-20 07:43:40 +00:00
|
|
|
if($route.current.params._g){
|
|
|
|
if($route.current.params._g.startsWith("h@")){
|
|
|
|
gParameter = sessionStorage.getItem($route.current.params._g);
|
|
|
|
}else{
|
|
|
|
gParameter = $route.current.params._g;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else{
|
|
|
|
gParameter="()";
|
2017-05-30 10:37:38 +00:00
|
|
|
}
|
|
|
|
if (gParameter == "()")
|
2017-01-03 11:00:59 +00:00
|
|
|
$scope.timefilter.time.from = "now-24h";
|
2016-11-23 21:29:44 +00:00
|
|
|
|
2017-01-03 11:00:59 +00:00
|
|
|
// the saved savedSearch
|
|
|
|
const savedSearch = $scope._savedSearch;
|
|
|
|
$scope.$on('$destroy', savedSearch.destroy);
|
2016-11-23 21:29:44 +00:00
|
|
|
|
2017-01-03 11:00:59 +00:00
|
|
|
// the actual courier.SearchSource
|
|
|
|
$scope.searchSource = savedSearch.searchSource;
|
|
|
|
$scope.indexPattern = resolveIndexPatternLoading();
|
|
|
|
$scope.searchSource.set('index', $scope.indexPattern);
|
2016-12-21 18:30:24 +00:00
|
|
|
|
2017-01-03 11:00:59 +00:00
|
|
|
if (savedSearch.id) {
|
|
|
|
docTitle.change(savedSearch.title);
|
|
|
|
}
|
2016-11-23 21:29:44 +00:00
|
|
|
|
2017-02-07 16:05:53 +00:00
|
|
|
|
2017-01-03 11:00:59 +00:00
|
|
|
// Configure AppState. Get App State, if there is no App State create new one
|
|
|
|
let currentAppState = getAppState();
|
|
|
|
if (!currentAppState) {
|
|
|
|
$scope.state = new AppState(getStateDefaults());
|
|
|
|
} else {
|
|
|
|
$scope.state = currentAppState;
|
|
|
|
$scope.state.columns = disDecoded.columns.length > 0 ? disDecoded.columns : config.get('defaultColumns');
|
2017-02-07 16:05:53 +00:00
|
|
|
$scope.state.sort = disDecoded.sort.length > 0 ? disDecoded.sort : getSort.array(savedSearch.sort, $scope.indexPattern);
|
2017-01-03 11:00:59 +00:00
|
|
|
}
|
2017-02-07 16:05:53 +00:00
|
|
|
|
|
|
|
let stateMonitor;
|
2017-07-20 08:32:59 +00:00
|
|
|
const $appStatus = $scope.appStatus = {
|
|
|
|
dirty: !savedSearch.id
|
|
|
|
};
|
2017-01-03 11:00:59 +00:00
|
|
|
const $state = $scope.state;
|
|
|
|
$scope.uiState = $state.makeStateful('uiState');
|
|
|
|
$scope.uiState.set('vis.legendOpen', false);
|
|
|
|
$state.query = ($scope.stateQuery ? $scope.stateQuery : '*');
|
|
|
|
|
|
|
|
function getStateDefaults() {
|
2017-08-01 17:29:11 +00:00
|
|
|
return {
|
|
|
|
query: $scope.searchSource.get('query') || '',
|
|
|
|
sort: getSort.array(savedSearch.sort, $scope.indexPattern),
|
|
|
|
columns: savedSearch.columns.length > 0 ? savedSearch.columns : config.get('defaultColumns').slice(),
|
|
|
|
index: $scope.indexPattern.id,
|
|
|
|
interval: 'auto',
|
|
|
|
filters: _.cloneDeep($scope.searchSource.getOwn('filter'))
|
|
|
|
};
|
|
|
|
}
|
2017-02-07 16:05:53 +00:00
|
|
|
|
2017-01-03 11:00:59 +00:00
|
|
|
$state.index = $scope.indexPattern.id;
|
|
|
|
$state.sort = getSort.array($state.sort, $scope.indexPattern);
|
2017-02-07 16:05:53 +00:00
|
|
|
|
2017-01-03 11:00:59 +00:00
|
|
|
$scope.opts = {
|
|
|
|
// number of records to fetch, then paginate through
|
|
|
|
sampleSize: config.get('discover:sampleSize'),
|
|
|
|
// Index to match
|
|
|
|
index: $scope.indexPattern.id,
|
|
|
|
timefield: $scope.indexPattern.timeFieldName,
|
|
|
|
savedSearch: savedSearch,
|
|
|
|
indexPatternList: $scope.indexSelector ? $scope._ip.list : [],
|
|
|
|
timefilter: $scope.timefilter
|
|
|
|
};
|
2016-11-23 21:29:44 +00:00
|
|
|
|
2017-01-03 11:00:59 +00:00
|
|
|
const init = _.once(function() {
|
|
|
|
const showTotal = 5;
|
|
|
|
$scope.failuresShown = showTotal;
|
|
|
|
$scope.showAllFailures = function() {
|
|
|
|
$scope.failuresShown = $scope.failures.length;
|
|
|
|
};
|
|
|
|
$scope.showLessFailures = function() {
|
|
|
|
$scope.failuresShown = showTotal;
|
|
|
|
};
|
|
|
|
|
2017-02-07 16:05:53 +00:00
|
|
|
stateMonitor = stateMonitorFactory.create($state, getStateDefaults());
|
|
|
|
stateMonitor.onChange((status) => {
|
2017-07-20 08:32:59 +00:00
|
|
|
$appStatus.dirty = status.dirty || !savedSearch.id;
|
2017-02-07 16:05:53 +00:00
|
|
|
});
|
|
|
|
$scope.$on('$destroy', () => stateMonitor.destroy());
|
|
|
|
|
2017-01-03 11:00:59 +00:00
|
|
|
$scope.updateDataSource()
|
|
|
|
.then(function() {
|
|
|
|
$scope.$listen(timefilter, 'fetch', function() {
|
|
|
|
$scope.fetch();
|
|
|
|
});
|
|
|
|
|
|
|
|
$scope.$watchCollection('state.sort', function(sort) {
|
|
|
|
if (!sort) return;
|
|
|
|
|
|
|
|
// get the current sort from {key: val} to ["key", "val"];
|
|
|
|
const currentSort = _.pairs($scope.searchSource.get('sort')).pop();
|
|
|
|
|
|
|
|
// if the searchSource doesn't know, tell it so
|
|
|
|
if (!angular.equals(sort, currentSort)) $scope.fetch();
|
|
|
|
});
|
|
|
|
|
|
|
|
// update data source when filters update
|
|
|
|
$scope.$listen(queryFilter, 'update', function() {
|
|
|
|
return $scope.updateDataSource().then(function() {});
|
|
|
|
});
|
|
|
|
|
|
|
|
// update data source when hitting forward/back and the query changes
|
|
|
|
$scope.$listen($state, 'fetch_with_changes', function(diff) {
|
|
|
|
$scope.fetch();
|
|
|
|
});
|
|
|
|
|
|
|
|
// fetch data when filters fire fetch event
|
|
|
|
$scope.$listen(queryFilter, 'fetch', $scope.fetch);
|
|
|
|
|
|
|
|
$scope.$watch('opts.timefield', function(timefield) {
|
|
|
|
timefilter.enabled = !!timefield;
|
|
|
|
});
|
|
|
|
|
|
|
|
$scope.$watch('state.interval', function(interval, oldInterval) {
|
|
|
|
if (interval !== oldInterval && interval === 'auto') {
|
|
|
|
$scope.showInterval = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
$scope.$watch('vis.aggs', function() {
|
|
|
|
// no timefield, no vis, nothing to update
|
|
|
|
if (!$scope.opts.timefield) return;
|
|
|
|
|
|
|
|
const buckets = $scope.vis.aggs.bySchemaGroup.buckets;
|
|
|
|
|
|
|
|
if (buckets && buckets.length === 1) {
|
|
|
|
$scope.intervalName = 'by ' + buckets[0].buckets.getInterval().description;
|
|
|
|
} else {
|
|
|
|
$scope.intervalName = 'auto';
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
$scope.$watchMulti([
|
|
|
|
'rows',
|
|
|
|
'fetchStatus'
|
|
|
|
], (function updateResultState() {
|
|
|
|
let prev = {};
|
|
|
|
const status = {
|
|
|
|
LOADING: 'loading', // initial data load
|
|
|
|
READY: 'ready', // results came back
|
|
|
|
NO_RESULTS: 'none' // no results came back
|
|
|
|
};
|
|
|
|
|
|
|
|
function pick(rows, oldRows, fetchStatus) {
|
|
|
|
// initial state, pretend we are loading
|
|
|
|
if (rows == null && oldRows == null) return status.LOADING;
|
|
|
|
|
|
|
|
const rowsEmpty = _.isEmpty(rows);
|
|
|
|
// An undefined fetchStatus means the requests are still being
|
|
|
|
// prepared to be sent. When all requests are completed,
|
|
|
|
// fetchStatus is set to null, so it's important that we
|
|
|
|
// specifically check for undefined to determine a loading status.
|
|
|
|
const preparingForFetch = _.isUndefined(fetchStatus);
|
|
|
|
if (preparingForFetch) return status.LOADING;
|
|
|
|
else if (rowsEmpty && fetchStatus) return status.LOADING;
|
|
|
|
else if (!rowsEmpty) return status.READY;
|
|
|
|
else return status.NO_RESULTS;
|
|
|
|
}
|
|
|
|
|
|
|
|
return function() {
|
|
|
|
const current = {
|
|
|
|
rows: $scope.rows,
|
|
|
|
fetchStatus: $scope.fetchStatus
|
|
|
|
};
|
|
|
|
|
|
|
|
$scope.resultState = pick(
|
|
|
|
current.rows,
|
|
|
|
prev.rows,
|
|
|
|
current.fetchStatus,
|
|
|
|
prev.fetchStatus
|
|
|
|
);
|
|
|
|
|
|
|
|
prev = current;
|
|
|
|
};
|
|
|
|
}()));
|
|
|
|
|
|
|
|
$scope.searchSource.onError(function(err) {
|
|
|
|
notify.error(err);
|
|
|
|
}).catch(notify.fatal);
|
|
|
|
|
|
|
|
function initForTime() {
|
|
|
|
return setupVisualization().then($scope.updateTime);
|
|
|
|
}
|
|
|
|
|
|
|
|
return Promise.resolve($scope.opts.timefield && initForTime())
|
|
|
|
.then(function() {
|
|
|
|
init.complete = true;
|
|
|
|
$scope.$emit('application.load');
|
|
|
|
$scope.fetch();
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
$scope.opts.saveDataSource = function() {
|
|
|
|
return $scope.updateDataSource()
|
|
|
|
.then(function() {
|
|
|
|
savedSearch.id = savedSearch.title;
|
|
|
|
savedSearch.columns = $scope.state.columns;
|
|
|
|
savedSearch.sort = $scope.state.sort;
|
|
|
|
|
|
|
|
return savedSearch.save()
|
|
|
|
.then(function(id) {
|
|
|
|
$scope.kbnTopNav.close('save');
|
|
|
|
|
|
|
|
if (id) {
|
|
|
|
notify.info('Saved Data Source "' + savedSearch.title + '"');
|
|
|
|
if (savedSearch.id !== $route.current.params.id) {} else {
|
|
|
|
// Update defaults so that "reload saved query" functions correctly
|
|
|
|
$state.setDefaults(getStateDefaults());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
})
|
|
|
|
.catch(notify.error);
|
2016-11-23 21:29:44 +00:00
|
|
|
};
|
|
|
|
|
2017-01-03 11:00:59 +00:00
|
|
|
$scope.opts.fetch = $scope.fetch = function() {
|
2016-11-23 21:29:44 +00:00
|
|
|
|
2017-01-03 11:00:59 +00:00
|
|
|
// ignore requests to fetch before the app inits
|
|
|
|
if (!init.complete) return;
|
2016-11-23 21:29:44 +00:00
|
|
|
|
2017-01-03 11:00:59 +00:00
|
|
|
$scope.updateTime();
|
2016-11-23 21:29:44 +00:00
|
|
|
|
2017-01-03 11:00:59 +00:00
|
|
|
$scope.updateDataSource()
|
|
|
|
.then(setupVisualization)
|
|
|
|
.then(function() {
|
|
|
|
return courier.fetch();
|
|
|
|
})
|
|
|
|
.catch(notify.error);
|
|
|
|
};
|
|
|
|
$scope.searchSource.onBeginSegmentedFetch(function(segmented) {
|
|
|
|
|
|
|
|
function flushResponseData() {
|
|
|
|
$scope.hits = 0;
|
|
|
|
$scope.faliures = [];
|
|
|
|
$scope.rows = [];
|
|
|
|
$scope.fieldCounts = {};
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!$scope.rows) flushResponseData();
|
2017-02-07 16:05:53 +00:00
|
|
|
|
|
|
|
if(!$state.sort)
|
|
|
|
$state.sort = ["@timestamp","desc"];
|
2017-01-03 11:00:59 +00:00
|
|
|
const sort = $state.sort;
|
|
|
|
const timeField = $scope.indexPattern.timeFieldName;
|
|
|
|
const totalSize = $scope.size || $scope.opts.sampleSize;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Basically an emum.
|
|
|
|
*
|
|
|
|
* opts:
|
|
|
|
* "time" - sorted by the timefield
|
|
|
|
* "non-time" - explicitly sorted by a non-time field, NOT THE SAME AS `sortBy !== "time"`
|
|
|
|
* "implicit" - no sorting set, NOT THE SAME AS "non-time"
|
|
|
|
*
|
|
|
|
* @type {String}
|
|
|
|
*/
|
|
|
|
const sortBy = (function() {
|
|
|
|
if (!_.isArray(sort)) return 'implicit';
|
|
|
|
else if (sort[0] === '_score') return 'implicit';
|
|
|
|
else if (sort[0] === timeField) return 'time';
|
|
|
|
else return 'non-time';
|
|
|
|
}());
|
|
|
|
|
|
|
|
let sortFn = null;
|
|
|
|
if (sortBy !== 'implicit') {
|
|
|
|
sortFn = new HitSortFn(sort[1]);
|
|
|
|
}
|
|
|
|
|
|
|
|
$scope.updateTime();
|
|
|
|
if (sort[0] === '_score') segmented.setMaxSegments(1);
|
|
|
|
segmented.setDirection(sortBy === 'time' ? (sort[1] || 'desc') : 'desc');
|
|
|
|
segmented.setSortFn(sortFn);
|
|
|
|
segmented.setSize($scope.opts.sampleSize);
|
|
|
|
|
|
|
|
// triggered when the status updated
|
|
|
|
segmented.on('status', function(status) {
|
|
|
|
$scope.fetchStatus = status;
|
|
|
|
});
|
|
|
|
|
|
|
|
segmented.on('first', function() {
|
|
|
|
flushResponseData();
|
|
|
|
});
|
|
|
|
|
|
|
|
segmented.on('segment', notify.timed('handle each segment', function(resp) {
|
|
|
|
if (resp._shards.failed > 0) {
|
|
|
|
$scope.failures = _.union($scope.failures, resp._shards.failures);
|
|
|
|
$scope.failures = _.uniq($scope.failures, false, function(failure) {
|
|
|
|
return failure.index + failure.shard + failure.reason;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}));
|
|
|
|
|
|
|
|
segmented.on('mergedSegment', function(merged) {
|
|
|
|
$scope.mergedEsResp = merged;
|
|
|
|
$scope.hits = merged.hits.total;
|
|
|
|
const indexPattern = $scope.searchSource.get('index');
|
|
|
|
|
|
|
|
// the merge rows, use a new array to help watchers
|
|
|
|
$scope.rows = merged.hits.hits.slice();
|
|
|
|
|
|
|
|
notify.event('flatten hit and count fields', function() {
|
|
|
|
let counts = $scope.fieldCounts;
|
|
|
|
|
|
|
|
// if we haven't counted yet, or need a fresh count because we are sorting, reset the counts
|
|
|
|
if (!counts || sortFn) counts = $scope.fieldCounts = {};
|
|
|
|
|
|
|
|
$scope.rows.forEach(function(hit) {
|
|
|
|
// skip this work if we have already done it
|
|
|
|
if (hit.$$_counted) return;
|
|
|
|
|
|
|
|
// when we are sorting results, we need to redo the counts each time because the
|
|
|
|
// "top 500" may change with each response, so don't mark this as counted
|
|
|
|
if (!sortFn) hit.$$_counted = true;
|
|
|
|
|
|
|
|
const fields = _.keys(indexPattern.flattenHit(hit));
|
|
|
|
let n = fields.length;
|
|
|
|
let field;
|
|
|
|
while (field = fields[--n]) {
|
|
|
|
if (counts[field]) counts[field] += 1;
|
|
|
|
else counts[field] = 1;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
segmented.on('complete', function() {
|
|
|
|
|
|
|
|
if ($scope.fetchStatus.hitCount === 0) {
|
|
|
|
flushResponseData();
|
|
|
|
}
|
|
|
|
|
|
|
|
$scope.fetchStatus = null;
|
|
|
|
});
|
|
|
|
}).catch(notify.fatal);
|
|
|
|
|
|
|
|
$scope.updateTime = function() {
|
|
|
|
$scope.timeRange = {
|
|
|
|
from: dateMath.parse(timefilter.time.from),
|
|
|
|
to: dateMath.parse(timefilter.time.to, true)
|
|
|
|
};
|
|
|
|
};
|
2016-11-23 21:29:44 +00:00
|
|
|
|
2017-02-07 16:05:53 +00:00
|
|
|
$scope.updateDataSource = Promise.method(function updateDataSource() {
|
2017-01-03 11:00:59 +00:00
|
|
|
|
|
|
|
$scope.searchSource
|
|
|
|
.size($scope.opts.sampleSize)
|
|
|
|
.sort(getSort($state.sort, $scope.indexPattern))
|
|
|
|
.query(!$scope.stateQuery ? null : $scope.stateQuery)
|
2017-03-29 07:34:48 +00:00
|
|
|
.set('filter', queryFilter.getFilters())
|
|
|
|
.highlightAll(true);
|
2017-01-03 11:00:59 +00:00
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
// TODO: On array fields, negating does not negate the combination, rather all terms
|
|
|
|
$scope.filterQuery = function(field, values, operation) {
|
|
|
|
$scope.indexPattern.popularizeField(field, 1);
|
|
|
|
filterManager.add(field, values, operation, $state.index);
|
|
|
|
};
|
2016-11-23 21:29:44 +00:00
|
|
|
|
2017-01-03 11:00:59 +00:00
|
|
|
$scope.toTop = function() {
|
|
|
|
$window.scrollTo(0, 0);
|
|
|
|
};
|
2016-11-23 21:29:44 +00:00
|
|
|
|
2017-01-03 11:00:59 +00:00
|
|
|
let loadingVis;
|
|
|
|
|
|
|
|
function setupVisualization() {
|
|
|
|
// If we're not setting anything up we need to return an empty promise
|
|
|
|
if (!$scope.opts.timefield) return Promise.resolve();
|
|
|
|
if (loadingVis) return loadingVis;
|
|
|
|
|
|
|
|
const visStateAggs = [{
|
|
|
|
type: 'count',
|
|
|
|
schema: 'metric'
|
|
|
|
},
|
|
|
|
{
|
|
|
|
type: 'date_histogram',
|
|
|
|
schema: 'segment',
|
|
|
|
params: {
|
|
|
|
field: $scope.opts.timefield,
|
|
|
|
interval: $state.interval
|
|
|
|
}
|
|
|
|
}
|
|
|
|
];
|
|
|
|
|
|
|
|
// we have a vis, just modify the aggs
|
|
|
|
if ($scope.vis) {
|
|
|
|
const visState = $scope.vis.getEnabledState();
|
|
|
|
visState.aggs = visStateAggs;
|
|
|
|
|
|
|
|
$scope.vis.setState(visState);
|
|
|
|
return Promise.resolve($scope.vis);
|
|
|
|
}
|
|
|
|
|
|
|
|
$scope.vis = new Vis($scope.indexPattern, {
|
|
|
|
title: savedSearch.title,
|
|
|
|
type: 'histogram',
|
|
|
|
params: {
|
|
|
|
addLegend: false,
|
|
|
|
addTimeMarker: true
|
|
|
|
},
|
|
|
|
listeners: {
|
|
|
|
click: function(e) {
|
|
|
|
notify.log(e);
|
|
|
|
timefilter.time.from = moment(e.point.x);
|
|
|
|
timefilter.time.to = moment(e.point.x + e.data.ordered.interval);
|
|
|
|
timefilter.time.mode = 'absolute';
|
|
|
|
},
|
2017-02-07 19:45:31 +00:00
|
|
|
brush: brushEvent($state)
|
2017-01-03 11:00:59 +00:00
|
|
|
},
|
|
|
|
aggs: visStateAggs
|
|
|
|
});
|
|
|
|
|
|
|
|
$scope.searchSource.aggs(function() {
|
|
|
|
$scope.vis.requesting();
|
|
|
|
return $scope.vis.aggs.toDsl();
|
|
|
|
});
|
|
|
|
|
|
|
|
// stash this promise so that other calls to setupVisualization will have to wait
|
|
|
|
loadingVis = new Promise(function(resolve) {
|
|
|
|
$rootScope.visCounter--;
|
|
|
|
$scope.$on('ready:vis', function() {
|
|
|
|
resolve($scope.vis);
|
|
|
|
});
|
|
|
|
})
|
|
|
|
.finally(function() {
|
|
|
|
// clear the loading flag
|
|
|
|
loadingVis = null;
|
|
|
|
});
|
|
|
|
|
|
|
|
return loadingVis;
|
|
|
|
}
|
|
|
|
// Listen for visualization queue prepared
|
|
|
|
var fetchVisualizationWatch = $rootScope.$on('fetchVisualization', function(event) {
|
|
|
|
//$scope.fetch();
|
|
|
|
courier.fetch()
|
|
|
|
});
|
|
|
|
|
|
|
|
function resolveIndexPatternLoading() {
|
|
|
|
const props = $scope._ip;
|
|
|
|
const loaded = props.loaded;
|
|
|
|
const stateVal = props.stateVal;
|
|
|
|
const stateValFound = props.stateValFound;
|
|
|
|
|
|
|
|
const own = $scope.searchSource.getOwn('index');
|
|
|
|
|
|
|
|
if (own && !stateVal) return own;
|
|
|
|
if (stateVal && !stateValFound) {
|
|
|
|
const err = '"' + stateVal + '" is not a configured pattern. ';
|
|
|
|
if (own) {
|
|
|
|
notify.warning(err + ' Using the saved index pattern: "' + own.id + '"');
|
|
|
|
return own;
|
|
|
|
}
|
|
|
|
|
|
|
|
notify.warning(err + ' Using the default index pattern: "' + loaded.id + '"');
|
|
|
|
}
|
|
|
|
return loaded;
|
|
|
|
}
|
2016-11-23 21:29:44 +00:00
|
|
|
|
2017-01-03 11:00:59 +00:00
|
|
|
init();
|
|
|
|
});
|
|
|
|
});
|
2016-11-23 21:29:44 +00:00
|
|
|
|
|
|
|
|
2017-01-03 11:00:59 +00:00
|
|
|
});
|
|
|
|
|
2017-05-29 15:05:47 +00:00
|
|
|
});
|