mirror of https://github.com/apache/lucene.git
SOLR-494: Added Ajaxed schema explorer (contributed Greg Ludington)
git-svn-id: https://svn.apache.org/repos/asf/lucene/solr/trunk@637466 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
45d3ab73a2
commit
2f39d9ff04
|
@ -211,6 +211,9 @@ New Features
|
|||
41. SOLR-356: Pluggable functions (value sources) that allow
|
||||
registration of new functions via solrconfig.xml
|
||||
(Doug Daniels via yonik)
|
||||
|
||||
42. SOLR-494: Added cool admin Ajaxed schema explorer.
|
||||
(Greg Ludington via ehatcher)
|
||||
|
||||
Changes in runtime behavior
|
||||
|
||||
|
|
|
@ -17,6 +17,22 @@
|
|||
|
||||
package org.apache.solr.handler.admin;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
import org.apache.lucene.analysis.Analyzer;
|
||||
import org.apache.lucene.document.Document;
|
||||
import org.apache.lucene.document.Fieldable;
|
||||
import org.apache.lucene.index.IndexReader;
|
||||
|
@ -28,6 +44,9 @@ import org.apache.lucene.search.Query;
|
|||
import org.apache.lucene.search.Sort;
|
||||
import org.apache.lucene.store.Directory;
|
||||
import org.apache.lucene.util.PriorityQueue;
|
||||
import org.apache.solr.analysis.TokenFilterFactory;
|
||||
import org.apache.solr.analysis.TokenizerChain;
|
||||
import org.apache.solr.analysis.TokenizerFactory;
|
||||
import org.apache.solr.common.SolrException;
|
||||
import org.apache.solr.common.luke.FieldFlag;
|
||||
import org.apache.solr.common.params.CommonParams;
|
||||
|
@ -45,13 +64,6 @@ import org.apache.solr.search.DocList;
|
|||
import org.apache.solr.search.SolrIndexSearcher;
|
||||
import org.apache.solr.search.SolrQueryParser;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
import java.util.*;
|
||||
import java.util.logging.Level;
|
||||
import java.util.logging.Logger;
|
||||
|
||||
/**
|
||||
* This handler exposes the internal lucene index. It is inspired by and
|
||||
* modeled on Luke, the Lucene Index Browser by Andrzej Bialecki.
|
||||
|
@ -259,12 +271,11 @@ public class LukeRequestHandler extends RequestHandlerBase
|
|||
}
|
||||
return finfo;
|
||||
}
|
||||
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static SimpleOrderedMap<Object> getIndexedFieldsInfo(
|
||||
final SolrIndexSearcher searcher, final Set<String> fields, final int numTerms )
|
||||
throws Exception
|
||||
{
|
||||
throws Exception {
|
||||
Query matchAllDocs = new MatchAllDocsQuery();
|
||||
SolrQueryParser qp = searcher.getSchema().getSolrQueryParser(null);
|
||||
|
||||
|
@ -290,6 +301,9 @@ public class LukeRequestHandler extends RequestHandlerBase
|
|||
|
||||
f.add( "type", (ftype==null)?null:ftype.getTypeName() );
|
||||
f.add( "schema", getFieldFlags( sfield ) );
|
||||
if (schema.getDynamicPattern(sfield.getName()) != null) {
|
||||
f.add("dynamicBase", schema.getDynamicPattern(sfield.getName()));
|
||||
}
|
||||
|
||||
// If numTerms==0, the call is just asking for a quick field list
|
||||
if( ttinfo != null && sfield != null && sfield.indexed() ) {
|
||||
|
@ -337,12 +351,68 @@ public class LukeRequestHandler extends RequestHandlerBase
|
|||
/**
|
||||
* Return info from the index
|
||||
*/
|
||||
private static SimpleOrderedMap<Object> getSchemaInfo( IndexSchema schema )
|
||||
{
|
||||
private static SimpleOrderedMap<Object> getSchemaInfo( IndexSchema schema ) {
|
||||
Map<String, List<String>> typeusemap = new HashMap<String, List<String>>();
|
||||
SimpleOrderedMap<Object> fields = new SimpleOrderedMap<Object>();
|
||||
SchemaField uniqueField = schema.getUniqueKeyField();
|
||||
for( SchemaField f : schema.getFields().values() ) {
|
||||
populateFieldInfo(schema, typeusemap, fields, uniqueField, f);
|
||||
}
|
||||
|
||||
SimpleOrderedMap<Object> dynamicFields = new SimpleOrderedMap<Object>();
|
||||
for (SchemaField f : schema.getDynamicFieldPrototypes()) {
|
||||
populateFieldInfo(schema, typeusemap, dynamicFields, uniqueField, f);
|
||||
}
|
||||
SimpleOrderedMap<Object> types = new SimpleOrderedMap<Object>();
|
||||
for( FieldType ft : schema.getFieldTypes().values() ) {
|
||||
SimpleOrderedMap<Object> field = new SimpleOrderedMap<Object>();
|
||||
field.add("fields", typeusemap.get( ft.getTypeName() ) );
|
||||
field.add("tokenized", ft.isTokenized() );
|
||||
field.add("className", ft.getClass().getName());
|
||||
field.add("indexAnalyzer", getAnalyzerInfo(ft.getAnalyzer()));
|
||||
field.add("queryAnalyzer", getAnalyzerInfo(ft.getQueryAnalyzer()));
|
||||
types.add( ft.getTypeName(), field );
|
||||
}
|
||||
|
||||
SimpleOrderedMap<Object> finfo = new SimpleOrderedMap<Object>();
|
||||
finfo.add("fields", fields);
|
||||
finfo.add("dynamicFields", dynamicFields);
|
||||
finfo.add("uniqueKeyField", uniqueField.getName());
|
||||
finfo.add("defaultSearchField", schema.getDefaultSearchFieldName());
|
||||
finfo.add("types", types);
|
||||
return finfo;
|
||||
}
|
||||
|
||||
|
||||
private static SimpleOrderedMap<Object> getAnalyzerInfo(Analyzer analyzer) {
|
||||
SimpleOrderedMap<Object> aninfo = new SimpleOrderedMap<Object>();
|
||||
aninfo.add("className", analyzer.getClass().getName());
|
||||
if (analyzer instanceof TokenizerChain) {
|
||||
SimpleOrderedMap<Object> tokenizer = new SimpleOrderedMap<Object>();
|
||||
TokenizerChain tchain = (TokenizerChain)analyzer;
|
||||
TokenizerFactory tfac = tchain.getTokenizerFactory();
|
||||
tokenizer.add("className", tfac.getClass().getName());
|
||||
tokenizer.add("args", tfac.getArgs());
|
||||
aninfo.add("tokenizer", tokenizer);
|
||||
TokenFilterFactory[] filtfacs = tchain.getTokenFilterFactories();
|
||||
|
||||
List<Map<String, Object>> filters = new ArrayList<Map<String, Object>>();
|
||||
for (TokenFilterFactory filtfac : filtfacs) {
|
||||
Map<String, Object> tok = new HashMap<String, Object>();
|
||||
tok.put("className", filtfac.getClass().getName());
|
||||
tok.put("args", filtfac.getArgs());
|
||||
filters.add(tok);
|
||||
}
|
||||
if (!filters.isEmpty()) {
|
||||
aninfo.add("filters", filters);
|
||||
}
|
||||
}
|
||||
return aninfo;
|
||||
}
|
||||
|
||||
private static void populateFieldInfo(IndexSchema schema,
|
||||
Map<String, List<String>> typeusemap, SimpleOrderedMap<Object> fields,
|
||||
SchemaField uniqueField, SchemaField f) {
|
||||
FieldType ft = f.getType();
|
||||
SimpleOrderedMap<Object> field = new SimpleOrderedMap<Object>();
|
||||
field.add( "type", ft.getTypeName() );
|
||||
|
@ -356,6 +426,13 @@ public class LukeRequestHandler extends RequestHandlerBase
|
|||
if (f == uniqueField){
|
||||
field.add("uniqueKey", true);
|
||||
}
|
||||
if (ft.getAnalyzer().getPositionIncrementGap(f.getName()) != 0) {
|
||||
field.add("positionIncrementGap", ft.getAnalyzer().getPositionIncrementGap(f.getName()));
|
||||
}
|
||||
field.add("copyDests", schema.getCopyFields(f.getName()));
|
||||
field.add("copySources", schema.getCopySources(f.getName()));
|
||||
|
||||
|
||||
fields.add( f.getName(), field );
|
||||
|
||||
List<String> v = typeusemap.get( ft.getTypeName() );
|
||||
|
@ -364,29 +441,12 @@ public class LukeRequestHandler extends RequestHandlerBase
|
|||
}
|
||||
v.add( f.getName() );
|
||||
typeusemap.put( ft.getTypeName(), v );
|
||||
}
|
||||
|
||||
SimpleOrderedMap<Object> types = new SimpleOrderedMap<Object>();
|
||||
for( FieldType ft : schema.getFieldTypes().values() ) {
|
||||
SimpleOrderedMap<Object> field = new SimpleOrderedMap<Object>();
|
||||
field.add( "fields", typeusemap.get( ft.getTypeName() ) );
|
||||
field.add( "tokenized", ft.isTokenized() );
|
||||
field.add("className", ft.getClass().getName());
|
||||
field.add( "analyzer", ft.getAnalyzer().getClass().getName());
|
||||
types.add( ft.getTypeName(), field );
|
||||
}
|
||||
|
||||
SimpleOrderedMap<Object> finfo = new SimpleOrderedMap<Object>();
|
||||
finfo.add("fields", fields);
|
||||
finfo.add("uniqueKeyField", uniqueField.getName());
|
||||
finfo.add("types", types);
|
||||
return finfo;
|
||||
}
|
||||
|
||||
public static SimpleOrderedMap<Object> getIndexInfo( IndexReader reader, boolean countTerms ) throws IOException
|
||||
{
|
||||
public static SimpleOrderedMap<Object> getIndexInfo( IndexReader reader, boolean countTerms ) throws IOException {
|
||||
Directory dir = reader.directory();
|
||||
SimpleOrderedMap<Object> indexInfo = new SimpleOrderedMap<Object>();
|
||||
|
||||
indexInfo.add("numDocs", reader.numDocs());
|
||||
indexInfo.add("maxDoc", reader.maxDoc());
|
||||
|
||||
|
|
|
@ -829,8 +829,21 @@ public final class IndexSchema {
|
|||
}
|
||||
|
||||
private DynamicField[] dynamicFields;
|
||||
public SchemaField[] getDynamicFieldPrototypes() {
|
||||
SchemaField[] df = new SchemaField[dynamicFields.length];
|
||||
for (int i=0;i<dynamicFields.length;i++) {
|
||||
df[i] = dynamicFields[i].prototype;
|
||||
}
|
||||
return df;
|
||||
}
|
||||
|
||||
|
||||
public String getDynamicPattern(String fieldName) {
|
||||
for (DynamicField df : dynamicFields) {
|
||||
if (df.matches(fieldName)) return df.regex;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Does the schema have the specified field defined explicitly, i.e.
|
||||
* not as a result of a copyField declaration with a wildcard? We
|
||||
|
@ -965,6 +978,28 @@ public final class IndexSchema {
|
|||
private DynamicCopy[] dynamicCopyFields;
|
||||
private final Set<SchemaField> copyFieldTarget = new HashSet<SchemaField>();
|
||||
|
||||
|
||||
/**
|
||||
* Get all copy fields, both the static and the dynamic ones.
|
||||
* @param destField
|
||||
* @return Array of fields copied into this field
|
||||
*/
|
||||
|
||||
public SchemaField[] getCopySources(String destField) {
|
||||
SchemaField f = getField(destField);
|
||||
if (!isCopyFieldTarget(f)) {
|
||||
return new SchemaField[0];
|
||||
}
|
||||
List<SchemaField> sf = new ArrayList<SchemaField>();
|
||||
for (Map.Entry<String, SchemaField[]> cfs : copyFields.entrySet()) {
|
||||
for (SchemaField cf : cfs.getValue()) {
|
||||
if (cf.getName().equals(destField)) {
|
||||
sf.add(getField(cfs.getKey()));
|
||||
}
|
||||
}
|
||||
}
|
||||
return sf.toArray(new SchemaField[1]);
|
||||
}
|
||||
/**
|
||||
* Get all copy fields, both the static and the dynamic ones.
|
||||
* @param sourceField
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -0,0 +1,655 @@
|
|||
<%@ page contentType="text/html; charset=utf-8" pageEncoding="UTF-8"%>
|
||||
<%--
|
||||
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.
|
||||
--%>
|
||||
|
||||
<%-- $Id: index.jsp 608150 2008-01-02 17:15:30Z ryan $ --%>
|
||||
<%-- $Source: /cvs/main/searching/SolrServer/resources/admin/index.jsp,v $ --%>
|
||||
<%-- $Name: $ --%>
|
||||
|
||||
<script src="/solr/admin/jquery-1.2.3.min.js"></script>
|
||||
<script>
|
||||
|
||||
(function($, libName) {
|
||||
var solr = {
|
||||
|
||||
//The default location of the luke handler relative to this page
|
||||
// Can be overridden in the init(url) method
|
||||
pathToLukeHandler: 'luke',
|
||||
|
||||
// Base properties to hold schema information
|
||||
schemaInfo: {},
|
||||
schemaFields: {},
|
||||
schemaDynamicFields: {},
|
||||
schemaTypes: {},
|
||||
schemaFlags: {},
|
||||
|
||||
//The basic function to call to make the initail JSON calls
|
||||
// takes one option parameter, the path to the luke handler
|
||||
// if undefined, it will use the default, 'luke', which means
|
||||
// this is being called from the same relative URL path
|
||||
init: function(pathToLukeHandler) {
|
||||
if (pathToLukeHandler != undefined) {
|
||||
solr.pathToLukeHandler = pathToLukeHandler;
|
||||
}
|
||||
solr.loadSchema(function() {
|
||||
solr.loadFromLukeHandler(function () {
|
||||
solr.createMenu('menu');
|
||||
solr.displaySchemaInfo();
|
||||
});
|
||||
});
|
||||
|
||||
},
|
||||
|
||||
//load the Schema from the LukeRequestHandler
|
||||
// this loads every field, and in each field the copy source/dests and flags
|
||||
// we also load the list of field types, and the list of flags
|
||||
loadSchema: function(func) {
|
||||
$.getJSON(solr.pathToLukeHandler +'?show=schema&wt=json', function(data) {
|
||||
//populate all non field/type/flag data in the info block
|
||||
$.each(data.index, function(i, item) {
|
||||
solr.schemaInfo[i] = item;
|
||||
});
|
||||
|
||||
//LukeRequestHandler places these two attributes outside of the "index" node, but
|
||||
// we want it here so we can more easily display it in the "HOME" block
|
||||
solr.schemaInfo['uniqueKeyField'] = data.schema.uniqueKeyField;
|
||||
solr.schemaInfo['defaultSearchField'] = data.schema.defaultSearchField;
|
||||
//a one-off hack, because the directory string is so long and unbroken
|
||||
// that it can break CSS layouts
|
||||
solr.schemaInfo['directory'] = solr.schemaInfo['directory'].substring(0, solr.schemaInfo['directory'].indexOf('@')+1) + ' ' + solr.schemaInfo['directory'].substring(solr.schemaInfo['directory'].indexOf('@') +1);
|
||||
|
||||
// populate the list of fields
|
||||
$.each(data.schema.fields, function(i,item){
|
||||
solr.schemaFields[i]=item;
|
||||
});
|
||||
// populate the list of field types
|
||||
$.each(data.schema.types, function(type, ft) {
|
||||
solr.schemaTypes[type] = ft;
|
||||
});
|
||||
//populate the list of dynamic fields
|
||||
$.each(data.schema.dynamicFields, function(i, dynField) {
|
||||
solr.schemaDynamicFields[i] = dynField;
|
||||
});
|
||||
//populate the list of flags, so we can convert flags to text in display
|
||||
$.each(data.info.key, function(i, flag) {
|
||||
solr.schemaFlags[i] = flag;
|
||||
});
|
||||
|
||||
//LukeRequestHandler returns copyFields src/dest as the entire toString of the field
|
||||
// we only need the field name, so here we loop through the fields, and replace the full
|
||||
// field definitions with the name in the copySources/copyDests properties
|
||||
$.each(solr.schemaFields, function(i, field) {
|
||||
$.each(['copySources', 'copyDests'], function(i, copyProp) {
|
||||
var newFields = new Array();
|
||||
$.each(field[copyProp], function(i, fullName) {
|
||||
newFields.push(fullName.substring(fullName.lastIndexOf(':')+1, fullName.indexOf('{')));
|
||||
});
|
||||
field[copyProp] = newFields;
|
||||
});
|
||||
|
||||
});
|
||||
//An additional optional callback
|
||||
// used in init to trigger the 2nd call to LukeRequestHandler only
|
||||
// after the first one is finished
|
||||
if ($.isFunction(func)) {
|
||||
func(solr);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
//further populates the loaded schema with information gathered
|
||||
// from the no argument LukeRequestHandler
|
||||
loadFromLukeHandler: function(func) {
|
||||
$.getJSON(solr.pathToLukeHandler+'?wt=json', function(data) {
|
||||
$.each(data.fields, function(i, item) {
|
||||
var field = solr.schemaFields[i];
|
||||
|
||||
//If undefined, then we have a dynamicField which does not show up
|
||||
// in the LukeRequestHandler show=schema variant
|
||||
if (field == undefined) {
|
||||
field = item;
|
||||
//Attach this field to its dynamicField
|
||||
var base = field.dynamicBase;
|
||||
var dynField = solr.schemaDynamicFields[base];
|
||||
var synFields = dynField['fields'];
|
||||
|
||||
if (synFields== undefined) {
|
||||
synFields= new Array();
|
||||
}
|
||||
synFields.push(i);
|
||||
dynField['fields'] = synFields;
|
||||
solr.schemaFields[i] = item;
|
||||
}
|
||||
//Populate other data in this field that would not have been loaded in
|
||||
// the show=schema variant
|
||||
$.each(item, function(k, v) {
|
||||
if (k == 'topTerms' || k == 'histogram') {
|
||||
solr.schemaFields[i][k] = solr.lukeArrayToHash(v);
|
||||
} else {
|
||||
solr.schemaFields[i][k] = v;
|
||||
}
|
||||
});
|
||||
});
|
||||
//another optional callback; used in the init case to lay out the page
|
||||
// after the data is loaded
|
||||
if ($.isFunction(func)) {
|
||||
func();
|
||||
}
|
||||
});
|
||||
},
|
||||
//some elements in the JSON response are arrays, where odd/even elements
|
||||
// are the name/value, and convert it to a standard map/associative array
|
||||
// incoming: ['foo', 'bar', 'bat', 'baz']
|
||||
// output: {'foo':'bar', 'bat':baz'}
|
||||
lukeArrayToHash: function(termsArr) {
|
||||
var hash = new Object();
|
||||
var temp;
|
||||
//topTerms comes in as an array, with odd indexes the field name
|
||||
// and even indexes the number
|
||||
$.each(termsArr, function(i, item) {
|
||||
if (i%2 ==0) {
|
||||
temp = item;
|
||||
} else {
|
||||
hash[temp] = item;
|
||||
}
|
||||
});
|
||||
return hash;
|
||||
},
|
||||
|
||||
//gets the top Terms via an Ajax call the LukeRequestHandler for that field
|
||||
// The callback is used here to redraw the table after the ajax call returns
|
||||
getTopTerms: function(fieldName, numTerms, func) {
|
||||
if (numTerms == undefined) {
|
||||
var numTerms = 10;
|
||||
}
|
||||
if (isNaN(numTerms) || numTerms <=0 || numTerms.indexOf('.') != -1) {
|
||||
return;
|
||||
}
|
||||
$.getJSON(solr.pathToLukeHandler+'?fl='+fieldName+'&wt=json&numTerms='+numTerms, function(data) {
|
||||
solr.schemaFields[fieldName]['topTerms'] = solr.lukeArrayToHash(data.fields[fieldName].topTerms);
|
||||
if ($.isFunction(func)) {
|
||||
func(solr.schemaFields[fieldName]['topTerms'], fieldName);
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
// Displays the SchemaInfo in the main content panel
|
||||
// dispayed on data load, and also when 'Home' is clicked
|
||||
displaySchemaInfo: function() {
|
||||
$('#mainInfo').html('');
|
||||
$('#topTerms').html('');
|
||||
$('#histogram').html('');
|
||||
$('#mainInfo').append(solr.createSimpleText('Schema Information'));
|
||||
//Make sure the uniqueKeyField and defaultSearchFields come first
|
||||
$.each({'Unique Key':'uniqueKeyField', 'Default Search Field':'defaultSearchField'}, function(text, prop) {
|
||||
if (solr.schemaInfo[prop] != undefined) {
|
||||
$('#mainInfo').append(solr.createNameValueText(text, function(p) {
|
||||
p.appendChild(solr.createLink(solr.schemaInfo[prop], solr.schemaInfo[prop]));
|
||||
return p;
|
||||
}));
|
||||
}
|
||||
});
|
||||
$.each(solr.schemaInfo, function(i, item) {
|
||||
if (i == 'uniqueKeyField' || i == 'defaultSearchField') {
|
||||
//noop; we took care of this above
|
||||
} else {
|
||||
$('#mainInfo').append(solr.createNameValueText(i, item));
|
||||
}
|
||||
});
|
||||
//Close all menus when we display schema home
|
||||
solr.toggleMenus(undefined, ['fields', 'types', 'dynFields']);
|
||||
},
|
||||
|
||||
// display a dynamic field in the main content panel
|
||||
displayDynamicField: function(dynamicPattern) {
|
||||
var df = solr.schemaDynamicFields[dynamicPattern];
|
||||
$('#mainInfo').html('');
|
||||
$('#topTerms').html('');
|
||||
$('#histogram').html('');
|
||||
$('#mainInfo').append(solr.createSimpleText('Dynamic Field: ' + dynamicPattern));
|
||||
$('#mainInfo').append(solr.createNameValueText('Fields', function(p) {
|
||||
if (df.fields != undefined) {
|
||||
$.each(df.fields, function(i, item) {
|
||||
p.appendChild(solr.createLink(item, item));
|
||||
});
|
||||
} else {
|
||||
p.appendChild(document.createTextNode(' None currently in index'));
|
||||
}
|
||||
return p;
|
||||
}));
|
||||
var ft = solr.schemaTypes[df.type];
|
||||
$('#mainInfo').append(solr.createNameValueText('Field Type', function(p) {
|
||||
p.appendChild(solr.createLink(df.type, df.type, solr.displayFieldType));
|
||||
return p;
|
||||
}));
|
||||
if (df.flags != undefined) {
|
||||
$('#mainInfo').append(solr.createNameValueText('Properties', solr.createTextFromFlags(df.flags, df.type)));
|
||||
}
|
||||
solr.displayAnalyzer(ft.indexAnalyzer, 'Index Analyzer', true);
|
||||
solr.displayAnalyzer(ft.queryAnalyzer, 'Query Analyzer', true);
|
||||
|
||||
solr.toggleMenus('dynFields', ['fields', 'types'], dynamicPattern);
|
||||
},
|
||||
|
||||
// display a field type in the main area
|
||||
displayFieldType: function(typeName) {
|
||||
var ft = solr.schemaTypes[typeName];
|
||||
$('#mainInfo').html('');
|
||||
$('#topTerms').html('');
|
||||
$('#histogram').html('');
|
||||
$('#mainInfo').append(solr.createSimpleText('Field Type: ' + typeName));
|
||||
$('#mainInfo').append(solr.createNameValueText('Fields', function(p) {
|
||||
if (ft.fields != undefined) {
|
||||
$.each(ft.fields, function(i, item) {
|
||||
if (solr.schemaFields[item] != undefined) {
|
||||
p.appendChild(solr.createLink(item, item));
|
||||
} else {
|
||||
p.appendChild(solr.createLink(item, item, solr.displayDynamicField));
|
||||
}
|
||||
p.appendChild(document.createTextNode(' '));
|
||||
});
|
||||
} else {
|
||||
p.appendChild(document.createTextNode('No fields in index'));
|
||||
}
|
||||
return p;
|
||||
}));
|
||||
$('#mainInfo').append(solr.createNameValueText('Tokenzied', ft.tokenized));
|
||||
$('#mainInfo').append(solr.createNameValueText('Class Name', ft.className));
|
||||
|
||||
solr.displayAnalyzer(ft.indexAnalyzer, 'Index Analyzer');
|
||||
solr.displayAnalyzer(ft.queryAnalyzer, 'Query Analyzer');
|
||||
solr.toggleMenus('types', ['fields', 'dynFields'], typeName);
|
||||
},
|
||||
|
||||
//Displays information about an Analyzer in the main content area
|
||||
displayAnalyzer: function(analyzer, type, shouldCollapse) {
|
||||
var tid = type.replace(' ', '');
|
||||
var collapse = shouldCollapse && (analyzer.tokenizer != undefined || analyzer.filters != undefined);
|
||||
$('#mainInfo').append(solr.createNameValueText(type, function(p) {
|
||||
p.appendChild(document.createTextNode(analyzer.className + ' '));
|
||||
if (collapse) {
|
||||
p.appendChild(solr.createLink(type, 'Details', function() {
|
||||
$('#'+tid).toggle("slow");
|
||||
}));
|
||||
}
|
||||
return p;
|
||||
}));
|
||||
var adiv = document.createElement('div');
|
||||
adiv.id=tid;
|
||||
adiv.className='analyzer';
|
||||
if (collapse) {
|
||||
adiv.style.display='none';
|
||||
}
|
||||
if (analyzer.tokenizer != undefined) {
|
||||
adiv.appendChild(solr.createNameValueText("Tokenizer Class", analyzer.tokenizer.className));
|
||||
}
|
||||
if (analyzer.filters != undefined) {
|
||||
adiv.appendChild(solr.createNameValueText('Filters', ''));
|
||||
var f = document.createElement('ol');
|
||||
$.each(analyzer.filters, function(i, item) {
|
||||
var fil = document.createElement('li');
|
||||
var filterText = item.className;
|
||||
if (item.args != undefined) {
|
||||
filterText += ' args:{'
|
||||
$.each(item.args, function(fi, fitem) {
|
||||
filterText += fi + ': ' + fitem + ' ';
|
||||
});
|
||||
filterText +='}';
|
||||
fil.innerHTML = filterText;
|
||||
f.appendChild(fil);
|
||||
}
|
||||
});
|
||||
adiv.appendChild(f);
|
||||
}
|
||||
$('#mainInfo').append(adiv);
|
||||
},
|
||||
|
||||
// display information about a Field in the main content area
|
||||
// and its TopTerms and Histogram in related divs
|
||||
displayField: function(fieldName) {
|
||||
var field = solr.schemaFields[fieldName];
|
||||
var isDynamic = field.dynamicBase != undefined ? true : false;
|
||||
var ft;
|
||||
var ftName;
|
||||
$('#mainInfo').html('');
|
||||
$('#mainInfo').append(solr.createSimpleText('Field: ' + fieldName));
|
||||
|
||||
//For regular fields, we take their properties; for dynamicFields,
|
||||
// we take them from their dynamicField definitions
|
||||
if (isDynamic) {
|
||||
ftName = solr.schemaDynamicFields[field.dynamicBase].type
|
||||
$('#mainInfo').append(solr.createNameValueText('Dynamically Created From Pattern', function(p) {
|
||||
p.appendChild(solr.createLink(field.dynamicBase, field.dynamicBase, solr.displayDynamicField));
|
||||
return p;
|
||||
}));
|
||||
} else {
|
||||
ftName = field.type;
|
||||
}
|
||||
ft = solr.schemaTypes[field.type];
|
||||
$('#mainInfo').append(solr.createNameValueText('Field Type', function(p) {
|
||||
p.appendChild(solr.createLink(ftName, ftName, solr.displayFieldType));
|
||||
return p;
|
||||
}));
|
||||
if (solr.schemaFlags != '') {
|
||||
$.each({'flags':'Properties', 'schema':'Schema', 'index':'Index'}, function(prop, text) {
|
||||
if (field[prop] != undefined) {
|
||||
$('#mainInfo').append(solr.createNameValueText(text, solr.createTextFromFlags(field[prop], ft)));
|
||||
}
|
||||
});
|
||||
}
|
||||
$.each({'copySources':'Copied From', 'copyDests':'Copied Into'}, function(prop, text) {
|
||||
if (field[prop] != undefined && field[prop] != '') {
|
||||
$('#mainInfo').append(solr.createNameValueText(text, function(p) {
|
||||
$.each(field[prop], function(i, item) {
|
||||
p.appendChild(solr.createLink(item, item));
|
||||
p.appendChild(document.createTextNode(' '));
|
||||
});
|
||||
return p;
|
||||
}));
|
||||
}
|
||||
});
|
||||
if (field.positionIncrementGap != undefined) {
|
||||
$('#mainInfo').append(solr.createNameValueText('Position Increment Gap', field.positionIncrementGap));
|
||||
}
|
||||
solr.displayAnalyzer(ft.indexAnalyzer, 'Index Analyzer', true);
|
||||
solr.displayAnalyzer(ft.queryAnalyzer, 'Query Analyzer', true);
|
||||
if (field.docs != undefined) {
|
||||
$('#mainInfo').append(solr.createNameValueText('Docs', field.docs));
|
||||
}
|
||||
if (field.distinct != undefined) {
|
||||
$('#mainInfo').append(solr.createNameValueText('Distinct', field.distinct));
|
||||
}
|
||||
|
||||
if (field.topTerms != undefined) {
|
||||
solr.displayTopTerms(field.topTerms, fieldName);
|
||||
}
|
||||
$('#histogram').html('');
|
||||
if (field.histogram != undefined) {
|
||||
solr.drawHistogram(field.histogram);
|
||||
}
|
||||
solr.toggleMenus('fields', ['types', 'dynFields'], fieldName);
|
||||
},
|
||||
|
||||
//utility method to create a single sentence list of properties from a flag set
|
||||
// or pass it on, if the flags are (unstored field)
|
||||
createTextFromFlags: function(fieldFlags, fieldType) {
|
||||
var value;
|
||||
if (fieldFlags != '(unstored field)') {
|
||||
var value = '';
|
||||
for (var i=0;i<fieldFlags.length;i++) {
|
||||
if (fieldFlags.charAt(i) != '-') {
|
||||
value += solr.schemaFlags[fieldFlags.charAt(i)];
|
||||
value += ', ';
|
||||
}
|
||||
}
|
||||
value = value.substring(0, value.length-2);
|
||||
} else {
|
||||
value = fieldFlags;
|
||||
}
|
||||
return value;
|
||||
},
|
||||
|
||||
//Store the currently highlighted menu item, as otherwise we
|
||||
// must traverse all li menu items, which is very slow on schemas with
|
||||
// large number of fields
|
||||
// for example $('#menu ul li').siblings().removeClass('selected');
|
||||
currentlyHighlightedMenuId: undefined,
|
||||
|
||||
//add a highlight to the currently selected menu item, and remove
|
||||
// the highlights from all other menu items
|
||||
highlightMenuItem: function(idToSelect) {
|
||||
if (solr.currentlyHighlightedMenuId != undefined) {
|
||||
$('#'+solr.currentlyHighlightedMenuId).removeClass('selected');
|
||||
}
|
||||
$('#'+idToSelect).addClass('selected');
|
||||
solr.currentlyHighlightedMenuId = idToSelect;
|
||||
},
|
||||
|
||||
//Opens one menu group, close the others, and optionally highlight one
|
||||
// item, which should be in the opened menu
|
||||
toggleMenus: function(idToShow, idsToHide, idToSelect) {
|
||||
if (idToSelect != undefined) {
|
||||
solr.highlightMenuItem(idToShow + idToSelect);
|
||||
}
|
||||
$('#'+idToShow).show("slow");
|
||||
$.each(idsToHide, function(i, idToHide) {
|
||||
$('#'+idToHide).hide("slow");
|
||||
});
|
||||
},
|
||||
|
||||
//A utility method to create a paragraph, which takes two arguments;
|
||||
// an opening text, and either text or a callback function to follow
|
||||
// any callback function must return the node passed into it
|
||||
createNameValueText: function(openingText, func) {
|
||||
var p = document.createElement('p');
|
||||
p.appendChild(solr.createSimpleText(openingText + ': ', 'b'));
|
||||
return solr.applyFuncToNode(p, func);
|
||||
},
|
||||
|
||||
//utility method to create an HTML text element node
|
||||
// with the literal text to place, and an optional function to apply
|
||||
// any callback function must return the node passed into it
|
||||
createSimpleText: function(text, n, func) {
|
||||
if (n == undefined) {
|
||||
n = 'h2';
|
||||
}
|
||||
var no= document.createElement(n);
|
||||
no.appendChild(document.createTextNode(text));
|
||||
return solr.applyFuncToNode(no, func);
|
||||
},
|
||||
|
||||
//Utility method that applies a function or a string to append
|
||||
// an additional child to a node
|
||||
applyFuncToNode: function(no, func) {
|
||||
if ($.isFunction(func)) {
|
||||
no = func(no);
|
||||
} else {
|
||||
// if it is not a function, append it as a string
|
||||
if (func != undefined) {
|
||||
no.appendChild(document.createTextNode(' ' + func));
|
||||
}
|
||||
}
|
||||
return no;
|
||||
},
|
||||
|
||||
//show a table of top terms for a given field
|
||||
displayTopTerms: function(topTerms, fieldName) {
|
||||
$('#topTerms').html('');
|
||||
var tbl = document.createElement('table');
|
||||
tbl.className='topTerms';
|
||||
var thead= document.createElement('thead');
|
||||
var headerRow = document.createElement('tr');
|
||||
$.each(['term', 'frequency'], function() {
|
||||
var cell = document.createElement('th');
|
||||
cell.innerHTML= this;
|
||||
headerRow.appendChild(cell);
|
||||
});
|
||||
thead.appendChild(headerRow);
|
||||
tbl.appendChild(thead);
|
||||
var tbody = document.createElement('tbody');
|
||||
|
||||
var numTerms = 0;
|
||||
$.each(topTerms, function(term, count) {
|
||||
var row = document.createElement('tr');
|
||||
var c1 = document.createElement('td');
|
||||
c1.innerHTML=term;
|
||||
var c2 = document.createElement('td');
|
||||
c2.innerHTML=count;
|
||||
row.appendChild(c1);
|
||||
row.appendChild(c2);
|
||||
tbody.appendChild(row);
|
||||
numTerms++;
|
||||
});
|
||||
tbl.appendChild(tbody);
|
||||
|
||||
//create a header along with an input widget so the user
|
||||
// can request a different number of Top Terms
|
||||
var h2 = document.createElement('h2');
|
||||
h2.appendChild(document.createTextNode('Top '));
|
||||
var termsGetter = document.createElement('input');
|
||||
termsGetter.type='text';
|
||||
termsGetter.size=5;
|
||||
termsGetter.value=numTerms;
|
||||
|
||||
termsGetter.onchange=function() {
|
||||
solr.getTopTerms(fieldName, this.value, solr.displayTopTerms);
|
||||
}
|
||||
h2.appendChild(termsGetter);
|
||||
h2.appendChild(document.createTextNode(' Terms'));
|
||||
$('#topTerms').append(h2);
|
||||
|
||||
document.getElementById('topTerms').appendChild(tbl);
|
||||
$('#topTerms').append(tbl);
|
||||
},
|
||||
|
||||
//draws a histogram, taking a map of values and an optional total height and width for the table
|
||||
drawHistogram: function(histogram, totalHeightArg, totalWidthArg) {
|
||||
$('#histogram').html('');
|
||||
$('#histogram').append(solr.createSimpleText('Histogram'));
|
||||
var max = 0;
|
||||
var bars =0;
|
||||
//find the # of columns and max value in the histogram
|
||||
// so we can create an appropriately scaled chart
|
||||
$.each(histogram, function(i, item) {
|
||||
if (item > max) max = item;
|
||||
bars += 1;
|
||||
});
|
||||
if (max ==0) {
|
||||
$('#histogram').append(solr.createNameValueText('No histogram available'));
|
||||
} else {
|
||||
var totalHeight = totalHeightArg == undefined ? 208 : totalHeightArg;
|
||||
var totalWidth = totalWidthArg == undefined ? 160 : totalWidthArg;
|
||||
var tbl = document.createElement('table');
|
||||
tbl.style.width=totalWidth+'px';
|
||||
tbl.className = 'histogram';
|
||||
var h = document.createElement('tbody');
|
||||
var r = document.createElement('tr');
|
||||
var r2 = document.createElement('tr');
|
||||
$.each(histogram, function(i, item) {
|
||||
var c = document.createElement('td');
|
||||
c.innerHTML=item+'<div style="width:'+totalWidth/bars+'px;height:'+(item*totalHeight/max)+'px;background:blue"> </div>';
|
||||
r.appendChild(c);
|
||||
var c2 = document.createElement('td');
|
||||
c2.innerHTML='' + i;
|
||||
r2.appendChild(c2);
|
||||
});
|
||||
h.appendChild(r);
|
||||
h.appendChild(r2);
|
||||
tbl.appendChild(h);
|
||||
$('#histogram').append(tbl);
|
||||
}
|
||||
},
|
||||
|
||||
//dynamically creates a link to be appended
|
||||
createLink: function(idToDisplay, linkText, linkFunction) {
|
||||
var link = document.createElement('a');
|
||||
if (!$.isFunction(linkFunction)) {
|
||||
linkFunction = solr.displayField
|
||||
}
|
||||
link.onclick=function() {
|
||||
linkFunction(idToDisplay);
|
||||
return false;
|
||||
};
|
||||
link.href='#';
|
||||
link.innerHTML=linkText;
|
||||
return link;
|
||||
},
|
||||
|
||||
//Creates a menu header that can expand or collapse its children
|
||||
createMenuHeader: function(text, idToShow, idsToHide) {
|
||||
var head = document.createElement('h3');
|
||||
var a = document.createElement('a');
|
||||
a.onclick=function() {
|
||||
solr.toggleMenus(idToShow, idsToHide);
|
||||
return false;
|
||||
};
|
||||
a.href='#';
|
||||
a.innerHTML=text;
|
||||
head.appendChild(a);
|
||||
return head;
|
||||
},
|
||||
|
||||
//Creates an element in a menu (e.g. each field in a list of fields)
|
||||
createMenuItem: function(tagName, text, link, type, func) {
|
||||
var fieldEle = document.createElement('li');
|
||||
fieldEle.id=type+text;
|
||||
var funct = func == undefined ? undefined : func;
|
||||
fieldEle.appendChild(solr.createLink(text, link, funct));
|
||||
return fieldEle;
|
||||
},
|
||||
|
||||
//populates the menu div
|
||||
createMenu: function(menuId) {
|
||||
var m = $('#'+menuId);
|
||||
var home = document.createElement('h2');
|
||||
home.appendChild(solr.createLink('Home', 'Home', solr.displaySchemaInfo));
|
||||
m.append(home);
|
||||
m.append(solr.createMenuHeader('Fields', 'fields', ['types', 'dynFields']));
|
||||
var fields= document.createElement('ul');
|
||||
fields.style.display='none';
|
||||
fields.id = 'fields';
|
||||
$.each(solr.schemaFields, function(i, item) {
|
||||
fields.appendChild(solr.createMenuItem('li', i, i, fields.id));
|
||||
});
|
||||
m.append(fields);
|
||||
m.append(solr.createMenuHeader('Dynamic Fields', 'dynFields', ['fields', 'types']));
|
||||
var dyns = document.createElement('ul');
|
||||
dyns.style.display = 'none';
|
||||
dyns.id = 'dynFields';
|
||||
$.each(solr.schemaDynamicFields, function(i, item) {
|
||||
dyns.appendChild(solr.createMenuItem('li', i,i, dyns.id, solr.displayDynamicField));
|
||||
});
|
||||
m.append(dyns);
|
||||
m.append(solr.createMenuHeader('Field Types', 'types', ['fields', 'dynFields']));
|
||||
var types = document.createElement('ul');
|
||||
types.style.display='none';
|
||||
types.id='types';
|
||||
$.each(this.schemaTypes, function(i, item) {
|
||||
types.appendChild(solr.createMenuItem('li', i, i,types.id, solr.displayFieldType));
|
||||
});
|
||||
m.append(types);
|
||||
}
|
||||
};
|
||||
|
||||
window[libName] = solr;
|
||||
})(jQuery, 'solr');
|
||||
$(document).ready(function() {
|
||||
solr.init();
|
||||
});
|
||||
|
||||
$(window).unload( function() {
|
||||
solr = null;
|
||||
$('#mainInfo').html('');
|
||||
$('#menu').html('');
|
||||
$('#topTerms').html('');
|
||||
$('#histogram').html('');
|
||||
});
|
||||
|
||||
</script>
|
||||
<%-- do a verbatim include so we can use the local vars --%>
|
||||
<%@include file="header.jsp" %>
|
||||
<div id="schemaTop">
|
||||
<h2>Schema Browser | See <a href="file/?file=schema.xml">Raw Schema.xml</a></h2>
|
||||
</div>
|
||||
<div id="menu"></div>
|
||||
<div id="content">
|
||||
<div id="mainInfo"><h2>Please wait...loading and parsing Schema Information from LukeRequestHandler</h2><p>If it does not load or your browser is not javascript or ajax-capable, you may wish to examine your schema using the <a href="luke?wt=xslt&tr=luke.xsl">Server side transformed LukeRequestHandler</a> or the raw <a href="file/?file=schema.xml">schema.xml</a> instead.</div>
|
||||
<div id="topTerms"></div>
|
||||
<div id="histogram"></div>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
|
@ -75,7 +75,6 @@ table {
|
|||
border-collapse: collapse
|
||||
}
|
||||
|
||||
|
||||
tr > td:first-child {
|
||||
width: 30%;
|
||||
}
|
||||
|
@ -138,3 +137,70 @@ table.analysis th, table.analysis td {
|
|||
border-right:1px solid black;
|
||||
}
|
||||
|
||||
/**
|
||||
* styles for the schema browser
|
||||
*/
|
||||
|
||||
table.topTerms {
|
||||
width: 450px;
|
||||
}
|
||||
|
||||
table.histogram {
|
||||
vertical-align: bottom;
|
||||
}
|
||||
|
||||
table.histogram td, table.histogram th {
|
||||
text-align: center;
|
||||
vertical-align: bottom;
|
||||
border-bottom: 1px solid #ff9933;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
#menu {
|
||||
background-color: #FAF7E4;
|
||||
height:100%;
|
||||
min-height:100%;
|
||||
width:140px;
|
||||
float:left;
|
||||
margin-right:20px
|
||||
}
|
||||
#menu h3 {
|
||||
padding-left:10px;
|
||||
}
|
||||
#menu ul {
|
||||
list-style: none;
|
||||
text-align: right;
|
||||
margin: 0;
|
||||
padding: 0
|
||||
}
|
||||
#menu li.header {
|
||||
text-align: left;
|
||||
}
|
||||
#menu li {
|
||||
border: 1px solid #ff9933;
|
||||
margin: 0
|
||||
}
|
||||
#menu li a {
|
||||
display:block;
|
||||
}
|
||||
#menu li.selected a {
|
||||
background-color: #ccccff
|
||||
}
|
||||
#menu a:hover {
|
||||
background: #ccccff
|
||||
}
|
||||
|
||||
#schemaTop {
|
||||
border-bottom:1px black solid;
|
||||
}
|
||||
|
||||
#content {
|
||||
margin-left: 160px;
|
||||
}
|
||||
#topTerms {
|
||||
float:left;
|
||||
margin-right:40px;
|
||||
}
|
||||
div.analyzer {
|
||||
margin-left:20px;
|
||||
}
|
Loading…
Reference in New Issue