Просмотр исходного кода

Merge remote-tracking branch 'upstream/master'

Zachary Tong 12 лет назад
Родитель
Сommit
f8c5e464f9

+ 4 - 0
common/css/main.css

@@ -106,6 +106,10 @@
   font-size: 85%;
   font-size: 85%;
 }
 }
 
 
+.input-append label {
+  font-size: inherit !important;
+}
+
 .large {
 .large {
   font-size: 120%;
   font-size: 120%;
 }
 }

Разница между файлами не показана из-за своего большого размера
+ 0 - 0
common/lib/d3.v3.min.js


+ 1 - 1
config.js

@@ -16,6 +16,6 @@ var config = new Settings(
   kibana_index:   "kibana-int", 
   kibana_index:   "kibana-int", 
   modules:        ['histogram','map','map2','pie','table','stringquery','sort',
   modules:        ['histogram','map','map2','pie','table','stringquery','sort',
                   'timepicker','text','fields','hits','dashcontrol',
                   'timepicker','text','fields','hits','dashcontrol',
-                  'column'], 
+                  'column', 'parallelcoordinates'],
   }
   }
 );
 );

+ 66 - 54
dashboards/default

@@ -5,7 +5,7 @@
       "title": "Options",
       "title": "Options",
       "height": "50px",
       "height": "50px",
       "editable": true,
       "editable": true,
-      "collapse": true,
+      "collapse": false,
       "collapsable": true,
       "collapsable": true,
       "panels": [
       "panels": [
         {
         {
@@ -28,7 +28,7 @@
             "2d",
             "2d",
             "5d"
             "5d"
           ],
           ],
-          "timespan": "15m",
+          "timespan": "6h",
           "timefield": "@timestamp",
           "timefield": "@timestamp",
           "index": "\"logstash-\"yyyy.mm.dd",
           "index": "\"logstash-\"yyyy.mm.dd",
           "defaultindex": "NOINDEX",
           "defaultindex": "NOINDEX",
@@ -37,39 +37,7 @@
             "enable": false,
             "enable": false,
             "interval": 30,
             "interval": 30,
             "min": 3
             "min": 3
-          },
-          "time": {
-            "from": "04/03/2013 08:58:07",
-            "to": "04/03/2013 09:13:07"
           }
           }
-        }
-      ]
-    },
-    {
-      "title": "Query Control",
-      "height": "50px",
-      "editable": true,
-      "collapse": false,
-      "collapsable": true,
-      "panels": [
-        {
-          "loading": false,
-          "error": false,
-          "span": 7,
-          "editable": true,
-          "group": [
-            "default"
-          ],
-          "type": "stringquery",
-          "label": "Search",
-          "query": "",
-          "size": 100,
-          "sort": [
-            "_score",
-            "desc"
-          ],
-          "multi": false,
-          "multi_arrange": "horizontal"
         },
         },
         {
         {
           "loading": false,
           "loading": false,
@@ -96,36 +64,46 @@
           "elasticsearch_saveto": "kibana-int",
           "elasticsearch_saveto": "kibana-int",
           "temp": true,
           "temp": true,
           "temp_ttl": "30d"
           "temp_ttl": "30d"
-        },
+        }
+      ]
+    },
+    {
+      "title": "Query",
+      "height": "50px",
+      "editable": true,
+      "collapse": false,
+      "collapsable": true,
+      "panels": [
         {
         {
           "loading": false,
           "loading": false,
           "error": false,
           "error": false,
-          "span": 2,
+          "span": 12,
           "editable": true,
           "editable": true,
           "group": [
           "group": [
             "default"
             "default"
           ],
           ],
-          "type": "hits",
+          "type": "stringquery",
+          "label": "Search",
           "query": "*",
           "query": "*",
-          "style": {
-            "font-size": "24pt",
-            "font-weight": "bold"
-          },
-          "run_query": false,
-          "title": "Hits"
+          "size": 100,
+          "sort": [
+            "_score",
+            "desc"
+          ],
+          "multi": false,
+          "multi_arrange": "horizontal"
         }
         }
       ]
       ]
     },
     },
     {
     {
       "title": "Graph",
       "title": "Graph",
-      "height": "150px",
+      "height": "350px",
       "editable": true,
       "editable": true,
       "collapse": false,
       "collapse": false,
       "collapsable": true,
       "collapsable": true,
       "panels": [
       "panels": [
         {
         {
           "loading": false,
           "loading": false,
-          "error": false,
           "span": 12,
           "span": 12,
           "editable": true,
           "editable": true,
           "group": [
           "group": [
@@ -134,11 +112,11 @@
           "type": "histogram",
           "type": "histogram",
           "query": [
           "query": [
             {
             {
-              "query": "",
-              "label": ""
+              "query": "*",
+              "label": "*"
             }
             }
           ],
           ],
-          "interval": "30s",
+          "interval": "5m",
           "show": [
           "show": [
             "points",
             "points",
             "lines",
             "lines",
@@ -150,9 +128,43 @@
           "spyable": true,
           "spyable": true,
           "zoomlinks": true,
           "zoomlinks": true,
           "index": [
           "index": [
-            "logstash-2013.04.03"
+            "logstash-2013.04.11"
+          ],
+          "fill": 0,
+          "linewidth": 2,
+          "bars": true,
+          "stack": true,
+          "points": false,
+          "lines": false,
+          "legend": true,
+          "x-axis": true,
+          "y-axis": true
+        },
+        {
+          "loading": false,
+          "span": 0,
+          "editable": true,
+          "group": [
+            "default"
           ],
           ],
-          "fill": 1
+          "type": "hits",
+          "query": [
+            {
+              "query": "*",
+              "label": "*"
+            }
+          ],
+          "style": {
+            "font-size": "9pt"
+          },
+          "aggregate": false,
+          "arrangement": "horizontal",
+          "chart": true,
+          "counters": true,
+          "count_pos": "above",
+          "index": [
+            "logstash-2013.04.11"
+          ]
         }
         }
       ]
       ]
     },
     },
@@ -179,7 +191,7 @@
             "@timestamp",
             "@timestamp",
             "desc"
             "desc"
           ],
           ],
-          "query": ""
+          "query": "*"
         },
         },
         {
         {
           "loading": false,
           "loading": false,
@@ -190,7 +202,7 @@
             "default"
             "default"
           ],
           ],
           "type": "table",
           "type": "table",
-          "query": "",
+          "query": "*",
           "interval": "1y",
           "interval": "1y",
           "show": [
           "show": [
             "bars",
             "bars",
@@ -218,11 +230,11 @@
           ],
           ],
           "sortable": true,
           "sortable": true,
           "index": [
           "index": [
-            "logstash-2013.04.03"
+            "logstash-2013.04.11"
           ]
           ]
         }
         }
       ]
       ]
     }
     }
   ],
   ],
   "editable": true
   "editable": true
-}
+}

+ 4 - 0
panels/fields/module.js

@@ -22,6 +22,10 @@ angular.module('kibana.fields', [])
       $scope.panel.query = docs.query;
       $scope.panel.query = docs.query;
       $scope.docs = docs.docs;
       $scope.docs = docs.docs;
     });
     });
+    eventBus.register($scope,"get_fields", function(event,id) {
+        console.log("field.get_fields");
+      eventBus.broadcast($scope.$id,$scope.panel.group,"selected_fields",$scope.active);
+    });
   }
   }
 
 
   $scope.reload_list = function () {
   $scope.reload_list = function () {

+ 2 - 8
panels/histogram/module.js

@@ -249,7 +249,7 @@ angular.module('kibana.histogram', [])
               },
               },
               selection: {
               selection: {
                 mode: "x",
                 mode: "x",
-                color: '#666'
+                color: '#999'
               },
               },
               grid: {
               grid: {
                 backgroundColor: '#fff',
                 backgroundColor: '#fff',
@@ -258,13 +258,7 @@ angular.module('kibana.histogram', [])
                 color: "#eee",
                 color: "#eee",
                 hoverable: true,
                 hoverable: true,
               },
               },
-              colors: ['#86B22D',
-                      '#BF6730',
-                      '#1D7373',
-                      '#BFB930',
-                      '#BF3030',
-                      '#77207D'
-                      ]
+              colors: ['#86B22D','#BF6730','#1D7373','#BFB930','#BF3030','#77207D']
             })
             })
             
             
             // Work around for missing legend at initialization
             // Work around for missing legend at initialization

+ 21 - 7
panels/hits/editor.html

@@ -1,18 +1,32 @@
 <div ng-controller="hits">
 <div ng-controller="hits">
   <div class="row-fluid">
   <div class="row-fluid">
-    <div class="span2   "><label class="small">Font Size</label> 
+    <div class="span3"> 
+      <label class="small">Style</label> 
+      <select class="input-small" ng-model="panel.chart" ng-options="f for f in ['bar','pie','list','total']"></select></span>
+    </div>
+    <div class="span2" ng-show="panel.chart == 'total' || panel.chart == 'list'">
+      <label class="small">Font Size</label> 
       <select class="input-mini" ng-model="panel.style['font-size']" ng-options="f for f in ['7pt','8pt','9pt','10pt','12pt','14pt','16pt','18pt','20pt','24pt','28pt','32pt','36pt','42pt','48pt','52pt','60pt','72pt']"></select></span>
       <select class="input-mini" ng-model="panel.style['font-size']" ng-options="f for f in ['7pt','8pt','9pt','10pt','12pt','14pt','16pt','18pt','20pt','24pt','28pt','32pt','36pt','42pt','48pt','52pt','60pt','72pt']"></select></span>
     </div>
     </div>
-    <div class="span2"> 
-      <label class="small">Aggregate</label><input type="checkbox" ng-model="panel.aggregate" ng-checked="panel.aggregate">
+    <div class="span3" ng-show="panel.chart == 'bar' || panel.chart == 'pie'"> 
+      <label class="small">Legend</label> 
+      <select class="input-small" ng-model="panel.counter_pos" ng-options="f for f in ['above','below','none']"></select></span>
+    </div>
+    <div class="span3" ng-show="panel.chart != 'total' && panel.counter_pos != 'none'">
+      <label class="small" >List Format</label> 
+      <select class="input-small" ng-model="panel.arrangement" ng-options="f for f in ['horizontal','vertical']"></select></span>
+    </div>
+    <div class="span1" ng-show='panel.chart == "pie"'>
+      <label class="small">Donut</label><input type="checkbox" ng-model="panel.donut" ng-checked="panel.donut">
     </div>
     </div>
-    <div class="span3" ng-show="!panel.aggregate"><label class="small">Counter Style</label> 
-      <select class="input-small" ng-model="panel.arrangement" ng-options="f for f in ['none','horizontal','vertical']"></select></span>
+    <div class="span1" ng-show='panel.chart == "pie"'>
+      <label class="small">Tilt</label><input type="checkbox" ng-model="panel.tilt" ng-checked="panel.tilt">
     </div>
     </div>
-    <div class="span2" ng-show="!panel.aggregate"> 
-      <label class="small">Chart</label><input type="checkbox" ng-model="panel.chart" ng-checked="panel.chart">
+    <div class="span1" ng-show='panel.chart == "pie"'>
+      <label class="small">Labels</label><input type="checkbox" ng-model="panel.labels" ng-checked="panel.labels">
     </div>
     </div>
   </div>
   </div>
+  <h5>Queries</h5>    
   <div class="row-fluid">
   <div class="row-fluid">
     <div class="span3">
     <div class="span3">
       <form style="margin-bottom: 0px">
       <form style="margin-bottom: 0px">

+ 37 - 9
panels/hits/module.html

@@ -1,14 +1,42 @@
 <kibana-panel ng-controller='hits' ng-init="init()">
 <kibana-panel ng-controller='hits' ng-init="init()">
-  <div ng-show="panel.counters">
-    <p ng-style="panel.style" ng-show="panel.aggregate">{{hits}}</p>
-    <table ng-style="panel.style" ng-show="!panel.aggregate && panel.arrangement == 'vertical'">  
-      <tr style="line-height:{{panel.style['font-size']}}" ng-repeat="query in plot.getData()">
-        <td ng-show="panel.chart" style="background:{{query.color}};width:{{panel.style['font-size']}}"></td> <td style="padding-right:10px;padding-left:10px;">{{query.label}}</td><td>{{query.hits}}</td>
+
+  <div ng-show="panel.counter_pos == 'above' && (panel.chart == 'bar' || panel.chart == 'pie')" id='{{$id}}-legend'>
+    <!-- vertical legend -->
+    <table class="small" ng-show="panel.arrangement == 'vertical'">  
+      <tr ng-repeat="query in plot.getData()">
+        <td><div style="display:inline-block;border-radius:5px;background:{{query.color}};height:10px;width:10px"></div></td> <td style="padding-right:10px;padding-left:10px;">{{query.label}}</td><td>{{query.data[0][1]}}</td>
       </tr>
       </tr>
     </table>
     </table>
-    <div ng-style="panel.style" ng-show="!panel.aggregate && panel.arrangement == 'horizontal'" ng-repeat="query in plot.getData()" style="float:left;padding-left: 10px;">
-     <span ng-show='panel.chart'><div style="display:inline-block;border-radius:{{panel.style['font-size']}};background:{{query.color}};height:{{panel.style['font-size']}};width:{{panel.style['font-size']}}"></div></span> {{query.label}} ({{query.hits}}) <span ng-show="!$last">|</span>
+
+    <!-- horizontal legend -->
+    <div class="small" ng-show="panel.arrangement == 'horizontal'" ng-repeat="query in plot.getData()" style="float:left;padding-left: 10px;">
+     <span><div style="display:inline-block;border-radius:5px;background:{{query.color}};height:10px;width:10px"></div></span> {{query.label}} ({{query.data[0][1]}}) </span>
     </div><br>
     </div><br>
-  </div><div style="clear:both"></div>
-  <div ng-show='panel.chart && !panel.aggregate ' hits-chart params="{{panel}}" style="height:{{panel.height || row.height}};position:relative"></div>
+
+  </div>
+
+  <div style="clear:both"></div>
+
+  <div ng-show="panel.chart == 'pie' || panel.chart == 'bar'" hits-chart params="{{panel}}" style="height:{{panel.height || row.height}};position:relative"></div>
+
+  <div ng-show="panel.counter_pos == 'below' && (panel.chart == 'bar' || panel.chart == 'pie')" id='{{$id}}-legend'>
+
+    <!-- vertical legend -->
+    <table class="small" ng-show="panel.arrangement == 'vertical'">  
+      <tr ng-repeat="query in plot.getData()">
+        <td><div style="display:inline-block;border-radius:5px;background:{{query.color}};height:10px;width:10px"></div></td> <td style="padding-right:10px;padding-left:10px;">{{query.label}}</td><td>{{query.data[0][1]}}</td>
+      </tr>
+    </table>
+
+    <!-- horizontal legend -->
+    <div class="small" ng-show="panel.arrangement == 'horizontal'" ng-repeat="query in plot.getData()" style="float:left;padding-left: 10px;">
+     <span><div style="display:inline-block;border-radius:5px;background:{{query.color}};height:10px;width:10px"></div></span> {{query.label}} ({{query.data[0][1]}}) </span>
+    </div><br>
+
+  </div>
+
+  <div ng-show="panel.chart == 'total'"><div ng-style="panel.style" style="line-height:{{panel.style['font-size']}}">{{hits}}</div></div>
+
+  <span ng-show="panel.chart == 'list'"><span ng-style="panel.style" style="line-height:{{panel.style['font-size']}}" ng-repeat="query in data">{{query.label}} ({{query.hits}})<span></span><br ng-show="panel.arrangement == 'vertical' && panel.chart == 'list'">
+
 </kibana-panel>         
 </kibana-panel>         

+ 64 - 27
panels/hits/module.js

@@ -8,9 +8,11 @@ angular.module('kibana.hits', [])
     style   : { "font-size": '10pt'},
     style   : { "font-size": '10pt'},
     aggregate   : true,
     aggregate   : true,
     arrangement : 'vertical',
     arrangement : 'vertical',
-    chart   : true,
-    counters: true,
-    count_pos: 'above'
+    chart       : 'none',
+    counter_pos : 'above',
+    donut   : false,
+    tilt    : false,
+    labels  : true
   }
   }
   _.defaults($scope.panel,_d)
   _.defaults($scope.panel,_d)
 
 
@@ -96,7 +98,6 @@ angular.module('kibana.hits', [])
 
 
           i++;
           i++;
         });
         });
-
         $scope.$emit('render');
         $scope.$emit('render');
         if(_segment < $scope.panel.index.length-1) 
         if(_segment < $scope.panel.index.length-1) 
           $scope.get_data(_segment+1,query_id)
           $scope.get_data(_segment+1,query_id)
@@ -143,31 +144,65 @@ angular.module('kibana.hits', [])
       function render_panel() {
       function render_panel() {
 
 
         var scripts = $LAB.script("common/lib/panels/jquery.flot.js")
         var scripts = $LAB.script("common/lib/panels/jquery.flot.js")
-                    
-        // Populate element. Note that jvectormap appends, does not replace.
+                          .script("common/lib/panels/jquery.flot.pie.js")
+
+        // Populate element.
         scripts.wait(function(){
         scripts.wait(function(){
           // Populate element
           // Populate element
           try {
           try {
             // Add plot to scope so we can build out own legend 
             // Add plot to scope so we can build out own legend 
-            scope.plot = $.plot(elem, scope.data, {
-              legend: { show: false },
-              series: {
-                lines:  { show: false, },
-                bars:   { show: true,  fill: 1, barWidth: 0.8, horizontal: false },
-                shadowSize: 1
-              },
-              yaxis: { show: true, min: 0, color: "#000" },
-              xaxis: { show: false },
-              grid: {
-                backgroundColor: '#fff',
-                borderWidth: 0,
-                borderColor: '#eee',
-                color: "#eee",
-                hoverable: true,
-              },
-              colors: ['#86B22D','#BF6730','#1D7373','#BFB930','#BF3030','#77207D']
-            })
-            
+            if(scope.panel.chart === 'bar')
+              scope.plot = $.plot(elem, scope.data, {
+                legend: { show: false },
+                series: {
+                  lines:  { show: false, },
+                  bars:   { show: true,  fill: 1, barWidth: 0.8, horizontal: false },
+                  shadowSize: 1
+                },
+                yaxis: { show: true, min: 0, color: "#000" },
+                xaxis: { show: false },
+                grid: {
+                  backgroundColor: '#fff',
+                  borderWidth: 0,
+                  borderColor: '#eee',
+                  color: "#eee",
+                  hoverable: true,
+                },
+                colors: ['#86B22D','#BF6730','#1D7373','#BFB930','#BF3030','#77207D']
+              })
+            if(scope.panel.chart === 'pie')
+              scope.plot = $.plot(elem, scope.data, {
+                legend: { show: false },
+                series: {
+                  pie: {
+                    innerRadius: scope.panel.donut ? 0.4 : 0,
+                    tilt: scope.panel.tilt ? 0.45 : 1,
+                    radius: 1,
+                    show: true,
+                    combine: {
+                      color: '#999',
+                      label: 'The Rest'
+                    },
+                    label: { 
+                      show: scope.panel.labels,
+                      radius: 2/3,
+                      formatter: function(label, series){
+                        return '<div ng-click="build_search(panel.query.field,\''+label+'\') "style="font-size:8pt;text-align:center;padding:2px;color:white;">'+
+                          label+'<br/>'+Math.round(series.percent)+'%</div>';
+                      },
+                      threshold: 0.1 
+                    }
+                  }
+                },
+                //grid: { hoverable: true, clickable: true },
+                grid:   { hoverable: true, clickable: true },
+                colors: ['#86B22D','#BF6730','#1D7373','#BFB930','#BF3030','#77207D']
+              });
+
+            // Compensate for the height of the  legend. Gross
+            elem.height(
+              (scope.panel.height || scope.row.height).replace('px','') - $("#"+scope.$id+"-legend").height())
+
             // Work around for missing legend at initialization
             // Work around for missing legend at initialization
             if(!scope.$$phase)
             if(!scope.$$phase)
               scope.$apply()
               scope.$apply()
@@ -198,9 +233,11 @@ angular.module('kibana.hits', [])
 
 
       elem.bind("plothover", function (event, pos, item) {
       elem.bind("plothover", function (event, pos, item) {
         if (item) {
         if (item) {
+          var value = scope.panel.chart === 'bar' ? 
+            item.datapoint[1] : item.datapoint[1][0][1];
           tt(pos.pageX, pos.pageY,
           tt(pos.pageX, pos.pageY,
-            "<div style='vertical-align:middle;border-radius:10px;display:inline-block;background:"+item.series.color+";height:20px;width:20px'></div> "+
-            item.datapoint[1].toFixed(0))
+            "<div style='vertical-align:middle;border-radius:10px;display:inline-block;background:"+
+            item.series.color+";height:20px;width:20px'></div> "+value.toFixed(0))
         } else {
         } else {
           $("#pie-tooltip").remove();
           $("#pie-tooltip").remove();
         }
         }

+ 1 - 1
panels/map/module.js

@@ -82,7 +82,7 @@ angular.module('kibana.map', [])
   $scope.build_search = function(field,value) {
   $scope.build_search = function(field,value) {
     $scope.panel.query = add_to_query($scope.panel.query,field,value,false)
     $scope.panel.query = add_to_query($scope.panel.query,field,value,false)
     $scope.get_data();
     $scope.get_data();
-    eventBus.broadcast($scope.$id,$scope.panel.group,'query',$scope.panel.query);
+    eventBus.broadcast($scope.$id,$scope.panel.group,'query',[$scope.panel.query]);
   }
   }
 
 
 })
 })

+ 21 - 0
panels/parallelcoordinates/editor.html

@@ -0,0 +1,21 @@
+<div class="row-fluid" ng-controller="parallelcoordinates">
+    <div style="width:90%">
+        <form class="input-append">
+            <h6>Query</h6>
+            <input type="text" style="width:90%" ng-model="panel.query">
+            <button class="btn" ng-click="get_data();"><i class="icon-search"></i></button>
+        </form>
+    </div>
+</div>
+<h5>Panel Spy</h5>
+<div class="row-fluid">
+    <div class="span2">
+        <label class="small"> Spyable </label><input type="checkbox" ng-model="panel.spyable" ng-checked="panel.spyable">
+    </div>
+    <div class="span9 small">
+        The panel spy shows 'behind the scenes' information about a panel. It can
+        be accessed by clicking the <i class='icon-eye-open'></i> in the top right
+        of the panel.
+    </div>
+</div>
+

+ 42 - 0
panels/parallelcoordinates/module.html

@@ -0,0 +1,42 @@
+<style>
+    svg {
+        font-size: 14px;
+    }
+
+    .foregroundlines {
+        fill: none;
+        stroke-opacity: 0.3;
+        stroke-width: 1.5px;
+    }
+
+    .foregroundlines.fade {
+        display:none
+    }
+
+    .legend line {
+        stroke-width: 2px;
+    }
+
+    .brush .extent {
+        fill-opacity: .3;
+        stroke: #fff;
+        shape-rendering: crispEdges;
+    }
+
+    .axis line, .axis path {
+        fill: none;
+        stroke: #000;
+        shape-rendering: crispEdges;
+    }
+
+    .axis text {
+        text-shadow: 0 1px 0 #fff;
+        cursor: move;
+    }
+</style>
+<kibana-panel ng-controller='parallelcoordinates' ng-init="init()">
+  <span ng-show="panel.spyable" style="position:absolute;right:0px;top:0px" class='panelextra pointer'>
+    <i bs-modal="'partials/modal.html'" class="icon-eye-open"></i>
+  </span>
+    <div parallelcoordinates params="{{panel}}" style="height:{{panel.height || row.height}}"></div>
+</kibana-panel>

+ 444 - 0
panels/parallelcoordinates/module.js

@@ -0,0 +1,444 @@
+angular.module('kibana.parallelcoordinates', [])
+  .controller('parallelcoordinates', function ($scope, eventBus) {
+
+
+    $scope.activeDocs = [];
+
+    // Set and populate defaults
+    var _d = {
+      query   : "*",
+      size    : 100, // Per page
+      pages   : 5,   // Pages available
+      offset  : 0,
+      sort    : ['@timestamp','desc'],
+      group   : "default",
+      style   : {'font-size': '9pt'},
+      fields  : [],
+      sortable: true,
+      spyable: true
+    }
+
+    _.defaults($scope.panel, _d)
+
+    $scope.init = function () {
+
+      $scope.set_listeners($scope.panel.group);
+      // Now that we're all setup, request the time from our group
+      eventBus.broadcast($scope.$id,$scope.panel.group,"get_time")
+
+      //and get the currently selected fields
+      eventBus.broadcast($scope.$id,$scope.panel.group,"get_fields")
+    };
+
+    $scope.set_listeners = function(group) {
+      eventBus.register($scope,'time',function(event,time) {
+        $scope.panel.offset = 0;
+        set_time(time)
+      });
+      eventBus.register($scope,'query',function(event,query) {
+        $scope.panel.offset = 0;
+        $scope.panel.query = _.isArray(query) ? query[0] : query;
+        $scope.get_data();
+      });
+      eventBus.register($scope,'sort', function(event,sort){
+        $scope.panel.sort = _.clone(sort);
+        $scope.get_data();
+      });
+      eventBus.register($scope,'selected_fields', function(event, fields) {
+        $scope.panel.fields = _.clone(fields)
+        $scope.$emit('render');
+      });
+    };
+
+
+    $scope.get_data = function (segment,query_id) {
+
+      // Make sure we have everything for the request to complete
+      if (_.isUndefined($scope.panel.index) || _.isUndefined($scope.time))
+        return;
+
+      var _segment = _.isUndefined(segment) ? 0 : segment
+      $scope.segment = _segment;
+
+      $scope.panel.loading = true;
+      var request = $scope.ejs.Request().indices($scope.panel.index[_segment])
+        .query(ejs.FilteredQuery(
+          ejs.QueryStringQuery($scope.panel.query || '*'),
+          ejs.RangeFilter($scope.time.field)
+            .from($scope.time.from)
+            .to($scope.time.to)
+        )
+        )
+        .size($scope.panel.size*$scope.panel.pages)
+        .sort($scope.panel.sort[0],$scope.panel.sort[1]);
+
+      $scope.populate_modal(request);
+
+      var results = request.doSearch();
+
+
+      // Populate scope when we have results
+      results.then(function (results) {
+        $scope.panel.loading = false;
+        if(_segment === 0) {
+          $scope.hits = 0;
+          $scope.data = [];
+          query_id = $scope.query_id = new Date().getTime()
+        }
+
+        // Check for error and abort if found
+        if(!(_.isUndefined(results.error))) {
+          $scope.panel.error = $scope.parse_error(results.error);
+          return;
+        }
+
+        // Check that we're still on the same query, if not stop
+        if($scope.query_id === query_id) {
+          $scope.data= $scope.data.concat(_.map(results.hits.hits, function(hit) {
+            return flatten_json(hit['_source']);
+          }));
+
+          $scope.hits += results.hits.total;
+
+          // Sort the data
+          $scope.data = _.sortBy($scope.data, function(v){
+            return v[$scope.panel.sort[0]]
+          });
+
+          // Reverse if needed
+          if($scope.panel.sort[1] == 'desc')
+            $scope.data.reverse();
+
+          // Keep only what we need for the set
+          $scope.data = $scope.data.slice(0,$scope.panel.size * $scope.panel.pages)
+
+        } else {
+          return;
+        }
+        $scope.$emit('render')
+      });
+
+
+
+    };
+
+    // I really don't like this function, too much dom manip. Break out into directive?
+    $scope.populate_modal = function (request) {
+      $scope.modal = {
+        title: "Inspector",
+        body: "<h5>Last Elasticsearch Query</h5><pre>" + 'curl -XGET ' + config.elasticsearch + '/' + $scope.panel.index + "/_search?pretty -d'\n" + angular.toJson(JSON.parse(request.toString()), true) + "'</pre>"
+      }
+    };
+
+    function set_time(time) {
+      $scope.time = time;
+      $scope.panel.index = _.isUndefined(time.index) ? $scope.panel.index : time.index
+      $scope.get_data();
+    }
+
+
+    $scope.$watch('activeDocs', function(v) {
+      eventBus.broadcast($scope.$id,$scope.panel.group,"table_documents",
+        {query:$scope.panel.query,docs:$scope.activeDocs});
+    });
+
+  })
+  .directive('parallelcoordinates', function () {
+    return {
+      restrict: 'A',
+      link: function (scope, elem, attrs) {
+
+        //used to store a variety of directive-level variables
+        var directive = {};
+
+        scope.initializing = false;
+
+
+        /**
+         * Initialize the panels if new, or render existing panels
+         */
+        scope.init_or_render = function() {
+          if (typeof directive.svg === 'undefined') {
+
+            //prevent duplicate initialization steps, if render is called again
+            //before the svg is setup
+            if (!scope.initializing) {
+              init_panel();
+            }
+          } else {
+            render_panel();
+          }
+        };
+
+
+        /**
+         * Receive render events
+         */
+        scope.$on('render', function () {
+          scope.init_or_render();
+        });
+
+        /**
+         * On window resize, re-render the panel
+         */
+        angular.element(window).bind('resize', function () {
+          scope.init_or_render();
+        });
+
+
+        /**
+         * Load the various panel-specific scripts then initialize
+         * the svg and set appropriate D3 settings
+         */
+        function init_panel() {
+
+          directive.m = [80, 100, 80, 100];
+          directive.w = $(elem[0]).width() - directive.m[1] - directive.m[3];
+          directive.h = $(elem[0]).height() - directive.m[0] - directive.m[2];
+
+
+          scope.initializing = true;
+          // Using LABjs, wait until all scripts are loaded before rendering panel
+          var scripts = $LAB.script("common/lib/d3.v3.min.js?rand="+Math.floor(Math.random()*10000));
+
+          scripts.wait(function () {
+
+            directive.x = d3.scale.ordinal().domain(scope.panel.fields).rangePoints([0, directive.w]);
+            directive.y = {};
+
+            directive.line = d3.svg.line().interpolate('cardinal');
+            directive.axis = d3.svg.axis().orient("left");
+
+            var viewbox = "0 0 " + (directive.w + directive.m[1] + directive.m[3]) + " " + (directive.h + directive.m[0] + directive.m[2]);
+            directive.svg = d3.select(elem[0]).append("svg")
+              .attr("width", "100%")
+              .attr("height", "100%")
+              .attr("viewbox", viewbox)
+              .append("svg:g")
+              .attr("transform", "translate(" + directive.m[3] + "," + directive.m[0] + ")");
+
+            // Add foreground lines.
+            directive.foreground = directive.svg.append("svg:g")
+              .attr("class", "foreground");
+
+            scope.initializing = false;
+            render_panel();
+          });
+
+
+        }
+
+        // Returns the path for a given data point.
+        function path(d) {
+          return directive.line(scope.panel.fields.map(function(p) { return [directive.x(p), directive.y[p](d[p])]; }));
+        }
+
+        // Handles a brush event, toggling the display of foreground lines.
+        function brush() {
+          var actives = scope.panel.fields.filter(function(p) { return !directive.y[p].brush.empty(); }),
+            extents = actives.map(function(p) { return directive.y[p].brush.extent(); });
+
+          //.fade class hides the "inactive" lines, helps speed up rendering significantly
+          directive.foregroundLines.classed("fade", function(d) {
+            return !actives.every(function(p, i) {
+              var inside = extents[i][0] <= d[p] && d[p] <= extents[i][1];
+              return inside;
+            });
+          });
+
+          //activeDocs contains the actual doc records for selected lines.
+          //will be broadcast out to the table
+          var activeDocs = _.filter(scope.data, function(v) {
+            return actives.every(function(p,i) {
+              var inside = extents[i][0] <= v[p] && v[p] <= extents[i][1];
+              return inside;
+            });
+          })
+
+          scope.$apply(function() {
+            scope.activeDocs = activeDocs;
+          });
+        }
+
+
+        //Drag functions are used for dragging the axis aroud
+        function dragstart(d) {
+          directive.i = scope.panel.fields.indexOf(d);
+        }
+
+        function drag(d) {
+          directive.x.range()[directive.i] = d3.event.x;
+          scope.panel.fields.sort(function(a, b) { return directive.x(a) - directive.x(b); });
+          directive.foregroundLines.attr("transform", function(d) { return "translate(" + directive.x(d) + ")"; });
+          directive.traits.attr("transform", function(d) { return "translate(" + directive.x(d) + ")"; });
+          directive.brushes.attr("transform", function(d) { return "translate(" + directive.x(d) + ")"; });
+          directive.axisLines.attr("transform", function(d) { return "translate(" + directive.x(d) + ")"; });
+          directive.foregroundLines.attr("d", path);
+        }
+
+        function dragend(d) {
+          directive.x.domain(scope.panel.fields).rangePoints([0, directive.w]);
+          var t = d3.transition().duration(500);
+          t.selectAll(".trait").attr("transform", function(d) { return "translate(" + directive.x(d) + ")"; });
+          t.selectAll(".axis").attr("transform", function(d) { return "translate(" + directive.x(d) + ")"; });
+          t.selectAll(".brush").attr("transform", function(d) { return "translate(" + directive.x(d) + ")"; });
+          t.selectAll(".foregroundlines").attr("d", path);
+        }
+
+
+
+
+        /**
+         * Render updates to the SVG. Typically happens when the data changes (time, query)
+         * or when new options are selected
+         */
+        function render_panel() {
+
+
+          //update the svg if the size has changed
+          directive.w = $(elem[0]).width() - directive.m[1] - directive.m[3];
+          directive.h = $(elem[0]).height() - directive.m[0] - directive.m[2];
+          directive.svg.attr("viewbox", "0 0 " + (directive.w + directive.m[1] + directive.m[3]) + " " + (directive.h + directive.m[0] + directive.m[2]));
+
+
+          directive.x = d3.scale.ordinal().domain(scope.panel.fields).rangePoints([0, directive.w]);
+          directive.y = {};
+
+          directive.line = d3.svg.line().interpolate('cardinal');
+          directive.axis = d3.svg.axis().orient("left");
+
+
+          var colorExtent = d3.extent(scope.data, function(p) { return +p[scope.panel.fields[0]]; });
+
+          directive.colors = d3.scale.linear()
+            .domain([colorExtent[0],colorExtent[1]])
+            .range(["#4580FF", "#FF9245"]);
+
+
+          scope.panel.fields.forEach(function(d) {
+
+            //If it is a string, setup an ordinal scale.
+            //Otherwise, use a linear scale for numbers
+            if (_.isString(scope.data[0][d])) {
+
+              var value = function(v) { return v[d]; };
+              var values = _.map(_.uniq(scope.data, value),value);
+
+              directive.y[d] = d3.scale.ordinal()
+                .domain(values)
+                .rangeBands([directive.h, 0]);
+            } else if (_.isNumber(scope.data[0][d])) {
+              directive.y[d] = d3.scale.linear()
+                .domain(d3.extent(scope.data, function(p) { return +p[d]; }))
+                .range([directive.h, 0]);
+            }
+
+            directive.y[d].brush = d3.svg.brush()
+              .y(directive.y[d])
+              .on("brush", brush);
+          });
+
+
+          //pull out the actively selected columns for rendering the axis/lines
+          var activeData = _.map(scope.data, function(d) {
+            var t = {};
+            _.each(scope.panel.fields, function(f) {
+              t[f] = d[f];
+            });
+            return t;
+          });
+
+
+          //Lines
+          directive.foregroundLines = directive.foreground
+            .selectAll(".foregroundlines")
+            .data(activeData, function(d, i){
+              var id = "";
+              _.each(d, function(v) {
+                id += i + "_" + v;
+              });
+              return id;
+            });
+          directive.foregroundLines
+            .enter().append("svg:path")
+            .attr("d", path)
+            .attr("class", "foregroundlines")
+            .attr("style", function(d) {
+              return "stroke:" + directive.colors(d[scope.panel.fields[0]]) + ";";
+            });
+          directive.foregroundLines.exit().remove();
+
+
+
+          //Axis group
+          directive.traits = directive.svg.selectAll(".trait")
+            .data(scope.panel.fields, String);
+          directive.traits
+            .enter().append("svg:g")
+            .attr("class", "trait")
+            .attr("transform", function(d) { return "translate(" + directive.x(d) + ")"; });
+          directive.traits
+            .exit().remove();
+
+
+          //brushes used to select lines
+          directive.brushes = directive.svg.selectAll(".brush")
+            .data(scope.panel.fields, String);
+          directive.brushes
+            .enter()
+            .append("svg:g")
+            .attr("class", "brush")
+            .each(function(d) {
+              d3.select(this)
+                .call(directive.y[d].brush)
+                .attr("transform", function(d) { return "translate(" + directive.x(d) + ")"; });
+            })
+            .selectAll("rect")
+            .attr("x", -8)
+            .attr("width", 16);
+
+          //this section is repeated because enter() only works on "new" data, but we always need to
+          //update the brushes if things change.  This just calls the brushing function, so it doesn't
+          //affect currently active rects
+          directive.brushes
+            .each(function(d) {
+              d3.select(this)
+                .call(directive.y[d].brush)
+                .attr("transform", function(d) { return "translate(" + directive.x(d) + ")"; });
+            });
+          directive.brushes
+            .exit().remove();
+
+
+          //vertical axis and labels
+          directive.axisLines =  directive.svg.selectAll(".axis")
+            .data(scope.panel.fields, String);
+          directive.axisLines
+            .enter()
+            .append("svg:g")
+            .attr("class", "axis")
+            .each(function(d) {
+              d3.select(this)
+                .call(directive.axis.scale(directive.y[d]))
+                .attr("transform", function(d) { return "translate(" + directive.x(d) + ")"; });
+            }).call(d3.behavior.drag()
+              .origin(function(d) { return {x: directive.x(d)}; })
+              .on("dragstart", dragstart)
+              .on("drag", drag)
+              .on("dragend", dragend))
+
+            .append("svg:text")
+            .attr("text-anchor", "middle")
+            .attr("y", -9)
+            .text(String);
+          directive.axisLines
+            .exit().remove();
+
+          //Simulate a dragend in case there is new data and we need to rearrange
+          dragend();
+
+        }
+
+      }
+    };
+  });

+ 24 - 54
panels/pie/editor.html

@@ -1,28 +1,32 @@
 <div class="row-fluid" ng-switch="panel.mode" ng-controller="pie">
 <div class="row-fluid" ng-switch="panel.mode" ng-controller="pie">
   <div ng-switch-when="terms">
   <div ng-switch-when="terms">
-    <div class="row-fluid">    
-      <div class="span4">
+    <div class="row-fluid">
+      <div class="span3">
+        <label class="small">Mode</label> 
+        <select class="input-small" ng-change="set_mode(panel.mode)" ng-model="panel.mode" ng-options="f for f in ['terms','goal']"></select>
+      </div> 
+      <div class="span3">
         <form style="margin-bottom: 0px">
         <form style="margin-bottom: 0px">
-          <h6> Field</h6>
+          <label class="small">Field</label>
           <input type="text" style="width:90%" bs-typeahead="fields.list" ng-model="panel.query.field">
           <input type="text" style="width:90%" bs-typeahead="fields.list" ng-model="panel.query.field">
         </form>
         </form>
       </div>
       </div>
-      <div class="span8">
+      <div class="span5">
         <form class="input-append" style="margin-bottom: 0px">
         <form class="input-append" style="margin-bottom: 0px">
-          <h6>Query</h6>
+          <label class="small">Query</label>
           <input type="text" style="width:80%" ng-model="panel.query.query">
           <input type="text" style="width:80%" ng-model="panel.query.query">
           <button class="btn" ng-click="get_data()"><i class="icon-search"></i></button>
           <button class="btn" ng-click="get_data()"><i class="icon-search"></i></button>
         </form>
         </form>
       </div>
       </div>
     </div>  
     </div>  
     <div class="row-fluid">
     <div class="row-fluid">
-      <div class="span1">
-        <h6>Length</h6>
+      <div class="span3">
+        <label class="small">Length</label>
         <input type="number" style="width:80%" ng-model="panel.size" ng-change="get_data()">
         <input type="number" style="width:80%" ng-model="panel.size" ng-change="get_data()">
       </div>
       </div>
-      <div class="span11">
+      <div class="span8">
         <form class="input-append" style="margin-bottom: 0px">
         <form class="input-append" style="margin-bottom: 0px">
-          <h6>Exclude Terms(s) (comma seperated)</h6>
+          <label class="small">Exclude Terms(s) (comma seperated)</label>
           <input array-join type="text" style="width:90%"  ng-model='panel.exclude'></input>
           <input array-join type="text" style="width:90%"  ng-model='panel.exclude'></input>
           <button class="btn" ng-click="get_data()"><i class="icon-search"></i></button>
           <button class="btn" ng-click="get_data()"><i class="icon-search"></i></button>
         </form>
         </form>
@@ -30,57 +34,26 @@
     </div>
     </div>
   </div>
   </div>
   <div ng-switch-when="goal">
   <div ng-switch-when="goal">
-    <div class="row-fluid">    
+    <div class="row-fluid">
+      <div class="span3">
+        <label class="small">Mode</label> 
+        <select class="input-small" ng-change="set_mode(panel.mode)" ng-model="panel.mode" ng-options="f for f in ['terms','goal']"></select>
+      </div>   
       <div class="span2">
       <div class="span2">
         <form style="margin-bottom: 0px">
         <form style="margin-bottom: 0px">
-          <h6>Goal</h6>
+          <label class="small">Goal</label>
           <input type="number" style="width:90%" ng-model="panel.query.goal">
           <input type="number" style="width:90%" ng-model="panel.query.goal">
         </form>
         </form>
       </div>
       </div>
-      <div class="span9">
+      <div class="span5">
         <form class="input-append" style="margin-bottom: 0px">
         <form class="input-append" style="margin-bottom: 0px">
-          <h6>Query</h6>
+          <label class="small">Query</label>
           <input type="text" style="width:80%" ng-model="panel.query.query">
           <input type="text" style="width:80%" ng-model="panel.query.query">
           <button class="btn" ng-click="get_data()"><i class="icon-search"></i></button>
           <button class="btn" ng-click="get_data()"><i class="icon-search"></i></button>
         </form>
         </form>
       </div>
       </div>
     </div>
     </div>
   </div>
   </div>
-  <div ng-switch-when="query">
-    <div class="row-fluid">    
-      <div class="span3">
-        <form style="margin-bottom: 0px">
-          <h6>Label</h6>
-          <input type="text" placeholder="New Label" style="width:70%" ng-model="newlabel">
-        </form>
-      </div>
-      <div class="span8">
-        <form class="input-append" style="margin-bottom: 0px">
-          <h6>Query</h6>
-          <input type="text" placeholder="New Query" style="width:80%" ng-model="newquery">
-          <button class="btn" ng-click="add_query(newlabel,newquery);newlabel='';newquery=''"><i class="icon-plus"></i></button>
-        </form>
-      </div>
-      <div class="span1">
-      </div>  
-    </div>
-    <div class="row-fluid" ng-repeat="q in panel.query">
-      <div class="span3">
-        <form style="margin-bottom: 0px">
-          <input type="text" style="width:70%" ng-model="q.label">
-        </form>
-      </div>
-      <div class="span8">
-        <form class="input-append" style="margin-bottom: 0px">
-          <input type="text" style="width:80%" ng-model="q.query">
-          <button class="btn" ng-click="get_data()"><i class="icon-search"></i></button>
-        </form>
-      </div>
-      <div class="span1">
-        <i class="icon-remove pointer" ng-click="remove_query(q)"></i>
-      </div>    
-    </div>
-  </div>
 </div>
 </div>
 <div class="row-fluid">    
 <div class="row-fluid">    
   <div class="span1">
   <div class="span1">
@@ -89,15 +62,12 @@
   <div class="span1">
   <div class="span1">
     <label class="small"> Tilt </label><input type="checkbox" ng-model="panel.tilt" ng-checked="panel.tilt">
     <label class="small"> Tilt </label><input type="checkbox" ng-model="panel.tilt" ng-checked="panel.tilt">
   </div>
   </div>
-  <div class="span1">
-    <label class="small"> Legend </label><input type="checkbox" ng-model="panel.legend" ng-checked="panel.legend">
-  </div>
   <div class="span1">
   <div class="span1">
     <label class="small"> Labels </label><input type="checkbox" ng-model="panel.labels" ng-checked="panel.labels">
     <label class="small"> Labels </label><input type="checkbox" ng-model="panel.labels" ng-checked="panel.labels">
   </div>
   </div>
-  <div class="span2">
-    <label class="small">Mode</label> 
-    <select class="input-small" ng-change="set_mode(panel.mode)" ng-model="panel.mode" ng-options="f for f in ['query','terms','goal']"></select>
+  <div class="span3"> 
+    <label class="small">Legend</label> 
+    <select class="input-small" ng-model="panel.legend" ng-options="f for f in ['above','below','none']"></select></span>
   </div>
   </div>
 </div>
 </div>
 <h5>Panel Spy</h5>
 <h5>Panel Spy</h5>

+ 10 - 6
panels/pie/module.html

@@ -2,11 +2,15 @@
   <span ng-show='panel.spyable' style="position:absolute;right:0px;top:0px" class='panelextra pointer'>
   <span ng-show='panel.spyable' style="position:absolute;right:0px;top:0px" class='panelextra pointer'>
       <i bs-modal="'partials/modal.html'" class="icon-eye-open"></i>
       <i bs-modal="'partials/modal.html'" class="icon-eye-open"></i>
   </span>
   </span>
-  <span ng-show="panel.legend" ng-repeat='series in legend' style='padding-right:5px'>
-    <div style='white-space:nowrap;display:table-cell'>
-      <div style="display:inline-block;background:{{series.color}};height:10px;width:10px;border-radius:5px;"></div>
-      <div class='small' style='display:inline-block'>{{series.label}} ({{series.percent}}%)</div>
-    </div>
-  </span>  
+
+  <div ng-show="panel.legend == 'above'" ng-repeat="query in plot.getData()" style="float:left;padding-left: 10px;">
+    <span ng-show='panel.chart != "none"'><div style="display:inline-block;border-radius:5px;background:{{query.color}};height:10px;width:10px"></div></span><span class="small"> {{query.label}} ({{query.data[0][1]}}) </span></span>
+  </div><br>
+  <div style="clear:both"></div>
+
   <div pie params="{{panel}}" style="height:{{panel.height || row.height}};position:relative"></div>
   <div pie params="{{panel}}" style="height:{{panel.height || row.height}};position:relative"></div>
+
+  <div ng-show="panel.legend == 'below'" ng-repeat="query in plot.getData()" style="float:left;padding-left: 10px;">
+    <span ng-show='panel.chart != "none"'><div style="display:inline-block;border-radius:5px;background:{{query.color}};height:10px;width:10px"></div></span><span class="small"> {{query.label}} ({{query.data[0][1]}}) </span></span>
+  </div>
 </kibana-panel>         
 </kibana-panel>         

+ 8 - 68
panels/pie/module.js

@@ -12,7 +12,7 @@ angular.module('kibana.pie', [])
     labels  : true,
     labels  : true,
     mode    : "terms",
     mode    : "terms",
     group   : "default",
     group   : "default",
-    default_field : '_all',
+    default_field : 'DEFAULT',
     spyable : true,
     spyable : true,
   }
   }
   _.defaults($scope.panel,_d)
   _.defaults($scope.panel,_d)
@@ -20,16 +20,7 @@ angular.module('kibana.pie', [])
   $scope.init = function() {
   $scope.init = function() {
     eventBus.register($scope,'time', function(event,time){set_time(time)});
     eventBus.register($scope,'time', function(event,time){set_time(time)});
     eventBus.register($scope,'query', function(event, query) {
     eventBus.register($scope,'query', function(event, query) {
-      if($scope.panel.mode !== 'query') {
-        $scope.panel.query.query = query;
-        $scope.panel.query.query = _.isArray(query) ? query[0] : query;
-      } else {
-        if(_.isArray(query))
-          $scope.panel.query = _.map(query,function(q) {
-            return {query: q, label: q}}) 
-        else
-          $scope.panel.query[0] = {query: query, label: query}
-      }
+      $scope.panel.query.query = _.isArray(query) ? query[0] : query;
       $scope.get_data();
       $scope.get_data();
     });
     });
     // Now that we're all setup, request the time from our group
     // Now that we're all setup, request the time from our group
@@ -60,9 +51,6 @@ angular.module('kibana.pie', [])
     case 'terms':
     case 'terms':
       $scope.panel.query = {query:"*",field:"_all"};
       $scope.panel.query = {query:"*",field:"_all"};
       break;
       break;
-    case 'query':
-      $scope.panel.query = [{query:"*",label:"*"}];
-      break;
     case 'goal':
     case 'goal':
       $scope.panel.query = {query:"*",goal:100};
       $scope.panel.query = {query:"*",goal:100};
       break;
       break;
@@ -77,49 +65,8 @@ angular.module('kibana.pie', [])
     $scope.panel.loading = true;
     $scope.panel.loading = true;
     var request = $scope.ejs.Request().indices($scope.panel.index);
     var request = $scope.ejs.Request().indices($scope.panel.index);
 
 
-    // If we have an array, use query facet
-    if($scope.panel.mode == "query") {
-      if(!(_.isArray($scope.panel.query)))
-        $scope.panel.query = [$scope.panel.query];
-      var queries = [];
-      // Build the question part of the query
-      _.each($scope.panel.query, function(v) {
-        queries.push(ejs.FilteredQuery(
-          ejs.QueryStringQuery(v.query || '*'),
-          ejs.RangeFilter($scope.time.field)
-            .from($scope.time.from)
-            .to($scope.time.to))
-        )
-      });
-
-      // Then the insert into facet and make the request
-      _.each(queries, function(v) {
-        request = request.facet(ejs.QueryFacet(_.indexOf(queries,v))
-          .query(v)
-          .facetFilter(ejs.QueryFilter(v))
-        )
-      })
-      $scope.populate_modal(request);
-      var results = request.doSearch();
-
-      // Populate scope when we have results
-      results.then(function(results) {
-        $scope.panel.loading = false;
-        $scope.hits = results.hits.total;
-        $scope.data = [];
-        _.each(results.facets, function(v, k) {
-          var series = {};
-          var label = _.isUndefined($scope.panel.query[k].label) ? 
-            $scope.panel.query[k].query : $scope.panel.query[k].label 
-          var slice = { label : label, data : v.count }; 
-          if (!(_.isUndefined($scope.panel.query[k].color)))
-            slice.color = $scope.panel.query[k].color;
-          $scope.data.push(slice)
-        });
-        $scope.$emit('render');
-      });
-    // If we don't have an array, assume its a term facet.
-    } else if ($scope.panel.mode == "terms") {
+    // Terms mode
+    if ($scope.panel.mode == "terms") {
       request = request
       request = request
         .facet(ejs.TermsFacet('pie')
         .facet(ejs.TermsFacet('pie')
           .field($scope.panel.query.field || $scope.panel.default_field)
           .field($scope.panel.query.field || $scope.panel.default_field)
@@ -131,7 +78,6 @@ angular.module('kibana.pie', [])
               ejs.RangeFilter($scope.time.field)
               ejs.RangeFilter($scope.time.field)
                 .from($scope.time.from)
                 .from($scope.time.from)
                 .to($scope.time.to)
                 .to($scope.time.to)
-                .cache(false)
               )))).size(0)
               )))).size(0)
 
 
       $scope.populate_modal(request);
       $scope.populate_modal(request);
@@ -157,6 +103,7 @@ angular.module('kibana.pie', [])
         });
         });
         $scope.$emit('render');
         $scope.$emit('render');
       });
       });
+    // Goal mode
     } else {
     } else {
       request = request
       request = request
         .query(ejs.QueryStringQuery($scope.panel.query.query || '*'))
         .query(ejs.QueryStringQuery($scope.panel.query.query || '*'))
@@ -175,7 +122,7 @@ angular.module('kibana.pie', [])
         var complete  = results.hits.total;
         var complete  = results.hits.total;
         var remaining = $scope.panel.query.goal - complete;
         var remaining = $scope.panel.query.goal - complete;
         $scope.data = [
         $scope.data = [
-          { label : 'Complete', data : complete, color: '#51A351' },
+          { label : 'Complete', data : complete, color: '#86B22D' },
           { data : remaining, color: '#EEE'}]
           { data : remaining, color: '#EEE'}]
         $scope.$emit('render');
         $scope.$emit('render');
       });
       });
@@ -196,7 +143,7 @@ angular.module('kibana.pie', [])
   $scope.build_search = function(field,value) {
   $scope.build_search = function(field,value) {
     $scope.panel.query.query = add_to_query($scope.panel.query.query,field,value,false)
     $scope.panel.query.query = add_to_query($scope.panel.query.query,field,value,false)
     $scope.get_data();
     $scope.get_data();
-    eventBus.broadcast($scope.$id,$scope.panel.group,'query',$scope.panel.query.query);
+    eventBus.broadcast($scope.$id,$scope.panel.group,'query',[$scope.panel.query.query]);
   }
   }
 
 
   function set_time(time) {
   function set_time(time) {
@@ -275,14 +222,7 @@ angular.module('kibana.pie', [])
         // Populate element
         // Populate element
         if(elem.is(":visible")){
         if(elem.is(":visible")){
           scripts.wait(function(){
           scripts.wait(function(){
-            var plot = $.plot(elem, scope.data, pie);
-            scope.legend = [];
-            _.each(plot.getData(),function(series) {
-              var item = _.pick(series,'label','color','percent')
-              item.percent = parseFloat(item.percent).toFixed(1)
-              scope.legend.push(item)
-            })
-            console.log(scope.legend)
+            scope.plot = $.plot(elem, scope.data, pie);
           });
           });
         }
         }
       }
       }

+ 5 - 11
panels/stringquery/module.js

@@ -13,24 +13,18 @@ angular.module('kibana.stringquery', [])
   }
   }
   _.defaults($scope.panel,_d);
   _.defaults($scope.panel,_d);
 
 
-  var _groups = _.isArray($scope.panel.group) ? 
-    $scope.panel.group : [$scope.panel.group];
-
   $scope.init = function() {
   $scope.init = function() {
 
 
-    // I don't like this compromise. I'm not totally sure what this panel
-    // Should do if its in multi query mode and receives a query. For now, just
-    // replace the first one, though I feel like that isn't right.
+    // If we're in multi query mode, they all get wiped out if we receive a 
+    // query. Query events must be exchanged as arrays.
     eventBus.register($scope,'query',function(event,query) {
     eventBus.register($scope,'query',function(event,query) {
-      if (_.isArray($scope.panel.query))
-        $scope.panel.query[0] = query
-      else
       $scope.panel.query = query;
       $scope.panel.query = query;
     });   
     });   
   }
   }
 
 
   $scope.send_query = function(query) {
   $scope.send_query = function(query) {
-    eventBus.broadcast($scope.$id,$scope.panel.group,'query',query)
+    var _query = _.isArray(query) ? query : [query]
+    eventBus.broadcast($scope.$id,$scope.panel.group,'query',_query)
   }
   }
 
 
   $scope.add_query = function() {
   $scope.add_query = function() {
@@ -40,7 +34,7 @@ angular.module('kibana.stringquery', [])
       $scope.panel.query = new Array($scope.panel.query)
       $scope.panel.query = new Array($scope.panel.query)
       $scope.panel.query.push("")
       $scope.panel.query.push("")
     }
     }
-  } 
+  }
 
 
   $scope.set_multi = function(multi) {
   $scope.set_multi = function(multi) {
     $scope.panel.query = multi ? 
     $scope.panel.query = multi ? 

+ 4 - 0
panels/table/module.js

@@ -40,6 +40,10 @@ angular.module('kibana.table', [])
     eventBus.register($scope,'selected_fields', function(event, fields) {
     eventBus.register($scope,'selected_fields', function(event, fields) {
       $scope.panel.fields = _.clone(fields)
       $scope.panel.fields = _.clone(fields)
     });
     });
+      eventBus.register($scope,'table_documents', function(event, docs) {
+          $scope.panel.query = docs.query;
+          $scope.data = docs.docs;
+      });
   }
   }
 
 
   $scope.set_sort = function(field) {
   $scope.set_sort = function(field) {

Некоторые файлы не были показаны из-за большого количества измененных файлов