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:
Erik Hatcher 2008-03-15 20:49:21 +00:00
parent 45d3ab73a2
commit 2f39d9ff04
6 changed files with 884 additions and 33 deletions

View File

@ -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

View File

@ -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());

View File

@ -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

View File

@ -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">&nbsp</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>

View File

@ -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;
}