You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@solr.apache.org by ep...@apache.org on 2022/07/08 14:09:05 UTC
[solr] branch main updated: SOLR-15853 Solr paramset Admin UI implementation (#923)
This is an automated email from the ASF dual-hosted git repository.
epugh pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/solr.git
The following commit(s) were added to refs/heads/main by this push:
new 277f9e69449 SOLR-15853 Solr paramset Admin UI implementation (#923)
277f9e69449 is described below
commit 277f9e69449322ba8079a095f655ef8e7f8b30ff
Author: Eric Pugh <ep...@opensourceconnections.com>
AuthorDate: Fri Jul 8 10:09:00 2022 -0400
SOLR-15853 Solr paramset Admin UI implementation (#923)
Co-authored-by: betulince <bt...@gmail.com>
---
solr/CHANGES.txt | 2 +
solr/webapp/web/css/angular/menu.css | 1 +
solr/webapp/web/css/angular/paramsets.css | 177 +++++++++++++++++++++
solr/webapp/web/img/ico/paramset.png | Bin 0 -> 275 bytes
solr/webapp/web/index.html | 4 +
solr/webapp/web/js/angular/app.js | 4 +
.../webapp/web/js/angular/controllers/paramsets.js | 158 ++++++++++++++++++
solr/webapp/web/js/angular/controllers/query.js | 61 ++++++-
solr/webapp/web/js/angular/services.js | 7 +
solr/webapp/web/libs/angular-chosen.min.js | 20 ---
solr/webapp/web/partials/paramsets.html | 93 +++++++++++
solr/webapp/web/partials/query.html | 9 ++
12 files changed, 511 insertions(+), 25 deletions(-)
diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt
index d04a5d7fb43..ba8d5bea868 100644
--- a/solr/CHANGES.txt
+++ b/solr/CHANGES.txt
@@ -35,6 +35,8 @@ New Features
* SOLR-15921: Load jars in <solr-install-dir>/lib/ by default (janhoy)
+* SOLR-15853: Admin UI support for managing Paramsets and using in Queries. (Betul Ince, Eric Pugh)
+
Improvements
---------------------
* SOLR-15986: CommitUpdateCommand and SplitIndexCommand can write user commit metadata. (Bruno Roustant)
diff --git a/solr/webapp/web/css/angular/menu.css b/solr/webapp/web/css/angular/menu.css
index a89e7ca3ec6..47bf5c8458a 100644
--- a/solr/webapp/web/css/angular/menu.css
+++ b/solr/webapp/web/css/angular/menu.css
@@ -289,6 +289,7 @@ limitations under the License.
.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 .paramsets a { background-image: url( ../../img/ico/paramset.png ); }
.sub-menu .files a { background-image: url( ../../img/ico/folder.png ); }
.sub-menu .schema a { background-image: url( ../../img/ico/book-open-text.png ); }
.sub-menu .replication a { background-image: url( ../../img/ico/node.png ); }
diff --git a/solr/webapp/web/css/angular/paramsets.css b/solr/webapp/web/css/angular/paramsets.css
new file mode 100644
index 00000000000..704fe2eae61
--- /dev/null
+++ b/solr/webapp/web/css/angular/paramsets.css
@@ -0,0 +1,177 @@
+/*
+
+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 #paramsets
+{
+ background-image: url( ../../img/div.gif );
+ background-position: 45% 0;
+ background-repeat: repeat-y;
+}
+
+#content #paramsets #form
+{
+ float: left;
+}
+
+#content #paramsets #sample-paramset
+{
+ margin-top: 15px;
+}
+
+#content #paramsets #delete-paramset
+{
+ margin-top: 5px;
+ margin-bottom: 5px;
+}
+
+
+#content #paramsets #form fieldset legend, #content #paramsets #form .optional.expanded legend {
+ display: block;
+ margin-left: 10px;
+ margin-top: 15px;
+ padding: 0px 5px;
+}
+
+legend {
+ display: block;
+ padding-inline-start: 2px;
+ padding-inline-end: 2px;
+ border-width: initial;
+ border-style: none;
+ border-color: initial;
+ border-image: initial;
+}
+
+#content #paramsets #form label
+{
+ cursor: pointer;
+ display: block;
+ margin-top: 5px;
+}
+
+#content #paramsets #form input,
+#content #paramsets #form select,
+#content #paramsets #form textarea
+{
+ margin-bottom: 2px;
+}
+
+#content #paramsets #form input,
+#content #paramsets #form textarea
+{
+ margin-bottom: 2px;
+}
+
+#content #paramsets #form #start
+{
+ float: left;
+}
+
+#content #paramsets #form #rows
+{
+ float: right;
+}
+
+#content #paramsets #form .checkbox input
+{
+ margin-bottom: 0;
+ width: auto;
+}
+
+#content #paramsets #form fieldset,
+#content #paramsets #form .optional.expanded
+{
+ border: 1px solid #fff;
+ border-top: 1px solid #c0c0c0;
+ margin-bottom: 5px;
+}
+
+#content #paramsets #form fieldset.common
+{
+ margin-top: 10px;
+}
+
+#content #paramsets #form fieldset legend,
+#content #paramsets #form .optional.expanded legend
+{
+ display: block;
+ margin-left: 10px;
+ padding: 0px 5px;
+}
+
+#content #paramsets #form fieldset legend label
+{
+ margin-top: 0;
+}
+
+#content #paramsets #form fieldset .fieldset
+{
+ border-bottom: 1px solid #f0f0f0;
+ margin-bottom: 5px;
+ padding-bottom: 10px;
+}
+
+#content #paramsets #form .optional
+{
+ border: 0;
+}
+
+#content #paramsets #form .optional legend
+{
+ margin-left: 0;
+ padding-left: 0;
+}
+
+#content #paramsets #form .optional.expanded .fieldset
+{
+ display: block;
+}
+
+#content #paramsets #result
+{
+ float: right;
+ width: 54%;
+}
+
+#content #paramsets #result #response
+{
+}
+
+#content #paramsets #result #response pre
+{
+ padding-left: 20px;
+}
+
+.description{
+ font-weight: bold;
+}
+
+#content #paramsets .chosen-container
+{
+ width: 71% !important;
+}
+
+#content #paramsets .chosen-container {
+ margin-left: 6px;
+ width: 100%;
+}
+#content #paramsets .chosen-drop input,
+#content #paramsets .chosen-results {
+ width: 100% !important;
+}
diff --git a/solr/webapp/web/img/ico/paramset.png b/solr/webapp/web/img/ico/paramset.png
new file mode 100644
index 00000000000..7d2f006917c
Binary files /dev/null and b/solr/webapp/web/img/ico/paramset.png differ
diff --git a/solr/webapp/web/index.html b/solr/webapp/web/index.html
index b9f27580193..afffe67f764 100644
--- a/solr/webapp/web/index.html
+++ b/solr/webapp/web/index.html
@@ -40,6 +40,7 @@ limitations under the License.
<link rel="stylesheet" type="text/css" href="css/angular/menu.css?_=${version}">
<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/paramsets.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}">
@@ -85,6 +86,7 @@ limitations under the License.
<script src="js/angular/controllers/collection-overview.js?_=${version}"></script>
<script src="js/angular/controllers/analysis.js?_=${version}"></script>
<script src="js/angular/controllers/documents.js?_=${version}"></script>
+ <script src="js/angular/controllers/paramsets.js?_=${version}"></script>
<script src="js/angular/controllers/files.js?_=${version}"></script>
<script src="js/angular/controllers/query.js?_=${version}"></script>
<script src="js/angular/controllers/stream.js?_=${version}"></script>
@@ -212,6 +214,7 @@ limitations under the License.
<li class="overview" ng-show="currentCollection.type === 'alias'" ng-class="{active:page=='alias-overview'}"><a href="#/{{currentCollection.name}}/alias-overview"><span>Overview</span></a></li>
<li class="analysis" ng-show="!isMultiDestAlias(currentCollection)" ng-class="{active:page=='analysis'}"><a href="#/{{currentCollection.name}}/analysis"><span>Analysis</span></a></li>
<li class="documents" ng-show="!isMultiDestAlias(currentCollection)" ng-class="{active:page=='documents'}"><a href="#/{{currentCollection.name}}/documents"><span>Documents</span></a></li>
+ <li class="paramsets" ng-show="!isMultiDestAlias(currentCollection)" ng-class="{active:page=='paramsets'}"><a href="#/{{currentCollection.name}}/paramsets"><span>Paramsets</span></a></li>
<li class="files" ng-show="!isMultiDestAlias(currentCollection)" 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>
@@ -237,6 +240,7 @@ limitations under the License.
<li class="overview" ng-class="{active:page=='overview'}"><a href="#/{{currentCore.name}}/core-overview"><span>Overview</span></a></li>
<li ng-hide="isCloudEnabled" class="analysis" ng-class="{active:page=='analysis'}"><a href="#/{{currentCore.name}}/analysis"><span>Analysis</span></a></li>
<li ng-hide="isCloudEnabled" class="documents" ng-class="{active:page=='documents'}"><a href="#/{{currentCore.name}}/documents"><span>Documents</span></a></li>
+ <li ng-hide="isCloudEnabled" class="paramsets" ng-class="{active:page=='paramsets'}"><a href="#/{{currentCore.name}}/paramsets"><span>Paramsets</span></a></li>
<li ng-hide="isCloudEnabled" class="files" ng-class="{active:page=='files'}"><a href="#/{{currentCore.name}}/files"><span>Files</span></a></li>
<li class="ping" ng-class="{active:page=='ping'}"><a ng-click="ping()"><span>Ping</span><small class="qtime" ng-show="showPing"> (<span>{{pingMS}}ms</span>)</small></a></li>
<li class="plugins" ng-class="{active:page=='plugins'}"><a href="#/{{currentCore.name}}/plugins"><span>Plugins / Stats</span></a></li>
diff --git a/solr/webapp/web/js/angular/app.js b/solr/webapp/web/js/angular/app.js
index d0de77acbee..a8bd2f5eb0e 100644
--- a/solr/webapp/web/js/angular/app.js
+++ b/solr/webapp/web/js/angular/app.js
@@ -135,6 +135,10 @@ solrAdminApp.config([
templateUrl: 'partials/documents.html',
controller: 'DocumentsController'
}).
+ when('/:core/paramsets', {
+ templateUrl: 'partials/paramsets.html',
+ controller: 'ParamSetsController'
+ }).
when('/:core/files', {
templateUrl: 'partials/files.html',
controller: 'FilesController'
diff --git a/solr/webapp/web/js/angular/controllers/paramsets.js b/solr/webapp/web/js/angular/controllers/paramsets.js
new file mode 100644
index 00000000000..e18870b3ce3
--- /dev/null
+++ b/solr/webapp/web/js/angular/controllers/paramsets.js
@@ -0,0 +1,158 @@
+/*
+ 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
+
+solrAdminApp.controller('ParamSetsController',
+ function($scope, $location, $routeParams, ParamSet, Constants) {
+
+ $scope.paramsetList = [];
+
+ $scope.resetMenu("paramsets", Constants.IS_COLLECTION_PAGE);
+
+ $scope.sampleAPICommand = {
+ "set": {
+ "myQueries": {
+ "defType": "edismax",
+ "rows": "5",
+ "df": "text_all"
+ }
+ }
+ }
+
+ $scope.selectParamset = function() {
+ $location.search("paramset", $scope.name);
+ $scope.getParamset($scope.name);
+ }
+
+ $scope.getParamset = function (paramset) {
+ $scope.refresh();
+
+ var params = {};
+ params.core = $routeParams.core;
+ params.wt = "json";
+ params.name = paramset;
+
+ ParamSet.get(params, callback, failure);
+
+ ///////
+
+ function callback(success) {
+ $scope.responseStatus = "success";
+ delete success.$promise;
+ delete success.$resolved;
+ $scope.response = JSON.stringify(success, null, ' ');
+ var apiPayload = {
+ "set": success.response.params
+ };
+ // remove json key that is defined as "", it can't be submitted via the API.
+ var paramsetName = Object.keys(apiPayload.set)[0];
+ delete apiPayload.set[paramsetName][""]
+
+ $scope.paramsetContent = JSON.stringify(apiPayload, null, ' ');
+ }
+
+ function failure (failure) {
+ $scope.responseStatus = failure;
+ }
+ }
+
+
+ $scope.getParamsets = function () {
+ $scope.refresh();
+
+ var params = {};
+ params.core = $routeParams.core;
+ params.wt = "json";
+
+ ParamSet.get(params, callback, failure);
+
+ ///////
+
+ function callback(success) {
+ $scope.responseStatus = "success";
+ delete success.$promise;
+ delete success.$resolved;
+ $scope.response = JSON.stringify(success, null, ' ');
+ $scope.paramsetList = success.response.params ? Object.keys(success.response.params) : [];
+ }
+
+ function failure (failure) {
+ $scope.responseStatus = failure;
+ }
+ }
+
+ $scope.getParamsets();
+ if ($routeParams.paramset){
+ $scope.name = $routeParams.paramset;
+ $scope.getParamset($routeParams.paramset);
+ }
+
+ $scope.refresh = function () {
+ $scope.paramsetContent = "";
+ $scope.placeholder = JSON.stringify($scope.sampleAPICommand, null, ' ');
+ };
+ $scope.refresh();
+
+ $scope.submit = function () {
+ var params = {};
+
+ params.core = $routeParams.core;
+ params.wt = "json";
+
+ ParamSet.submit(params, $scope.paramsetContent, callback, failure);
+
+ ///////
+ function callback(success) {
+ $scope.responseStatus = "success";
+ delete success.$promise;
+ delete success.$resolved;
+ $scope.response = JSON.stringify(success, null, ' ');
+ $scope.name = null;
+ $scope.getParamsets();
+ }
+ function failure (failure) {
+ $scope.responseStatus = failure;
+ }
+ }
+
+ $scope.deleteParamset = function () {
+ var params = {};
+
+ params.core = $routeParams.core;
+ params.wt = "json";
+ params.name = $scope.name;
+
+ var apiPayload = {
+ "delete": [$scope.name]
+ };
+
+ ParamSet.submit(params, apiPayload, callback, failure);
+
+ ///////
+ function callback(success) {
+ $scope.responseStatus = "success";
+ delete success.$promise;
+ delete success.$resolved;
+ $scope.response = JSON.stringify(success, null, ' ');
+ $scope.getParamsets();
+ }
+ function failure (failure) {
+ $scope.responseStatus = failure;
+ }
+ }
+
+ });
diff --git a/solr/webapp/web/js/angular/controllers/query.js b/solr/webapp/web/js/angular/controllers/query.js
index 5e36cfb73bd..14678d26572 100644
--- a/solr/webapp/web/js/angular/controllers/query.js
+++ b/solr/webapp/web/js/angular/controllers/query.js
@@ -16,7 +16,7 @@
*/
solrAdminApp.controller('QueryController',
- function($scope, $route, $routeParams, $location, Query, Constants){
+ function($scope, $route, $routeParams, $location, Query, Constants, ParamSet){
$scope.resetMenu("query", Constants.IS_COLLECTION_PAGE);
$scope._models = [];
@@ -27,6 +27,31 @@ solrAdminApp.controller('QueryController',
$scope.val['q.op'] = "OR";
$scope.val['defType'] = "";
$scope.val['indent'] = true;
+ $scope.useParams = [];
+
+ getParamsets();
+
+ function getParamsets() {
+
+ var params = {};
+ params.core = $routeParams.core;
+ params.wt = "json";
+
+ ParamSet.get(params, callback, failure);
+
+ ///////
+
+ function callback(success) {
+ $scope.responseStatus = "success";
+ delete success.$promise;
+ delete success.$resolved;
+ $scope.paramsetList = success.response.params ? Object.keys(success.response.params) : [];
+ }
+
+ function failure (failure) {
+ $scope.responseStatus = failure;
+ }
+ }
// get list of ng-models that have a form element
function setModels(argTagName){
@@ -68,7 +93,12 @@ solrAdminApp.controller('QueryController',
$scope.val[argKey] = argValue;
} else if( $scope._models.map(function(field){return field.modelName}).indexOf(argKey) > -1 ) {
// parameters that will only be used to generate the admin link
- $scope[argKey] = argValue;
+ if (argKey === 'useParams'){
+ $scope[argKey] = argValue.split(",")
+ }
+ else {
+ $scope[argKey] = argValue;
+ }
} else {
insertToRawParams(argKey, argValue);
}
@@ -135,16 +165,13 @@ solrAdminApp.controller('QueryController',
return param.indexOf(argParam) === 0;
});
}
-
copy(params, $scope.val);
-
purgeParams(params, ["q.alt", "qf", "mm", "pf", "ps", "qs", "tie", "bq", "bf"], $scope.val.defType !== "dismax" && $scope.val.defType !== "edismax");
purgeParams(params, ["uf", "pf2", "pf3", "ps2", "ps3", "boost", "stopwords", "lowercaseOperators"], $scope.val.defType !== "edismax");
purgeParams(params, getDependentFields("hl"), $scope.val.hl !== true);
purgeParams(params, getDependentFields("facet"), $scope.val.facet !== true);
purgeParams(params, ["spatial", "pt", "sfield", "d"], $scope.val.spatial !== true);
purgeParams(params, getDependentFields("spellcheck"), $scope.val.spellcheck !== true);
-
var qt = $scope.qt ? $scope.qt : "/select";
for (var filter in $scope.filters) {
@@ -173,17 +200,41 @@ solrAdminApp.controller('QueryController',
params.handler = "select";
set("qt", qt);
}
+
+ // convert useParams to array to generate nice URL.
+ if (!Array.isArray($scope.useParams)){
+ params.useParams = $scope.useParams.split(",");
+ }
+ else {
+ params.useParams = $scope.useParams;
+ }
+
// create rest result url
var url = Query.url(params);
+ // convert useParams back to string
+ if (Array.isArray($scope.useParams)){
+ params.useParams = $scope.useParams.join(",");
+ }
+ else {
+ params.useParams = $scope.useParams;
+ }
+
// create admin page url
var adminParams = {...params};
delete adminParams.handler;
delete adminParams.core
+ if (!Array.isArray(adminParams.useParams)){
+ adminParams.useParams = adminParams.useParams.split(",");
+ }
if( $scope.qt != null ) {
adminParams.qt = [$scope.qt];
}
if (isPageReload) {
+ if (!Array.isArray(params.useParams)){
+ params.useParams = params.useParams.split(",");
+ }
+
Query.query(params, function (data) {
$scope.lang = $scope.val['wt'];
if (!$scope.lang || $scope.lang === '') {
diff --git a/solr/webapp/web/js/angular/services.js b/solr/webapp/web/js/angular/services.js
index f8b0b0082bc..1c0b702dbe1 100644
--- a/solr/webapp/web/js/angular/services.js
+++ b/solr/webapp/web/js/angular/services.js
@@ -126,6 +126,13 @@ solrAdminServices.factory('System',
"postCsv": {headers: {'Content-type': 'application/csv'}, method: "POST", params: {handler: '@handler'}}
});
}])
+.factory('ParamSet',
+ ['$resource', function($resource) {
+ return $resource(':core/config/params/:name', {core: '@core', wt:'json', _:Date.now()}, {
+ "submit": {headers: {'Content-type': 'application/json'}, method: "POST"},
+ "get": {headers: {'Content-type': 'application/json'}, method: "GET"}
+ });
+ }])
.service('FileUpload', function ($http) {
this.upload = function(params, file, success, error){
var url = "" + params.core + "/" + params.handler + "?";
diff --git a/solr/webapp/web/libs/angular-chosen.min.js b/solr/webapp/web/libs/angular-chosen.min.js
index b7088e2ba9a..0b12c17492b 100644
--- a/solr/webapp/web/libs/angular-chosen.min.js
+++ b/solr/webapp/web/libs/angular-chosen.min.js
@@ -1,23 +1,3 @@
-/*
-The MIT License
-Copyright (c) 2013 Localytics http://www.localytics.com
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.
-*/
-
/**
* angular-chosen-localytics - Angular Chosen directive is an AngularJS Directive that brings the Chosen jQuery in a Angular way
* @version v1.9.2
diff --git a/solr/webapp/web/partials/paramsets.html b/solr/webapp/web/partials/paramsets.html
new file mode 100644
index 00000000000..1867b35d927
--- /dev/null
+++ b/solr/webapp/web/partials/paramsets.html
@@ -0,0 +1,93 @@
+<!--
+/*
+* 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="paramsets" class="clearfix">
+ <div id="form">
+ <form>
+
+ <fieldset>
+ <legend>Select Paramset</legend>
+ <div id="paramset-name-container">
+
+ <label for="paramsetName">
+ <a rel="help">Paramsets</a>
+ </label>
+ <select id="paramsetName"
+ ng-model="name"
+ chosen
+ data-placeholder="Please select..."
+ ng-change="selectParamset()"
+ ng-options="item for item in paramsetList"><option value=""></option></select>
+
+ </div>
+
+ <div id="delete-paramset" ng-show="name">
+ <button id="delete-paramset" class="warn" ng-click="deleteParamset()">Delete Paramset</button>
+ <div>
+ </fieldset>
+
+ <div>
+ <form>
+ <label>
+ <input type="checkbox"
+ ng-model="switch.showSample">
+ Sample Paramset
+ </label>
+ </form>
+ <div ng-switch="switch.showSample">
+ <div id="sample-paramset" ng-switch-when="true">
+ <pre class="syntax language-json"><code ng-bind-html="placeholder | highlight:'json' | unsafe"></code></pre>
+ </div>
+ </div>
+
+ <fieldset>
+ <legend>Update Paramset(s)</legend>
+ <div class="fieldset" id="paramsetContent-container">
+ <label for="paramset">
+ Paramset(s) JSON
+ </label>
+ <textarea ng-model="paramsetContent"
+ name="paramsetContent"
+ id="paramsetContent"
+ title="Request Parameters API Payload"
+ rows="10"
+ cols="65"></textarea>
+ <p>
+ <a href="https://solr.apache.org/guide/solr/latest/configuration-guide/request-parameters-api.html" target="_out">syntax help</a>
+ </p>
+ </div>
+ </fieldset>
+
+ </div>
+ <button type="submit" ng-click="submit()" id="submit">Submit Updates</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>
diff --git a/solr/webapp/web/partials/query.html b/solr/webapp/web/partials/query.html
index f0544ca2a70..58575eb8b9f 100644
--- a/solr/webapp/web/partials/query.html
+++ b/solr/webapp/web/partials/query.html
@@ -77,6 +77,15 @@ limitations under the License.
</label>
<input type="text" ng-model="val['df']" name="df" id="df" value="" title="Default search field">
+ <label for="useParams">paramset(s)</label>
+ <select id="useParams"
+ ng-model="useParams"
+ title="The paramsets to apply to the query."
+ chosen
+ multiple
+ data-placeholder="Select paramset(s)..."
+ ng-options="item for item in paramsetList"><option value=""></option></select>
+
<label for="wt" title="The writer type (response format).">
<a rel="help">wt</a>
</label>