moved bleve-explorer mapping UI to bleve/http/mapping
All this work comes from Marty Schoch's bleve-explorer UI, but was moved here for increased reusability.
This commit is contained in:
parent
8f70def63b
commit
9a09689e61
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,428 @@
|
|||
// Copyright (c) 2015 Couchbase, Inc.
|
||||
// Licensed 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.
|
||||
|
||||
package mapping
|
||||
|
||||
//go:generate go-bindata-assetfs -pkg=mapping ./mapping_static/...
|
||||
//go:generate go fmt .
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net/http"
|
||||
"sort"
|
||||
|
||||
"github.com/elazarl/go-bindata-assetfs"
|
||||
|
||||
"github.com/gorilla/mux"
|
||||
|
||||
"github.com/blevesearch/bleve"
|
||||
"github.com/blevesearch/bleve/analysis"
|
||||
"github.com/blevesearch/bleve/registry"
|
||||
)
|
||||
|
||||
func AssetFS() *assetfs.AssetFS {
|
||||
return assetFS()
|
||||
}
|
||||
|
||||
// RegisterHandlers registers mapping handlers on a router at the
|
||||
// given pathBase, such as at "/api".
|
||||
func RegisterHandlers(router *mux.Router, pathBase string) {
|
||||
router.HandleFunc(pathBase+"/_analyzerNames", ListAnalyzerNames).Methods("POST")
|
||||
router.HandleFunc(pathBase+"/_datetimeParserNames", ListDateTimeParserNames).Methods("POST")
|
||||
router.HandleFunc(pathBase+"/_charFilterNames", ListCharFilterNames).Methods("POST")
|
||||
router.HandleFunc(pathBase+"/_charFilterTypes", ListCharFilterTypes).Methods("GET")
|
||||
router.HandleFunc(pathBase+"/_tokenizerNames", ListTokenizerNames).Methods("POST")
|
||||
router.HandleFunc(pathBase+"/_tokenizerTypes", ListTokenizerTypes).Methods("GET")
|
||||
router.HandleFunc(pathBase+"/_tokenFilterNames", ListTokenFilterNames).Methods("POST")
|
||||
router.HandleFunc(pathBase+"/_tokenFilterTypes", ListTokenFilterTypes).Methods("GET")
|
||||
router.HandleFunc(pathBase+"/_tokenMapNames", ListTokenMapNames).Methods("POST")
|
||||
router.HandleFunc(pathBase+"/_analyze", AnalyzerText).Methods("POST")
|
||||
router.HandleFunc(pathBase+"/_validateMapping", ValidateMapping).Methods("POST")
|
||||
}
|
||||
|
||||
func ListAnalyzerNames(w http.ResponseWriter, req *http.Request) {
|
||||
|
||||
indexMapping := bleve.NewIndexMapping()
|
||||
|
||||
// read the request body
|
||||
requestBody, err := ioutil.ReadAll(req.Body)
|
||||
if err != nil {
|
||||
showError(w, req, fmt.Sprintf("error reading request body: %v", err), 400)
|
||||
return
|
||||
}
|
||||
|
||||
// interpret request body as index mapping
|
||||
if len(requestBody) > 0 {
|
||||
err := json.Unmarshal(requestBody, &indexMapping)
|
||||
if err != nil {
|
||||
showError(w, req, fmt.Sprintf("error parsing index mapping: %v", err), 400)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// built in analyzer names
|
||||
_, analyzerNames := registry.AnalyzerTypesAndInstances()
|
||||
// add custom analyzer names
|
||||
for name := range indexMapping.CustomAnalysis.Analyzers {
|
||||
analyzerNames = append(analyzerNames, name)
|
||||
}
|
||||
|
||||
sort.Strings(analyzerNames)
|
||||
|
||||
rv := struct {
|
||||
Status string `json:"status"`
|
||||
Analyzers []string `json:"analyzers"`
|
||||
}{
|
||||
Status: "ok",
|
||||
Analyzers: analyzerNames,
|
||||
}
|
||||
mustEncode(w, rv)
|
||||
}
|
||||
|
||||
func AnalyzerText(w http.ResponseWriter, req *http.Request) {
|
||||
// read the request body
|
||||
requestBody, err := ioutil.ReadAll(req.Body)
|
||||
if err != nil {
|
||||
showError(w, req, fmt.Sprintf("error reading request body: %v", err), 400)
|
||||
return
|
||||
}
|
||||
|
||||
mapping := bleve.NewIndexMapping()
|
||||
var analyzeRequest = struct {
|
||||
Analyzer string `json:"analyzer"`
|
||||
Text string `json:"text"`
|
||||
Mapping *bleve.IndexMapping `json:"mapping"`
|
||||
}{}
|
||||
|
||||
err = json.Unmarshal(requestBody, &analyzeRequest)
|
||||
if err != nil {
|
||||
showError(w, req, fmt.Sprintf("error parsing index mapping: %v", err), 400)
|
||||
return
|
||||
}
|
||||
|
||||
if analyzeRequest.Mapping != nil {
|
||||
mapping = analyzeRequest.Mapping
|
||||
}
|
||||
|
||||
ts, err := mapping.AnalyzeText(analyzeRequest.Analyzer, []byte(analyzeRequest.Text))
|
||||
if err != nil {
|
||||
showError(w, req, fmt.Sprintf("error analyzing text: %v", err), 400)
|
||||
return
|
||||
}
|
||||
|
||||
rv := struct {
|
||||
Status string `json:"status"`
|
||||
Text string `json:"text"`
|
||||
TokenStream analysis.TokenStream `json:"token_stream"`
|
||||
}{
|
||||
Status: "ok",
|
||||
Text: analyzeRequest.Text,
|
||||
TokenStream: ts,
|
||||
}
|
||||
mustEncode(w, rv)
|
||||
}
|
||||
|
||||
func ListDateTimeParserNames(w http.ResponseWriter, req *http.Request) {
|
||||
|
||||
indexMapping := bleve.NewIndexMapping()
|
||||
|
||||
// read the request body
|
||||
requestBody, err := ioutil.ReadAll(req.Body)
|
||||
if err != nil {
|
||||
showError(w, req, fmt.Sprintf("error reading request body: %v", err), 400)
|
||||
return
|
||||
}
|
||||
|
||||
// interpret request body as index mapping
|
||||
if len(requestBody) > 0 {
|
||||
err := json.Unmarshal(requestBody, &indexMapping)
|
||||
if err != nil {
|
||||
showError(w, req, fmt.Sprintf("error parsing index mapping: %v", err), 400)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// built in char filter names
|
||||
_, dateTimeParserNames := registry.DateTimeParserTypesAndInstances()
|
||||
// add custom date time parser names
|
||||
for name := range indexMapping.CustomAnalysis.DateTimeParsers {
|
||||
dateTimeParserNames = append(dateTimeParserNames, name)
|
||||
}
|
||||
|
||||
sort.Strings(dateTimeParserNames)
|
||||
|
||||
rv := struct {
|
||||
Status string `json:"status"`
|
||||
DateTimeParsers []string `json:"datetime_parsers"`
|
||||
}{
|
||||
Status: "ok",
|
||||
DateTimeParsers: dateTimeParserNames,
|
||||
}
|
||||
mustEncode(w, rv)
|
||||
}
|
||||
|
||||
func ListCharFilterNames(w http.ResponseWriter, req *http.Request) {
|
||||
|
||||
indexMapping := bleve.NewIndexMapping()
|
||||
|
||||
// read the request body
|
||||
requestBody, err := ioutil.ReadAll(req.Body)
|
||||
if err != nil {
|
||||
showError(w, req, fmt.Sprintf("error reading request body: %v", err), 400)
|
||||
return
|
||||
}
|
||||
|
||||
// interpret request body as index mapping
|
||||
if len(requestBody) > 0 {
|
||||
err := json.Unmarshal(requestBody, &indexMapping)
|
||||
if err != nil {
|
||||
showError(w, req, fmt.Sprintf("error parsing index mapping: %v", err), 400)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// built in char filter names
|
||||
_, charFilterNames := registry.CharFilterTypesAndInstances()
|
||||
// add custom char filter names
|
||||
for name := range indexMapping.CustomAnalysis.CharFilters {
|
||||
charFilterNames = append(charFilterNames, name)
|
||||
}
|
||||
|
||||
sort.Strings(charFilterNames)
|
||||
|
||||
rv := struct {
|
||||
Status string `json:"status"`
|
||||
CharFilters []string `json:"char_filters"`
|
||||
}{
|
||||
Status: "ok",
|
||||
CharFilters: charFilterNames,
|
||||
}
|
||||
mustEncode(w, rv)
|
||||
}
|
||||
|
||||
func ListCharFilterTypes(w http.ResponseWriter, req *http.Request) {
|
||||
|
||||
// built in char filter names
|
||||
charFilterTypes, _ := registry.CharFilterTypesAndInstances()
|
||||
|
||||
sort.Strings(charFilterTypes)
|
||||
|
||||
rv := struct {
|
||||
Status string `json:"status"`
|
||||
CharFilterTypes []string `json:"char_filter_types"`
|
||||
}{
|
||||
Status: "ok",
|
||||
CharFilterTypes: charFilterTypes,
|
||||
}
|
||||
mustEncode(w, rv)
|
||||
}
|
||||
|
||||
func ListTokenizerNames(w http.ResponseWriter, req *http.Request) {
|
||||
|
||||
indexMapping := bleve.NewIndexMapping()
|
||||
|
||||
// read the request body
|
||||
requestBody, err := ioutil.ReadAll(req.Body)
|
||||
if err != nil {
|
||||
showError(w, req, fmt.Sprintf("error reading request body: %v", err), 400)
|
||||
return
|
||||
}
|
||||
|
||||
// interpret request body as index mapping
|
||||
if len(requestBody) > 0 {
|
||||
err := json.Unmarshal(requestBody, &indexMapping)
|
||||
if err != nil {
|
||||
showError(w, req, fmt.Sprintf("error parsing index mapping: %v", err), 400)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// built in char filter names
|
||||
_, tokenizerNames := registry.TokenizerTypesAndInstances()
|
||||
// add custom char filter names
|
||||
for name := range indexMapping.CustomAnalysis.Tokenizers {
|
||||
tokenizerNames = append(tokenizerNames, name)
|
||||
}
|
||||
|
||||
sort.Strings(tokenizerNames)
|
||||
|
||||
rv := struct {
|
||||
Status string `json:"status"`
|
||||
Tokenizers []string `json:"tokenizers"`
|
||||
}{
|
||||
Status: "ok",
|
||||
Tokenizers: tokenizerNames,
|
||||
}
|
||||
mustEncode(w, rv)
|
||||
}
|
||||
|
||||
func ListTokenizerTypes(w http.ResponseWriter, req *http.Request) {
|
||||
|
||||
// built in char filter names
|
||||
tokenizerTypes, _ := registry.TokenizerTypesAndInstances()
|
||||
|
||||
sort.Strings(tokenizerTypes)
|
||||
|
||||
rv := struct {
|
||||
Status string `json:"status"`
|
||||
TokenizerTypes []string `json:"tokenizer_types"`
|
||||
}{
|
||||
Status: "ok",
|
||||
TokenizerTypes: tokenizerTypes,
|
||||
}
|
||||
mustEncode(w, rv)
|
||||
}
|
||||
|
||||
func ListTokenFilterNames(w http.ResponseWriter, req *http.Request) {
|
||||
|
||||
indexMapping := bleve.NewIndexMapping()
|
||||
|
||||
// read the request body
|
||||
requestBody, err := ioutil.ReadAll(req.Body)
|
||||
if err != nil {
|
||||
showError(w, req, fmt.Sprintf("error reading request body: %v", err), 400)
|
||||
return
|
||||
}
|
||||
|
||||
// interpret request body as index mapping
|
||||
if len(requestBody) > 0 {
|
||||
err := json.Unmarshal(requestBody, &indexMapping)
|
||||
if err != nil {
|
||||
showError(w, req, fmt.Sprintf("error parsing index mapping: %v", err), 400)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// built in char filter names
|
||||
_, tokenFilterNames := registry.TokenFilterTypesAndInstances()
|
||||
// add custom char filter names
|
||||
for name := range indexMapping.CustomAnalysis.TokenFilters {
|
||||
tokenFilterNames = append(tokenFilterNames, name)
|
||||
}
|
||||
|
||||
sort.Strings(tokenFilterNames)
|
||||
|
||||
rv := struct {
|
||||
Status string `json:"status"`
|
||||
TokenFilters []string `json:"token_filters"`
|
||||
}{
|
||||
Status: "ok",
|
||||
TokenFilters: tokenFilterNames,
|
||||
}
|
||||
mustEncode(w, rv)
|
||||
}
|
||||
|
||||
func ListTokenFilterTypes(w http.ResponseWriter, req *http.Request) {
|
||||
|
||||
// built in char filter names
|
||||
tokenFilterTypes, _ := registry.TokenFilterTypesAndInstances()
|
||||
|
||||
sort.Strings(tokenFilterTypes)
|
||||
|
||||
rv := struct {
|
||||
Status string `json:"status"`
|
||||
TokenFilterTypes []string `json:"token_filter_types"`
|
||||
}{
|
||||
Status: "ok",
|
||||
TokenFilterTypes: tokenFilterTypes,
|
||||
}
|
||||
mustEncode(w, rv)
|
||||
}
|
||||
|
||||
func ListTokenMapNames(w http.ResponseWriter, req *http.Request) {
|
||||
|
||||
indexMapping := bleve.NewIndexMapping()
|
||||
|
||||
// read the request body
|
||||
requestBody, err := ioutil.ReadAll(req.Body)
|
||||
if err != nil {
|
||||
showError(w, req, fmt.Sprintf("error reading request body: %v", err), 400)
|
||||
return
|
||||
}
|
||||
|
||||
// interpret request body as index mapping
|
||||
if len(requestBody) > 0 {
|
||||
err := json.Unmarshal(requestBody, &indexMapping)
|
||||
if err != nil {
|
||||
showError(w, req, fmt.Sprintf("error parsing index mapping: %v", err), 400)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// built in char filter names
|
||||
_, tokenMapNames := registry.TokenMapTypesAndInstances()
|
||||
// add custom char map names
|
||||
for name := range indexMapping.CustomAnalysis.TokenMaps {
|
||||
tokenMapNames = append(tokenMapNames, name)
|
||||
}
|
||||
|
||||
sort.Strings(tokenMapNames)
|
||||
|
||||
rv := struct {
|
||||
Status string `json:"status"`
|
||||
TokenMaps []string `json:"token_maps"`
|
||||
}{
|
||||
Status: "ok",
|
||||
TokenMaps: tokenMapNames,
|
||||
}
|
||||
mustEncode(w, rv)
|
||||
}
|
||||
|
||||
func ValidateMapping(w http.ResponseWriter, req *http.Request) {
|
||||
|
||||
indexMapping := bleve.NewIndexMapping()
|
||||
|
||||
// read the request body
|
||||
requestBody, err := ioutil.ReadAll(req.Body)
|
||||
if err != nil {
|
||||
showError(w, req, fmt.Sprintf("error reading request body: %v", err), 400)
|
||||
return
|
||||
}
|
||||
|
||||
// interpret request body as index mapping
|
||||
if len(requestBody) > 0 {
|
||||
err := json.Unmarshal(requestBody, &indexMapping)
|
||||
if err != nil {
|
||||
showError(w, req, fmt.Sprintf("error parsing index mapping: %v", err), 400)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
rv := struct {
|
||||
Status string `json:"status"`
|
||||
}{
|
||||
Status: "ok",
|
||||
}
|
||||
mustEncode(w, rv)
|
||||
|
||||
}
|
||||
|
||||
func showError(w http.ResponseWriter, r *http.Request,
|
||||
msg string, code int) {
|
||||
log.Printf("Reporting error %v/%v", code, msg)
|
||||
http.Error(w, msg, code)
|
||||
}
|
||||
|
||||
func mustEncode(w io.Writer, i interface{}) {
|
||||
if headered, ok := w.(http.ResponseWriter); ok {
|
||||
headered.Header().Set("Cache-Control", "no-cache")
|
||||
headered.Header().Set("Content-type", "application/json")
|
||||
}
|
||||
|
||||
e := json.NewEncoder(w)
|
||||
if err := e.Encode(i); err != nil {
|
||||
panic(err)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,144 @@
|
|||
var AnalyzerModalCtrl = function ($scope, $modalInstance, $http, name, value, mapping) {
|
||||
$scope.origName = name;
|
||||
$scope.name = name;
|
||||
$scope.errorMessage = "";
|
||||
$scope.formpath = "";
|
||||
$scope.mapping = mapping;
|
||||
|
||||
$scope.analyzer = {};
|
||||
// copy in value for editing
|
||||
for (var k in value) {
|
||||
// need deeper copy of nested arrays
|
||||
if (k == "char_filters") {
|
||||
newcharfilters = [];
|
||||
for (var cfi in value.char_filters) {
|
||||
newcharfilters.push(value.char_filters[cfi]);
|
||||
}
|
||||
$scope.analyzer.char_filters = newcharfilters;
|
||||
} else if (k == "token_filters") {
|
||||
newtokenfilters = [];
|
||||
for (var tfi in value.token_filters) {
|
||||
newtokenfilters.push(value.token_filters[tfi]);
|
||||
}
|
||||
$scope.analyzer.token_filters = newtokenfilters;
|
||||
} else {
|
||||
$scope.analyzer[k] = value[k];
|
||||
}
|
||||
}
|
||||
|
||||
$scope.tokenizerNames = [];
|
||||
|
||||
$scope.loadTokenizerNames = function() {
|
||||
$http.post('/api/_tokenizerNames',mapping).success(function(data) {
|
||||
$scope.tokenizerNames = data.tokenizers;
|
||||
}).
|
||||
error(function(data, code) {
|
||||
$scope.errorMessage = data;
|
||||
});
|
||||
};
|
||||
|
||||
$scope.loadTokenizerNames();
|
||||
|
||||
$scope.charFilterNames = [];
|
||||
|
||||
$scope.loadCharFilterNames = function() {
|
||||
$http.post('/api/_charFilterNames',mapping).success(function(data) {
|
||||
$scope.charFilterNames = data.char_filters;
|
||||
}).
|
||||
error(function(data, code) {
|
||||
$scope.errorMessage = data;
|
||||
});
|
||||
};
|
||||
|
||||
$scope.loadCharFilterNames();
|
||||
|
||||
$scope.addCharFilter = function(scope) {
|
||||
filter = scope.addCharacterFilterName;
|
||||
if (filter !== undefined && filter !== "") {
|
||||
$scope.selectedAnalyzer.char_filters.push(filter);
|
||||
}
|
||||
console.log($scope.selectedAnalyzer.char_filters);
|
||||
};
|
||||
|
||||
$scope.removeCharFilter = function(index) {
|
||||
$scope.selectedAnalyzer.char_filters.splice(index, 1);
|
||||
};
|
||||
|
||||
$scope.tokenFilterNames = [];
|
||||
|
||||
$scope.loadTokenFilterNames = function() {
|
||||
$http.post('/api/_tokenFilterNames',mapping).success(function(data) {
|
||||
$scope.tokenFilterNames = data.token_filters;
|
||||
}).
|
||||
error(function(data, code) {
|
||||
$scope.errorMessage = data;
|
||||
});
|
||||
};
|
||||
|
||||
$scope.loadTokenFilterNames();
|
||||
|
||||
$scope.addCharFilter = function(scope) {
|
||||
filter = scope.addCharacterFilterName;
|
||||
if (filter !== undefined && filter !== "") {
|
||||
$scope.analyzer.char_filters.push(filter);
|
||||
}
|
||||
console.log($scope.analyzer.char_filters);
|
||||
};
|
||||
|
||||
$scope.removeCharFilter = function(index) {
|
||||
$scope.analyzer.char_filters.splice(index, 1);
|
||||
};
|
||||
|
||||
$scope.addTokenFilter = function(scope) {
|
||||
filter = scope.addTokenFilterName;
|
||||
if (filter !== undefined && filter !== "") {
|
||||
$scope.analyzer.token_filters.push(filter);
|
||||
}
|
||||
console.log($scope.analyzer.token_filters);
|
||||
};
|
||||
|
||||
$scope.removeTokenFilter = function(index) {
|
||||
$scope.analyzer.token_filters.splice(index, 1);
|
||||
};
|
||||
|
||||
$scope.cancel = function () {
|
||||
$modalInstance.dismiss('cancel');
|
||||
};
|
||||
|
||||
$scope.build = function() {
|
||||
// must have a name
|
||||
if (!$scope.name) {
|
||||
$scope.errorMessage = "Name is required";
|
||||
return;
|
||||
}
|
||||
|
||||
// name must not already be used
|
||||
if ($scope.name != $scope.origName && $scope.mapping.analysis.analyzers[$scope.name]) {
|
||||
$scope.errorMessage = "Analyzer named '" + $scope.name + "' already exists";
|
||||
return;
|
||||
}
|
||||
|
||||
// ensure that this new mapping component is valid
|
||||
analysis = {};
|
||||
for (var ak in $scope.mapping.analysis) {
|
||||
analysis[ak] = $scope.mapping.analysis[ak];
|
||||
}
|
||||
analyzers = {};
|
||||
analyzers[$scope.name] = $scope.analyzer;
|
||||
analysis["analyzers"] = analyzers;
|
||||
testMapping = {
|
||||
"analysis": analysis
|
||||
};
|
||||
$http.post('/api/_validateMapping',testMapping).success(function(data) {
|
||||
// if its valid return it
|
||||
result = {};
|
||||
result[$scope.name] = $scope.analyzer;
|
||||
$modalInstance.close(result);
|
||||
}).
|
||||
error(function(data, code) {
|
||||
// otherwise display error
|
||||
$scope.errorMessage = data;
|
||||
});
|
||||
|
||||
};
|
||||
};
|
|
@ -0,0 +1,105 @@
|
|||
var CharFilterModalCtrl = function ($scope, $modalInstance, $http, name, value, mapping) {
|
||||
$scope.origName = name;
|
||||
$scope.name = name;
|
||||
$scope.errorMessage = "";
|
||||
$scope.formpath = "";
|
||||
$scope.mapping = mapping;
|
||||
|
||||
$scope.charfilter = {};
|
||||
// copy in value for editing
|
||||
for (var k in value) {
|
||||
$scope.charfilter[k] = value[k];
|
||||
}
|
||||
|
||||
$scope.unknownCharFilterTypeTemplate = "/static/partials/analysis/charfilters/generic.html";
|
||||
$scope.charFilterTypeTemplates = {
|
||||
"regexp": "/static/partials/analysis/charfilters/regexp.html",
|
||||
};
|
||||
$scope.charFilterTypeDefaults = {
|
||||
"regexp": function() {
|
||||
return {
|
||||
"regexp": "",
|
||||
"replace": ""
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
$scope.charFilterTypes = [];
|
||||
|
||||
updateCharFilterTypes = function() {
|
||||
$http.get('/api/_charFilterTypes').success(function(data) {
|
||||
$scope.charFilterTypes = data.char_filter_types;
|
||||
}).
|
||||
error(function(data, code) {
|
||||
$scope.errorMessage = data;
|
||||
});
|
||||
};
|
||||
|
||||
updateCharFilterTypes();
|
||||
|
||||
if (!$scope.charfilter.type) {
|
||||
defaultType = "regexp";
|
||||
if ($scope.charFilterTypeDefaults[defaultType]) {
|
||||
$scope.charfilter = $scope.charFilterTypeDefaults[defaultType]();
|
||||
}
|
||||
else {
|
||||
$scope.charfilter = {};
|
||||
}
|
||||
$scope.charfilter.type = defaultType;
|
||||
|
||||
}
|
||||
$scope.formpath = $scope.charFilterTypeTemplates[$scope.charfilter.type];
|
||||
|
||||
$scope.charFilterTypeChange = function() {
|
||||
newType = $scope.charfilter.type;
|
||||
if ($scope.charFilterTypeDefaults[$scope.charfilter.type]) {
|
||||
$scope.charfilter = $scope.charFilterTypeDefaults[$scope.charfilter.type]();
|
||||
} else {
|
||||
$scope.charfilter = {};
|
||||
}
|
||||
$scope.charfilter.type = newType;
|
||||
if ($scope.charFilterTypeTemplates[$scope.charfilter.type]) {
|
||||
$scope.formpath = $scope.charFilterTypeTemplates[$scope.charfilter.type];
|
||||
} else {
|
||||
$scope.formpath = unknownCharFilterTypeTemplate;
|
||||
}
|
||||
};
|
||||
|
||||
$scope.cancel = function () {
|
||||
$modalInstance.dismiss('cancel');
|
||||
};
|
||||
|
||||
$scope.build = function() {
|
||||
// must have a name
|
||||
if (!$scope.name) {
|
||||
$scope.errorMessage = "Name is required";
|
||||
return;
|
||||
}
|
||||
|
||||
// name must not already be used
|
||||
if ($scope.name != $scope.origName && $scope.mapping.analysis.char_filters[$scope.name]) {
|
||||
$scope.errorMessage = "Character filter named '" + $scope.name + "' already exists";
|
||||
return;
|
||||
}
|
||||
|
||||
// ensure that this new mapping component is valid
|
||||
charFilters = {};
|
||||
charFilters[$scope.name] = $scope.charfilter;
|
||||
testMapping = {
|
||||
"analysis": {
|
||||
"char_filters": charFilters
|
||||
}
|
||||
};
|
||||
$http.post('/api/_validateMapping',testMapping).success(function(data) {
|
||||
// if its valid return it
|
||||
result = {};
|
||||
result[$scope.name] = $scope.charfilter;
|
||||
$modalInstance.close(result);
|
||||
}).
|
||||
error(function(data, code) {
|
||||
// otherwise display error
|
||||
$scope.errorMessage = data;
|
||||
});
|
||||
|
||||
};
|
||||
};
|
|
@ -0,0 +1,179 @@
|
|||
var TokenFilterModalCtrl = function ($scope, $modalInstance, $http, name, value, mapping) {
|
||||
$scope.origName = name;
|
||||
$scope.name = name;
|
||||
$scope.errorMessage = "";
|
||||
$scope.formpath = "";
|
||||
$scope.mapping = mapping;
|
||||
|
||||
$scope.tokenfilter = {};
|
||||
// copy in value for editing
|
||||
for (var k in value) {
|
||||
$scope.tokenfilter[k] = value[k];
|
||||
}
|
||||
|
||||
$scope.tokenMapNames = [];
|
||||
|
||||
$scope.loadTokenMapNames = function() {
|
||||
$http.post('/api/_tokenMapNames',mapping).success(function(data) {
|
||||
$scope.tokenMapNames = data.token_maps;
|
||||
}).
|
||||
error(function(data, code) {
|
||||
$scope.errorMessage = data;
|
||||
});
|
||||
};
|
||||
|
||||
$scope.loadTokenMapNames();
|
||||
|
||||
$scope.unknownTokenFilterTypeTemplate = "/static/partials/analysis/tokenfilters/generic.html";
|
||||
$scope.tokenFilterTypeTemplates = {
|
||||
"dict_compound": "/static/partials/analysis/tokenfilters/dict_compound.html",
|
||||
"edge_ngram": "/static/partials/analysis/tokenfilters/edge_ngram.html",
|
||||
"elision": "/static/partials/analysis/tokenfilters/elision.html",
|
||||
"keyword_marker": "/static/partials/analysis/tokenfilters/keyword_marker.html",
|
||||
"length": "/static/partials/analysis/tokenfilters/length.html",
|
||||
"ngram": "/static/partials/analysis/tokenfilters/ngram.html",
|
||||
"normalize_unicode": "/static/partials/analysis/tokenfilters/normalize_unicode.html",
|
||||
"shingle": "/static/partials/analysis/tokenfilters/shingle.html",
|
||||
"stop_tokens": "/static/partials/analysis/tokenfilters/stop_tokens.html",
|
||||
"truncate_token": "/static/partials/analysis/tokenfilters/truncate_token.html",
|
||||
};
|
||||
$scope.tokenFilterTypeDefaults = {
|
||||
"dict_compound": function() {
|
||||
return {
|
||||
"dict_token_map": $scope.tokenMapNames[0]
|
||||
};
|
||||
},
|
||||
"edge_ngram": function() {
|
||||
return {
|
||||
"edge": "front",
|
||||
"min": 3,
|
||||
"max": 3,
|
||||
};
|
||||
},
|
||||
"elision": function() {
|
||||
return {
|
||||
"articles_token_map": $scope.tokenMapNames[0]
|
||||
};
|
||||
},
|
||||
"keyword_marker": function() {
|
||||
return {
|
||||
"keywords_token_map": $scope.tokenMapNames[0]
|
||||
};
|
||||
},
|
||||
"length": function() {
|
||||
return {
|
||||
"min": 3,
|
||||
"max": 255
|
||||
};
|
||||
},
|
||||
"ngram": function() {
|
||||
return {
|
||||
"min": 3,
|
||||
"max": 3
|
||||
};
|
||||
},
|
||||
"normalize_unicode": function() {
|
||||
return {
|
||||
"form": "nfc"
|
||||
};
|
||||
},
|
||||
"shingle": function() {
|
||||
return {
|
||||
"min": 2,
|
||||
"max": 2,
|
||||
"output_original": false,
|
||||
"separator": "",
|
||||
"filler": ""
|
||||
};
|
||||
},
|
||||
"stop_tokens": function() {
|
||||
return {
|
||||
"stop_token_map": $scope.tokenMapNames[0]
|
||||
};
|
||||
},
|
||||
"truncate_token": function() {
|
||||
return {
|
||||
"length": 25
|
||||
};
|
||||
},
|
||||
};
|
||||
|
||||
$scope.tokenFilterTypes = [];
|
||||
|
||||
updateTokenFilterTypes = function() {
|
||||
$http.get('/api/_tokenFilterTypes').success(function(data) {
|
||||
$scope.tokenFilterTypes = data.token_filter_types;
|
||||
}).
|
||||
error(function(data, code) {
|
||||
$scope.errorMessage = data;
|
||||
});
|
||||
};
|
||||
|
||||
updateTokenFilterTypes();
|
||||
|
||||
if (!$scope.tokenfilter.type) {
|
||||
defaultType = "length";
|
||||
if ($scope.tokenFilterTypeDefaults[defaultType]) {
|
||||
$scope.tokenfilter = $scope.tokenFilterTypeDefaults[defaultType]();
|
||||
}
|
||||
else {
|
||||
$scope.tokenfilter = {};
|
||||
}
|
||||
$scope.tokenfilter.type = defaultType;
|
||||
}
|
||||
$scope.formpath = $scope.tokenFilterTypeTemplates[$scope.tokenfilter.type];
|
||||
|
||||
$scope.tokenFilterTypeChange = function() {
|
||||
newType = $scope.tokenfilter.type;
|
||||
if ($scope.tokenFilterTypeDefaults[$scope.tokenfilter.type]) {
|
||||
$scope.tokenfilter = $scope.tokenFilterTypeDefaults[$scope.tokenfilter.type]();
|
||||
} else {
|
||||
$scope.tokenfilter = {};
|
||||
}
|
||||
$scope.tokenfilter.type = newType;
|
||||
if ($scope.tokenFilterTypeTemplates[$scope.tokenfilter.type]) {
|
||||
$scope.formpath = $scope.tokenFilterTypeTemplates[$scope.tokenfilter.type];
|
||||
} else {
|
||||
$scope.formpath = $scope.unknownTokenFilterTypeTemplate;
|
||||
}
|
||||
};
|
||||
|
||||
$scope.cancel = function () {
|
||||
$modalInstance.dismiss('cancel');
|
||||
};
|
||||
|
||||
$scope.build = function() {
|
||||
// must have a name
|
||||
if (!$scope.name) {
|
||||
$scope.errorMessage = "Name is required";
|
||||
return;
|
||||
}
|
||||
|
||||
// name must not already be used
|
||||
if ($scope.name != $scope.origName && $scope.mapping.analysis.token_filters[$scope.name]) {
|
||||
$scope.errorMessage = "Token filter named '" + $scope.name + "' already exists";
|
||||
return;
|
||||
}
|
||||
|
||||
// ensure that this new mapping component is valid
|
||||
tokenfilters = {};
|
||||
tokenfilters[$scope.name] = $scope.tokenfilter;
|
||||
testMapping = {
|
||||
"analysis": {
|
||||
"token_filters": tokenfilters,
|
||||
"token_maps": $scope.mapping.analysis.token_maps
|
||||
}
|
||||
};
|
||||
$http.post('/api/_validateMapping',testMapping).success(function(data) {
|
||||
// if its valid return it
|
||||
result = {};
|
||||
result[$scope.name] = $scope.tokenfilter;
|
||||
$modalInstance.close(result);
|
||||
}).
|
||||
error(function(data, code) {
|
||||
// otherwise display error
|
||||
$scope.errorMessage = data;
|
||||
});
|
||||
|
||||
};
|
||||
};
|
|
@ -0,0 +1,138 @@
|
|||
var TokenizerModalCtrl = function ($scope, $modalInstance, $http, name, value, mapping) {
|
||||
$scope.origName = name;
|
||||
$scope.name = name;
|
||||
$scope.errorMessage = "";
|
||||
$scope.formpath = "";
|
||||
$scope.mapping = mapping;
|
||||
|
||||
$scope.tokenizer = {};
|
||||
// copy in value for editing
|
||||
for (var k in value) {
|
||||
$scope.tokenizer[k] = value[k];
|
||||
}
|
||||
|
||||
$scope.tokenizerNames = [];
|
||||
|
||||
$scope.loadTokenizerNames = function() {
|
||||
$http.post('/api/_tokenizerNames',mapping).success(function(data) {
|
||||
$scope.tokenizerNames = data.tokenizers;
|
||||
}).
|
||||
error(function(data, code) {
|
||||
$scope.errorMessage = data;
|
||||
});
|
||||
};
|
||||
|
||||
$scope.loadTokenizerNames();
|
||||
|
||||
$scope.unknownTokenizerTypeTemplate = "/static/partials/analysis/tokenizers/generic.html";
|
||||
$scope.tokenizerTypeTemplates = {
|
||||
"regexp": "/static/partials/analysis/tokenizers/regexp.html",
|
||||
"exception": "/static/partials/analysis/tokenizers/exception.html"
|
||||
};
|
||||
$scope.tokenizerTypeDefaults = {
|
||||
"regexp": function() {
|
||||
return {
|
||||
"regexp": ""
|
||||
};
|
||||
},
|
||||
"exception": function() {
|
||||
return {
|
||||
"exceptions": [],
|
||||
"tokenizer": "unicode"
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
$scope.tokenizerTypes = [];
|
||||
|
||||
updateTokenizerTypes = function() {
|
||||
$http.get('/api/_tokenizerTypes').success(function(data) {
|
||||
$scope.tokenizerTypes = data.tokenizer_types;
|
||||
}).
|
||||
error(function(data, code) {
|
||||
$scope.errorMessage = data;
|
||||
});
|
||||
};
|
||||
|
||||
updateTokenizerTypes();
|
||||
|
||||
if (!$scope.tokenizer.type) {
|
||||
defaultType = "regexp";
|
||||
if ($scope.tokenizerTypeDefaults[defaultType]) {
|
||||
$scope.tokenizer = $scope.tokenizerTypeDefaults[defaultType]();
|
||||
}
|
||||
else {
|
||||
$scope.tokenizer = {};
|
||||
}
|
||||
$scope.tokenizer.type = defaultType;
|
||||
}
|
||||
$scope.formpath = $scope.tokenizerTypeTemplates[$scope.tokenizer.type];
|
||||
|
||||
$scope.tokenizerTypeChange = function() {
|
||||
newType = $scope.tokenizer.type;
|
||||
if ($scope.tokenizerTypeDefaults[$scope.tokenizer.type]) {
|
||||
$scope.tokenizer = $scope.tokenizerTypeDefaults[$scope.tokenizer.type]();
|
||||
} else {
|
||||
$scope.tokenizer = {};
|
||||
}
|
||||
$scope.tokenizer.type = newType;
|
||||
if ($scope.tokenizerTypeTemplates[$scope.tokenizer.type]) {
|
||||
$scope.formpath = $scope.tokenizerTypeTemplates[$scope.tokenizer.type];
|
||||
} else {
|
||||
$scope.formpath = $scope.unknownTokenizerTypeTemplate;
|
||||
}
|
||||
};
|
||||
|
||||
$scope.addException = function(scope) {
|
||||
if (scope.newregexp) {
|
||||
$scope.tokenizer.exceptions.push(scope.newregexp);
|
||||
scope.newregexp = "";
|
||||
}
|
||||
};
|
||||
|
||||
$scope.removeException = function(index) {
|
||||
$scope.tokenizer.exceptions.splice(index, 1);
|
||||
};
|
||||
|
||||
$scope.cancel = function () {
|
||||
$modalInstance.dismiss('cancel');
|
||||
};
|
||||
|
||||
$scope.build = function() {
|
||||
// must have a name
|
||||
if (!$scope.name) {
|
||||
$scope.errorMessage = "Name is required";
|
||||
return;
|
||||
}
|
||||
|
||||
// name must not already be used
|
||||
if ($scope.name != $scope.origName && $scope.mapping.analysis.tokenizers[$scope.name]) {
|
||||
$scope.errorMessage = "Tokenizer named '" + $scope.name + "' already exists";
|
||||
return;
|
||||
}
|
||||
|
||||
// ensure that this new mapping component is valid
|
||||
tokenizers = {};
|
||||
tokenizers[$scope.name] = $scope.tokenizer;
|
||||
// add in all the existing tokenizers, since we might be referencing them
|
||||
for (var t in $scope.mapping.analysis.tokenizers) {
|
||||
tokenizers[t] = $scope.mapping.analysis.tokenizers[t];
|
||||
}
|
||||
testMapping = {
|
||||
"analysis": {
|
||||
"tokenizers": tokenizers
|
||||
}
|
||||
};
|
||||
$http.post('/api/_validateMapping',testMapping).success(function(data) {
|
||||
// if its valid return it
|
||||
result = {};
|
||||
result[$scope.name] = $scope.tokenizer;
|
||||
$modalInstance.close(result);
|
||||
}).
|
||||
error(function(data, code) {
|
||||
// otherwise display error
|
||||
$scope.errorMessage = data;
|
||||
});
|
||||
|
||||
};
|
||||
};
|
|
@ -0,0 +1,51 @@
|
|||
var WordListModalCtrl = function ($scope, $modalInstance, name, words, mapping) {
|
||||
$scope.name = name;
|
||||
$scope.origName = name;
|
||||
$scope.errorMessage = "";
|
||||
$scope.newWord = "";
|
||||
$scope.words = words.slice(0); // create copy
|
||||
$scope.selectedWords = [];
|
||||
$scope.mapping = mapping;
|
||||
|
||||
$scope.cancel = function () {
|
||||
$modalInstance.dismiss('cancel');
|
||||
};
|
||||
|
||||
$scope.addWord = function() {
|
||||
if ($scope.newWord) {
|
||||
$scope.words.push($scope.newWord);
|
||||
$scope.newWord = "";
|
||||
}
|
||||
};
|
||||
|
||||
$scope.removeWord = function() {
|
||||
// sort the selected word indexes into descending order
|
||||
// so we can delete items without having to adjust indexes
|
||||
$scope.selectedWords.sort(function(a,b){ return b - a; });
|
||||
for (var index in $scope.selectedWords) {
|
||||
$scope.words.splice($scope.selectedWords[index], 1);
|
||||
}
|
||||
$scope.selectedWords = [];
|
||||
};
|
||||
|
||||
$scope.build = function() {
|
||||
// must have a name
|
||||
if (!$scope.name) {
|
||||
$scope.errorMessage = "Name is required";
|
||||
return;
|
||||
}
|
||||
|
||||
// name must not already be used
|
||||
if ($scope.name != $scope.origName && $scope.mapping.analysis.token_maps[$scope.name]) {
|
||||
$scope.errorMessage = "Word list named '" + $scope.name + "' already exists";
|
||||
return;
|
||||
}
|
||||
|
||||
result = {};
|
||||
result[$scope.name] = {
|
||||
"type": "custom",
|
||||
"tokens": $scope.words
|
||||
};
|
||||
$modalInstance.close(result);
|
||||
};
|
||||
};
|
|
@ -0,0 +1,379 @@
|
|||
// controller responsible for building a custom analysis components
|
||||
|
||||
function AnalysisCtrl($scope, $http, $routeParams, $log, $sce, $location, $modal) {
|
||||
|
||||
// analyzers
|
||||
|
||||
$scope.newAnalyzer = function () {
|
||||
return $scope.editAnalyzer("", {
|
||||
"type": "custom",
|
||||
"char_filters": [],
|
||||
"tokenizer": "unicode",
|
||||
"token_filters": []
|
||||
});
|
||||
};
|
||||
|
||||
$scope.deleteAnalyzer = function (name) {
|
||||
used = $scope.isAnalyzerUsed(name);
|
||||
if (used) {
|
||||
alert("This analyzer cannot be deleted because it is being used by the " + used + ".");
|
||||
return;
|
||||
}
|
||||
if (confirm("Are you sure you want to delete '" + name + "'?")) {
|
||||
delete $scope.$parent.mapping.analysis.analyzers[name];
|
||||
}
|
||||
};
|
||||
|
||||
$scope.isAnalyzerUsed = function(name) {
|
||||
// analyzers are used in mappings (in various places)
|
||||
|
||||
// first check index level default analyzer
|
||||
if ($scope.$parent.mapping.default_analyzer == name) {
|
||||
return "index mapping default analyzer";
|
||||
}
|
||||
|
||||
// then check the default documnt mapping
|
||||
used = $scope.isAnalyzerUsedInDocMapping(name, $scope.$parent.mapping.default_mapping, "");
|
||||
if (used) {
|
||||
return "default document mapping " + used;
|
||||
}
|
||||
|
||||
// then check the document mapping for each type
|
||||
for (var docType in $scope.$parent.mapping.types) {
|
||||
docMapping = $scope.$parent.mapping.types[docType];
|
||||
used = $scope.isAnalyzerUsedInDocMapping(name, docMapping, "");
|
||||
if (used) {
|
||||
return "document mapping type '" + docType + "' ";
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
// a recursive helper
|
||||
$scope.isAnalyzerUsedInDocMapping = function(name, docMapping, path) {
|
||||
// first check the document level default analyzer
|
||||
if (docMapping.default_analyzer == name) {
|
||||
if (path) {
|
||||
return "default analyzer at " + path;
|
||||
} else {
|
||||
return "default analyzer";
|
||||
}
|
||||
}
|
||||
// now check fields at this level
|
||||
for (var fieldIndex in docMapping.fields) {
|
||||
field = docMapping.fields[fieldIndex];
|
||||
if (field.analyzer == name) {
|
||||
if (field.name) {
|
||||
return "in the field named " + field.name;
|
||||
}
|
||||
return "in the field at path " + path;
|
||||
}
|
||||
}
|
||||
|
||||
// now check each nested property
|
||||
for (var propertyName in docMapping.properties) {
|
||||
subDoc = docMapping.properties[propertyName];
|
||||
if (path) {
|
||||
return $scope.isAnalyzerUsedInDocMapping(name, subDoc, path+"."+propertyName);
|
||||
} else {
|
||||
return $scope.isAnalyzerUsedInDocMapping(name, subDoc, propertyName);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
$scope.editAnalyzer = function (name, value) {
|
||||
var modalInstance = $modal.open({
|
||||
animation: $scope.animationsEnabled,
|
||||
templateUrl: '/static/partials/analysis/analyzer.html',
|
||||
controller: 'AnalyzerModalCtrl',
|
||||
resolve: {
|
||||
name: function () {
|
||||
return name;
|
||||
},
|
||||
value: function () {
|
||||
return value;
|
||||
},
|
||||
mapping: function() {
|
||||
return $scope.$parent.mapping;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
modalInstance.result.then(function (result) {
|
||||
// add this result to the mapping
|
||||
for (var resultKey in result) {
|
||||
if (name !== "" && resultKey != name) {
|
||||
// remove the old name
|
||||
delete $scope.$parent.mapping.analysis.analyzers[name];
|
||||
}
|
||||
$scope.$parent.mapping.analysis.analyzers[resultKey] = result[resultKey];
|
||||
// reload parent available analyzers
|
||||
$scope.$parent.loadAnalyzerNames();
|
||||
}
|
||||
}, function () {
|
||||
$log.info('Modal dismissed at: ' + new Date());
|
||||
});
|
||||
};
|
||||
|
||||
// word lists
|
||||
|
||||
$scope.newWordList = function () {
|
||||
return $scope.editWordList("", {tokens:[]});
|
||||
};
|
||||
|
||||
$scope.deleteWordList = function (name) {
|
||||
used = $scope.isWordListUsed(name);
|
||||
if (used) {
|
||||
alert("This word list cannot be deleted because it is being used by the " + used + ".");
|
||||
return;
|
||||
}
|
||||
if (confirm("Are you sure you want to delete '" + name + "'?")) {
|
||||
delete $scope.$parent.mapping.analysis.token_maps[name];
|
||||
}
|
||||
};
|
||||
|
||||
$scope.isWordListUsed = function(name) {
|
||||
// word lists are only used by token filters
|
||||
for (var tokenFilterName in $scope.$parent.mapping.analysis.token_filters) {
|
||||
tokenFilter = $scope.$parent.mapping.analysis.token_filters[tokenFilterName];
|
||||
// word lists are embeded in a variety of different field names
|
||||
if (tokenFilter.dict_token_map == name ||
|
||||
tokenFilter.articles_token_map == name ||
|
||||
tokenFilter.keywords_token_map == name ||
|
||||
tokenFilter.stop_token_map == name) {
|
||||
return "token filter named '" + tokenFilterName + "'";
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
$scope.editWordList = function (name, value) {
|
||||
var modalInstance = $modal.open({
|
||||
animation: $scope.animationsEnabled,
|
||||
templateUrl: '/static/partials/analysis/wordlist.html',
|
||||
controller: 'WordListModalCtrl',
|
||||
resolve: {
|
||||
name: function () {
|
||||
return name;
|
||||
},
|
||||
words: function () {
|
||||
return value.tokens;
|
||||
},
|
||||
mapping: function() {
|
||||
return $scope.$parent.mapping;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
modalInstance.result.then(function (result) {
|
||||
// add this result to the mapping
|
||||
for (var resultKey in result) {
|
||||
if (name !== "" && resultKey != name) {
|
||||
// remove the old name
|
||||
delete $scope.$parent.mapping.analysis.token_maps[name];
|
||||
}
|
||||
$scope.$parent.mapping.analysis.token_maps[resultKey] = result[resultKey];
|
||||
}
|
||||
}, function () {
|
||||
$log.info('Modal dismissed at: ' + new Date());
|
||||
});
|
||||
};
|
||||
|
||||
// character filters
|
||||
|
||||
$scope.newCharFilter = function() {
|
||||
return $scope.editCharFilter("", {});
|
||||
};
|
||||
|
||||
$scope.deleteCharFilter = function(name) {
|
||||
used = $scope.isCharFilterUsed(name);
|
||||
if (used) {
|
||||
alert("This character filter cannot be deleted because it is being used by the " + used + ".");
|
||||
return;
|
||||
}
|
||||
if (confirm("Are you sure you want to delete '" + name + "'?")) {
|
||||
delete $scope.$parent.mapping.analysis.char_filters[name];
|
||||
}
|
||||
};
|
||||
|
||||
$scope.isCharFilterUsed = function(name) {
|
||||
// character filters can only be used by analyzers
|
||||
for (var analyzerName in $scope.$parent.mapping.analysis.analyzers) {
|
||||
analyzer = $scope.$parent.mapping.analysis.analyzers[analyzerName];
|
||||
for (var charFilterIndex in analyzer.char_filters) {
|
||||
charFilterName = analyzer.char_filters[charFilterIndex];
|
||||
if (charFilterName == name) {
|
||||
return "analyzer named '" + analyzerName + "'";
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
$scope.editCharFilter = function (name, value) {
|
||||
var modalInstance = $modal.open({
|
||||
animation: $scope.animationsEnabled,
|
||||
templateUrl: '/static/partials/analysis/charfilter.html',
|
||||
controller: 'CharFilterModalCtrl',
|
||||
resolve: {
|
||||
name: function () {
|
||||
return name;
|
||||
},
|
||||
value: function () {
|
||||
return value;
|
||||
},
|
||||
mapping: function() {
|
||||
return $scope.$parent.mapping;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
modalInstance.result.then(function (result) {
|
||||
// add this result to the mapping
|
||||
for (var resultKey in result) {
|
||||
if (name !== "" && resultKey != name) {
|
||||
// remove the old name
|
||||
delete $scope.$parent.mapping.analysis.char_filters[name];
|
||||
}
|
||||
$scope.$parent.mapping.analysis.char_filters[resultKey] = result[resultKey];
|
||||
}
|
||||
}, function () {
|
||||
$log.info('Modal dismissed at: ' + new Date());
|
||||
});
|
||||
};
|
||||
|
||||
// tokenizers
|
||||
|
||||
$scope.newTokenizer = function () {
|
||||
return $scope.editTokenizer("", {});
|
||||
};
|
||||
|
||||
$scope.deleteTokenizer = function (name) {
|
||||
used = $scope.isTokenizerUsed(name);
|
||||
if (used) {
|
||||
alert("This tokenizer cannot be deleted because it is being used by the " + used + ".");
|
||||
return;
|
||||
}
|
||||
if (confirm("Are you sure you want to delete '" + name + "'?")) {
|
||||
delete $scope.$parent.mapping.analysis.tokenizers[name];
|
||||
}
|
||||
};
|
||||
|
||||
$scope.isTokenizerUsed = function(name) {
|
||||
// tokenizers can be used by *other* tokenizers
|
||||
for (var tokenizerName in $scope.$parent.mapping.analysis.tokenizers) {
|
||||
tokenizer = $scope.$parent.mapping.analysis.tokenizers[tokenizerName];
|
||||
if (tokenizer.tokenizer == name) {
|
||||
return "tokenizer named '" + tokenizerName + "'";
|
||||
}
|
||||
}
|
||||
|
||||
// tokenizers can be used by analyzers
|
||||
for (var analyzerName in $scope.$parent.mapping.analysis.analyzers) {
|
||||
analyzer = $scope.$parent.mapping.analysis.analyzers[analyzerName];
|
||||
if (analyzer.tokenizer == name) {
|
||||
return "analyzer named '" + analyzerName + "'";
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
$scope.editTokenizer = function (name, value) {
|
||||
var modalInstance = $modal.open({
|
||||
animation: $scope.animationsEnabled,
|
||||
templateUrl: '/static/partials/analysis/tokenizer.html',
|
||||
controller: 'TokenizerModalCtrl',
|
||||
resolve: {
|
||||
name: function () {
|
||||
return name;
|
||||
},
|
||||
value: function () {
|
||||
return value;
|
||||
},
|
||||
mapping: function() {
|
||||
return $scope.$parent.mapping;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
modalInstance.result.then(function (result) {
|
||||
// add this result to the mapping
|
||||
for (var resultKey in result) {
|
||||
if (name !== "" && resultKey != name) {
|
||||
// remove the old name
|
||||
delete $scope.$parent.mapping.analysis.tokenizers[name];
|
||||
}
|
||||
$scope.$parent.mapping.analysis.tokenizers[resultKey] = result[resultKey];
|
||||
}
|
||||
}, function () {
|
||||
$log.info('Modal dismissed at: ' + new Date());
|
||||
});
|
||||
};
|
||||
|
||||
// token filters
|
||||
|
||||
$scope.newTokenFilter = function () {
|
||||
return $scope.editTokenFilter("", {});
|
||||
};
|
||||
|
||||
$scope.deleteTokenFilter = function (name) {
|
||||
used = $scope.isTokenFilterUsed(name);
|
||||
if (used) {
|
||||
alert("This token filter cannot be deleted because it is being used by the " + used + ".");
|
||||
return;
|
||||
}
|
||||
if (confirm("Are you sure you want to delete '" + name + "'?")) {
|
||||
delete $scope.$parent.mapping.analysis.token_filters[name];
|
||||
}
|
||||
};
|
||||
|
||||
$scope.isTokenFilterUsed = function(name) {
|
||||
// token filters can only be used by analyzers
|
||||
for (var analyzerName in $scope.$parent.mapping.analysis.analyzers) {
|
||||
analyzer = $scope.$parent.mapping.analysis.analyzers[analyzerName];
|
||||
for (var tokenFilterIndex in analyzer.token_filters) {
|
||||
tokenFilterName = analyzer.token_filters[tokenFilterIndex];
|
||||
if (tokenFilterName == name) {
|
||||
return "analyzer named '" + analyzerName + "'";
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
};
|
||||
|
||||
$scope.editTokenFilter = function (name, value) {
|
||||
var modalInstance = $modal.open({
|
||||
animation: $scope.animationsEnabled,
|
||||
templateUrl: '/static/partials/analysis/tokenfilter.html',
|
||||
controller: 'TokenFilterModalCtrl',
|
||||
resolve: {
|
||||
name: function () {
|
||||
return name;
|
||||
},
|
||||
value: function () {
|
||||
return value;
|
||||
},
|
||||
mapping: function() {
|
||||
return $scope.$parent.mapping;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
modalInstance.result.then(function (result) {
|
||||
// add this result to the mapping
|
||||
for (var resultKey in result) {
|
||||
if (name !== "" && resultKey != name) {
|
||||
// remove the old name
|
||||
delete $scope.$parent.mapping.analysis.token_filters[name];
|
||||
}
|
||||
$scope.$parent.mapping.analysis.token_filters[resultKey] = result[resultKey];
|
||||
}
|
||||
}, function () {
|
||||
$log.info('Modal dismissed at: ' + new Date());
|
||||
});
|
||||
};
|
||||
|
||||
}
|
|
@ -0,0 +1,110 @@
|
|||
// controller responsible for building a mapping
|
||||
|
||||
function MappingCtrl($scope, $http, $routeParams, $log, $sce, $location) {
|
||||
|
||||
newFieldSection = function() {
|
||||
return {
|
||||
"enabled": true,
|
||||
"dynamic": true,
|
||||
"default_analyzer": "",
|
||||
"properties": {},
|
||||
"fields": [
|
||||
{
|
||||
"type": "",
|
||||
"index": true,
|
||||
"store": true,
|
||||
"include_in_all": true,
|
||||
"include_term_vectors": true
|
||||
}
|
||||
]
|
||||
};
|
||||
};
|
||||
|
||||
$scope.$parent.mapping = {
|
||||
"default_mapping": newFieldSection(),
|
||||
"type_field": "_type",
|
||||
"default_type": "_default",
|
||||
"default_analyzer": "standard",
|
||||
"default_datetime_parser": "dateTimeOptional",
|
||||
"default_field": "_all",
|
||||
"byte_array_converter": "json",
|
||||
"analysis": {
|
||||
"analyzers": {},
|
||||
"token_maps": {},
|
||||
"char_filters": {},
|
||||
"tokenizers": {},
|
||||
"token_filters": {}
|
||||
}
|
||||
};
|
||||
|
||||
$scope.analyzerNames = [];
|
||||
|
||||
$scope.loadAnalyzerNames = function() {
|
||||
$http.post('/api/_analyzerNames',$scope.$parent.mapping).success(function(data) {
|
||||
$scope.analyzerNames = data.analyzers;
|
||||
}).
|
||||
error(function(data, code) {
|
||||
$scope.errorMessage = data;
|
||||
});
|
||||
};
|
||||
|
||||
$scope.loadAnalyzerNames();
|
||||
|
||||
$scope.datetimeParserNames = [];
|
||||
|
||||
$scope.loadDatetimeParserNames = function() {
|
||||
$http.post('/api/_datetimeParserNames',$scope.$parent.mapping).success(function(data) {
|
||||
$scope.datetimeParserNames = data.datetime_parsers;
|
||||
}).
|
||||
error(function(data, code) {
|
||||
$scope.errorMessage = data;
|
||||
});
|
||||
};
|
||||
|
||||
$scope.loadDatetimeParserNames();
|
||||
|
||||
$scope.mappingType = "default";
|
||||
$scope.selectedItem = null;
|
||||
$scope.selectedLabel = "";
|
||||
|
||||
$scope.fieldTypes = [
|
||||
{
|
||||
"name": "text",
|
||||
"label": "Text",
|
||||
"description": "a text field"
|
||||
},
|
||||
{
|
||||
"name": "number",
|
||||
"label": "Number",
|
||||
"description": "a numerical value, indexed to facilitate range queries"
|
||||
},
|
||||
{
|
||||
"name": "datetime",
|
||||
"label": "Date/Time",
|
||||
"description": "a date/time value, indexed to facilitate range queries"
|
||||
},
|
||||
{
|
||||
"name": "disabled",
|
||||
"label": "Disabled",
|
||||
"description": "a section of JSON to be completely ignored"
|
||||
}
|
||||
];
|
||||
|
||||
$scope.clickItem = function(x, y) {
|
||||
$scope.selectedItem = x;
|
||||
$scope.selectedLabel = y;
|
||||
};
|
||||
|
||||
$scope.clickItem($scope.$parent.mapping.default_mapping);
|
||||
|
||||
$scope.addField = function(scope) {
|
||||
if (scope.newFieldName) {
|
||||
$scope.selectedItem.properties[scope.newFieldName] = newFieldSection();
|
||||
scope.newFieldName = "";
|
||||
console.log($scope.selectedItem);
|
||||
}
|
||||
};
|
||||
|
||||
$scope.changeType = function(scope) {
|
||||
};
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
<div class="modal-header">
|
||||
<h3 class="modal-title">Custom Analyzer</h3>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
|
||||
<div ng-show="errorMessage" class="alert alert-danger ng-cloak" role="alert"> {{errorMessage}}
|
||||
</div>
|
||||
|
||||
<form class="form" role="form">
|
||||
<div class="form-group">
|
||||
<label for="aname">Name</label>
|
||||
<input ng-model="name" type="text" class="form-control" id="tname" placeholder="Name">
|
||||
</div>
|
||||
|
||||
|
||||
<div class="form-group">
|
||||
<label for="exampleInputPassword1">Character Filters</label>
|
||||
<ul class="list-group" ng-show="analyzer.char_filters.length < 1">
|
||||
<li class="list-group-item">None</li>
|
||||
</ul>
|
||||
<ul class="list-group" ng-show="analyzer.char_filters.length > 0" ui-sortable ng-model="analyzer.char_filters">
|
||||
<li class="list-group-item" ng-repeat="analyzerCharFilter in analyzer.char_filters track by $index"><span class="glyphicon glyphicon-minus"></span> {{ analyzerCharFilter }}<span ng-click="removeCharFilter($index)" class="glyphicon glyphicon-remove pull-right"></span></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="exampleInputPassword2"></label>
|
||||
<div class="col-sm-10">
|
||||
<select ng-change="addCharFilterChanged()" ng-model="addCharacterFilterName" class="form-control" id="addCharacterFilters">
|
||||
<option ng-repeat="charFilter in charFilterNames">{{charFilter}}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-sm-2">
|
||||
<button ng-click="addCharFilter(this)" type="button" class="btn btn-default pull-right">Add</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="analyzerTokenizer">Tokenizer</label>
|
||||
<select ng-change="tokenizerChanged()" ng-model="analyzer.tokenizer" class="form-control" id="analyzerTokenizer">
|
||||
<option ng-repeat="tokenizer in tokenizerNames">{{tokenizer}}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="exampleInputPassword2">Token Filters</label>
|
||||
<ul class="list-group" ng-show="analyzer.token_filters.length < 1">
|
||||
<li class="list-group-item">None</li>
|
||||
</ul>
|
||||
<ul class="list-group" ng-show="analyzer.token_filters.length > 0" ui-sortable ng-model="analyzer.token_filters">
|
||||
<li class="list-group-item" ng-repeat="analyzerTokenFilter in analyzer.token_filters"><span class="glyphicon glyphicon-minus"></span> {{ analyzerTokenFilter }}<span ng-click="removeTokenFilter($index)" class="glyphicon glyphicon-remove pull-right"></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="exampleInputPassword2"></label>
|
||||
<div class="col-sm-10">
|
||||
<select ng-change="addTokenFilterChanged()" ng-model="addTokenFilterName" class="form-control" id="addTokenFilters">
|
||||
<option ng-repeat="tokenFilter in tokenFilterNames">{{tokenFilter}}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="col-sm-2">
|
||||
<button ng-click="addTokenFilter(this)" type="button" class="btn btn-default pull-right">Add</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
</form>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
<button class="btn btn-default" ng-click="cancel()">Cancel</button>
|
||||
<button ng-click="build()" type="button" class="btn btn-primary pull-right">Save</button>
|
||||
</div>
|
|
@ -0,0 +1,34 @@
|
|||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="(aname,aval) in mapping.analysis.analyzers">
|
||||
<td>{{aname}}</td>
|
||||
<td>
|
||||
<div class="btn-group btn-group-xs" role="group">
|
||||
<button ng-click="editAnalyzer(aname, aval)" type="button" class="btn btn-default btn-xs">
|
||||
<span class="glyphicon glyphicon-edit" aria-hidden="true"></span> Edit
|
||||
</button>
|
||||
<button ng-click="deleteAnalyzer(aname)" type="button" class="btn btn-default btn-xs">
|
||||
<span class="glyphicon glyphicon-trash" aria-hidden="true"></span> Delete
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr ng-show="Utils.keys(mapping.analysis.analyzers).length < 1">
|
||||
<td colspan="2">None</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<button ng-click="newAnalyzer()" type="button" class="btn btn-sm btn-default pull-right">New Analyzer</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
|
|
@ -0,0 +1,34 @@
|
|||
<div class="modal-header">
|
||||
<h3 class="modal-title">Custom Char Filter</h3>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
|
||||
<div ng-show="errorMessage" class="alert alert-danger ng-cloak" role="alert"> {{errorMessage}}
|
||||
</div>
|
||||
|
||||
<form class="form" role="form">
|
||||
<div class="form-group">
|
||||
<label for="tname">Name</label>
|
||||
<input ng-model="name" type="text" class="form-control" id="tname" placeholder="Name">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="charfiltertype">Type</label>
|
||||
<div class="col-sm-12 input-group">
|
||||
<select ng-change="charFilterTypeChange()" ng-model="charfilter.type" class="form-control" id="charfiltertype">
|
||||
<option ng-repeat="charFilterTyp in charFilterTypes">{{charFilterTyp}}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-show="charfilter.type" ng-include src="formpath"/>
|
||||
|
||||
|
||||
</form>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
<button class="btn btn-default" ng-click="cancel()">Cancel</button>
|
||||
<button ng-click="build()" type="button" class="btn btn-primary pull-right">Save</button>
|
||||
</div>
|
|
@ -0,0 +1,36 @@
|
|||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Type</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="(cfname,cfval) in mapping.analysis.char_filters">
|
||||
<td>{{cfname}}</td>
|
||||
<td>{{cfval.type}}</td>
|
||||
<td>
|
||||
<div class="btn-group btn-group-xs" role="group">
|
||||
<button ng-click="editCharFilter(cfname, cfval)" type="button" class="btn btn-default btn-xs">
|
||||
<span class="glyphicon glyphicon-edit" aria-hidden="true"></span> Edit
|
||||
</button>
|
||||
<button ng-click="deleteCharFilter(cfname)" type="button" class="btn btn-default btn-xs">
|
||||
<span class="glyphicon glyphicon-trash" aria-hidden="true"></span> Delete
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr ng-show="Utils.keys(mapping.analysis.char_filters).length < 1">
|
||||
<td colspan="3">None</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<td colspan="3">
|
||||
<button ng-click="newCharFilter()" type="button" class="btn btn-sm btn-default pull-right">New Character Filter</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
<div class="form-group">
|
||||
<label for="charfilterRegexp">Regular Expression</label>
|
||||
<input ng-model="charfilter.regexp" type="text" class="form-control" id="charfilterRegexp" placeholder="">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="charfilterReplace">Replacement</label>
|
||||
<input ng-model="charfilter.replace" type="text" class="form-control" id="charfilterReplace" placeholder="">
|
||||
</div>
|
|
@ -0,0 +1,34 @@
|
|||
<div class="modal-header">
|
||||
<h3 class="modal-title">Custom Token Filter</h3>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
|
||||
<div ng-show="errorMessage" class="alert alert-danger ng-cloak" role="alert"> {{errorMessage}}
|
||||
</div>
|
||||
|
||||
<form class="form" role="form">
|
||||
<div class="form-group">
|
||||
<label for="tname">Name</label>
|
||||
<input ng-model="name" type="text" class="form-control" id="tname" placeholder="Name">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="tokenfiltertype">Type</label>
|
||||
<div class="col-sm-12 input-group">
|
||||
<select ng-change="tokenFilterTypeChange()" ng-model="tokenfilter.type" class="form-control" id="tokenfiltertype">
|
||||
<option ng-repeat="tokenFilterTyp in tokenFilterTypes">{{tokenFilterTyp}}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-show="tokenfilter.type" ng-include src="formpath"/>
|
||||
|
||||
|
||||
</form>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
<button class="btn btn-default" ng-click="cancel()">Cancel</button>
|
||||
<button ng-click="build()" type="button" class="btn btn-primary pull-right">Save</button>
|
||||
</div>
|
|
@ -0,0 +1,36 @@
|
|||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Type</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="(tfname,tfval) in mapping.analysis.token_filters">
|
||||
<td>{{tfname}}</td>
|
||||
<td>{{tfval.type}}</td>
|
||||
<td>
|
||||
<div class="btn-group btn-group-xs" role="group">
|
||||
<button ng-click="editTokenFilter(tfname, tfval)" type="button" class="btn btn-default btn-xs">
|
||||
<span class="glyphicon glyphicon-edit" aria-hidden="true"></span> Edit
|
||||
</button>
|
||||
<button ng-click="deleteTokenFilter(tfname)" type="button" class="btn btn-default btn-xs">
|
||||
<span class="glyphicon glyphicon-trash" aria-hidden="true"></span> Delete
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr ng-show="Utils.keys(mapping.analysis.token_filters).length < 1">
|
||||
<td colspan="3">None</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<td colspan="3">
|
||||
<button ng-click="newTokenFilter()" type="button" class="btn btn-sm btn-default pull-right">New Token Filter</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
<div class="form-group">
|
||||
<label for="tokenfilterTokenMaps">Sub Words</label>
|
||||
<select ng-model="tokenfilter.dict_token_map" class="form-control" id="tokenfilterTokenMaps">
|
||||
<option ng-repeat="tokenMap in tokenMapNames">{{tokenMap}}</option>
|
||||
</select>
|
||||
</div>
|
|
@ -0,0 +1,17 @@
|
|||
<div class="form-group">
|
||||
<label for="tokenfilterEdge">Edge</label>
|
||||
<select class="form-control" id="tokenfilterEdge" ng-model="tokenfilter.edge">
|
||||
<option>front</option>
|
||||
<option>back</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="tokenfilterMin">Min</label>
|
||||
<input ng-model="tokenfilter.min" type="number" class="form-control" id="tokenfilterMin" placeholder="">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="tokenfilterMax">Max</label>
|
||||
<input ng-model="tokenfilter.max" type="number" class="form-control" id="tokenfilterMax" placeholder="">
|
||||
</div>
|
|
@ -0,0 +1,6 @@
|
|||
<div class="form-group">
|
||||
<label for="tokenfilterTokenMaps">Articles</label>
|
||||
<select ng-model="tokenfilter.articles_token_map" class="form-control" id="tokenfilterTokenMaps">
|
||||
<option ng-repeat="tokenMap in tokenMapNames">{{tokenMap}}</option>
|
||||
</select>
|
||||
</div>
|
|
@ -0,0 +1,6 @@
|
|||
<div class="form-group">
|
||||
<label for="tokenfilterTokenMaps">Keywords</label>
|
||||
<select ng-model="tokenfilter.keywords_token_map" class="form-control" id="tokenfilterTokenMaps">
|
||||
<option ng-repeat="tokenMap in tokenMapNames">{{tokenMap}}</option>
|
||||
</select>
|
||||
</div>
|
|
@ -0,0 +1,9 @@
|
|||
<div class="form-group">
|
||||
<label for="tokenfilterMin">Min</label>
|
||||
<input ng-model="tokenfilter.min" type="number" class="form-control" id="tokenfilterMin" placeholder="">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="tokenfilterMax">Max</label>
|
||||
<input ng-model="tokenfilter.max" type="number" class="form-control" id="tokenfilterMax" placeholder="">
|
||||
</div>
|
|
@ -0,0 +1,9 @@
|
|||
<div class="form-group">
|
||||
<label for="tokenfilterMin">Min</label>
|
||||
<input ng-model="tokenfilter.min" type="number" class="form-control" id="tokenfilterMin" placeholder="">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="tokenfilterMax">Max</label>
|
||||
<input ng-model="tokenfilter.max" type="number" class="form-control" id="tokenfilterMax" placeholder="">
|
||||
</div>
|
|
@ -0,0 +1,9 @@
|
|||
<div class="form-group">
|
||||
<label for="tokenfilterNormalizeUnicode">Form</label>
|
||||
<select class="form-control" id="tokenfilterNormalizeUnicode" ng-model="tokenfilter.form">
|
||||
<option>nfc</option>
|
||||
<option>nfd</option>
|
||||
<option>nfkc</option>
|
||||
<option>nfkd</option>
|
||||
</select>
|
||||
</div>
|
|
@ -0,0 +1,24 @@
|
|||
<div class="form-group">
|
||||
<label for="tokenfilterMin">Min</label>
|
||||
<input ng-model="tokenfilter.min" type="number" class="form-control" id="tokenfilterMin" placeholder="">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="tokenfilterMax">Max</label>
|
||||
<input ng-model="tokenfilter.max" type="number" class="form-control" id="tokenfilterMax" placeholder="">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="tokenfilterInclude">Include Original Token</label>
|
||||
<input ng-model="tokenfilter.output_original" type="checkbox" class="form-control" id="tokenfilterInclude">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="tokenfilterSep">Separator</label>
|
||||
<input ng-model="tokenfilter.separator" type="text" class="form-control" id="tokenfilterSep" placeholder="">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="tokenfilterFiller">Filler</label>
|
||||
<input ng-model="tokenfilter.filler" type="text" class="form-control" id="tokenfilterFiller" placeholder="">
|
||||
</div>
|
|
@ -0,0 +1,6 @@
|
|||
<div class="form-group">
|
||||
<label for="tokenfilterTokenMaps">Stop Words</label>
|
||||
<select ng-model="tokenfilter.stop_token_map" class="form-control" id="tokenfilterTokenMaps">
|
||||
<option ng-repeat="tokenMap in tokenMapNames">{{tokenMap}}</option>
|
||||
</select>
|
||||
</div>
|
|
@ -0,0 +1,4 @@
|
|||
<div class="form-group">
|
||||
<label for="tokenfilterLen">Length</label>
|
||||
<input ng-model="tokenfilter.length" type="number" class="form-control" id="tokenfilterLen" placeholder="">
|
||||
</div>
|
|
@ -0,0 +1,3 @@
|
|||
<select ng-model="tokenfilter.word_map" class="form-control" id="tokenfilterTokenMaps">
|
||||
<option ng-repeat="tokenMap in tokenMapNames">{{tokenMap}}</option>
|
||||
</select>
|
|
@ -0,0 +1,34 @@
|
|||
<div class="modal-header">
|
||||
<h3 class="modal-title">Custom Tokenizer</h3>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
|
||||
<div ng-show="errorMessage" class="alert alert-danger ng-cloak" role="alert"> {{errorMessage}}
|
||||
</div>
|
||||
|
||||
<form class="form" role="form">
|
||||
<div class="form-group">
|
||||
<label for="tname">Name</label>
|
||||
<input ng-model="name" type="text" class="form-control" id="tname" placeholder="Name">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="tokenizertype">Type</label>
|
||||
<div class="col-sm-12 input-group">
|
||||
<select ng-change="tokenizerTypeChange()" ng-model="tokenizer.type" class="form-control" id="tokenizertype">
|
||||
<option ng-repeat="tokenizerTyp in tokenizerTypes">{{tokenizerTyp}}</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-show="tokenizer.type" ng-include src="formpath"/>
|
||||
|
||||
|
||||
</form>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
<button class="btn btn-default" ng-click="cancel()">Cancel</button>
|
||||
<button ng-click="build()" type="button" class="btn btn-primary pull-right">Save</button>
|
||||
</div>
|
|
@ -0,0 +1,36 @@
|
|||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Type</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="(tname,tval) in mapping.analysis.tokenizers">
|
||||
<td>{{tname}}</td>
|
||||
<td>{{tval.type}}</td>
|
||||
<td>
|
||||
<div class="btn-group btn-group-xs" role="group">
|
||||
<button ng-click="editTokenizer(tname, tval)" type="button" class="btn btn-default btn-xs">
|
||||
<span class="glyphicon glyphicon-edit" aria-hidden="true"></span> Edit
|
||||
</button>
|
||||
<button ng-click="deleteTokenizer(tname)" type="button" class="btn btn-default btn-xs">
|
||||
<span class="glyphicon glyphicon-trash" aria-hidden="true"></span> Delete
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr ng-show="Utils.keys(mapping.analysis.tokenizers).length < 1">
|
||||
<td colspan="3">None</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<td colspan="3">
|
||||
<button ng-click="newTokenizer()" type="button" class="btn btn-sm btn-default pull-right">New Tokenizer</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
<div class="form-group">
|
||||
<label for="exampleInputPassword1">Exception Patterns</label>
|
||||
<ul class="list-group" ng-show="tokenizer.exceptions.length < 1">
|
||||
<li class="list-group-item">None</li>
|
||||
</ul>
|
||||
<ul class="list-group" ng-show="tokenizer.exceptions.length > 0" ui-sortable ng-model="tokenizer.exceptions">
|
||||
<li class="list-group-item" ng-repeat="e in tokenizer.exceptions track by $index"><span class="glyphicon glyphicon-minus"></span> {{ e }}<span ng-click="removeException($index)" class="glyphicon glyphicon-remove pull-right"></span></li>
|
||||
</ul>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="what"></label>
|
||||
<div class="col-sm-10">
|
||||
<input ng-model="newregexp" type="text" class="form-control" id="exceptionRegexp" placeholder="">
|
||||
</div>
|
||||
<div class="col-sm-2">
|
||||
<button ng-click="addException(this)" type="button" class="btn btn-default pull-right">Add</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<label for="analyzerTokenizer">Tokenizer for Remaining Input</label>
|
||||
<select ng-change="tokenizerChanged()" ng-model="tokenizer.tokenizer" class="form-control" id="tokenizer">
|
||||
<option ng-repeat="tokenizer in tokenizerNames">{{tokenizer}}</option>
|
||||
</select>
|
||||
</div>
|
|
@ -0,0 +1,4 @@
|
|||
<div class="form-group">
|
||||
<label for="tokenizerRegexp">Regular Expression</label>
|
||||
<input ng-model="tokenizer.regexp" type="text" class="form-control" id="tokenizerRegexp" placeholder="">
|
||||
</div>
|
|
@ -0,0 +1,39 @@
|
|||
<div class="modal-header">
|
||||
<h3 class="modal-title">Custom Word List</h3>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
|
||||
<div ng-show="errorMessage" class="alert alert-danger ng-cloak" role="alert"> {{errorMessage}}
|
||||
</div>
|
||||
|
||||
<form class="form" role="form">
|
||||
<div class="form-group">
|
||||
<label for="tname">Name</label>
|
||||
<input ng-model="name" type="text" class="form-control" id="tname" placeholder="Name">
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="words">Words</label>
|
||||
<select ng-model="selectedWords" multiple ng-multiple="true" id="words" size="5" class="form-control" ng-options="idx as word for (idx, word) in words">
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="what"></label>
|
||||
<div class="col-sm-8">
|
||||
<input ng-model="newWord" type="text" class="form-control" id="newWord" placeholder="word">
|
||||
</div>
|
||||
<div class="col-sm-4">
|
||||
<button ng-click="addWord()" type="button" class="btn btn-sm btn-default">Add</button>
|
||||
<button ng-click="removeWord()" ng-disabled="selectedWords.length < 1" type="button" class="btn btn-sm btn-default pull-right">Remove</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</form>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="modal-footer">
|
||||
<button class="btn btn-default" ng-click="cancel()">Cancel</button>
|
||||
<button ng-click="build()" type="button" class="btn btn-primary pull-right">Save</button>
|
||||
</div>
|
|
@ -0,0 +1,34 @@
|
|||
<table class="table table-striped">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr ng-repeat="(tmname,tmval) in mapping.analysis.token_maps">
|
||||
<td>{{tmname}}</td>
|
||||
<td>
|
||||
<div class="btn-group btn-group-xs" role="group">
|
||||
<button ng-click="editWordList(tmname, tmval)" type="button" class="btn btn-default btn-xs">
|
||||
<span class="glyphicon glyphicon-edit" aria-hidden="true"></span> Edit
|
||||
</button>
|
||||
<button ng-click="deleteWordList(tmname)" type="button" class="btn btn-default btn-xs">
|
||||
<span class="glyphicon glyphicon-trash" aria-hidden="true"></span> Delete
|
||||
</button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
<tr ng-show="Utils.keys(mapping.analysis.token_maps).length < 1">
|
||||
<td colspan="2">None</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<tfoot>
|
||||
<tr>
|
||||
<td colspan="2">
|
||||
<button ng-click="newWordList()" type="button" class="btn btn-sm btn-default pull-right">New Word List</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tfoot>
|
||||
</table>
|
||||
|
|
@ -0,0 +1,119 @@
|
|||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title"><span class="glyphicon glyphicon-file" aria-hidden="true"></span> Document Structure</h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
|
||||
<ul class="list-custom">
|
||||
<span class="list-item" ng-click="clickItem(mapping.default_mapping, '<document root>')" ng-class="{selected: mapping.default_mapping==selectedItem}">
|
||||
• <document root>
|
||||
</span>
|
||||
<ul class="list-custom">
|
||||
<li ng-repeat="(pname,pval) in mapping.default_mapping.properties" ng-include="'/static/partials/mapping/mapping-node.html'" ng-init="parent = pname"></li>
|
||||
</ul>
|
||||
</ul>
|
||||
|
||||
<div class="form-group form-group-sm">
|
||||
<div class="col-sm-10">
|
||||
<input ng-model="newFieldName" type="text" class="form-control" id="fieldName" placeholder="field name">
|
||||
</div>
|
||||
<button ng-click="addField(this)" type="button" class="btn btn-sm btn-default">Add</button>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<div class="panel panel-default">
|
||||
<div class="panel-heading">
|
||||
<h3 class="panel-title"><span class="glyphicon glyphicon-list" aria-hidden="true"></span> Indexing Behavior <small>{{selectedLabel}}</small></h3>
|
||||
</div>
|
||||
<div class="panel-body">
|
||||
|
||||
<div ng-show="selectedItem == null">Select an item in the document structure.</div>
|
||||
<div ng-hide="selectedItem == null">
|
||||
|
||||
<div class="form-group form-group-sm">
|
||||
<div class="col-sm-10">
|
||||
<label>Type
|
||||
<select ng-change="changeType(this)" ng-model="selectedItem.fields[0].type" ng-options="t.name as t.label for t in fieldTypes">
|
||||
<option value="">Object</option>
|
||||
</select>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div ng-switch="selectedItem.fields[0].type">
|
||||
<div ng-switch-when="text">
|
||||
|
||||
<div class="form-group form-group-sm">
|
||||
<div class="col-sm-10">
|
||||
<label>Analyzer
|
||||
<select ng-change="changeType(this)" ng-model="selectedItem.fields[0].analyzer" ng-options="t as t for t in analyzerNames">
|
||||
<option value="">Inherit</option>
|
||||
</select>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div ng-switch-when="datetime">
|
||||
|
||||
<div class="form-group form-group-sm">
|
||||
<div class="col-sm-10">
|
||||
<label>Date/TimeParser
|
||||
<select ng-change="changeType(this)" ng-model="selectedItem.fields[0].date_format" ng-options="t as t for t in datetimeParserNames">
|
||||
<option value="">Inherit</option>
|
||||
</select>
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div ng-if="selectedItem.fields[0].type != '' && selectedItem.fields[0].type != 'disabled'">
|
||||
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input ng-model="selectedItem.fields[0].index" type="checkbox"> Index
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input ng-model="selectedItem.fields[0].store" type="checkbox"> Store
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input ng-model="selectedItem.fields[0].include_in_all" type="checkbox"> Include in 'All' Field
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div ng-if="selectedItem.fields[0].type == 'text'">
|
||||
|
||||
<div class="checkbox">
|
||||
<label>
|
||||
<input ng-model="selectedItem.fields[0].include_term_vectors" type="checkbox"> Include Term Vectors
|
||||
</label>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
|
@ -0,0 +1,6 @@
|
|||
<span class="list-item"ng-click="clickItem(pval,pname)" ng-class="{selected: pval==selectedItem}">• {{pname}}</span>
|
||||
<ul class="list-custom">
|
||||
<li ng-repeat="(pname,pval) in pval.properties" ng-init="parent = parent + '.' + pname">
|
||||
<span class="list-item" ng-click="clickItem(pval,parent)" ng-class="{selected: pval==selectedItem}">• {{pname}}</span>
|
||||
</li>
|
||||
</ul>
|
|
@ -0,0 +1,59 @@
|
|||
|
||||
|
||||
<div ng-show="errorMessage" class="alert alert-danger ng-cloak" role="alert"> {{errorMessage}}
|
||||
</div>
|
||||
|
||||
<form class="form-horizontal" role="form">
|
||||
|
||||
<div class="form-group">
|
||||
<label for="inputDoc" class="col-sm-2 control-label">Index Mapping</label>
|
||||
|
||||
|
||||
<div class="col-sm-10">
|
||||
<div class="radio">
|
||||
<label>
|
||||
<input ng-model="mappingType" type="radio" name="mappingType" value="default" checked>
|
||||
Default
|
||||
</label>
|
||||
</div>
|
||||
<div class="radio">
|
||||
<label>
|
||||
<input ng-model="mappingType" type="radio" name="mappingType" value="custom">
|
||||
Custom
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="form-group" ng-show="mappingType == 'custom'">
|
||||
<label for="inputDoc" class="col-sm-2 control-label"> </label>
|
||||
<div class="col-sm-10">
|
||||
<div ng-include src="'/static/partials/mapping/mapping-custom.html'"/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group" ng-controller="AnalysisCtrl" ng-show="mappingType == 'custom'">
|
||||
<label for="inputDoc" class="col-sm-2 control-label">Custom Analysis</label>
|
||||
<div class="col-sm-10">
|
||||
<tabset>
|
||||
<tab heading="Analyzers">
|
||||
<div ng-include src="'/static/partials/analysis/analyzers.html'"/>
|
||||
</tab>
|
||||
<tab heading="Character Filters">
|
||||
<div ng-include src="'/static/partials/analysis/charfilters.html'"/>
|
||||
</tab>
|
||||
<tab heading="Tokenizers">
|
||||
<div ng-include src="'/static/partials/analysis/tokenizers.html'"/>
|
||||
</tab>
|
||||
<tab heading="Token Filters">
|
||||
<div ng-include src="'/static/partials/analysis/tokenfilters.html'"/>
|
||||
</tab>
|
||||
<tab heading="Word Lists">
|
||||
<div ng-include src="'/static/partials/analysis/wordlists.html'"/>
|
||||
</tab>
|
||||
</tabset>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</form>
|
Loading…
Reference in New Issue