SOLR-7241: Add document tab support to AngularJS adminUI

git-svn-id: https://svn.apache.org/repos/asf/lucene/dev/trunk@1671279 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Erick Erickson 2015-04-04 17:10:48 +00:00
parent 024df3e4a0
commit 1aaf420958
7 changed files with 475 additions and 4 deletions

118
partials/documents.html Normal file
View File

@ -0,0 +1,118 @@
<!--
/*
* 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="documents" class="clearfix">
<div id="form">
<form>
<label for="qt">
<a rel="help">Request-Handler (qt)</a>
</label>
<input ng-model="handler" type="text" id="qt" value="/update" title="Request handler in solrconfig.xml.">
<label for="document-type">
<a rel="help">Document Type</a>
</label>
<div><select ng-model="type" id="document-type" ng-change="changeDocumentType()" placeholder="The type of the document field">
<!-- TODO: support the Builder -->
<option value="csv">CSV</option>
<option value="wizard">Document Builder</option>
<option value="upload">File Upload</option>
<option value="json">JSON</option>
<option value="solr">Solr Command (raw XML or JSON)</option>
<option value="xml">XML</option>
</select>
</div>
<div id="document-container">
<div id="wizard" ng-show="type=='wizard'">
<div id="wizard-fields">
<div><span class="description">Field</span>: <select ng-model="fieldName" id="wiz-field-select" name="wiz-field-select"
ng-options="field for field in fields"></select>
</div>
<div><span id="wiz-field-data"><span class="description">Field Data</span>:</span>
<textarea ng-model="fieldData"
id="wizard-doc"
name="wizard-doc"
rows="10"
cols="40"
placeholder="Enter your field text here and then click 'Add Field' to add the field to the document.">
</textarea>
</div>
</div>
<div id="wizard-add"><a ng-click="addWizardField()" id="add-field-href"><img border="0" src="./img/ico/plus-button.png"/>Add
Field</a></div>
</div>
<label for="document">
<a rel="help">Document(s)</a>
</label>
<textarea ng-show="type!='upload'" ng-model="document" name="document" id="document" title="The Document" rows="10"
cols="70" placeholder="{{placeholder}}"></textarea>
<div id="file-upload" ng-show="type=='upload'">
<input type="file" id="the-file" name="the-file" file-model="fileUpload"/>
</div>
</div>
<div id="advanced">
<!-- TODO: only show for JSON/XML-->
<div id="attribs">
<div id="upload-only" ng-show="type=='upload'">
<label for="erh-params"><!-- TODO: cleaner way to do this? -->
<a rel="help">Extracting Req. Handler Params</a>
</label>
<input ng-model="literalParams" type="text" id="erh-params" value="&literal.id=change.me"
title="Extracting Request Handler Parameters" size="50">
</div>
<div id="general-attribs">
<label for="commitWithin">
<a rel="help">Commit Within</a>
</label>
<input type="text" ng-model="commitWithin" id="commitWithin" value="1000" title="Commit Within (ms)">
<label for="overwrite">
<a rel="help">Overwrite</a>
</label>
<input ng-model="overwrite" type="text" id="overwrite" value="true" title="Overwrite">
</div>
<!-- Boost is json only, since the XML has it embedded -->
<div id="json-only" ng-show="type=='json'">
<label for="boost">
<a rel="help">Boost</a>
</label>
<input ng-model="boost" type="text" id="boost" value="1.0" title="Document Boost">
</div>
</div>
</div>
<button type="submit" ng-click="submit()" id="submit">Submit Document</button>
</form>
</div>
<div id="result">
<div id="response" ng-show="response">
<div>
<span class="description">Status: </span>{{ responseStatus }}
</div>
<div>
<span class="description">Response:</span>
<pre class="syntax language-json"><code ng-bind-html="response | highlight:'json' | unsafe"></code></pre>
</div>
</div>
</div>
</div>

View File

@ -84,6 +84,9 @@ New Features
* SOLR-6637: Solr should have a way to restore a core from a backed up index.
(Varun Thacker, noble, shalin)
* SOLR-7241, SOLR-7263, SOLR-7279: More functionality moving the Admin UI to Angular JS
(Upayavira via Erick)
Bug Fixes
----------------------

View File

@ -0,0 +1,179 @@
/*
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 #documents
{
background-image: url( ../../img/div.gif );
background-position: 45% 0;
background-repeat: repeat-y;
}
#content #documents #form
{
float: left;
/*width: 21%;*/
}
#content #documents #form label
{
cursor: pointer;
display: block;
margin-top: 5px;
}
#content #documents #form input,
#content #documents #form select,
#content #documents #form textarea
{
margin-bottom: 2px;
/*width: 100%;*/
}
#content #documents #form input,
#content #documents #form textarea
{
margin-bottom: 2px;
/*width: 98%;*/
}
#content #documents #form #start
{
float: left;
/*width: 45%;*/
}
#content #documents #form #rows
{
float: right;
/* width: 45%;*/
}
#content #documents #form .checkbox input
{
margin-bottom: 0;
width: auto;
}
#content #documents #form fieldset,
#content #documents #form .optional.expanded
{
border: 1px solid #fff;
border-top: 1px solid #c0c0c0;
margin-bottom: 5px;
}
#content #documents #form fieldset.common
{
margin-top: 10px;
}
#content #documents #form fieldset legend,
#content #documents #form .optional.expanded legend
{
display: block;
margin-left: 10px;
padding: 0px 5px;
}
#content #documents #form fieldset legend label
{
margin-top: 0;
}
#content #documents #form fieldset .fieldset
{
border-bottom: 1px solid #f0f0f0;
margin-bottom: 5px;
padding-bottom: 10px;
}
#content #documents #form .optional
{
border: 0;
}
#content #documents #form .optional legend
{
margin-left: 0;
padding-left: 0;
}
#content #documents #form .optional.expanded .fieldset
{
display: block;
}
#content #documents #result
{
float: right;
width: 54%;
}
#content #documents #result #url
{
margin-bottom: 10px;
background-image: url( ../../img/ico/ui-address-bar.png );
background-position: 5px 50%;
border: 1px solid #f0f0f0;
box-shadow: 1px 1px 0 #f0f0f0;
-moz-box-shadow: 1px 1px 0 #f0f0f0;
-webkit-box-shadow: 1px 1px 0 #f0f0f0;
color: #c0c0c0;
display: block;
overflow: hidden;
padding: 5px;
padding-left: 26px;
white-space: nowrap;
}
#content #documents #result #url:focus,
#content #documents #result #url:hover
{
border-color: #c0c0c0;
box-shadow: 1px 1px 0 #d8d8d8;
-moz-box-shadow: 1px 1px 0 #d8d8d8;
-webkit-box-shadow: 1px 1px 0 #d8d8d8;
color: #333;
}
#content #documents #result #response
{
}
#content #documents #result #response pre
{
padding-left: 20px;
}
.description{
font-weight: bold;
}
#document-type{
padding-bottom: 5px;
}
#wizard-fields div{
padding-top: 5px;
padding-bottom: 5px;
}
#wiz-field-data, #wiz-field-data span{
vertical-align: top;
}

View File

@ -35,7 +35,7 @@ limitations under the License.
<link rel="stylesheet" type="text/css" href="css/angular/logging.css?_=${version}">
<link rel="stylesheet" type="text/css" href="css/angular/menu.css?_=${version}">
<link rel="stylesheet" type="text/css" href="css/styles/plugins.css?_=${version}">
<link rel="stylesheet" type="text/css" href="css/styles/documents.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/styles/replication.css?_=${version}">
<link rel="stylesheet" type="text/css" href="css/styles/schema-browser.css?_=${version}">
@ -63,6 +63,7 @@ limitations under the License.
<script src="js/angular/controllers/java-properties.js"></script>
<script src="js/angular/controllers/core-overview.js"></script>
<script src="js/angular/controllers/analysis.js"></script>
<script src="js/angular/controllers/documents.js"></script>
<script src="js/angular/controllers/query.js"></script>
</head>

View File

@ -67,6 +67,10 @@ solrAdminApp.config([
templateUrl: 'partials/analysis.html',
controller: 'AnalysisController'
}).
when('/:core/documents', {
templateUrl: 'partials/documents.html',
controller: 'DocumentsController'
}).
when('/:core/query', {
templateUrl: 'partials/query.html',
controller: 'QueryController'
@ -197,9 +201,23 @@ solrAdminApp.config([
return {request: started, response: ended, responseError: failed};
})
.config(function($httpProvider) {
$httpProvider.interceptors.push("httpInterceptor");
})
.directive('fileModel', function ($parse) {
return {
restrict: 'A',
link: function(scope, element, attrs) {
var model = $parse(attrs.fileModel);
var modelSetter = model.assign;
element.bind('change', function(){
scope.$apply(function(){
modelSetter(scope, element[0].files[0]);
});
});
}
};
});
var solrAdminControllers = angular.module('solrAdminControllers', []);

View File

@ -0,0 +1,131 @@
/*
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.
*/
//helper for formatting JSON and others
var DOC_PLACEHOLDER = '<doc>\n' +
'<field name="id">change.me</field>' +
'<field name="title">change.me</field>' +
'</doc>';
var ADD_PLACEHOLDER = '<add>\n' + DOC_PLACEHOLDER + '</add>\n';
solrAdminApp.controller('DocumentsController',
function($scope, $rootScope, $routeParams, $location, Luke, Update, FileUpload) {
$scope.resetMenu("documents");
$scope.refresh = function () {
Luke.schema({core: $routeParams.core}, function(data) {
//TODO: handle dynamic fields
delete data.schema.fields._version_;
$scope.fields = Object.keys(data.schema.fields);
});
$scope.document = "";
$scope.handler = "/update";
$scope.type = "json";
$scope.commitWithin = 1000;
$scope.overwrite = true;
$scope.boost = "1.0";
};
$scope.refresh();
$scope.changeDocumentType = function () {
$scope.placeholder = "";
if ($scope.type == 'json') {
$scope.placeholder = '{"id":"change.me","title":"change.me"}';
} else if ($scope.type == 'csv') {
$scope.placeholder = "id,title\nchange.me,change.me";
} else if ($scope.type == 'solr') {
$scope.placeholder = ADD_PLACEHOLDER;
} else if ($scope.type == 'xml') {
$scope.placeholder = DOC_PLACEHOLDER;
}
};
$scope.addWizardField = function () {
if ($scope.document == "") $scope.document = "{}";
var doc = JSON.parse($scope.document);
doc[$scope.fieldName] = $scope.fieldData;
$scope.document = JSON.stringify(doc, null, '\t');
$scope.fieldData = "";
};
$scope.submit = function () {
var contentType = "";
var postData = "";
var params = {};
var doingFileUpload = false;
if ($scope.handler[0] == '/') {
params.handler = $scope.handler.substring(1);
} else {
params.handler = 'update';
params.qt = $scope.handler;
}
params.commitWithin = $scope.commitWithin;
params.boost = $scope.boost;
params.overwrite = $scope.overwrite;
params.core = $routeParams.core;
params.wt = "json";
if ($scope.type == "json" || $scope.type == "wizard") {
postData = "[" + $scope.document + "]";
contentType = "application/json";
} else if ($scope.type == "csv") {
postData = $scope.document;
contentType = "application/csv";
} else if ($scope.type == "xml") {
postData = "<add>" + $scope.document + "</add>";
contentType = "text/xml";
} else if ($scope.type == "upload") {
doingFileUpload = true;
params.raw = $scope.literalParams;
} else if ($scope.type == "solr") {
postData = $scope.document;
if (postData[0] == "<") {
contentType = "text/xml";
} else if (postData[0] == "{") {
contentType = "application/json";
} else {
alert("Cannot identify content type")
}
}
if (!doingFileUpload) {
Update.post(params, postData).then(function (success) {
$scope.responseStatus = "success";
delete success.$promise;
delete success.$resolved;
$scope.response = JSON.stringify(success, null, ' ');
}).fail(function (failure) {
$scope.responseStatus = failure;
});
} else {
var file = $scope.fileUpload;
console.log('file is ' + JSON.stringify(file));
var uploadUrl = "/fileUpload";
FileUpload.upload(params, $scope.fileUpload, function (success) {
$scope.responseStatus = "success";
$scope.response = JSON.stringify(success, null, ' ');
}, function (failure) {
$scope.responseStatus = "failure";
$scope.response = JSON.stringify(failure, null, ' ');
});
}
}
});

View File

@ -77,11 +77,32 @@ solrAdminServices.factory('System',
}])
.factory('Update',
['$resource', function($resource) {
return $resource('/solr/:core/update', {core: '@core', wt:'json', _:Date.now()}, {
return $resource('/solr/:core/:handler', {core: '@core', wt:'json', _:Date.now(), handler:'/update'}, {
"optimize": {params: { optimize: "true"}},
"commit": {params: {commit: "true"}}
"commit": {params: {commit: "true"}},
"post": {method: "POST", params: {handler: '@handler'}}
});
}])
.service('FileUpload', function ($http) {
this.upload = function(params, file, success, error){
var url = "/solr/" + params.core + "/" + params.handler + "?";
raw = params.raw;
delete params.core;
delete params.handler;
delete params.raw;
url += $.param(params);
if (raw && raw.length>0) {
if (raw[0] != "&") raw = "&" + raw;
url += raw;
}
var fd = new FormData();
fd.append('file', file);
$http.post(url, fd, {
transformRequest: angular.identity,
headers: {'Content-Type': undefined}
}).success(success).error(error);
}
})
.factory('Luke',
['$resource', function($resource) {
return $resource('/solr/:core/admin/luke', {core: '@core', wt:'json', _:Date.now()}, {