You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by dp...@apache.org on 2016/04/19 17:42:25 UTC

lucene-solr:master: SOLR-8918: Adds Streaming to the admin page under the collections section

Repository: lucene-solr
Updated Branches:
  refs/heads/master 2e95a54a5 -> af7dad682


SOLR-8918: Adds Streaming to the admin page under the collections section

Includes ability to see graphically the expression explanation


Project: http://git-wip-us.apache.org/repos/asf/lucene-solr/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucene-solr/commit/af7dad68
Tree: http://git-wip-us.apache.org/repos/asf/lucene-solr/tree/af7dad68
Diff: http://git-wip-us.apache.org/repos/asf/lucene-solr/diff/af7dad68

Branch: refs/heads/master
Commit: af7dad6825d47e76c39842e97be8b37ab4c2cffd
Parents: 2e95a54
Author: Dennis Gove <dp...@gmail.com>
Authored: Tue Apr 19 11:40:20 2016 -0400
Committer: Dennis Gove <dp...@gmail.com>
Committed: Tue Apr 19 11:40:20 2016 -0400

----------------------------------------------------------------------
 solr/CHANGES.txt                                |   3 +
 solr/webapp/web/css/angular/menu.css            |   1 +
 solr/webapp/web/css/angular/stream.css          | 233 +++++++++++++++++
 solr/webapp/web/index.html                      |   3 +
 solr/webapp/web/js/angular/app.js               |   4 +
 .../webapp/web/js/angular/controllers/stream.js | 251 +++++++++++++++++++
 solr/webapp/web/partials/stream.html            |  64 +++++
 7 files changed, 559 insertions(+)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/af7dad68/solr/CHANGES.txt
----------------------------------------------------------------------
diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt
index e97c2f8..2ae4ebd 100644
--- a/solr/CHANGES.txt
+++ b/solr/CHANGES.txt
@@ -100,6 +100,9 @@ New Features
 
 * SOLR-9009: Adds ability to get an Explanation of a Streaming Expression (Dennis Gove)
 
+* SOLR-8918: Adds Streaming to the admin page under the collections section. Includes
+  ability to see graphically the expression explanation (Dennis Gove)
+
 Bug Fixes
 ----------------------
 

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/af7dad68/solr/webapp/web/css/angular/menu.css
----------------------------------------------------------------------
diff --git a/solr/webapp/web/css/angular/menu.css b/solr/webapp/web/css/angular/menu.css
index aba701b..0e73a59 100644
--- a/solr/webapp/web/css/angular/menu.css
+++ b/solr/webapp/web/css/angular/menu.css
@@ -275,6 +275,7 @@ limitations under the License.
 
 .sub-menu .overview a { background-image: url( ../../img/ico/home.png ); }
 .sub-menu .query a { background-image: url( ../../img/ico/magnifier.png ); }
+.sub-menu .stream a { background-image: url( ../../img/ico/node.png ); }
 .sub-menu .analysis a { background-image: url( ../../img/ico/funnel.png ); }
 .sub-menu .documents a { background-image: url( ../../img/ico/documents-stack.png ); }
 .sub-menu .files a { background-image: url( ../../img/ico/folder.png ); }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/af7dad68/solr/webapp/web/css/angular/stream.css
----------------------------------------------------------------------
diff --git a/solr/webapp/web/css/angular/stream.css b/solr/webapp/web/css/angular/stream.css
new file mode 100644
index 0000000..c007425
--- /dev/null
+++ b/solr/webapp/web/css/angular/stream.css
@@ -0,0 +1,233 @@
+/*
+
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements.  See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License.  You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+
+*/
+
+#content #stream
+{
+}
+
+#content #stream #form
+{
+  float: top;
+}
+
+#content #stream #form label
+{
+  cursor: pointer;
+  display: inline;
+  margin-top: 5px;
+}
+
+#content #stream #form input,
+#content #stream #form select,
+#content #stream #form textarea
+{
+  margin-bottom: 2px;
+  width: 100%;
+}
+
+#content #stream #form textarea
+{
+  height: 125px;
+}
+
+#content #stream #form #start
+{
+  float: left;
+  width: 45%;
+}
+
+#content #stream #form #rows
+{
+  float: right;
+  width: 45%;
+}
+
+#content #stream #form input[type=checkbox]
+{
+  margin-bottom: 0;
+  margin-left: 5px;
+  margin-right: 0px;
+  width: 10px;
+  height: 10px;
+  display: inline;
+}
+
+#content #stream #form fieldset
+{
+  border: 1px solid #fff;
+  border-top: 1px solid #c0c0c0;
+  margin-bottom: 5px;
+}
+
+#content #stream #form fieldset.common
+{
+  margin-top: 10px;
+}
+
+#content #stream #form fieldset legend
+{
+  display: block;
+  margin-left: 10px;
+  padding: 0px 5px;
+}
+
+#content #stream #form fieldset legend label
+{
+  margin-top: 0;
+}
+
+#content #stream #form button
+{
+  margin-right: 10px;
+}
+
+#content #stream #form fieldset .fieldset
+{
+  border-bottom: 1px solid #f0f0f0;
+  margin-bottom: 5px;
+  padding-bottom: 10px;
+}
+
+#content #stream #result
+{
+  float: bottom;
+}
+
+#content #stream #result #response
+{
+}
+
+/************************/
+
+#content #stream #result #explanation
+{
+  border-top: 1px solid #f0f0f0;
+  border-bottom: 1px solid #f0f0f0;
+  border-left: 1px solid #f0f0f0;
+  border-right: 1px solid #f0f0f0;
+}
+
+#content #stream #result #explanation .loader
+{
+  background-position: 0 50%;
+  padding-left: 21px;
+}
+
+#content #stream #result #explanation #error
+{
+  background-color: #f00;
+  background-image: url( ../../img/ico/construction.png );
+  background-position: 10px 12px;
+  color: #fff;
+  font-weight: bold;
+  margin-bottom: 20px;
+  padding: 10px;
+  padding-left: 35px;
+}
+
+#content #stream #result #explanation #error .msg
+{
+  font-style: italic;
+  font-weight: normal;
+  margin-top: 10px;
+}
+
+#content #stream #result #explanation .content
+{
+  padding-left: 0;
+  padding-right: 0;
+}
+
+#content #stream #result #explanation .content.show
+{
+  background-image: url( ../../img/div.gif );
+  background-repeat: repeat-y;
+  background-position: 31% 0;
+}
+
+#content #stream #result #explanation #legend
+{
+  border: 1px solid #f0f0f0;
+  padding: 10px;
+  /*position: absolute;
+  right: 0;
+  bottom: 0;*/
+}
+
+#content #stream #result #explanation #legend li
+{
+  padding-left: 15px;
+  position: relative;
+  -webkit-box-sizing: border-box;
+}
+
+#content #stream #result #explanation #legend li svg
+{
+  position: absolute;
+  left: 0;
+  top: 2px;
+}
+
+#content #stream #result #explanation #graph-content
+{
+  min-height: 50px;
+  width: 100%
+}
+
+#content #stream #result #explanation #graph-content .node circle
+{
+  color: #c48f00;
+  stroke: #c48f00;
+  fill: #c48f00;
+}
+
+#content #stream #result #explanation #graph-content .link
+{
+  fill: none;
+  stroke: #e0e0e0;
+  stroke-width: 1.5px;
+}
+
+#content #stream #result #explanation #legend .datastore circle,
+#content #stream #result #explanation #graph-content .node.datastore circle
+{
+  stroke: #3800c4;
+  fill: #3800c4;
+}
+
+#content #stream #result #explanation #legend .stream-source circle,
+#content #stream #result #explanation #graph-content .node.stream-source circle
+{
+  stroke: #21a9ec;
+  fill: #21a9ec;
+}
+
+#content #stream #result #explanation #legend .stream-decorator circle,
+#content #stream #result #explanation #graph-content .node.stream-decorator circle
+{
+  stroke: #cb21ec;
+  fill: #cb21ec;
+}
+
+#content #stream #result #explanation #legend .graph-source circle,
+#content #stream #result #explanation #graph-content .node.graph-source circle
+{
+  stroke: #21eca9;
+  fill: #21eca9;
+}

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/af7dad68/solr/webapp/web/index.html
----------------------------------------------------------------------
diff --git a/solr/webapp/web/index.html b/solr/webapp/web/index.html
index eff88eb..2e01906 100644
--- a/solr/webapp/web/index.html
+++ b/solr/webapp/web/index.html
@@ -38,6 +38,7 @@ limitations under the License.
   <link rel="stylesheet" type="text/css" href="css/angular/plugins.css?_=${version}">
   <link rel="stylesheet" type="text/css" href="css/angular/documents.css?_=${version}">
   <link rel="stylesheet" type="text/css" href="css/angular/query.css?_=${version}">
+  <link rel="stylesheet" type="text/css" href="css/angular/stream.css?_=${version}">
   <link rel="stylesheet" type="text/css" href="css/angular/replication.css?_=${version}">
   <link rel="stylesheet" type="text/css" href="css/angular/schema.css?_=${version}">
   <link rel="stylesheet" type="text/css" href="css/angular/segments.css?_=${version}">
@@ -72,6 +73,7 @@ limitations under the License.
   <script src="js/angular/controllers/documents.js"></script>
   <script src="js/angular/controllers/files.js"></script>
   <script src="js/angular/controllers/query.js"></script>
+  <script src="js/angular/controllers/stream.js"></script>
   <script src="js/angular/controllers/plugins.js"></script>
   <script src="js/angular/controllers/replication.js"></script>
   <script src="js/angular/controllers/schema.js"></script>
@@ -183,6 +185,7 @@ limitations under the License.
               <li class="documents" ng-class="{active:page=='documents'}"><a href="#/{{currentCollection.name}}/documents"><span>Documents</span></a></li>
               <li class="files" ng-class="{active:page=='files'}"><a href="#/{{currentCollection.name}}/files"><span>Files</span></a></li>
               <li class="query" ng-class="{active:page=='query'}"><a href="#/{{currentCollection.name}}/query"><span>Query</span></a></li>
+              <li class="stream" ng-class="{active:page=='stream'}"><a href="#/{{currentCollection.name}}/stream"><span>Stream</span></a></li>
               <li class="schema" ng-class="{active:page=='schema'}"><a href="#/{{currentCollection.name}}/schema"><span>Schema</span></a></li>
         </ul>
           </div>

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/af7dad68/solr/webapp/web/js/angular/app.js
----------------------------------------------------------------------
diff --git a/solr/webapp/web/js/angular/app.js b/solr/webapp/web/js/angular/app.js
index 4238fd4..c67115c 100644
--- a/solr/webapp/web/js/angular/app.js
+++ b/solr/webapp/web/js/angular/app.js
@@ -109,6 +109,10 @@ solrAdminApp.config([
         templateUrl: 'partials/query.html',
         controller: 'QueryController'
       }).
+      when('/:core/stream', {
+        templateUrl: 'partials/stream.html',
+        controller: 'StreamController'
+      }).
       when('/:core/replication', {
         templateUrl: 'partials/replication.html',
         controller: 'ReplicationController'

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/af7dad68/solr/webapp/web/js/angular/controllers/stream.js
----------------------------------------------------------------------
diff --git a/solr/webapp/web/js/angular/controllers/stream.js b/solr/webapp/web/js/angular/controllers/stream.js
new file mode 100644
index 0000000..f99f24f
--- /dev/null
+++ b/solr/webapp/web/js/angular/controllers/stream.js
@@ -0,0 +1,251 @@
+/*
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements.  See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License.  You may obtain a copy of the License at
+
+     http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+ */
+solrAdminApp.controller('StreamController',
+  function($scope, $routeParams, $location, Query, Constants) {
+
+    $scope.resetMenu("stream", Constants.IS_COLLECTION_PAGE);
+
+    $scope.stream = {
+      wt: 'json',
+      expr: $scope.expr,
+      indent: 'on'
+    };
+    $scope.qt = "stream";
+    $scope.doExplanation = false
+
+    $scope.doStream = function() {
+
+      // alert("doing stream")
+
+      var params = {};
+      params.core = $routeParams.core;
+      params.handler = $scope.qt;
+      params.expr = [$scope.expr]
+      if($scope.doExplanation){
+        params.explain = [$scope.doExplanation]
+      }
+
+      $scope.lang = "json";
+      $scope.response = null;
+      $scope.url = "";
+
+      var url = Query.url(params);
+
+      Query.query(params, function(data) {
+
+        var jsonData = JSON.parse(data.toJSON().data);
+        if (undefined != jsonData["explanation"]) {
+          $scope.showExplanation = true;
+
+          graphSubController($scope, jsonData["explanation"])
+          delete jsonData["explanation"]
+        } else {
+          $scope.showExplanation = false;
+        }
+
+        data.data = JSON.stringify(jsonData,null,2);
+
+        $scope.lang = "json";
+        $scope.response = data;
+        $scope.url = $location.protocol() + "://" +
+          $location.host() + ":" +
+          $location.port() + url;
+
+      });
+    };
+
+    if ($location.search().expr) {
+      $scope.expr = $location.search()["expr"];
+      $scope.doStream();
+    }
+
+  }
+);
+
+
+var graphSubController = function($scope, explanation) {
+  $scope.showGraph = true;
+  $scope.pos = 0;
+  $scope.rows = 8;
+  $scope.helperData = {
+    protocol: [],
+    host: [],
+    hostname: [],
+    port: [],
+    pathname: []
+  };
+
+  $scope.resetGraph = function() {
+    $scope.pos = 0;
+    $scope.initGraph();
+  }
+
+  $scope.initGraph = function(explanation) {
+
+    data = explanation
+
+    var leafCount = 0;
+    var maxDepth = 0;
+    var rootNode = {};
+
+    leafCount = 0;
+
+    let recurse = function(dataNode, depth) {
+
+      if (depth > maxDepth) {
+        maxDepth = depth;
+      }
+
+      let graphNode = {
+        name: dataNode.expressionNodeId,
+        implementingClass: 'unknown',
+        data: {}
+      };
+
+      ["expressionNodeId", "expressionType", "functionName", "implementingClass", "expression", "note", "helpers"].forEach(function(key) {
+        graphNode.data[key] = dataNode[key];
+      });
+
+      if (dataNode.children && dataNode.children.length > 0) {
+        graphNode.children = [];
+        dataNode.children.forEach(function(n) {
+          graphNode.children.push(recurse(n, depth + 1));
+        });
+      } else {
+        ++leafCount;
+      }
+
+      return graphNode;
+    }
+
+    $scope.showPaging = false;
+    $scope.isRadial = false;
+    $scope.graphData = recurse(data, 1);
+
+    $scope.depth = maxDepth + 1;
+    $scope.leafCount = leafCount;
+  };
+
+  $scope.initGraph(explanation);
+};
+
+solrAdminApp.directive('foograph', function(Constants) {
+  return {
+    restrict: 'EA',
+    scope: {
+      data: "=",
+      leafCount: "=",
+      depth: "=",
+      helperData: "=",
+      isRadial: "="
+    },
+    link: function(scope, element, attrs) {
+      var helper_path_class = function(p) {
+        var classes = ['link'];
+
+        return classes.join(' ');
+      };
+
+      var helper_node_class = function(d) {
+        var classes = ['node'];
+
+        if (d.data && d.data.expressionType) {
+          classes.push(d.data.expressionType);
+        }
+
+        return classes.join(' ');
+      };
+
+      var helper_node_text = function(d) {
+        if (d.data && d.data.functionName) {
+          return d.data.functionName;
+        }
+
+        return d.name
+      };
+
+      var helper_tooltip = function(d) {
+
+        return [
+          "Function: " + d.data.functionName,
+          "Type: " + d.data.expressionType,
+          "Class: " + d.data.implementingClass.replace("org.apache.solr.client.solrj.io", "o.a.s.c.s.i"),
+          "=============",
+          d.data.expression
+        ].join("\n");
+      }
+
+      scope.$watch("data", function(newValue, oldValue) {
+        if (newValue) {
+          flatGraph(element, scope.data, scope.depth, scope.leafCount);
+        }
+      });
+
+      var flatGraph = function(element, graphData, depth, leafCount) {
+        var w = 100 + (depth * 100),
+          h = leafCount * 40;
+
+        var tree = d3.layout.tree().size([h, w]);
+
+        var diagonal = d3.svg.diagonal().projection(function(d) {
+          return [d.y * .7, d.x];
+        });
+
+        d3.select('#canvas', element).html('');
+        var vis = d3.select('#canvas', element).append('svg')
+          .attr('width', w)
+          .attr('height', h)
+          .append('g')
+          .attr('transform', 'translate(25, 0)');
+
+        var nodes = tree.nodes(graphData);
+
+        var link = vis.selectAll('path.link')
+          .data(tree.links(nodes))
+          .enter().append('path')
+          .attr('class', helper_path_class)
+          .attr('d', diagonal);
+
+        var node = vis.selectAll('g.node')
+          .data(nodes)
+          .enter().append('g')
+          .attr('class', helper_node_class)
+          .attr('transform', function(d) {
+            return 'translate(' + d.y * .7 + ',' + d.x + ')';
+          })
+
+        node.append('circle')
+          .attr('r', 4.5);
+
+        node.append('title')
+          .text(helper_tooltip);
+
+        node.append('text')
+          .attr('dx', function(d) {
+            return 8;
+          })
+          .attr('dy', function(d) {
+            return 5;
+          })
+          .attr('text-anchor', function(d) {
+            return 'start';
+          })
+          .text(helper_node_text)
+      };
+    }
+  };
+})

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/af7dad68/solr/webapp/web/partials/stream.html
----------------------------------------------------------------------
diff --git a/solr/webapp/web/partials/stream.html b/solr/webapp/web/partials/stream.html
new file mode 100644
index 0000000..f62007f
--- /dev/null
+++ b/solr/webapp/web/partials/stream.html
@@ -0,0 +1,64 @@
+<!--
+Licensed to the Apache Software Foundation (ASF) under one or more
+contributor license agreements.  See the NOTICE file distributed with
+this work for additional information regarding copyright ownership.
+The ASF licenses this file to You under the Apache License, Version 2.0
+(the "License"); you may not use this file except in compliance with
+the License.  You may obtain a copy of the License at
+
+http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+<div id="stream" class="clearfix">
+
+  <div id="form">
+    <form>
+      <label for="expr" title="The expression string">
+        Streaming Expression (expr)
+      </label>
+      <textarea name="expr" ng-model="expr" id="expr" title="The expression string.">search(....)</textarea>
+
+      <button type="submit" ng-click="doStream()">Execute</button>
+      <input type="checkbox" ng-model="doExplanation" name="doExplanation" id="doExplanation" value="true">
+      <label for="explain" class="checkbox" title="Enable Explanation." ng-click="doExplanation = !doExplanation">
+        with explanation
+      </label>
+      
+    </form>
+  </div>
+
+  <div id="result">
+
+    <a ng-show="response.data" id="url" class="address-bar" ng-href="{{url}}">{{url}}</a>
+
+    <div ng-show="showExplanation" id="explanation" class="clearfix">
+      <div id="frame">
+        <div foograph id="graph-content" data="graphData" depth="depth" leaf-count="leafCount" helper-data="helperData" is-radial="false" class="content clearfix" ng-show="showGraph">
+          <div id="legend">
+            <svg width="100%" height="15">
+                <g transform="translate(5,10)" class="stream-decorator"><circle r="4.5"></circle></g>
+                <g transform="translate(15,14)"><text>Stream Decorator</text></g>
+                <g transform="translate(140,10)" class="stream-source"><circle r="4.5"></circle></g>
+                <g transform="translate(150,14)"><text>Stream Source</text></g>
+                <g transform="translate(260,10)" class="graph-source"><circle r="4.5"></circle></g>
+                <g transform="translate(270,14)"><text>Graph Source</text></g>
+                <g transform="translate(375,10)" class="datastore"><circle r="4.5"></circle></g>
+                <g transform="translate(385,14)"><text>Datastore</text></g>
+            </svg>
+          </div>
+          <div id="canvas"></div>
+        </div>
+      </div>
+    </div>
+
+    <div id="response">
+      <pre class="syntax language-json"><code ng-bind-html="response.data | highlight:json | unsafe"></code></pre>
+    </div>
+  </div>
+
+</div>