diff --git a/.jshintrc b/.jshintrc index 15960ec73ab..54051651ff6 100755 --- a/.jshintrc +++ b/.jshintrc @@ -28,6 +28,7 @@ "globals": { "define": true, - "require": true + "require": true, + "Chromath": false } } \ No newline at end of file diff --git a/Gruntfile.js b/Gruntfile.js index 3bebe1f350d..bb8c95df267 100755 --- a/Gruntfile.js +++ b/Gruntfile.js @@ -189,6 +189,42 @@ module.exports = function (grunt) { dest: '<%= pkg.name %>-latest' } ] + }, + zip_release: { + options: { + archive: '<%= tempDir %>/<%= pkg.name %>-<%= pkg.version %>.zip' + }, + files : [ + { + expand: true, + cwd: '<%= destDir %>', + src: ['**/*'], + dest: '<%= pkg.name %>-<%= pkg.version %>' + }, + { + expand: true, + src: ['LICENSE.md', 'README.md'], + dest: '<%= pkg.name %>-<%= pkg.version %>' + } + ] + }, + tgz_release: { + options: { + archive: '<%= tempDir %>/<%= pkg.name %>-<%= pkg.version %>.tar.gz' + }, + files : [ + { + expand: true, + cwd: '<%= destDir %>', + src: ['**/*'], + dest: '<%= pkg.name %>-<%= pkg.version %>' + }, + { + expand: true, + src: ['LICENSE.md', 'README.md'], + dest: '<%= pkg.name %>-<%= pkg.version %>' + } + ] } }, s3: { @@ -206,6 +242,21 @@ module.exports = function (grunt) { dest: 'kibana/kibana/<%= pkg.name %>-latest.tar.gz', } ] + }, + release: { + bucket: 'download.elasticsearch.org', + access: 'private', + // debug: true, // uncommment to prevent actual upload + upload: [ + { + src: '<%= tempDir %>/<%= pkg.name %>-<%= pkg.version %>.zip', + dest: 'kibana/kibana/<%= pkg.name %>-<%= pkg.version %>.zip', + }, + { + src: '<%= tempDir %>/<%= pkg.name %>-<%= pkg.version %>.tar.gz', + dest: 'kibana/kibana/<%= pkg.name %>-<%= pkg.version %>.tar.gz', + } + ] } } }; @@ -306,6 +357,16 @@ module.exports = function (grunt) { 'clean:temp' ]); + // build, then zip and upload to s3 + grunt.registerTask('release', [ + 'distribute:load_s3_config', + 'build', + 'compress:zip_release', + 'compress:tgz_release', + 's3:release', + 'clean:temp' + ]); + // collect the key and secret from the .aws-config.json file, finish configuring the s3 task grunt.registerTask('distribute:load_s3_config', function () { var config = grunt.file.readJSON('.aws-config.json'); diff --git a/package.json b/package.json index 440c3f9b3da..98ced79a8d5 100755 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "company": "Elasticsearch BV" }, "name": "kibana", - "version": "3.0.0m4pre", + "version": "3.0.0milestone4", "devDependencies": { "rjs-build-analysis": "0.0.3", "grunt": "~0.4.0", diff --git a/sample/apache_ldap.conf b/sample/apache_ldap.conf index e290d5697f3..f7df159e335 100755 --- a/sample/apache_ldap.conf +++ b/sample/apache_ldap.conf @@ -31,15 +31,15 @@ # Proxy for _aliases and .*/_search - - ProxyPassMatch http://127.0.0.1:9200 - ProxyPassReverse http://127.0.0.1:9200 + + ProxyPassMatch http://127.0.0.1:9200/$1 + ProxyPassReverse http://127.0.0.1:9200/$1 - + # Proxy for kibana-int/{dashboard,temp} stuff (if you don't want auth on /, then you will want these to be protected) - - ProxyPassMatch http://127.0.0.1:9200 - ProxyPassReverse http://127.0.0.1:9200 + + ProxyPassMatch http://127.0.0.1:9200/$1$2 + ProxyPassReverse http://127.0.0.1:9200/$1$2 # Optional disable auth for a src IP (eg: your monitoring host or subnet) diff --git a/sample/nginx.conf b/sample/nginx.conf index 6015897cf7b..b1e2f362e60 100755 --- a/sample/nginx.conf +++ b/sample/nginx.conf @@ -25,6 +25,10 @@ server { proxy_pass http://127.0.0.1:9200; proxy_read_timeout 90; } + location ~ ^/_nodes$ { + proxy_pass http://127.0.0.1:9200; + proxy_read_timeout 90; + } location ~ ^/.*/_search$ { proxy_pass http://127.0.0.1:9200; proxy_read_timeout 90; @@ -53,4 +57,4 @@ server { auth_basic_user_file /etc/nginx/conf.d/kibana.myhost.org.htpasswd; } } -} \ No newline at end of file +} diff --git a/src/app/app.js b/src/app/app.js index 8481e3e150d..e331a1b38e1 100755 --- a/src/app/app.js +++ b/src/app/app.js @@ -6,7 +6,6 @@ define([ 'jquery', 'underscore', 'require', - 'elasticjs', 'bootstrap', 'angular-sanitize', diff --git a/src/app/components/kbn.js b/src/app/components/kbn.js index 9050de5e60f..19c88bc7ca3 100755 --- a/src/app/components/kbn.js +++ b/src/app/components/kbn.js @@ -1,4 +1,4 @@ -define(['jquery', 'underscore','moment'], +define(['jquery','underscore','moment','chromath'], function($, _, moment) { 'use strict'; @@ -14,9 +14,10 @@ function($, _, moment) { }; kbn.get_all_fields = function(data) { + var _d = data; var fields = []; - _.each(data,function(hit) { - fields = _.uniq(fields.concat(_.keys(hit))); + _.each(_d,function(hit) { + fields = _.uniq(fields.concat(_.keys(kbn.flatten_json(hit._source)))); }); // Remove stupid angular key fields = _.without(fields,'$$hashKey'); @@ -315,6 +316,15 @@ function($, _, moment) { } unit = mathString.charAt(i++); switch (unit) { + case 'y': + if (type === 0) { + roundUp ? dateTime.endOf('year') : dateTime.startOf('year'); + } else if (type === 1) { + dateTime.add('years',num); + } else if (type === 2) { + dateTime.subtract('years',num); + } + break; case 'M': if (type === 0) { roundUp ? dateTime.endOf('month') : dateTime.startOf('month'); @@ -458,5 +468,21 @@ function($, _, moment) { ].join(';') + '">'; }; + kbn.colorSteps = function(col,steps) { + + var _d = steps > 5 ? 1.6/steps : 0.3, // distance between steps + _p = []; // adjustment percentage + + // Create a range of numbers between -0.8 and 0.8 + for(var i = 1; i
' + + + '' + + ''+ + ''+ + '' + + '' + - '{{panel.type}}'+ + '}" ng-model="row.panels">'+ '' + '' + '{{panel.type}}'+ '' + - '' + - ''+ - ''+ - '' + - '' + ''+ ''+ diff --git a/src/app/filters/all.js b/src/app/filters/all.js index ec6187f294a..986a6eadc4a 100755 --- a/src/app/filters/all.js +++ b/src/app/filters/all.js @@ -49,7 +49,7 @@ define(['angular', 'jquery', 'underscore', 'moment'], function (angular, $, _, m if(_.isObject(arr) && !_.isArray(arr)) { return angular.toJson(arr); } else { - return arr.toString(); + return _.isNull(arr) ? null : arr.toString(); } }; }); diff --git a/src/app/panels/bettermap/module.js b/src/app/panels/bettermap/module.js index 90ad5a26f38..c6b070319fa 100755 --- a/src/app/panels/bettermap/module.js +++ b/src/app/panels/bettermap/module.js @@ -101,10 +101,11 @@ function (angular, app, _, L, localRequire) { var _segment = _.isUndefined(segment) ? 0 : segment; $scope.panel.queries.ids = querySrv.idsByMode($scope.panel.queries); - // This could probably be changed to a BoolFilter + var queries = querySrv.getQueryObjs($scope.panel.queries.ids); + var boolQuery = $scope.ejs.BoolQuery(); - _.each($scope.panel.queries.ids,function(id) { - boolQuery = boolQuery.should(querySrv.getEjsObj(id)); + _.each(queries,function(q) { + boolQuery = boolQuery.should(querySrv.toEjsObj(q)); }); var request = $scope.ejs.Request().indices(dashboard.indices[_segment]) diff --git a/src/app/panels/derivequeries/editor.html b/src/app/panels/derivequeries/editor.html index 9858498ce45..281c6866c37 100755 --- a/src/app/panels/derivequeries/editor.html +++ b/src/app/panels/derivequeries/editor.html @@ -1,23 +1 @@ -
-
-
- - -
-
- - -
-
- - -
-
- - -
-
- -
-
-
+
\ No newline at end of file diff --git a/src/app/panels/derivequeries/module.html b/src/app/panels/derivequeries/module.html index 3546f40bae0..58e96a13b14 100755 --- a/src/app/panels/derivequeries/module.html +++ b/src/app/panels/derivequeries/module.html @@ -1,33 +1,3 @@
- - -
-
+

This panel has been removed and replaced with the new topN query type. Click the colored dot associated with a query to configure the, much improved, equivilent of a derived query.

\ No newline at end of file diff --git a/src/app/panels/derivequeries/module.js b/src/app/panels/derivequeries/module.js index cf4e2051132..50c338b3f29 100755 --- a/src/app/panels/derivequeries/module.js +++ b/src/app/panels/derivequeries/module.js @@ -22,21 +22,10 @@ function (angular, app, _) { var module = angular.module('kibana.panels.derivequeries', []); app.useModule(module); - module.controller('derivequeries', function($scope, $rootScope, querySrv, fields, dashboard, filterSrv) { + module.controller('derivequeries', function($scope) { $scope.panelMeta = { - modals : [ - { - description: "Inspect", - icon: "icon-info-sign", - partial: "app/partials/inspector.html", - show: $scope.panel.spyable - } - ], - status : "Experimental", - description : "Creates a new set of queries using the Elasticsearch terms facet. For example,"+ - " you might want to create 5 queries showing the most frequent HTTP response codes. Be "+ - "careful not to select a high cardinality field, as Elasticsearch must load all unique values"+ - " into memory." + status : "Deprecated", + description : "This panel has been replaced with the 'topN' mode in the query pull down." }; // Set and populate defaults @@ -59,102 +48,6 @@ function (angular, app, _) { $scope.init = function() { $scope.editing = false; - $scope.panel.fields = fields.list; - }; - - $scope.get_data = function() { - update_history($scope.panel.query); - - // Make sure we have everything for the request to complete - if(dashboard.indices.length === 0) { - return; - } - - $scope.panelMeta.loading = true; - var request = $scope.ejs.Request().indices(dashboard.indices); - - // Terms mode - request = request - .facet($scope.ejs.TermsFacet('query') - .field($scope.panel.field) - .size($scope.panel.size) - .exclude($scope.panel.exclude) - .facetFilter($scope.ejs.QueryFilter( - $scope.ejs.FilteredQuery( - $scope.ejs.QueryStringQuery($scope.panel.query || '*'), - filterSrv.getBoolFilter(filterSrv.ids) - )))).size(0); - - $scope.populate_modal(request); - - var results = request.doSearch(); - - // Populate scope when we have results - results.then(function(results) { - $scope.panelMeta.loading = false; - var suffix; - if ($scope.panel.query === '' || $scope.panel.mode === 'terms only') { - suffix = ''; - } else if ($scope.panel.mode === 'AND') { - suffix = ' AND (' + $scope.panel.query + ')'; - } else if ($scope.panel.mode === 'OR') { - suffix = ' OR (' + $scope.panel.query + ')'; - } - var ids = []; - var terms = results.facets.query.terms; - var others = []; - _.each(terms, function(v) { - var _q = $scope.panel.field+':"'+v.term+'"'+suffix; - // if it isn't in the list, remove it - var _iq = querySrv.findQuery(_q); - if(!_iq) { - ids.push(querySrv.set({alias: v.term, query:_q})); - } else { - ids.push(_iq.id); - } - others.push("NOT (" + _q + ")"); - }); - if ($scope.panel.rest) { - var _other_q = others.join(' AND '); - var _iq = querySrv.findQuery(_other_q); - if (!_iq) { - ids.push(querySrv.set({alias: 'other', query: _other_q})); - } else { - ids.push(_iq.id); - } - } - _.each(_.difference($scope.panel.ids,ids),function(id){ - querySrv.remove(id); - }); - $scope.panel.ids = ids; - dashboard.refresh(); - }); - }; - - $scope.set_refresh = function (state) { - $scope.refresh = state; - }; - - $scope.close_edit = function() { - if($scope.refresh) { - $scope.get_data(); - } - $scope.refresh = false; - }; - - $scope.populate_modal = function(request) { - $scope.inspector = angular.toJson(JSON.parse(request.toString()),true); - }; - - var update_history = function(query) { - query = _.isArray(query) ? query : [query]; - if($scope.panel.remember > 0) { - $scope.panel.history = _.union(query.reverse(),$scope.panel.history); - var _length = $scope.panel.history.length; - if(_length > $scope.panel.remember) { - $scope.panel.history = $scope.panel.history.slice(0,$scope.panel.remember); - } - } }; }); }); \ No newline at end of file diff --git a/src/app/panels/histogram/editor.html b/src/app/panels/histogram/editor.html index 981992c2a97..acf0f854aea 100755 --- a/src/app/panels/histogram/editor.html +++ b/src/app/panels/histogram/editor.html @@ -1,7 +1,7 @@
- +
diff --git a/src/app/panels/histogram/module.html b/src/app/panels/histogram/module.html index ebad97ab5f9..066298a1ca8 100755 --- a/src/app/panels/histogram/module.html +++ b/src/app/panels/histogram/module.html @@ -63,12 +63,6 @@ Lines
-
- -
+ +
+ +
+
diff --git a/src/app/panels/histogram/module.js b/src/app/panels/histogram/module.js index f72ab9b83e9..9756fcff7a6 100755 --- a/src/app/panels/histogram/module.js +++ b/src/app/panels/histogram/module.js @@ -190,14 +190,17 @@ function (angular, app, $, _, kbn, moment, timeSeries) { var request = $scope.ejs.Request().indices(dashboard.indices[segment]); $scope.panel.queries.ids = querySrv.idsByMode($scope.panel.queries); + + var queries = querySrv.getQueryObjs($scope.panel.queries.ids); + // Build the query - _.each($scope.panel.queries.ids, function(id) { + _.each(queries, function(q) { var query = $scope.ejs.FilteredQuery( - querySrv.getEjsObj(id), + querySrv.toEjsObj(q), filterSrv.getBoolFilter(filterSrv.ids) ); - var facet = $scope.ejs.DateHistogramFacet(id); + var facet = $scope.ejs.DateHistogramFacet(q.id); if($scope.panel.mode === 'count') { facet = facet.field($scope.panel.time_field); @@ -220,6 +223,7 @@ function (angular, app, $, _, kbn, moment, timeSeries) { // Populate scope when we have results results.then(function(results) { + $scope.panelMeta.loading = false; if(segment === 0) { $scope.hits = 0; @@ -233,18 +237,15 @@ function (angular, app, $, _, kbn, moment, timeSeries) { return; } - // Convert facet ids to numbers - var facetIds = _.map(_.keys(results.facets),function(k){return parseInt(k, 10);}); - // Make sure we're still on the same query/queries - if($scope.query_id === query_id && _.difference(facetIds, $scope.panel.queries.ids).length === 0) { + if($scope.query_id === query_id) { var i = 0, time_series, hits; - _.each($scope.panel.queries.ids, function(id) { - var query_results = results.facets[id]; + _.each(queries, function(q) { + var query_results = results.facets[q.id]; // we need to initialize the data variable on the first run, // and when we are working on the first segment of the data. if(_.isUndefined($scope.data[i]) || segment === 0) { @@ -267,7 +268,7 @@ function (angular, app, $, _, kbn, moment, timeSeries) { $scope.hits += entry.count; // Entire dataset level hits counter }); $scope.data[i] = { - info: querySrv.list[id], + info: q, time_series: time_series, hits: hits }; diff --git a/src/app/panels/hits/module.js b/src/app/panels/hits/module.js index 066a0815758..09b9ee2ae9d 100755 --- a/src/app/panels/hits/module.js +++ b/src/app/panels/hits/module.js @@ -84,14 +84,16 @@ define([ var request = $scope.ejs.Request().indices(dashboard.indices[_segment]); $scope.panel.queries.ids = querySrv.idsByMode($scope.panel.queries); + var queries = querySrv.getQueryObjs($scope.panel.queries.ids); + // Build the question part of the query - _.each($scope.panel.queries.ids, function(id) { + _.each(queries, function(q) { var _q = $scope.ejs.FilteredQuery( - querySrv.getEjsObj(id), + querySrv.toEjsObj(q), filterSrv.getBoolFilter(filterSrv.ids)); request = request - .facet($scope.ejs.QueryFacet(id) + .facet($scope.ejs.QueryFacet(q.id) .query(_q) ).size(0); }); @@ -117,24 +119,19 @@ define([ return; } - // Convert facet ids to numbers - var facetIds = _.map(_.keys(results.facets),function(k){return parseInt(k, 10);}); - // Make sure we're still on the same query/queries - if($scope.query_id === query_id && - _.intersection(facetIds,$scope.panel.queries.ids).length === $scope.panel.queries.ids.length - ) { + if($scope.query_id === query_id) { var i = 0; - _.each($scope.panel.queries.ids, function(id) { - var v = results.facets[id]; + _.each(queries, function(q) { + var v = results.facets[q.id]; var hits = _.isUndefined($scope.data[i]) || _segment === 0 ? v.count : $scope.data[i].hits+v.count; $scope.hits += v.count; // Create series $scope.data[i] = { - info: querySrv.list[id], - id: id, + info: q, + id: q.id, hits: hits, data: [[i,hits]] }; diff --git a/src/app/panels/map/module.js b/src/app/panels/map/module.js index 02f5268281f..f13ed5fc7b9 100755 --- a/src/app/panels/map/module.js +++ b/src/app/panels/map/module.js @@ -76,14 +76,17 @@ function (angular, app, _, $) { $scope.panelMeta.loading = true; - var request; - request = $scope.ejs.Request().indices(dashboard.indices); + var request, + boolQuery, + queries; $scope.panel.queries.ids = querySrv.idsByMode($scope.panel.queries); - // This could probably be changed to a BoolFilter - var boolQuery = $scope.ejs.BoolQuery(); - _.each($scope.panel.queries.ids,function(id) { - boolQuery = boolQuery.should(querySrv.getEjsObj(id)); + request = $scope.ejs.Request().indices(dashboard.indices); + queries = querySrv.getQueryObjs($scope.panel.queries.ids); + + boolQuery = $scope.ejs.BoolQuery(); + _.each(queries,function(q) { + boolQuery = boolQuery.should(querySrv.toEjsObj(q)); }); // Then the insert into facet and make the request diff --git a/src/app/panels/pie/module.js b/src/app/panels/pie/module.js index 46feff92c97..216a6b19839 100755 --- a/src/app/panels/pie/module.js +++ b/src/app/panels/pie/module.js @@ -109,13 +109,14 @@ define([ var request = $scope.ejs.Request().indices(dashboard.indices); $scope.panel.queries.ids = querySrv.idsByMode($scope.panel.queries); + var queries = querySrv.getQueryObjs($scope.panel.queries.ids); + // This could probably be changed to a BoolFilter var boolQuery = $scope.ejs.BoolQuery(); - _.each($scope.panel.queries.ids,function(id) { - boolQuery = boolQuery.should(querySrv.getEjsObj(id)); + _.each(queries,function(q) { + boolQuery = boolQuery.should(querySrv.toEjsObj(q)); }); - var results; // Terms mode diff --git a/src/app/panels/query/editors/lucene.html b/src/app/panels/query/editors/lucene.html new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/app/panels/query/editors/regex.html b/src/app/panels/query/editors/regex.html new file mode 100644 index 00000000000..e69de29bb2d diff --git a/src/app/panels/query/editors/topN.html b/src/app/panels/query/editors/topN.html new file mode 100644 index 00000000000..71496541906 --- /dev/null +++ b/src/app/panels/query/editors/topN.html @@ -0,0 +1,12 @@ +
+
+ +

+
+ +

+
+ +

diff --git a/src/app/panels/query/help/lucene.html b/src/app/panels/query/help/lucene.html new file mode 100644 index 00000000000..9e860e97d47 --- /dev/null +++ b/src/app/panels/query/help/lucene.html @@ -0,0 +1,30 @@ +The lucene query type uses LUCENE query string syntax to find matching documents or events within Elasticsearch. + +

Examples

+
    +
  • + status field contains active +

    status:active
  • +
  • + title field contains quick or brown +

    title:(quick brown)
  • +
  • + author field contains the exact phrase "john smith" +

    author:"John Smith"
  • +
+ +

Wildcard searches can be run on individual terms, using ? to replace +a single character, and * to replace zero or more characters:

+
qu?ck bro*
+ +
    +
  • + Numbers 1..5 +

    count:[1 TO 5]
  • +
  • + Tags between alpha and omega, excluding alpha and omega: +

    tag:{alpha TO omega}
  • +
  • + Numbers from 10 upwards +

    count:[10 TO *]
  • +
diff --git a/src/app/panels/query/help/regex.html b/src/app/panels/query/help/regex.html new file mode 100644 index 00000000000..8e38ab96fe2 --- /dev/null +++ b/src/app/panels/query/help/regex.html @@ -0,0 +1,10 @@ +The regex query allows you to use regular expressions to match terms in the _all field. + +A detailed overview of lucene's regex engine is available here: Regular expressions in Elasticsearch + +
A note on anchoring
+Lucene’s patterns are always anchored. The pattern provided must match the entire string. For string "abcde": +

+ab.* will match
+abcd will not match
+ diff --git a/src/app/panels/query/help/topN.html b/src/app/panels/query/help/topN.html new file mode 100644 index 00000000000..9fc7c3aa8e0 --- /dev/null +++ b/src/app/panels/query/help/topN.html @@ -0,0 +1,14 @@ +The topN query uses an Elasticsearch terms facet to find the most common terms in a field and build queries from the result. The topN query uses LUCENE query string syntax + +

Parameters

+
    +
  • + Field / The field to facet on. Fields with a large number of unique terms will use more memory to calculate. +
  • +
  • + Count / How many queries to generate. The resulting queries will use brightness variations on the original query's color for their own. +
  • +
  • + Union / The relation the generated queries have to the original. For example, if your field was set to 'extension', your original query was "user:B.Awesome" and your union was AND. Kibana might generate the following example query: extension:"html" AND (user:B.Awesome) +
  • +
\ No newline at end of file diff --git a/src/app/panels/query/helpModal.html b/src/app/panels/query/helpModal.html new file mode 100644 index 00000000000..3cbbf8cdf7a --- /dev/null +++ b/src/app/panels/query/helpModal.html @@ -0,0 +1,12 @@ + + + \ No newline at end of file diff --git a/src/app/panels/query/meta.html b/src/app/panels/query/meta.html index 5a383ce2955..3113f7f0e8e 100755 --- a/src/app/panels/query/meta.html +++ b/src/app/panels/query/meta.html @@ -1,28 +1,34 @@ -
+
- × - - - -
- -
- +
+   About the {{querySrv.list[id].type}} query - - - - +
+ + + +
+ +
+ + +
+
+ +
+ + +
+ + Pin + +
\ No newline at end of file diff --git a/src/app/panels/query/module.html b/src/app/panels/query/module.html index 3cfc5f3a5d0..b00917f8527 100755 --- a/src/app/panels/query/module.html +++ b/src/app/panels/query/module.html @@ -2,7 +2,7 @@