You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@kylin.apache.org by zh...@apache.org on 2016/07/12 12:39:27 UTC

kylin git commit: KYLIN-1786 UI for extended columns as measure

Repository: kylin
Updated Branches:
  refs/heads/v1.5.3-release 6747b2a80 -> 469aa4003


KYLIN-1786 UI for extended columns as measure


Project: http://git-wip-us.apache.org/repos/asf/kylin/repo
Commit: http://git-wip-us.apache.org/repos/asf/kylin/commit/469aa400
Tree: http://git-wip-us.apache.org/repos/asf/kylin/tree/469aa400
Diff: http://git-wip-us.apache.org/repos/asf/kylin/diff/469aa400

Branch: refs/heads/v1.5.3-release
Commit: 469aa40030c930e64c7a6af5f6d011e3080dfa84
Parents: 6747b2a
Author: Jason <ji...@163.com>
Authored: Tue Jul 12 20:38:46 2016 +0800
Committer: Jason <ji...@163.com>
Committed: Tue Jul 12 20:38:46 2016 +0800

----------------------------------------------------------------------
 webapp/app/js/controllers/cubeEdit.js          |  52 ++++++++
 webapp/app/js/controllers/cubeMeasures.js      |  60 +++++----
 webapp/app/js/directives/directives.js         |  43 +++++++
 webapp/app/js/model/cubeConfig.js              |   2 +-
 webapp/app/partials/cubeDesigner/measures.html | 132 +++++++++++++++-----
 5 files changed, 237 insertions(+), 52 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/kylin/blob/469aa400/webapp/app/js/controllers/cubeEdit.js
----------------------------------------------------------------------
diff --git a/webapp/app/js/controllers/cubeEdit.js b/webapp/app/js/controllers/cubeEdit.js
index b6fc875..8475025 100755
--- a/webapp/app/js/controllers/cubeEdit.js
+++ b/webapp/app/js/controllers/cubeEdit.js
@@ -119,6 +119,58 @@ KylinApp.controller('CubeEditCtrl', function ($scope, $q, $routeParams, $locatio
   };
 
 
+  $scope.getExtendedColumns = function (measure) {
+    //metric from model
+    var me_columns = [];
+    if($scope.metaModel.model.metrics){
+      angular.forEach($scope.metaModel.model.metrics,function(metric,index){
+        me_columns.push(metric);
+      })
+    }
+    angular.forEach($scope.metaModel.model.dimensions,function(dimension,index){
+        if(dimension.columns){
+          me_columns = me_columns.concat(dimension.columns);
+        }
+    })
+
+    return me_columns;
+
+  };
+
+  $scope.getExtendedFactColumns = function (measure) {
+    var me_columns = [];
+    angular.forEach($scope.metaModel.model.dimensions,function(dimension,index){
+      if($scope.metaModel.model.fact_table !== dimension.table){
+        return;
+      }
+
+      if(dimension.columns){
+        me_columns = me_columns.concat(dimension.columns);
+      }
+    })
+
+    return me_columns;
+
+  };
+
+
+  $scope.getFactColumns = function () {
+    var me_columns = [];
+    angular.forEach($scope.cubeMetaFrame.dimensions,function(dimension,index){
+      if($scope.metaModel.model.fact_table !== dimension.table){
+        return;
+      }
+      if(dimension.column && dimension.derived == null){
+        me_columns.push(dimension.column);
+      }
+
+    });
+
+    return me_columns;
+
+  };
+
+
 
   $scope.getColumnType = function (_column, table) {
     var columns = $scope.getColumnsByTable(table);

http://git-wip-us.apache.org/repos/asf/kylin/blob/469aa400/webapp/app/js/controllers/cubeMeasures.js
----------------------------------------------------------------------
diff --git a/webapp/app/js/controllers/cubeMeasures.js b/webapp/app/js/controllers/cubeMeasures.js
index fb9d292..bb22a42 100644
--- a/webapp/app/js/controllers/cubeMeasures.js
+++ b/webapp/app/js/controllers/cubeMeasures.js
@@ -24,27 +24,30 @@ KylinApp.controller('CubeMeasuresCtrl', function ($scope, $modal,MetaModel,cubes
   $scope.addNewMeasure = function (measure) {
     $scope.nextParameters = [];
     $scope.newMeasure = (!!measure)? measure:CubeDescModel.createMeasure();
-    if(!!measure){
-      $scope.convertNextParameters();
+    //if(!!measure){
+    //  $scope.convertNextParameters();
+    //}
+    if(!!measure && measure.function.parameter.next_parameter){
+      $scope.nextPara.value = measure.function.parameter.next_parameter.value;
     }
   };
-  $scope.convertNextParameters = function(){
-    $scope.nextParameters = [];
-    var paramater = jQuery.extend(true, {}, $scope.newMeasure.function.parameter);
-    while(paramater.next_parameter){
-      var paraMeter =
-      {
-       "type": paramater.next_parameter.type,
-       "value":paramater.next_parameter.value,
-        "next_parameter":null
-      }
-      $scope.nextParameters.push(paraMeter);
-
-      paramater = paramater.next_parameter;
-
-    }
-
-  }
+  //$scope.convertNextParameters = function(){
+  //  $scope.nextParameters = [];
+  //  var paramater = jQuery.extend(true, {}, $scope.newMeasure.function.parameter);
+  //  while(paramater.next_parameter){
+  //    var paraMeter =
+  //    {
+  //     "type": paramater.next_parameter.type,
+  //     "value":paramater.next_parameter.value,
+  //      "next_parameter":null
+  //    }
+  //    $scope.nextParameters.push(paraMeter);
+  //
+  //    paramater = paramater.next_parameter;
+  //
+  //  }
+  //
+  //}
 
   $scope.updateNextParameter = function(){
     //jQuery.extend(true, {},$scope.newMeasure.function.parameter.next_parameter)
@@ -70,6 +73,14 @@ KylinApp.controller('CubeMeasuresCtrl', function ($scope, $modal,MetaModel,cubes
     }
     $scope.updateNextParameter();
   }
+
+
+  $scope.nextPara = {
+    "type":"column",
+    "value":"",
+    "next_parameter":null
+  }
+
   $scope.openParameterModal = function (parameter) {
     $modal.open({
       templateUrl: 'nextParameter.html',
@@ -95,13 +106,19 @@ KylinApp.controller('CubeMeasuresCtrl', function ($scope, $modal,MetaModel,cubes
 
   $scope.clearNewMeasure = function () {
     $scope.newMeasure = null;
+    $scope.nextPara.value = "";
   };
 
   $scope.saveNewMeasure = function () {
-    if ($scope.newMeasure.function.expression === 'TOP_N' && $scope.nextParameters.length == 0) {
+
+    if ($scope.newMeasure.function.expression === 'TOP_N' && $scope.nextPara.value == "") {
       SweetAlert.swal('', '[TOP_N] Group by Column is required', 'warning');
       return false;
     }
+    if($scope.nextPara.value!=="" && ($scope.newMeasure.function.expression == 'EXTENDED_COLUMN' || $scope.newMeasure.function.expression == 'TOP_N')){
+      $scope.newMeasure.function.parameter.next_parameter = $scope.nextPara;
+    }
+
     if ($scope.cubeMetaFrame.measures.indexOf($scope.newMeasure) === -1) {
       $scope.cubeMetaFrame.measures.push($scope.newMeasure);
     }
@@ -112,7 +129,8 @@ KylinApp.controller('CubeMeasuresCtrl', function ($scope, $modal,MetaModel,cubes
   //map right return type for param
   $scope.measureReturnTypeUpdate = function(){
 
-    if($scope.newMeasure.function.expression == 'TOP_N'){
+    if($scope.newMeasure.function.expression == 'TOP_N'||$scope.newMeasure.function.expression == 'EXTENDED_COLUMN'){
+      $scope.newMeasure.function.parameter.type= 'column';
       return;
     }
 

http://git-wip-us.apache.org/repos/asf/kylin/blob/469aa400/webapp/app/js/directives/directives.js
----------------------------------------------------------------------
diff --git a/webapp/app/js/directives/directives.js b/webapp/app/js/directives/directives.js
index ae90c82..4b83865 100644
--- a/webapp/app/js/directives/directives.js
+++ b/webapp/app/js/directives/directives.js
@@ -332,6 +332,29 @@ KylinApp.directive('kylinPagination', function ($parse, $q) {
       };
     }
   };
+}).directive("extendedcolumntree", function($compile) {
+  return {
+    restrict: "E",
+    transclude: true,
+    scope: {
+      nextpara: '='
+    },
+    template:
+    '<li class="parent_li">Host Column:<b>{{nextpara.value}}</b></b></li>' +
+    '<li class="parent_li">Extended Column:<b>{{nextpara.next_parameter.value}}</b></li>',
+    compile: function(tElement, tAttr, transclude) {
+      var contents = tElement.contents().remove();
+      var compiledContents;
+      return function(scope, iElement, iAttr) {
+        if(!compiledContents) {
+          compiledContents = $compile(contents, transclude);
+        }
+        compiledContents(scope, function(clone, scope) {
+          iElement.append(clone);
+        });
+      };
+    }
+  };
 }).directive('kylinpopover', function ($compile,$templateCache) {
   return {
     restrict: "A",
@@ -355,4 +378,24 @@ KylinApp.directive('kylinPagination', function ($parse, $q) {
       $(element).popover(options);
     }
   };
+}).directive('extendedColumnReturn', function() {
+  return {
+    require: 'ngModel',
+    link: function(scope, element, attrs, ngModelController) {
+
+      var prefix = "extendedcolumn(";
+      var suffix = ")";
+      ngModelController.$parsers.push(function(data) {
+        //convert data from view format to model format
+        return prefix +data+suffix; //converted
+      });
+
+      ngModelController.$formatters.push(function(data) {
+        //convert data from model format to view format
+        var prefixIndex = data.indexOf("(")+1;
+        var suffixIndex = data.indexOf(")");
+        return data.substring(prefixIndex,suffixIndex); //converted
+      });
+    }
+  }
 });

http://git-wip-us.apache.org/repos/asf/kylin/blob/469aa400/webapp/app/js/model/cubeConfig.js
----------------------------------------------------------------------
diff --git a/webapp/app/js/model/cubeConfig.js b/webapp/app/js/model/cubeConfig.js
index 82dfca4..88e2133 100644
--- a/webapp/app/js/model/cubeConfig.js
+++ b/webapp/app/js/model/cubeConfig.js
@@ -20,7 +20,7 @@ KylinApp.constant('cubeConfig', {
 
   //~ Define metadata & class
   measureParamType: ['column', 'constant'],
-  measureExpressions: ['SUM', 'MIN', 'MAX', 'COUNT', 'COUNT_DISTINCT',"TOP_N", 'RAW'],
+  measureExpressions: ['SUM', 'MIN', 'MAX', 'COUNT', 'COUNT_DISTINCT',"TOP_N", 'RAW','EXTENDED_COLUMN'],
   dimensionDataTypes: ["string", "tinyint", "int", "bigint", "date"],
   cubePartitionTypes: ['APPEND'],
   joinTypes: [

http://git-wip-us.apache.org/repos/asf/kylin/blob/469aa400/webapp/app/partials/cubeDesigner/measures.html
----------------------------------------------------------------------
diff --git a/webapp/app/partials/cubeDesigner/measures.html b/webapp/app/partials/cubeDesigner/measures.html
index 5778003..0a39210 100755
--- a/webapp/app/partials/cubeDesigner/measures.html
+++ b/webapp/app/partials/cubeDesigner/measures.html
@@ -43,8 +43,9 @@
                           <td>
                             <div class="paraTree">
                               <ul>
-                                <parametertree ng-if="measure.function.parameter!=null && measure.function.expression!=='TOP_N'" nextpara="measure.function.parameter"></parametertree>
+                                <parametertree ng-if="measure.function.parameter!=null && measure.function.expression!=='TOP_N' && measure.function.expression!=='EXTENDED_COLUMN'" nextpara="measure.function.parameter"></parametertree>
                                 <topntree ng-if="measure.function.parameter!=null && measure.function.expression=='TOP_N'" nextpara="measure.function.parameter"></topntree>
+                                <extendedcolumntree ng-if="measure.function.parameter!=null && measure.function.expression=='EXTENDED_COLUMN'" nextpara="measure.function.parameter"></extendedcolumntree>
                               </ul>
                             </div>
                         <!--<span ng-if="measure.function.parameter.next_parameter!=null">{{measure.function.parameter.next_parameter |json}}</span>-->
@@ -111,7 +112,7 @@
                           </div>
                       </div>
                       <!--Param Type-->
-                      <div class="form-group">
+                      <div class="form-group" ng-if="newMeasure.function.expression !== 'EXTENDED_COLUMN' && newMeasure.function.expression !== 'TOP_N'">
                           <div class="row">
                               <label class="col-xs-12 col-sm-3 control-label no-padding-right font-color-default"><b>Param Type</b></label>
                               <div class="col-xs-12 col-sm-6">
@@ -133,9 +134,11 @@
                       <div class="form-group middle-popover">
                           <div class="row">
                               <label class="col-xs-12 col-sm-3 control-label no-padding-right font-color-default">
-                                <b>Param Value</b>  <i class="fa fa-info-circle" kylinpopover placement="right" title="Param Value" template="paramvalueTip.html"></i>
+                                <b ng-if="newMeasure.function.expression == 'EXTENDED_COLUMN'">Host column On Fact Table</b>  <i ng-if="newMeasure.function.expression == 'EXTENDED_COLUMN'" title="Host Column" class="fa fa-info-circle" kylinpopover placement="right" template="hostTableTip.html"></i>
+                                <b ng-if="newMeasure.function.expression == 'TOP_N'">ORDER|SUM by Column</b>  <i ng-if="newMeasure.function.expression == 'TOP_N'" class="fa fa-info-circle" title="ORDER|SUM by Column" kylinpopover placement="right" template="topnTip.html"></i>
+                                <b ng-if="newMeasure.function.expression !== 'EXTENDED_COLUMN' && newMeasure.function.expression !== 'TOP_N' ">Param Value</b>  <i ng-if="newMeasure.function.expression !== 'EXTENDED_COLUMN' && newMeasure.function.expression !== 'TOP_N' " class="fa fa-info-circle" kylinpopover placement="right" title="Param Value" template="paramvalueTip.html"></i>
                                 <!--tip for top_n-->
-                                <small ng-if="newMeasure.function.expression == 'TOP_N'" class="help-block" style="color:#3a87ad">(SUM|ORDER BY Column for TOP_N)</small>
+                                <!--<small ng-if="newMeasure.function.expression == 'TOP_N'" class="help-block" style="color:#3a87ad">(SUM|ORDER BY Column for TOP_N)</small>-->
                               </label>
 
 
@@ -146,19 +149,29 @@
                                       ng-init="newMeasure.function.parameter.value = 1"><b>&nbsp;&nbsp;1</b></span>
                                 <!--!COUNT_DISTINCT-->
                                 <select class="form-control" chosen
-                                        ng-if="newMeasure.function.parameter.type == 'column'"
+                                        ng-if="newMeasure.function.parameter.type == 'column' && newMeasure.function.expression !== 'EXTENDED_COLUMN'"
                                         ng-model="newMeasure.function.parameter.value"
                                         ng-change="measureReturnTypeUpdate();"
                                         ng-options="column as column for column in getCommonMetricColumns(newMeasure)" >
                                   <option value="">-- Select a Column --</option>
                                 </select>
+                                <select class="form-control" chosen
+                                        ng-if="newMeasure.function.expression == 'EXTENDED_COLUMN'"
+                                        ng-model="newMeasure.function.parameter.value"
+                                        ng-change="measureReturnTypeUpdate();"
+                                        ng-options="column as column for column in getFactColumns()" >
+                                  <option value="">-- Select a Column --</option>
+                                </select>
                               </div>
                           </div>
                       </div>
                       <!--Return Type-->
-                      <div class="form-group">
+                      <div class="form-group middle-popover">
                           <div class="row">
-                              <label class="col-xs-12 col-sm-3 control-label no-padding-right font-color-default"><b>Return Type</b></label>
+                              <label class="col-xs-12 col-sm-3 control-label no-padding-right font-color-default">
+                                <b ng-if="newMeasure.function.expression !== 'EXTENDED_COLUMN'">Return Type</b>
+                                <b ng-if="newMeasure.function.expression == 'EXTENDED_COLUMN'">Maximum length of extended column</b>  <i ng-if="newMeasure.function.expression == 'EXTENDED_COLUMN'" title="Maximum Length" class="fa fa-info-circle" kylinpopover placement="right" template="extendedTypeTip.html"></i>
+                              </label>
                               <div class="col-xs-12 col-sm-6">
                                   <select class="form-control"
                                       ng-if="newMeasure.function.expression == 'COUNT_DISTINCT'"
@@ -174,13 +187,53 @@
                                         ng-options="ddt.value as ddt.name for ddt in cubeConfig.topNTypes">
                                   <option value=""></option>
                                 </select>
+
+                                <input extended-column-return
+                                  ng-if="newMeasure.function.expression == 'EXTENDED_COLUMN'"
+                                  type="text" placeholder="Kylin won\u2019t save more than this number of bytes" class="form-control"
+                                        tooltip-trigger="focus"
+                                        ng-init="newMeasure.function.returntype=newMeasure.function.returntype?newMeasure.function.returntype:'extendedcolumn(100)'"
+                                       ng-model="newMeasure.function.returntype" required />
+
                                   <span class="font-color-default"
-                                        ng-if="newMeasure.function.expression != 'COUNT_DISTINCT' && newMeasure.function.expression != 'TOP_N'"
+                                        ng-if="newMeasure.function.expression != 'COUNT_DISTINCT' && newMeasure.function.expression != 'TOP_N' && newMeasure.function.expression != 'EXTENDED_COLUMN' "
                                        ><b>&nbsp;&nbsp;{{newMeasure.function.returntype | uppercase}}</b>
                                   </span>
                               </div>
                           </div>
                       </div>
+
+
+                      <div class="form-group middle-popover" ng-if="newMeasure.function.expression == 'EXTENDED_COLUMN'">
+                        <div class="row">
+                          <label class="col-xs-12 col-sm-3 control-label no-padding-right font-color-default">
+                            <b>Extended column on fact table</b>    <i ng-if="newMeasure.function.expression == 'EXTENDED_COLUMN'" title="Extended Column" class="fa fa-info-circle" kylinpopover placement="right" template="extendedColumnTip.html"></i>
+                          </label>
+                          <div class="col-xs-12 col-sm-6">
+                            <select class="form-control" chosen ng-if="nextPara.type !== 'constant'" required
+                                    ng-model="nextPara.value"
+                                    ng-options="column as column for column in getExtendedFactColumns()" >
+                              <option value=""></option>
+                            </select>
+                          </div>
+                        </div>
+                      </div>
+
+                      <div class="form-group" ng-if="newMeasure.function.expression == 'TOP_N'">
+                        <div class="row">
+                          <label class="col-xs-12 col-sm-3 control-label no-padding-right font-color-default">
+                            <b>Group by Column</b>
+                          </label>
+                          <div class="col-xs-12 col-sm-6">
+                            <select class="form-control" chosen ng-if="nextPara.type !== 'constant'" required
+                                    ng-model="nextPara.value"
+                                    ng-options="column as column for column in getExtendedColumns(newMeasure)" >
+                              <option value=""></option>
+                            </select>
+                          </div>
+                        </div>
+                      </div>
+
                       <!--Name-->
                       <div class="form-group">
                         <div class="row">
@@ -207,26 +260,14 @@
                               </tr>
                             </table>
 
-                            <table class="table table-hover table-bordered list" ng-if="nextParameters.length" ng-show="newMeasure.function.expression == 'TOP_N'">
-                              <tr ng-repeat="n_parameter in nextParameters track by $index">
-                                <td><b>Group By Column</b></td>
-                                <td>{{n_parameter.value}}</td>
-                                <td>
-                                  <button class="btn btn-xs btn-info" ng-click="editNextParameter(n_parameter)">
-                                    <i class="fa fa-pencil"></i>
-                                  </button>
-                                  <button class="btn btn-xs btn-info" ng-click="removeParameter(nextParameters, $index)"><i class="fa fa-minus"></i>
-                                  </button>
-
-                                </td>
-
-                              </tr>
-                            </table>
 
 
-                            <button class="btn btn-sm btn-info" ng-click="addNextParameter()" ng-show="newMeasure.function.expression == 'TOP_N' && nextParameters.length==0"
-                                    ng-show="state.mode=='edit'"><i class="fa fa-plus"> Group by Column</i>
-                            </button>
+                            <!--<button class="btn btn-sm btn-info" ng-click="addNextParameter()" ng-show="newMeasure.function.expression == 'TOP_N' && nextParameters.length==0"-->
+                                    <!--ng-show="state.mode=='edit'"><i class="fa fa-plus"> Group by Column</i>-->
+                            <!--</button>-->
+                            <!--<button class="btn btn-sm btn-info" ng-click="addNextParameter()" ng-show="newMeasure.function.expression == 'EXTENDED_COLUMN' && nextParameters.length==0"-->
+                                    <!--ng-show="state.mode=='edit'"><i class="fa fa-plus"> Extended Column On Fact Table</i>-->
+                            <!--</button>-->
                           </div>
                         </div>
                       </div>
@@ -248,7 +289,7 @@
 
 <script type="text/ng-template" id="nextParameter.html">
   <div class="modal-header">
-    <h4 tooltip="submit" ng-if="newmea.measure&&newmea.measure.function.expression !== 'TOP_N'">Add Parameter</h4>
+    <h4 tooltip="submit" ng-if="newmea.measure&&newmea.measure.function.expression == 'EXTENDED_COLUMN'">Add Extended Column</h4>
     <h4 tooltip="submit" ng-if="newmea.measure&&newmea.measure.function.expression == 'TOP_N'">Select Group By Column</h4>
   </div>
   <div class="modal-body" style="background-color: white">
@@ -258,7 +299,7 @@
       <div class="col-md-8">
         <div class="row">
           <div class="form-group">
-            <div class="row" ng-if="newmea.measure&&newmea.measure.function.expression !== 'TOP_N'">
+            <div class="row" ng-if="newmea.measure&&newmea.measure.function.expression !== 'TOP_N' && newmea.measure.function.expression !== 'EXTENDED_COLUMN'">
               <label class="col-xs-12 col-sm-3 control-label no-padding-right font-color-default"><b>Param Type</b></label>
               <div class="col-xs-12 col-sm-6">
                 <select class="form-control"
@@ -280,7 +321,7 @@
         <div class="row">
           <div class="form-group">
 
-            <div ng-if="newmea.measure&&newmea.measure.function.expression !== 'TOP_N'" class="row">
+            <div ng-if="newmea.measure&&newmea.measure.function.expression !== 'TOP_N' && newmea.measure.function.expression !== 'EXTENDED_COLUMN" class="row">
               <label  class="col-xs-12 col-sm-3 control-label no-padding-right font-color-default"><b>Param Value</b></label>
                 <!--COUNT_DISTINCT-->
               <div class="col-xs-12 col-sm-6">
@@ -301,12 +342,23 @@
               <div class="col-xs-12 col-sm-6">
                 <select class="form-control" chosen
                         ng-model="nextPara.value"
-                        ng-options="column as column for column in getCommonMetricColumns(newmea.measure)" >
+                        ng-options="column as column for column in getExtendedColumns(newmea.measure)" >
                   <option value=""></option>
                 </select>
               </div>
             </div>
 
+            <div ng-if="newmea.measure&&newmea.measure.function.expression == 'EXTENDED_COLUMN'" ng-init="nextPara.type='column'" class="row">
+              <label  class="col-xs-12 col-sm-4 control-label no-padding-right font-color-default"><b>Extended Column On Fact Table</b></label>
+              <!--COUNT_DISTINCT-->
+              <div class="col-xs-12 col-sm-6">
+                <select class="form-control" chosen
+                        ng-model="nextPara.value"
+                        ng-options="column as column for column in getExtendedColumns(newmea.measure)" >
+                  <option value=""></option>
+                </select>
+              </div>
+            </div>
 
             </div>
           </div>
@@ -329,3 +381,23 @@
     <li>Distinct Count is approximate, please indicate Error Rate, higher accuracy degree accompanied with larger storage size and longer build time</li>
   </ol>
 </script>
+
+<script type="text/ng-template" id="extendedTypeTip.html">
+  <p>
+    Kylin won\u2019t save more than this number of bytes for each extended column. If exceeded it will be truncated.
+  </p>
+</script>
+
+<script type="text/ng-template" id="topnTip.html">
+  <p>Will use this column for SUM and Order by</p>
+</script>
+
+<script type="text/ng-template" id="hostTableTip.html">
+  <p>Host column is the dimension to derive from, e.g. page_id</p>
+</script>
+<script type="text/ng-template" id="extendedColumnTip.html">
+  <p>
+    Extended column is derived from host, e.g. page_url. No filters on extended column!
+  </p>
+</script>
+