mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
Added micro analysis and filter clicking
This commit is contained in:
parent
56ce853a1d
commit
4bf629dbd9
7 changed files with 392 additions and 52 deletions
|
@ -110,6 +110,11 @@ define(function (require) {
|
|||
}
|
||||
};
|
||||
|
||||
$scope.resetQuery = function () {
|
||||
$scope.query = initialQuery ? initialQuery.query_string.query : '';
|
||||
$scope.fetch();
|
||||
};
|
||||
|
||||
function updateDataSource() {
|
||||
if ($scope.opts.index !== search.get('index')) {
|
||||
// set the index on the savedSearch
|
||||
|
@ -171,7 +176,7 @@ define(function (require) {
|
|||
field.name = name;
|
||||
|
||||
_.defaults(field, currentState[name]);
|
||||
$scope.fields.push(field);
|
||||
$scope.fields.push(_.defaults(field, {display: false}));
|
||||
});
|
||||
|
||||
|
||||
|
@ -184,6 +189,17 @@ define(function (require) {
|
|||
});
|
||||
}
|
||||
|
||||
$scope.filterQuery = function (field, value, operation) {
|
||||
value = _.isArray(value) ? value : [value];
|
||||
operation = operation || '+';
|
||||
|
||||
_.each(value, function (clause) {
|
||||
$scope.query = $scope.query + ' ' + operation + field + ':"' + addSlashes(clause) + '"';
|
||||
});
|
||||
|
||||
$scope.fetch();
|
||||
};
|
||||
|
||||
$scope.toggleField = function (name) {
|
||||
var field = _.find($scope.fields, { name: name });
|
||||
|
||||
|
@ -225,6 +241,14 @@ define(function (require) {
|
|||
}
|
||||
}
|
||||
|
||||
var addSlashes = function (str) {
|
||||
str = str.replace(/\\/g, '\\\\');
|
||||
str = str.replace(/\'/g, '\\\'');
|
||||
str = str.replace(/\"/g, '\\"');
|
||||
str = str.replace(/\0/g, '\\0');
|
||||
return str;
|
||||
};
|
||||
|
||||
updateDataSource();
|
||||
$scope.$emit('application.load');
|
||||
});
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
define(function (require) {
|
||||
var app = require('modules').get('app/discover');
|
||||
var html = require('text!./partials/field_chooser.html');
|
||||
var _ = require('lodash');
|
||||
|
||||
require('directives/css_truncate');
|
||||
|
||||
app.directive('discFieldChooser', function () {
|
||||
return {
|
||||
|
@ -8,10 +11,41 @@ define(function (require) {
|
|||
scope: {
|
||||
fields: '=',
|
||||
toggle: '=',
|
||||
refresh: '='
|
||||
refresh: '=',
|
||||
data: '=',
|
||||
filterFunc: '=filter'
|
||||
},
|
||||
template: html,
|
||||
controller: function ($scope) {
|
||||
|
||||
$scope.$watch('data', function () {
|
||||
console.log('data change');
|
||||
_.each($scope.fields, function (field) {
|
||||
if (field.details) {
|
||||
console.log('recomputing ', field.name);
|
||||
$scope.details(field, true);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$scope.details = function (field, recompute) {
|
||||
if (_.isUndefined(field.details) || recompute) {
|
||||
field.details = getFieldValueCounts({
|
||||
data: $scope.data,
|
||||
field: field.name,
|
||||
count: 5,
|
||||
grouped: true
|
||||
});
|
||||
console.log(field.details);
|
||||
} else {
|
||||
delete field.details;
|
||||
}
|
||||
};
|
||||
|
||||
$scope.filter = function (field, value, operation) {
|
||||
$scope.filterFunc(field.name, value, operation);
|
||||
};
|
||||
|
||||
$scope.typeIcon = function (fieldType) {
|
||||
switch (fieldType)
|
||||
{
|
||||
|
@ -28,6 +62,82 @@ define(function (require) {
|
|||
default:
|
||||
}
|
||||
};
|
||||
|
||||
var getFieldValues = function (data, field) {
|
||||
return _.map(data, function (row) {
|
||||
var val;
|
||||
val = jsonPath.eval(row, '$._source.' + field)[0] || row[field];
|
||||
val = (val === null) ? '' : val;
|
||||
return val;
|
||||
});
|
||||
};
|
||||
|
||||
var getFieldValueCounts = function (params) {
|
||||
params = _.defaults(params, {
|
||||
count: 5,
|
||||
grouped: false
|
||||
});
|
||||
|
||||
var allValues = getFieldValues(params.data, params.field),
|
||||
groups = {},
|
||||
hasArrays = false,
|
||||
exists = 0,
|
||||
missing = 0,
|
||||
counts;
|
||||
|
||||
var value, k;
|
||||
for (var i = 0; i < allValues.length; ++i) {
|
||||
|
||||
value = allValues[i];
|
||||
if (_.isUndefined(value)) {
|
||||
missing++;
|
||||
}
|
||||
|
||||
if (_.isArray(value)) {
|
||||
hasArrays = true;
|
||||
} else if (_.isObject(value)) {
|
||||
return {error: 'Analysis is not available for object fields'};
|
||||
}
|
||||
|
||||
if (_.isArray(value) && !params.grouped) {
|
||||
k = value;
|
||||
} else {
|
||||
k = _.isUndefined(value) ? '' : [value.toString()];
|
||||
}
|
||||
|
||||
/* jshint -W083 */
|
||||
_.each(k, function (key) {
|
||||
if (_.has(groups, key)) {
|
||||
groups[key].count++;
|
||||
} else {
|
||||
groups[key] = {
|
||||
value: (params.grouped ? value : key),
|
||||
count: 1
|
||||
};
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
counts = _.map(
|
||||
_.sortBy(groups, 'count').reverse().slice(0, params.count),
|
||||
function (bucket) {
|
||||
return {
|
||||
value: bucket.value,
|
||||
count: bucket.count,
|
||||
percent: (bucket.count / (params.data.length - missing) * 100).toFixed(1)
|
||||
};
|
||||
});
|
||||
|
||||
if (params.data.length - missing === 0) return {error: 'Field not present in results'};
|
||||
|
||||
return {
|
||||
total: params.data.length,
|
||||
exists: params.data.length - missing,
|
||||
missing: missing,
|
||||
buckets: counts,
|
||||
hasArrays : hasArrays,
|
||||
};
|
||||
};
|
||||
}
|
||||
};
|
||||
});
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
<div ng-controller="discover">
|
||||
<nav class="navbar navbar-default navbar-static-top subnav">
|
||||
<ul class="nav navbar-nav navbar-right">
|
||||
<li><a class="navbar-link" ng-click="resetQuery()"><i class="fa fa-ban"></i></a></li>
|
||||
<li><a class="navbar-link" ng-click="toggleConfig()"><i class="fa fa-gear"></i></a></li>
|
||||
<li><a class="navbar-link" ng-click="toggleTimepicker()"><i class="fa fa-clock-o"></i></a></li>
|
||||
|
||||
</ul>
|
||||
<form class="navbar-form" ng-submit="fetch()">
|
||||
<div class="form-group" style="display:inline;">
|
||||
|
@ -20,7 +22,9 @@
|
|||
<disc-field-chooser
|
||||
fields="fields"
|
||||
toggle="toggleField"
|
||||
refresh="refreshFieldList">
|
||||
refresh="refreshFieldList"
|
||||
data="rows"
|
||||
filter="filterQuery">
|
||||
</disc-field-chooser>
|
||||
</div>
|
||||
<div class="col-md-10">
|
||||
|
|
|
@ -1,8 +1,63 @@
|
|||
<ul bindonce class="nav nav-pills nav-stacked nav-condensed">
|
||||
<li ng-repeat="field in fields track by $index" ng-class="{'active':field.display}">
|
||||
<a ng-click="toggle(field.name)">
|
||||
<i class="fa discovery-field-type" bo-class="typeIcon(field.type)"></i> <span bo-text="field.name"></span>
|
||||
</a>
|
||||
</li>
|
||||
<li><a ng-click="refresh()"><small>refresh field list</small></a></li>
|
||||
</ul>
|
||||
<div class="discover-field-list">
|
||||
<h5>Selected</h5>
|
||||
<ul bindonce class="list-unstyled">
|
||||
<li ng-repeat="field in fields|filter:{display:true}" class="discover-field-item">
|
||||
<a ng-click="toggle(field.name)" class="discover-field-add">
|
||||
<i class="fa fa-minus-circle"></i>
|
||||
</a>
|
||||
|
||||
<a ng-click="details(field)" class="discover-field-name"><i ng-click="clicked()" class="fa" bo-class="typeIcon(field.type)"></i> <span bo-text="field.name"></span></a>
|
||||
|
||||
|
||||
<div class="discover-field-details" ng-if="field.details">
|
||||
<div bo-if="field.details.error">{{field.details.error}}</div>
|
||||
|
||||
<div bo-if="!field.details.error">
|
||||
<div ng-repeat="bucket in field.details.buckets" class="discover-field-details-item">
|
||||
<div>
|
||||
<i class="fa fa-search-minus pull-right discover-field-details-filter"
|
||||
ng-click="filter(field, bucket.value, '-')"></i>
|
||||
<i class="fa fa-search-plus pull-right discover-field-details-filter"
|
||||
ng-click="filter(field, bucket.value, '+')"></i>
|
||||
<div css-truncate css-truncate-expandable="true" class="discover-field-details-value">
|
||||
{{bucket.value}}
|
||||
</div>
|
||||
</div>
|
||||
<progressbar value="bucket.percent" max="100" animate="false"><span>{{bucket.percent}}%</span></progressbar>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<h5>Available</h5>
|
||||
<ul bindonce class="list-unstyled">
|
||||
<li ng-repeat="field in fields|filter:{display:!true}" class="discover-field-item">
|
||||
<a ng-click="toggle(field.name)" class="discover-field-add">
|
||||
<i class="fa fa-minus-circle"></i>
|
||||
</a>
|
||||
|
||||
<a ng-click="details(field)" class="discover-field-name"><i ng-click="clicked()" class="fa" bo-class="typeIcon(field.type)"></i> <span bo-text="field.name"></span></a>
|
||||
|
||||
|
||||
<div class="discover-field-details" ng-if="field.details">
|
||||
<div bo-if="field.details.error">{{field.details.error}}</div>
|
||||
|
||||
<div bo-if="!field.details.error">
|
||||
<div ng-repeat="bucket in field.details.buckets" class="discover-field-details-item">
|
||||
<div>
|
||||
<i class="fa fa-search-minus pull-right discover-field-details-filter"
|
||||
ng-click="filter(field, bucket.value, '-')"></i>
|
||||
<i class="fa fa-search-plus pull-right discover-field-details-filter"
|
||||
ng-click="filter(field, bucket.value, '+')"></i>
|
||||
<div css-truncate css-truncate-expandable="true" class="discover-field-details-value">
|
||||
{{bucket.value}}
|
||||
</div>
|
||||
</div>
|
||||
<progressbar value="bucket.percent" max="100" animate="false"><span>{{bucket.percent}}%</span></progressbar>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
|
@ -1,16 +1,109 @@
|
|||
disc-field-chooser ul {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
.thumbnail > img,
|
||||
.thumbnail a > img,
|
||||
.carousel-inner > .item > img,
|
||||
.carousel-inner > .item > a > img {
|
||||
display: block;
|
||||
max-width: 100%;
|
||||
height: auto;
|
||||
}
|
||||
.discovery-field-container {
|
||||
padding-top: 20px;
|
||||
padding-left: 0 !important;
|
||||
.btn-group-lg > .btn {
|
||||
padding: 10px 16px;
|
||||
font-size: 18px;
|
||||
line-height: 1.33;
|
||||
border-radius: 6px;
|
||||
}
|
||||
.discovery-field-container li {
|
||||
margin-top: 0px !important;
|
||||
margin-bottom: 0px !important;
|
||||
.btn-group-sm > .btn {
|
||||
padding: 5px 10px;
|
||||
font-size: 12px;
|
||||
line-height: 1.5;
|
||||
border-radius: 3px;
|
||||
}
|
||||
.discovery-field-container li a {
|
||||
border-radius: 0 !important;
|
||||
.btn-group-xs > .btn {
|
||||
padding: 1px 5px;
|
||||
font-size: 12px;
|
||||
line-height: 1.5;
|
||||
border-radius: 3px;
|
||||
}
|
||||
.container:before,
|
||||
.container:after,
|
||||
.container-fluid:before,
|
||||
.container-fluid:after,
|
||||
.row:before,
|
||||
.row:after,
|
||||
.form-horizontal .form-group:before,
|
||||
.form-horizontal .form-group:after,
|
||||
.btn-toolbar:before,
|
||||
.btn-toolbar:after,
|
||||
.btn-group-vertical > .btn-group:before,
|
||||
.btn-group-vertical > .btn-group:after,
|
||||
.nav:before,
|
||||
.nav:after,
|
||||
.navbar:before,
|
||||
.navbar:after,
|
||||
.navbar-header:before,
|
||||
.navbar-header:after,
|
||||
.navbar-collapse:before,
|
||||
.navbar-collapse:after,
|
||||
.pager:before,
|
||||
.pager:after,
|
||||
.panel-body:before,
|
||||
.panel-body:after,
|
||||
.modal-footer:before,
|
||||
.modal-footer:after {
|
||||
content: " ";
|
||||
display: table;
|
||||
}
|
||||
.container:after,
|
||||
.container-fluid:after,
|
||||
.row:after,
|
||||
.form-horizontal .form-group:after,
|
||||
.btn-toolbar:after,
|
||||
.btn-group-vertical > .btn-group:after,
|
||||
.nav:after,
|
||||
.navbar:after,
|
||||
.navbar-header:after,
|
||||
.navbar-collapse:after,
|
||||
.pager:after,
|
||||
.panel-body:after,
|
||||
.modal-footer:after {
|
||||
clear: both;
|
||||
}
|
||||
.discover-field-list .discover-field-item .discover-field-add {
|
||||
visibility: hidden;
|
||||
}
|
||||
.discover-field-list .discover-field-item:hover .discover-field-add {
|
||||
visibility: visible;
|
||||
}
|
||||
.discover-field-list .discover-field-name:hover {
|
||||
text-decoration: none;
|
||||
margin-left: 0;
|
||||
}
|
||||
.discover-field-list .discover-field-details {
|
||||
font-size: 12px;
|
||||
margin-left: 15px;
|
||||
padding: 5px;
|
||||
background-color: #f5f5f5;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #dddddd;
|
||||
}
|
||||
.discover-field-list .discover-field-details .discover-field-details-item {
|
||||
margin-top: 5px;
|
||||
}
|
||||
.discover-field-list .discover-field-details .discover-field-details-filter {
|
||||
cursor: pointer;
|
||||
}
|
||||
.discover-field-list .discover-field-details .progress {
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
.discover-field-list .discover-field-details .progress .progress-bar {
|
||||
padding-left: 10px;
|
||||
text-align: right;
|
||||
}
|
||||
.discover-field-list .discover-field-details .progress .progress-bar span {
|
||||
background-color: #555555;
|
||||
padding: 3px;
|
||||
border-radius: 4px;
|
||||
font-size: 0.8em;
|
||||
margin-left: 3px;
|
||||
margin-right: 3px;
|
||||
}
|
||||
|
|
|
@ -1,21 +1,52 @@
|
|||
disc-field-chooser {
|
||||
ul {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
@import (reference) "../../../../bower_components/bootstrap/less/bootstrap.less";
|
||||
@import (reference) "../../../../bower_components/bootstrap/less/theme.less";
|
||||
|
||||
.discover-field-list {
|
||||
.discover-field-item .discover-field-add {
|
||||
visibility:hidden;
|
||||
}
|
||||
}
|
||||
|
||||
.discovery-field-container {
|
||||
padding-top: 20px;
|
||||
padding-left: 0 !important;
|
||||
}
|
||||
.discover-field-item:hover .discover-field-add {
|
||||
visibility:visible;
|
||||
}
|
||||
|
||||
.discovery-field-container li {
|
||||
margin-top: 0px !important;
|
||||
margin-bottom: 0px !important;
|
||||
}
|
||||
.discover-field-name:hover {
|
||||
text-decoration: none;
|
||||
margin-left: 0;
|
||||
}
|
||||
|
||||
.discovery-field-container li a {
|
||||
border-radius: 0 !important;
|
||||
}
|
||||
.discover-field-details {
|
||||
font-size: @font-size-small;
|
||||
margin-left: 15px;
|
||||
padding: 5px;
|
||||
background-color: @well-bg;
|
||||
border-radius: @border-radius-base;
|
||||
border: 1px solid @table-border-color;
|
||||
|
||||
.discover-field-details-item {
|
||||
margin-top: 5px;
|
||||
}
|
||||
|
||||
.discover-field-details-filter {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.progress {
|
||||
margin-bottom: 0px;
|
||||
|
||||
.progress-bar {
|
||||
padding-left: 10px;
|
||||
text-align: right;
|
||||
|
||||
span {
|
||||
background-color: @gray;
|
||||
padding: 3px;
|
||||
border-radius: 4px;
|
||||
font-size: 0.8em;
|
||||
margin-left: 3px;
|
||||
margin-right: 3px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -7047,21 +7047,44 @@ kbn-table tr.even td {
|
|||
word-break: normal;
|
||||
word-wrap: normal;
|
||||
}
|
||||
disc-field-chooser ul {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
list-style: none;
|
||||
.discover-field-list .discover-field-item .discover-field-add {
|
||||
visibility: hidden;
|
||||
}
|
||||
.discovery-field-container {
|
||||
padding-top: 20px;
|
||||
padding-left: 0 !important;
|
||||
.discover-field-list .discover-field-item:hover .discover-field-add {
|
||||
visibility: visible;
|
||||
}
|
||||
.discovery-field-container li {
|
||||
margin-top: 0px !important;
|
||||
margin-bottom: 0px !important;
|
||||
.discover-field-list .discover-field-name:hover {
|
||||
text-decoration: none;
|
||||
margin-left: 0;
|
||||
}
|
||||
.discovery-field-container li a {
|
||||
border-radius: 0 !important;
|
||||
.discover-field-list .discover-field-details {
|
||||
font-size: 12px;
|
||||
margin-left: 15px;
|
||||
padding: 5px;
|
||||
background-color: #f5f5f5;
|
||||
border-radius: 4px;
|
||||
border: 1px solid #dddddd;
|
||||
}
|
||||
.discover-field-list .discover-field-details .discover-field-details-item {
|
||||
margin-top: 5px;
|
||||
}
|
||||
.discover-field-list .discover-field-details .discover-field-details-filter {
|
||||
cursor: pointer;
|
||||
}
|
||||
.discover-field-list .discover-field-details .progress {
|
||||
margin-bottom: 0px;
|
||||
}
|
||||
.discover-field-list .discover-field-details .progress .progress-bar {
|
||||
padding-left: 10px;
|
||||
text-align: right;
|
||||
}
|
||||
.discover-field-list .discover-field-details .progress .progress-bar span {
|
||||
background-color: #555555;
|
||||
padding: 3px;
|
||||
border-radius: 4px;
|
||||
font-size: 0.8em;
|
||||
margin-left: 3px;
|
||||
margin-right: 3px;
|
||||
}
|
||||
.nav-condensed > li > a {
|
||||
padding-top: 2px;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue