Added micro analysis and filter clicking

This commit is contained in:
Rashid Khan 2014-03-26 15:09:31 -07:00
parent 56ce853a1d
commit 4bf629dbd9
7 changed files with 392 additions and 52 deletions

View file

@ -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');
});

View file

@ -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,
};
};
}
};
});

View file

@ -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">

View file

@ -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>

View file

@ -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;
}

View file

@ -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;
}
}
}
}
}

View file

@ -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;