mirror of https://github.com/apache/lucene.git
SOLR-1723: VelocityResponseWriter improvements
SOLR-2035: Add a VelocityResponseWriter $resource tool for locale-specific string lookups. Lots of VrW code cleanup, more and improved test cases. git-svn-id: https://svn.apache.org/repos/asf/lucene/dev/trunk@1650685 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
b870aed9b9
commit
ae0c13fae8
|
@ -6,8 +6,10 @@
|
|||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/test" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/test/velocity" type="java-test-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/test-files" type="java-test-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/java" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/resources" type="java-resource" />
|
||||
</content>
|
||||
<orderEntry type="inheritedJdk" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
|
|
|
@ -285,6 +285,9 @@ New Features
|
|||
|
||||
* SOLR-6766: Expose HdfsDirectoryFactory Block Cache statistics via JMX.
|
||||
(Mike Drob, Mark Miller)
|
||||
|
||||
* SOLR-2035: Add a VelocityResponseWriter $resource tool for locale-specific string lookups.
|
||||
(Erik Hatcher)
|
||||
|
||||
Bug Fixes
|
||||
----------------------
|
||||
|
@ -652,6 +655,8 @@ Other Changes
|
|||
* SOLR-6932: All HttpClient ConnectionManagers and SolrJ clients should always be shutdown
|
||||
in tests and regular code. (Mark Miller)
|
||||
|
||||
* SOLR-1723: VelocityResponseWriter improvements (Erik Hatcher)
|
||||
|
||||
================== 4.10.3 ==================
|
||||
|
||||
Bug Fixes
|
||||
|
|
|
@ -31,6 +31,8 @@ import java.util.Iterator;
|
|||
import java.util.Map;
|
||||
|
||||
public class SolrParamResourceLoader extends ResourceLoader {
|
||||
public static final String TEMPLATE_PARAM_PREFIX = VelocityResponseWriter.TEMPLATE + ".";
|
||||
|
||||
private Map<String,String> templates = new HashMap<>();
|
||||
public SolrParamResourceLoader(SolrQueryRequest request) {
|
||||
super();
|
||||
|
@ -44,8 +46,8 @@ public class SolrParamResourceLoader extends ResourceLoader {
|
|||
while (names.hasNext()) {
|
||||
String name = names.next();
|
||||
|
||||
if (name.startsWith("v.template.")) {
|
||||
templates.put(name.substring(11) + ".vm",params.get(name));
|
||||
if (name.startsWith(TEMPLATE_PARAM_PREFIX)) {
|
||||
templates.put(name.substring(TEMPLATE_PARAM_PREFIX.length()) + VelocityResponseWriter.TEMPLATE_EXTENSION,params.get(name));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,115 @@
|
|||
package org.apache.solr.response;
|
||||
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
import org.apache.velocity.runtime.RuntimeServices;
|
||||
import org.apache.velocity.runtime.log.LogChute;
|
||||
import org.slf4j.Logger;
|
||||
|
||||
public class SolrVelocityLogger implements LogChute {
|
||||
private final Logger log;
|
||||
|
||||
public SolrVelocityLogger(Logger log) {
|
||||
this.log = log;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(RuntimeServices runtimeServices) throws Exception {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void log(int level, String message) {
|
||||
switch(level) {
|
||||
case LogChute.TRACE_ID:
|
||||
log.trace(message);
|
||||
break;
|
||||
|
||||
case LogChute.DEBUG_ID:
|
||||
log.debug(message);
|
||||
break;
|
||||
|
||||
case LogChute.INFO_ID:
|
||||
log.info(message);
|
||||
break;
|
||||
|
||||
case LogChute.WARN_ID:
|
||||
log.warn(message);
|
||||
break;
|
||||
|
||||
case LogChute.ERROR_ID:
|
||||
log.error(message);
|
||||
break;
|
||||
|
||||
default: // unknown logging level, use warn
|
||||
log.warn(message);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void log(int level, String message, Throwable throwable) {
|
||||
switch(level) {
|
||||
case LogChute.TRACE_ID:
|
||||
log.trace(message, throwable);
|
||||
break;
|
||||
|
||||
case LogChute.DEBUG_ID:
|
||||
log.debug(message, throwable);
|
||||
break;
|
||||
|
||||
case LogChute.INFO_ID:
|
||||
log.info(message, throwable);
|
||||
break;
|
||||
|
||||
case LogChute.WARN_ID:
|
||||
log.warn(message, throwable);
|
||||
break;
|
||||
|
||||
case LogChute.ERROR_ID:
|
||||
log.error(message, throwable);
|
||||
break;
|
||||
|
||||
default: // unknown logging level, use warn
|
||||
log.warn(message, throwable);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isLevelEnabled(int level) {
|
||||
switch(level) {
|
||||
case LogChute.TRACE_ID:
|
||||
return log.isTraceEnabled();
|
||||
|
||||
case LogChute.DEBUG_ID:
|
||||
return log.isDebugEnabled();
|
||||
|
||||
case LogChute.INFO_ID:
|
||||
return log.isInfoEnabled();
|
||||
|
||||
case LogChute.WARN_ID:
|
||||
return log.isWarnEnabled();
|
||||
|
||||
case LogChute.ERROR_ID:
|
||||
return log.isErrorEnabled();
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -17,46 +17,181 @@
|
|||
|
||||
package org.apache.solr.response;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.StringWriter;
|
||||
import java.io.Writer;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Locale;
|
||||
import java.util.Properties;
|
||||
import java.util.ResourceBundle;
|
||||
|
||||
import org.apache.hadoop.util.StringUtils;
|
||||
import org.apache.solr.client.solrj.SolrResponse;
|
||||
import org.apache.solr.client.solrj.response.QueryResponse;
|
||||
import org.apache.solr.client.solrj.response.SolrResponseBase;
|
||||
import org.apache.solr.common.params.CommonParams;
|
||||
import org.apache.solr.common.util.NamedList;
|
||||
import org.apache.solr.core.SolrCore;
|
||||
import org.apache.solr.request.SolrQueryRequest;
|
||||
import org.apache.solr.util.plugin.SolrCoreAware;
|
||||
import org.apache.velocity.Template;
|
||||
import org.apache.velocity.VelocityContext;
|
||||
import org.apache.velocity.app.VelocityEngine;
|
||||
import org.apache.velocity.runtime.RuntimeConstants;
|
||||
import org.apache.velocity.runtime.resource.loader.ClasspathResourceLoader;
|
||||
import org.apache.velocity.tools.ConversionUtils;
|
||||
import org.apache.velocity.tools.generic.ComparisonDateTool;
|
||||
import org.apache.velocity.tools.generic.DisplayTool;
|
||||
import org.apache.velocity.tools.generic.EscapeTool;
|
||||
import org.apache.velocity.tools.generic.ListTool;
|
||||
import org.apache.velocity.tools.generic.MathTool;
|
||||
import org.apache.velocity.tools.generic.NumberTool;
|
||||
import org.apache.velocity.tools.generic.ResourceTool;
|
||||
import org.apache.velocity.tools.generic.SortTool;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class VelocityResponseWriter implements QueryResponseWriter {
|
||||
public class VelocityResponseWriter implements QueryResponseWriter, SolrCoreAware {
|
||||
// init param names, these are _only_ loaded at init time (no per-request control of these)
|
||||
// - multiple different named writers could be created with different init params
|
||||
public static final String TEMPLATE_BASE_DIR = "template.base.dir";
|
||||
public static final String PARAMS_RESOURCE_LOADER_ENABLED = "params.resource.loader.enabled";
|
||||
public static final String SOLR_RESOURCE_LOADER_ENABLED = "solr.resource.loader.enabled";
|
||||
public static final String PROPERTIES_FILE = "init.properties.file";
|
||||
|
||||
// TODO: maybe pass this Logger to the template for logging from there?
|
||||
// private static final Logger log = LoggerFactory.getLogger(VelocityResponseWriter.class);
|
||||
// request param names
|
||||
public static final String TEMPLATE = "v.template";
|
||||
public static final String LAYOUT = "v.layout";
|
||||
public static final String LAYOUT_ENABLED = "v.layout.enabled";
|
||||
public static final String CONTENT_TYPE = "v.contentType";
|
||||
public static final String JSON = "v.json";
|
||||
public static final String LOCALE = "v.locale";
|
||||
|
||||
public static final String TEMPLATE_EXTENSION = ".vm";
|
||||
public static final String DEFAULT_CONTENT_TYPE = "text/html;charset=UTF-8";
|
||||
|
||||
private File fileResourceLoaderBaseDir;
|
||||
private boolean paramsResourceLoaderEnabled;
|
||||
private boolean solrResourceLoaderEnabled;
|
||||
private String initPropertiesFileName; // used just to hold from init() to inform()
|
||||
|
||||
private static final Logger log = LoggerFactory.getLogger(VelocityResponseWriter.class);
|
||||
private static final SolrVelocityLogger velocityLogger = new SolrVelocityLogger(log);
|
||||
private Properties velocityInitProps = new Properties();
|
||||
|
||||
@Override
|
||||
public void init(NamedList args) {
|
||||
fileResourceLoaderBaseDir = null;
|
||||
String templateBaseDir = (String) args.get(TEMPLATE_BASE_DIR);
|
||||
|
||||
if (templateBaseDir != null && !templateBaseDir.isEmpty()) {
|
||||
fileResourceLoaderBaseDir = new File(templateBaseDir).getAbsoluteFile();
|
||||
if (!fileResourceLoaderBaseDir.exists()) { // "*not* exists" condition!
|
||||
log.warn(TEMPLATE_BASE_DIR + " specified does not exist: " + fileResourceLoaderBaseDir);
|
||||
fileResourceLoaderBaseDir = null;
|
||||
} else {
|
||||
if (!fileResourceLoaderBaseDir.isDirectory()) { // "*not* a directory" condition
|
||||
log.warn(TEMPLATE_BASE_DIR + " specified is not a directory: " + fileResourceLoaderBaseDir);
|
||||
fileResourceLoaderBaseDir = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// params resource loader: off by default
|
||||
Boolean prle = args.getBooleanArg(PARAMS_RESOURCE_LOADER_ENABLED);
|
||||
paramsResourceLoaderEnabled = (null == prle ? false : prle);
|
||||
|
||||
// solr resource loader: on by default
|
||||
Boolean srle = args.getBooleanArg(SOLR_RESOURCE_LOADER_ENABLED);
|
||||
solrResourceLoaderEnabled = (null == srle ? true : srle);
|
||||
|
||||
initPropertiesFileName = (String) args.get(PROPERTIES_FILE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void inform(SolrCore core) {
|
||||
// need to leverage SolrResourceLoader, so load init.properties.file here instead of init()
|
||||
if (initPropertiesFileName != null) {
|
||||
InputStream is = null;
|
||||
try {
|
||||
velocityInitProps.load(new InputStreamReader(core.getResourceLoader().openResource(initPropertiesFileName), StandardCharsets.UTF_8));
|
||||
} catch (IOException e) {
|
||||
log.warn("Error loading " + PROPERTIES_FILE + " specified property file: " + initPropertiesFileName, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getContentType(SolrQueryRequest request, SolrQueryResponse response) {
|
||||
return request.getParams().get(CONTENT_TYPE, DEFAULT_CONTENT_TYPE);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void write(Writer writer, SolrQueryRequest request, SolrQueryResponse response) throws IOException {
|
||||
VelocityEngine engine = getEngine(request); // TODO: have HTTP headers available for configuring engine
|
||||
VelocityEngine engine = createEngine(request); // TODO: have HTTP headers available for configuring engine
|
||||
|
||||
Template template = getTemplate(engine, request);
|
||||
|
||||
VelocityContext context = createContext(request, response);
|
||||
context.put("engine", engine); // for $engine.resourceExists(...)
|
||||
|
||||
String layoutTemplate = request.getParams().get(LAYOUT);
|
||||
boolean layoutEnabled = request.getParams().getBool(LAYOUT_ENABLED, true) && layoutTemplate != null;
|
||||
|
||||
String jsonWrapper = request.getParams().get(JSON);
|
||||
boolean wrapResponse = layoutEnabled || jsonWrapper != null;
|
||||
|
||||
// create output
|
||||
if (!wrapResponse) {
|
||||
// straight-forward template/context merge to output
|
||||
template.merge(context, writer);
|
||||
}
|
||||
else {
|
||||
// merge to a string buffer, then wrap with layout and finally as JSON
|
||||
StringWriter stringWriter = new StringWriter();
|
||||
template.merge(context, stringWriter);
|
||||
|
||||
if (layoutEnabled) {
|
||||
context.put("content", stringWriter.toString());
|
||||
stringWriter = new StringWriter();
|
||||
try {
|
||||
engine.getTemplate(layoutTemplate + TEMPLATE_EXTENSION).merge(context, stringWriter);
|
||||
} catch (Exception e) {
|
||||
throw new IOException(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
if (jsonWrapper != null) {
|
||||
writer.write(jsonWrapper + "(");
|
||||
writer.write(getJSONWrap(stringWriter.toString()));
|
||||
writer.write(')');
|
||||
} else { // using a layout, but not JSON wrapping
|
||||
writer.write(stringWriter.toString());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private VelocityContext createContext(SolrQueryRequest request, SolrQueryResponse response) {
|
||||
VelocityContext context = new VelocityContext();
|
||||
|
||||
context.put("request", request);
|
||||
|
||||
// Register useful Velocity "tools"
|
||||
context.put("esc", new EscapeTool());
|
||||
context.put("date", new ComparisonDateTool());
|
||||
context.put("list", new ListTool());
|
||||
context.put("math", new MathTool());
|
||||
context.put("number", new NumberTool());
|
||||
context.put("sort", new SortTool());
|
||||
context.put("display", new DisplayTool());
|
||||
context.put("resource", new SolrVelocityResourceTool(
|
||||
request.getCore().getSolrConfig().getResourceLoader().getClassLoader(),
|
||||
request.getParams().get(LOCALE)));
|
||||
|
||||
// Turn the SolrQueryResponse into a SolrResponse.
|
||||
// QueryResponse has lots of conveniences suitable for a view
|
||||
// Problem is, which SolrResponse class to use?
|
||||
|
@ -65,14 +200,15 @@ public class VelocityResponseWriter implements QueryResponseWriter {
|
|||
// create a new instance. But for now the implementation simply
|
||||
// uses QueryResponse, and if it chokes in a known way, fall back
|
||||
// to bare bones SolrResponseBase.
|
||||
// TODO: Can this writer know what the handler class is? With echoHandler=true it can get its string name at least
|
||||
// Can this writer know what the handler class is? With echoHandler=true it can get its string name at least
|
||||
SolrResponse rsp = new QueryResponse();
|
||||
NamedList<Object> parsedResponse = BinaryResponseWriter.getParsedResponse(request, response);
|
||||
try {
|
||||
rsp.setResponse(parsedResponse);
|
||||
|
||||
// page only injected if QueryResponse works
|
||||
context.put("page", new PageTool(request, response)); // page tool only makes sense for a SearchHandler request... *sigh*
|
||||
context.put("page", new PageTool(request, response)); // page tool only makes sense for a SearchHandler request
|
||||
context.put("debug",((QueryResponse)rsp).getDebugMap());
|
||||
} catch (ClassCastException e) {
|
||||
// known edge case where QueryResponse's extraction assumes "response" is a SolrDocumentList
|
||||
// (AnalysisRequestHandler emits a "response")
|
||||
|
@ -81,81 +217,69 @@ public class VelocityResponseWriter implements QueryResponseWriter {
|
|||
}
|
||||
context.put("response", rsp);
|
||||
|
||||
// Velocity context tools - TODO: make these pluggable
|
||||
context.put("esc", new EscapeTool());
|
||||
context.put("date", new ComparisonDateTool());
|
||||
context.put("list", new ListTool());
|
||||
context.put("math", new MathTool());
|
||||
context.put("number", new NumberTool());
|
||||
context.put("sort", new SortTool());
|
||||
context.put("display", new DisplayTool());
|
||||
|
||||
context.put("engine", engine); // for $engine.resourceExists(...)
|
||||
|
||||
String layout_template = request.getParams().get("v.layout");
|
||||
String json_wrapper = request.getParams().get("v.json");
|
||||
boolean wrap_response = (layout_template != null) || (json_wrapper != null);
|
||||
|
||||
// create output, optionally wrap it into a json object
|
||||
if (wrap_response) {
|
||||
StringWriter stringWriter = new StringWriter();
|
||||
template.merge(context, stringWriter);
|
||||
|
||||
if (layout_template != null) {
|
||||
context.put("content", stringWriter.toString());
|
||||
stringWriter = new StringWriter();
|
||||
try {
|
||||
engine.getTemplate(layout_template + ".vm").merge(context, stringWriter);
|
||||
} catch (Exception e) {
|
||||
throw new IOException(e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
if (json_wrapper != null) {
|
||||
writer.write(request.getParams().get("v.json") + "(");
|
||||
writer.write(getJSONWrap(stringWriter.toString()));
|
||||
writer.write(')');
|
||||
} else { // using a layout, but not JSON wrapping
|
||||
writer.write(stringWriter.toString());
|
||||
}
|
||||
} else {
|
||||
template.merge(context, writer);
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
||||
private VelocityEngine getEngine(SolrQueryRequest request) {
|
||||
private VelocityEngine createEngine(SolrQueryRequest request) {
|
||||
VelocityEngine engine = new VelocityEngine();
|
||||
|
||||
engine.setProperty("params.resource.loader.instance", new SolrParamResourceLoader(request));
|
||||
SolrVelocityResourceLoader resourceLoader =
|
||||
new SolrVelocityResourceLoader(request.getCore().getSolrConfig().getResourceLoader());
|
||||
engine.setProperty("solr.resource.loader.instance", resourceLoader);
|
||||
engine.setProperty(RuntimeConstants.RESOURCE_LOADER, "params,solr");
|
||||
// route all Velocity logging through Solr's logging facility
|
||||
engine.setProperty(RuntimeConstants.RUNTIME_LOG_LOGSYSTEM, velocityLogger);
|
||||
|
||||
// TODO: Externalize Velocity properties
|
||||
String propFile = request.getParams().get("v.properties");
|
||||
try {
|
||||
Properties props = new Properties();
|
||||
// Don't create a separate velocity log file by default.
|
||||
props.put(RuntimeConstants.RUNTIME_LOG, "");
|
||||
// Set some engine properties that improve the experience
|
||||
// - these could be considered in the future for parameterization, but can also be overridden by using
|
||||
// the init.properties.file setting. (TODO: add a test for this)
|
||||
|
||||
if (propFile == null) {
|
||||
engine.init(props);
|
||||
} else {
|
||||
InputStream is = null;
|
||||
try {
|
||||
is = resourceLoader.getResourceStream(propFile);
|
||||
props.load(new InputStreamReader(is, StandardCharsets.UTF_8));
|
||||
engine.init(props);
|
||||
}
|
||||
finally {
|
||||
if (is != null) is.close();
|
||||
}
|
||||
}
|
||||
// load the built-in _macros.vm first, then load VM_global_library.vm for legacy (pre-5.0) support,
|
||||
// and finally allow macros.vm to have the final say and override anything defined in the preceding files.
|
||||
engine.setProperty(RuntimeConstants.VM_LIBRARY, "_macros.vm,VM_global_library.vm,macros.vm");
|
||||
|
||||
// Standard templates autoload, but not the macro one(s), by default, so let's just make life
|
||||
// easier, and consistent, for macro development too.
|
||||
engine.setProperty(RuntimeConstants.VM_LIBRARY_AUTORELOAD, "true");
|
||||
|
||||
/*
|
||||
Set up Velocity resource loader(s)
|
||||
terminology note: "resource loader" is overloaded here, there is Solr's resource loader facility for plugins,
|
||||
and there are Velocity template resource loaders. It's confusing, they overlap: there is a Velocity resource
|
||||
loader that loads templates from Solr's resource loader (SolrVelocityResourceLoader).
|
||||
|
||||
The Velocity resource loader order is [params,][file,][solr], intentionally ordered in this manner, and each
|
||||
one optional and individually enable-able. By default, only "solr" (resource loader) is used, parsing templates
|
||||
from a velocity/ sub-tree in either the classpath or under conf/.
|
||||
|
||||
A common usage would be to enable the file template loader, keeping the solr loader enabled; the Velocity resource
|
||||
loader path would then be "file,solr" (params is disabled by default). The basic browse templates are built into
|
||||
this plugin, but can be individually overridden by placing a same-named template in the template.base.dir specified
|
||||
directory.
|
||||
*/
|
||||
ArrayList<String> loaders = new ArrayList<String>();
|
||||
if (paramsResourceLoaderEnabled) {
|
||||
loaders.add("params");
|
||||
engine.setProperty("params.resource.loader.instance", new SolrParamResourceLoader(request));
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
if (fileResourceLoaderBaseDir != null) {
|
||||
loaders.add("file");
|
||||
engine.setProperty(RuntimeConstants.FILE_RESOURCE_LOADER_PATH, fileResourceLoaderBaseDir.getAbsolutePath());
|
||||
}
|
||||
if (solrResourceLoaderEnabled) {
|
||||
// The solr resource loader serves templates under a velocity/ subtree from <lib>, conf/,
|
||||
// or SolrCloud's configuration tree. Or rather the other way around, other resource loaders are rooted
|
||||
// from the top, whereas this is velocity/ sub-tree rooted.
|
||||
loaders.add("solr");
|
||||
engine.setProperty("solr.resource.loader.instance", new SolrVelocityResourceLoader(request.getCore().getSolrConfig().getResourceLoader()));
|
||||
}
|
||||
|
||||
// Always have the built-in classpath loader. This is needed when using VM_LIBRARY macros, as they are required
|
||||
// to be present if specified, and we want to have a nice macros facility built-in for users to use easily, and to
|
||||
// extend in custom ways.
|
||||
loaders.add("builtin");
|
||||
engine.setProperty("builtin.resource.loader.instance", new ClasspathResourceLoader());
|
||||
|
||||
engine.setProperty(RuntimeConstants.RESOURCE_LOADER, StringUtils.join(",", loaders));
|
||||
|
||||
// bring in any custom properties too
|
||||
engine.init(velocityInitProps);
|
||||
|
||||
return engine;
|
||||
}
|
||||
|
@ -163,18 +287,19 @@ public class VelocityResponseWriter implements QueryResponseWriter {
|
|||
private Template getTemplate(VelocityEngine engine, SolrQueryRequest request) throws IOException {
|
||||
Template template;
|
||||
|
||||
String template_name = request.getParams().get("v.template");
|
||||
String qt = request.getParams().get("qt");
|
||||
String templateName = request.getParams().get(TEMPLATE);
|
||||
|
||||
String qt = request.getParams().get(CommonParams.QT);
|
||||
String path = (String) request.getContext().get("path");
|
||||
if (template_name == null && path != null) {
|
||||
template_name = path;
|
||||
if (templateName == null && path != null) {
|
||||
templateName = path;
|
||||
} // TODO: path is never null, so qt won't get picked up maybe special case for '/select' to use qt, otherwise use path?
|
||||
if (template_name == null && qt != null) {
|
||||
template_name = qt;
|
||||
if (templateName == null && qt != null) {
|
||||
templateName = qt;
|
||||
}
|
||||
if (template_name == null) template_name = "index";
|
||||
if (templateName == null) templateName = "index";
|
||||
try {
|
||||
template = engine.getTemplate(template_name + ".vm");
|
||||
template = engine.getTemplate(templateName + TEMPLATE_EXTENSION);
|
||||
} catch (Exception e) {
|
||||
throw new IOException(e.getMessage());
|
||||
}
|
||||
|
@ -182,12 +307,7 @@ public class VelocityResponseWriter implements QueryResponseWriter {
|
|||
return template;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getContentType(SolrQueryRequest request, SolrQueryResponse response) {
|
||||
return request.getParams().get("v.contentType", "text/html;charset=UTF-8");
|
||||
}
|
||||
|
||||
private String getJSONWrap(String xmlResult) { // TODO: maybe noggit or Solr's JSON utilities can make this cleaner?
|
||||
private String getJSONWrap(String xmlResult) { // maybe noggit or Solr's JSON utilities can make this cleaner?
|
||||
// escape the double quotes and backslashes
|
||||
String replace1 = xmlResult.replaceAll("\\\\", "\\\\\\\\");
|
||||
replace1 = replace1.replaceAll("\\n", "\\\\n");
|
||||
|
@ -197,7 +317,34 @@ public class VelocityResponseWriter implements QueryResponseWriter {
|
|||
return "{\"result\":\"" + replaced + "\"}";
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(NamedList args) {
|
||||
// see: http://svn.apache.org/repos/asf/velocity/tools/branches/2.0.x/src/main/java/org/apache/velocity/tools/generic/ResourceTool.java
|
||||
private class SolrVelocityResourceTool extends ResourceTool {
|
||||
|
||||
private final Locale locale;
|
||||
private ClassLoader solrClassLoader;
|
||||
|
||||
public SolrVelocityResourceTool(ClassLoader cl, String localeString) {
|
||||
this.solrClassLoader = cl;
|
||||
Locale l = toLocale(localeString);
|
||||
this.locale = (l == null ? Locale.ROOT : l);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected ResourceBundle getBundle(String baseName, Object loc) {
|
||||
// resource bundles for this tool must be in velocity "package"
|
||||
return ResourceBundle.getBundle("velocity." + baseName, locale, solrClassLoader);
|
||||
}
|
||||
|
||||
// Why did Velocity Tools make this private? Copied from ResourceTools.java
|
||||
private Locale toLocale(Object obj) {
|
||||
if (obj == null) {
|
||||
return null;
|
||||
}
|
||||
if (obj instanceof Locale) {
|
||||
return (Locale) obj;
|
||||
}
|
||||
String s = String.valueOf(obj);
|
||||
return ConversionUtils.toLocale(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
## legacy support, the writer will load this as a macro library as it used to
|
||||
## but best not to have your own file called VM_global_library.vm; put them in macros.vm instead.
|
||||
## This file is needed for the "builtin" resource loader as Velocity requires all macro library files exist, but
|
||||
## we don't want users to have to have a macro library file in their template directories.
|
|
@ -0,0 +1,32 @@
|
|||
#macro(param $key)$request.params.get($key)#end
|
||||
|
||||
#macro(url_root)/solr#end
|
||||
|
||||
#macro(core_name)$request.core.name#end
|
||||
#macro(url_for_solr)#{url_root}#if($request.core.name != "")/$request.core.name#end#end
|
||||
#macro(url_for_home)#url_for_solr$request.context.path#end
|
||||
|
||||
#macro(q)&q=$!{esc.url($request.params.get('q'))}#end
|
||||
|
||||
#macro(fqs $p)#foreach($fq in $p)#if($velocityCount>1)&#{end}fq=$esc.url($fq)#end#end
|
||||
|
||||
#macro(debug)#if($debug)&debug=true#end#end
|
||||
|
||||
#macro(sort $p)#if($p)#foreach($s in $p)&sort=$esc.url($s)#end#end#end
|
||||
|
||||
#macro(lensNoQ)?#if($request.params.getParams('fq') and $list.size($request.params.getParams('fq')) > 0)&#fqs($request.params.getParams('fq'))#end#sort($request.params.getParams('sort'))#debug#end
|
||||
#macro(lens)#lensNoQ#q#end
|
||||
|
||||
#macro(url_for_lens)#{url_for_home}#lens#end
|
||||
|
||||
#macro(url_for_start $start)#url_for_home#lens&start=$start#end
|
||||
|
||||
#macro(url_for_filters $p)#url_for_home?#q#if($list.size($p) > 0)&#fqs($p)#end#debug#end
|
||||
|
||||
#macro(url_for_nested_facet_query $field)#url_for_home#lens&fq=$esc.url($field)#end
|
||||
|
||||
#macro(url_for_facet_filter $field $value)#url_for_home#lens&fq=#if($value!=$null)$esc.url($field):%22$esc.url($value)%22#else-$esc.url($field):[*+TO+*]#end#end
|
||||
|
||||
#macro(url_for_facet_date_filter $field $value)#url_for_home#lens&fq=$esc.url($field):$esc.url($value)#end
|
||||
|
||||
#macro(url_for_facet_range_filter $field $value)#url_for_home#lens&fq=$esc.url($field):$esc.url($value)#end
|
|
@ -0,0 +1,3 @@
|
|||
## placeholder for users to specify their own macros
|
||||
## This file is needed for the "builtin" resource loader as Velocity requires all macro library files exist.
|
||||
## This is the file we want users to override to add their own macros.
|
|
@ -1,82 +0,0 @@
|
|||
#**
|
||||
* Global macros used by other templates.
|
||||
* This file must be named VM_global_library.vm
|
||||
* in order for Velocity to find it.
|
||||
*#
|
||||
|
||||
#macro(param $key)$request.params.get($key)#end
|
||||
|
||||
#macro(url_root)/solr#end
|
||||
|
||||
## TODO: s/url_for_solr/url_for_core/ and s/url_root/url_for_solr/
|
||||
#macro(core_name)$request.core.name#end
|
||||
#macro(url_for_solr)#{url_root}#if($request.core.name != "")/$request.core.name#end#end
|
||||
#macro(url_for_home)#url_for_solr/browse#end ## TODO: What request handler path to use for _home?
|
||||
|
||||
#macro(q)&q=$!{esc.url($params.get('q'))}#end
|
||||
|
||||
#macro(fqs $p)#foreach($fq in $p)#if($velocityCount>1)&#{end}fq=$esc.url($fq)#end#end
|
||||
|
||||
#macro(debug)#if($request.params.get('debugQuery'))&debugQuery=true#end#end ## TODO: leverage actual debug mode setting, or at least also support debug=all/query/etc
|
||||
|
||||
#macro(sort $p)#if($p)#foreach($s in $p)&sort=$esc.url($s)#end#end#end
|
||||
|
||||
#macro(lensNoQ)?#if($request.params.getParams('fq') and $list.size($request.params.getParams('fq')) > 0)&#fqs($request.params.getParams('fq'))#end#sort($request.params.getParams('sort'))#debug#end
|
||||
#macro(lens)#lensNoQ#q#end
|
||||
|
||||
#macro(url_for_lens)#{url_for_home}#lens#end
|
||||
|
||||
#macro(url_for_start $start)#url_for_home#lens&start=$start#end
|
||||
|
||||
#macro(url_for_filters $p)#url_for_home?#q#if($list.size($p) > 0)&#fqs($p)#end#debug#end
|
||||
|
||||
#macro(url_for_nested_facet_query $field)#url_for_home#lens&fq=$esc.url($field)#end
|
||||
|
||||
## TODO: convert to use {!raw f=$field}$value (with escaping of course)
|
||||
#macro(url_for_facet_filter $field $value)#url_for_home#lens&fq=#if($value!=$null)$esc.url($field):%22$esc.url($value)%22#else-$esc.url($field):[*+TO+*]#end#end
|
||||
|
||||
#macro(url_for_facet_date_filter $field $value)#url_for_home#lens&fq=$esc.url($field):$esc.url($value)#end
|
||||
|
||||
#macro(url_for_facet_range_filter $field $value)#url_for_home#lens&fq=$esc.url($field):$esc.url($value)#end
|
||||
|
||||
|
||||
#macro(link_to_previous_page $text)
|
||||
#if($page.current_page_number > 1)
|
||||
#set($prev_start = $page.start - $page.results_per_page)
|
||||
<a class="prev-page" href="#url_for_start($prev_start)">$text</a>
|
||||
#end
|
||||
#end
|
||||
|
||||
#macro(link_to_next_page $text)
|
||||
#if($page.current_page_number < $page.page_count)
|
||||
#set($next_start = $page.start + $page.results_per_page)
|
||||
<a class="next-page" href="#url_for_start($next_start)">$text</a>
|
||||
#end
|
||||
#end
|
||||
|
||||
#macro(link_to_page $page_number $text)
|
||||
#if($page_number == $page.current_page_number)
|
||||
$text
|
||||
#else
|
||||
#if($page_number <= $page.page_count)
|
||||
#set($page_start = $page_number * $page.results_per_page - $page.results_per_page)
|
||||
<a class="page" href="#url_for_start($page_start)">$text</a>
|
||||
#end
|
||||
#end
|
||||
#end
|
||||
|
||||
|
||||
#macro(field $f)
|
||||
#if($response.response.highlighting.get($docId).get($f).get(0))
|
||||
#set($pad = "")
|
||||
#foreach($v in $response.response.highlighting.get($docId).get($f))
|
||||
$pad$v##
|
||||
#set($pad = " ... ")
|
||||
#end
|
||||
#else
|
||||
#foreach($v in $doc.getFieldValues($f))
|
||||
$v##
|
||||
#end
|
||||
#end
|
||||
#end
|
||||
|
|
@ -1,5 +1,3 @@
|
|||
#set($params = $request.params)
|
||||
|
||||
## Show Error Message, if any
|
||||
<div class="error">
|
||||
#parse("error.vm")
|
||||
|
@ -7,11 +5,11 @@
|
|||
|
||||
<div class="query-box">
|
||||
<form id="query-form" action="#{url_for_home}" method="GET">
|
||||
Find:
|
||||
<input type="text" id="q" name="q" value="$!esc.html($params.get('q'))"/>
|
||||
$resource.find:
|
||||
<input type="text" id="q" name="q" value="$!esc.html($request.params.get('q'))"/>
|
||||
<input type="submit"/>
|
||||
|
||||
#if($request.params.get('debugQuery'))
|
||||
#if($debug) ## TODO: this would automatically happen when arbitrary parameters are kept on URLs
|
||||
<input type="hidden" name="debug" value="true"/>
|
||||
#end
|
||||
#foreach($fq in $request.params.getParams('fq'))
|
||||
|
@ -19,7 +17,7 @@
|
|||
#end
|
||||
|
||||
<div class="constraints">
|
||||
#foreach($fq in $params.getParams('fq'))
|
||||
#foreach($fq in $request.params.getParams('fq'))
|
||||
#set($previous_fq_count=$velocityCount - 1)
|
||||
#if($fq != '')
|
||||
>
|
||||
|
@ -29,7 +27,7 @@
|
|||
</div>
|
||||
|
||||
<div class="parsed_query_header">
|
||||
#if($request.params.get('debugQuery'))
|
||||
#if($debug)
|
||||
<a href="#" onclick='jQuery(this).siblings("div").toggle(); return false;'>toggle parsed query</a>
|
||||
<div class="parsed_query" style="display:none">$response.response.debug.parsedquery</div>
|
||||
#end
|
||||
|
@ -46,11 +44,10 @@
|
|||
<span>
|
||||
<span class="results-found">$page.results_found</span>
|
||||
results found in
|
||||
${response.responseHeader.QTime} ms
|
||||
${response.responseHeader.QTime}ms
|
||||
</span>
|
||||
|
||||
Page <span class="page-num">$page.current_page_number</span>
|
||||
of <span class="page-count">$page.page_count</span>
|
||||
$resource.page_of.insert($page.current_page_number,$page.page_count)
|
||||
</div>
|
||||
|
||||
## Render Results, actual matching docs
|
||||
|
@ -59,13 +56,12 @@
|
|||
</div>
|
||||
|
||||
<div class="pagination">
|
||||
#link_to_previous_page("previous")
|
||||
#link_to_previous_page
|
||||
|
||||
<span class="results-found">$page.results_found</span>
|
||||
results found.
|
||||
|
||||
Page <span class="page-num">$page.current_page_number</span>
|
||||
of <span class="page-count">$page.page_count</span>
|
||||
$resource.page_of.insert($page.current_page_number,$page.page_count)
|
||||
|
||||
#link_to_next_page("next")
|
||||
#link_to_next_page
|
||||
</div>
|
||||
|
|
|
@ -1,26 +0,0 @@
|
|||
#**
|
||||
* Show Debugging Information, if enabled
|
||||
*#
|
||||
|
||||
#if( $params.getBool("debugQuery",false) )
|
||||
<a href="#" onclick='jQuery(this).siblings("pre").toggle(); return false;'>toggle explain</a>
|
||||
|
||||
<pre style="display:none">
|
||||
$response.getExplainMap().get($doc.getFirstValue('id'))
|
||||
</pre>
|
||||
|
||||
<a href="#" onclick='jQuery(this).siblings("pre2").toggle(); return false;'>toggle all fields</a>
|
||||
|
||||
<pre2 style="display:none">
|
||||
#foreach($fieldname in $doc.fieldNames)
|
||||
<br>
|
||||
<span class="field-name">$fieldname :</span>
|
||||
<span>
|
||||
#foreach($value in $doc.getFieldValues($fieldname))
|
||||
$esc.html($value)
|
||||
#end
|
||||
</span>
|
||||
</br>
|
||||
#end
|
||||
</pre2>
|
||||
#end
|
|
@ -1,10 +1,3 @@
|
|||
#**
|
||||
* Show Error Message, if any
|
||||
*#
|
||||
|
||||
## Show Error Message, if any
|
||||
## Usually rendered inside div class=error
|
||||
|
||||
#if( $response.response.error.code )
|
||||
<h1>ERROR $response.response.error.code</h1>
|
||||
$response.response.error.msg
|
||||
|
|
|
@ -2,14 +2,14 @@
|
|||
<div>
|
||||
<span>Options:</span>
|
||||
|
||||
#if($request.params.get('debugQuery'))
|
||||
#if($debug)
|
||||
<a href="#url_for_home?#q#if($list.size($request.params.getParams('fq')) > 0)&#fqs($request.params.getParams('fq'))#end">
|
||||
disable debug</a>
|
||||
#else
|
||||
<a href="#url_for_lens&debug=true&fl=*,score">enable debug</a>
|
||||
#end
|
||||
-
|
||||
<a href="#url_for_lens&wt=xml#if($request.params.get('debugQuery'))&debug=true#end">XML results</a>
|
||||
<a href="#url_for_lens&wt=xml#if($debug)&debug=true#end">XML results</a> ## TODO: Add links for other formats, maybe dynamically?
|
||||
|
||||
</div>
|
||||
|
||||
|
|
|
@ -3,9 +3,12 @@
|
|||
*#
|
||||
|
||||
<title>Solr browse: #core_name</title>
|
||||
|
||||
<meta http-equiv="content-type" content="text/html; charset=UTF-8"/>
|
||||
|
||||
<link rel="icon" type="image/x-icon" href="#{url_root}/img/favicon.ico"/>
|
||||
<link rel="shortcut icon" type="image/x-icon" href="#{url_root}/img/favicon.ico"/>
|
||||
|
||||
<script type="text/javascript" src="#{url_root}/js/lib/jquery-1.7.2.min.js"></script>
|
||||
|
||||
<style>
|
||||
|
@ -17,17 +20,6 @@
|
|||
#head{
|
||||
width: 100%;
|
||||
}
|
||||
.array-field {
|
||||
border: 2px solid #474747;
|
||||
background: #FFE9D8;
|
||||
padding: 5px;
|
||||
margin: 5px;
|
||||
}
|
||||
|
||||
.array-field-list li {
|
||||
list-style: circle;
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
||||
.parsed_query_header {
|
||||
font-family: Helvetica, Arial, sans-serif;
|
||||
|
@ -47,7 +39,11 @@
|
|||
}
|
||||
|
||||
a {
|
||||
color: #43a4b1;
|
||||
color: #305CB3;
|
||||
}
|
||||
|
||||
em {
|
||||
color: #FF833D;
|
||||
}
|
||||
|
||||
.facets {
|
||||
|
@ -61,7 +57,7 @@
|
|||
}
|
||||
|
||||
.facets h2 {
|
||||
background: #EA897E;
|
||||
background: #D9411E;
|
||||
padding: 2px 5px;
|
||||
}
|
||||
|
||||
|
@ -82,18 +78,9 @@
|
|||
font-weight: bold;
|
||||
}
|
||||
|
||||
.highlight {
|
||||
color: white;
|
||||
background-color: gray;
|
||||
border: 1px black solid;
|
||||
}
|
||||
|
||||
.highlight-box {
|
||||
margin-left: 15px;
|
||||
}
|
||||
|
||||
.field-name {
|
||||
font-weight: bold;
|
||||
// align="right" valign="top"
|
||||
}
|
||||
|
||||
.highlighted-facet-field {
|
||||
|
|
|
@ -1,24 +1,27 @@
|
|||
#set($docId = $doc.getFieldValue('id'))
|
||||
#set($docId = $doc.getFirstValue($request.schema.uniqueKeyField.name))
|
||||
|
||||
|
||||
<div class="result-document">
|
||||
|
||||
<table>
|
||||
#foreach( $fieldName in $doc.fieldNames )
|
||||
#foreach( $value in $doc.getFieldValues($fieldName) )
|
||||
<tr>
|
||||
<th align="right" valign="top">
|
||||
#if( $foreach.count == 1 )
|
||||
$fieldName:
|
||||
#end
|
||||
<th align="right" valign="top" style="field-name">
|
||||
$esc.html($fieldName):
|
||||
</th>
|
||||
|
||||
<td align="left" valign="top">
|
||||
$esc.html($value) <br/>
|
||||
#field($fieldName)
|
||||
</td>
|
||||
</tr>
|
||||
#end
|
||||
#end
|
||||
</table>
|
||||
|
||||
#if($debug)
|
||||
<a href="#" onclick='jQuery(this).siblings("pre").toggle(); return false;'>toggle explain</a>
|
||||
|
||||
<pre style="display:none">
|
||||
$response.getExplainMap().get($docId)
|
||||
</pre>
|
||||
#end
|
||||
</div>
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
<body>
|
||||
<div id="admin"><a href="#url_root/#/#core_name">Solr Admin</a></div>
|
||||
<div id="head">
|
||||
<a href="#url_for_home#if($request.params.get('debugQuery'))?debugQuery=true#end"><img src="#{url_root}/img/solr.svg" id="logo"/></a>
|
||||
<a href="#url_for_home#if($debug)?debug=true#end"><img src="#{url_root}/img/solr.svg" id="logo"/></a>
|
||||
</div>
|
||||
|
||||
<div id="content">
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
# TODO: make this parameterized fully, no context sensitivity
|
||||
#macro(field $f)
|
||||
#if($response.response.highlighting.get($docId).get($f).get(0))
|
||||
#set($pad = "")
|
||||
#foreach($v in $response.response.highlighting.get($docId).get($f))
|
||||
$pad$v## #TODO: $esc.html() or maybe make that optional?
|
||||
#set($pad = " ... ")
|
||||
#end
|
||||
#else
|
||||
#foreach($v in $doc.getFieldValues($f))$esc.html($v)
|
||||
#end
|
||||
#end
|
||||
#end
|
||||
|
||||
#macro(link_to_previous_page)
|
||||
#if($page.current_page_number > 1)
|
||||
#set($prev_start = $page.start - $page.results_per_page)
|
||||
<a class="prev-page" href="#url_for_start($prev_start)">$resource.previous</a>
|
||||
#end
|
||||
#end
|
||||
|
||||
#macro(link_to_next_page)
|
||||
#if($page.current_page_number < $page.page_count)
|
||||
#set($next_start = $page.start + $page.results_per_page)
|
||||
<a class="next-page" href="#url_for_start($next_start)">$resource.next</a>
|
||||
#end
|
||||
#end
|
||||
|
||||
#macro(link_to_page $page_number $text)
|
||||
#if($page_number == $page.current_page_number)
|
||||
$text
|
||||
#else
|
||||
#if($page_number <= $page.page_count)
|
||||
#set($page_start = $page_number * $page.results_per_page - $page.results_per_page)
|
||||
<a class="page" href="#url_for_start($page_start)">$text</a>
|
||||
#end
|
||||
#end
|
||||
#end
|
|
@ -0,0 +1,6 @@
|
|||
find=Find
|
||||
page_of=Page <span class="page-num">{0}</span> of <span class="page-count">{1}</span>
|
||||
previous=previous
|
||||
next=next
|
||||
|
||||
|
|
@ -0,0 +1 @@
|
|||
testing
|
|
@ -16,66 +16,13 @@
|
|||
limitations under the License.
|
||||
-->
|
||||
|
||||
<!--
|
||||
For more details about configurations options that may appear in
|
||||
this file, see http://wiki.apache.org/solr/SolrConfigXml.
|
||||
-->
|
||||
<config>
|
||||
<luceneMatchVersion>${tests.luceneMatchVersion:LATEST}</luceneMatchVersion>
|
||||
<indexConfig>
|
||||
<useCompoundFile>${useCompoundFile:false}</useCompoundFile>
|
||||
</indexConfig>
|
||||
|
||||
<lib dir="../../contrib/velocity/lib" />
|
||||
<lib dir="../../dist/" regex="solr-velocity-\d.*\.jar" />
|
||||
<dataDir>${solr.data.dir:}</dataDir>
|
||||
<!--<lib dir="../../contrib/velocity/lib" />-->
|
||||
<!--<lib dir="../../dist/" regex="solr-velocity-\d.*\.jar" />-->
|
||||
|
||||
|
||||
<directoryFactory name="DirectoryFactory"
|
||||
class="${solr.directoryFactory:solr.StandardDirectoryFactory}"/>
|
||||
|
||||
<updateHandler class="solr.DirectUpdateHandler2">
|
||||
</updateHandler>
|
||||
|
||||
<query>
|
||||
<maxBooleanClauses>1024</maxBooleanClauses>
|
||||
<filterCache class="solr.FastLRUCache"
|
||||
size="512"
|
||||
initialSize="512"
|
||||
autowarmCount="0"/>
|
||||
<queryResultCache class="solr.LRUCache"
|
||||
size="512"
|
||||
initialSize="512"
|
||||
autowarmCount="0"/>
|
||||
<documentCache class="solr.LRUCache"
|
||||
size="512"
|
||||
initialSize="512"
|
||||
autowarmCount="0"/>
|
||||
<enableLazyFieldLoading>true</enableLazyFieldLoading>
|
||||
|
||||
<queryResultWindowSize>20</queryResultWindowSize>
|
||||
<queryResultMaxDocsCached>200</queryResultMaxDocsCached>
|
||||
<listener event="newSearcher" class="solr.QuerySenderListener">
|
||||
<arr name="queries">
|
||||
</arr>
|
||||
</listener>
|
||||
<listener event="firstSearcher" class="solr.QuerySenderListener">
|
||||
<arr name="queries">
|
||||
<lst>
|
||||
<str name="q">static firstSearcher warming in solrconfig.xml</str>
|
||||
</lst>
|
||||
</arr>
|
||||
</listener>
|
||||
<useColdSearcher>false</useColdSearcher>
|
||||
<maxWarmingSearchers>2</maxWarmingSearchers>
|
||||
|
||||
</query>
|
||||
<requestDispatcher handleSelect="true" >
|
||||
<requestParsers enableRemoteStreaming="true"
|
||||
multipartUploadLimitInKB="2048000" />
|
||||
<httpCaching never304="true" />
|
||||
</requestDispatcher>
|
||||
|
||||
<requestHandler name="search" class="solr.SearchHandler" default="true">
|
||||
<lst name="defaults">
|
||||
<str name="echoParams">explicit</str>
|
||||
|
@ -83,13 +30,9 @@
|
|||
</lst>
|
||||
</requestHandler>
|
||||
|
||||
<requestHandler name="/update" class="solr.UpdateRequestHandler" />
|
||||
|
||||
<queryResponseWriter name="velocity" class="solr.VelocityResponseWriter"/>
|
||||
|
||||
<!-- Legacy config for the admin interface -->
|
||||
<admin>
|
||||
<defaultQuery>*:*</defaultQuery>
|
||||
</admin>
|
||||
|
||||
<queryResponseWriter name="velocityWithInitProps" class="solr.VelocityResponseWriter">
|
||||
<str name="init.properties.file">velocity-init.properties</str>
|
||||
</queryResponseWriter>
|
||||
</config>
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
foreach.provide.scope.control=false
|
|
@ -1,50 +1,3 @@
|
|||
#macro(legacy_macro)legacy_macro_SUCCESS#end
|
||||
|
||||
|
||||
|
||||
#macro(nl2ul $named_list)
|
||||
<ul>
|
||||
#foreach($kv in $named_list)
|
||||
<li>$kv.key ($kv.value)
|
||||
#nl2ul($kv.value)
|
||||
</li>
|
||||
#end
|
||||
</ul>
|
||||
#end
|
||||
|
||||
#macro(url_for_filters $filter_queries)
|
||||
/solr/itas?fq=$filter_queries
|
||||
#end
|
||||
|
||||
#macro(url_for_home)/solr/itas#end
|
||||
|
||||
#macro(url_for_start $start)/solr/itas?start=$start&q=$!{esc.url($params.get('q'))}#end
|
||||
|
||||
## TODO: need to add escaping
|
||||
#macro(url_for_nested_facet $field $value)/solr/itas?fq=$field:%22$value%22#end
|
||||
|
||||
#macro(link_to_previous_page $text)
|
||||
#if($page.current_page_number > 1)
|
||||
#set($prev_start = $page.start - $page.results_per_page)
|
||||
<a class="prev-page" href="#url_for_start($prev_start)">$text</a>
|
||||
#end
|
||||
#end
|
||||
|
||||
#macro(link_to_next_page $text)
|
||||
#if($page.current_page_number < $page.page_count)
|
||||
#set($next_start = $page.start + $page.results_per_page)
|
||||
<a class="next-page" href="#url_for_start($next_start)">$text</a>
|
||||
#end
|
||||
#end
|
||||
|
||||
#macro(link_to_page $page_number $text)
|
||||
#if($page_number == $page.current_page_number)
|
||||
$text
|
||||
#else
|
||||
#if($page_number <= $page.page_count)
|
||||
#set($page_start = $page_number * $page.results_per_page - $page.results_per_page)
|
||||
<a class="page" href="#url_for_start($page_start)">$text</a>
|
||||
#end
|
||||
#end
|
||||
#end
|
||||
|
||||
#macro(param $key)$request.params.get($key)#end
|
||||
#macro(url_root)Loaded from: VM_global_library.vm#end
|
|
@ -0,0 +1 @@
|
|||
#foreach($x in ["a","b"])$!foreach.index#end
|
|
@ -0,0 +1 @@
|
|||
{{{$content}}}
|
|
@ -0,0 +1 @@
|
|||
$resource.color
|
|
@ -0,0 +1,3 @@
|
|||
#macro(test_macro)test_macro_SUCCESS#end
|
||||
|
||||
#macro(url_root)Loaded from: macros.vm#end
|
|
@ -0,0 +1 @@
|
|||
#legacy_macro
|
|
@ -0,0 +1 @@
|
|||
#url_root
|
|
@ -0,0 +1 @@
|
|||
#test_macro
|
|
@ -18,27 +18,39 @@
|
|||
package org.apache.solr.velocity;
|
||||
|
||||
import org.apache.solr.SolrTestCaseJ4;
|
||||
import org.apache.solr.common.util.NamedList;
|
||||
import org.apache.solr.response.QueryResponseWriter;
|
||||
import org.apache.solr.response.SolrParamResourceLoader;
|
||||
import org.apache.solr.response.SolrQueryResponse;
|
||||
import org.apache.solr.response.VelocityResponseWriter;
|
||||
import org.apache.solr.request.SolrQueryRequest;
|
||||
import org.junit.BeforeClass;
|
||||
import org.junit.Test;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.StringWriter;
|
||||
|
||||
public class VelocityResponseWriterTest extends SolrTestCaseJ4 {
|
||||
@BeforeClass
|
||||
public static void beforeClass() throws Exception {
|
||||
initCore("solrconfig.xml", "schema.xml", getFile("velocity/solr").getAbsolutePath());
|
||||
System.out.println(getFile("velocity/solr").getAbsolutePath());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testVelocityResponseWriterRegistered() {
|
||||
QueryResponseWriter writer = h.getCore().getQueryResponseWriter("velocity");
|
||||
assertTrue("VrW registered check", writer instanceof VelocityResponseWriter);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCustomParamTemplate() throws Exception {
|
||||
// This test doesn't use the Solr core, just the response writer directly
|
||||
|
||||
org.apache.solr.response.VelocityResponseWriter vrw = new VelocityResponseWriter();
|
||||
SolrQueryRequest req = req("v.template","custom", "v.template.custom","$response.response.response_data");
|
||||
NamedList<String> nl = new NamedList<String>();
|
||||
nl.add(VelocityResponseWriter.PARAMS_RESOURCE_LOADER_ENABLED, "true");
|
||||
vrw.init(nl);
|
||||
SolrQueryRequest req = req(VelocityResponseWriter.TEMPLATE,"custom",
|
||||
SolrParamResourceLoader.TEMPLATE_PARAM_PREFIX+"custom","$response.response.response_data");
|
||||
SolrQueryResponse rsp = new SolrQueryResponse();
|
||||
StringWriter buf = new StringWriter();
|
||||
rsp.add("response_data", "testing");
|
||||
|
@ -47,14 +59,92 @@ public class VelocityResponseWriterTest extends SolrTestCaseJ4 {
|
|||
}
|
||||
|
||||
@Test
|
||||
public void testVelocityResponseWriterRegistered() {
|
||||
QueryResponseWriter writer = h.getCore().getQueryResponseWriter("velocity");
|
||||
public void testParamResourceLoaderDisabled() throws Exception {
|
||||
org.apache.solr.response.VelocityResponseWriter vrw = new VelocityResponseWriter();
|
||||
// by default param resource loader is disabled, no need to set it here
|
||||
SolrQueryRequest req = req(VelocityResponseWriter.TEMPLATE,"custom",
|
||||
SolrParamResourceLoader.TEMPLATE_PARAM_PREFIX+"custom","$response.response.response_data");
|
||||
SolrQueryResponse rsp = new SolrQueryResponse();
|
||||
StringWriter buf = new StringWriter();
|
||||
try {
|
||||
vrw.write(buf, req, rsp);
|
||||
fail("Should have thrown exception due to missing template");
|
||||
} catch (IOException e) {
|
||||
// expected exception
|
||||
}
|
||||
}
|
||||
|
||||
assertTrue("VrW registered check", writer instanceof VelocityResponseWriter);
|
||||
@Test
|
||||
public void testFileResourceLoader() throws Exception {
|
||||
org.apache.solr.response.VelocityResponseWriter vrw = new VelocityResponseWriter();
|
||||
NamedList<String> nl = new NamedList<String>();
|
||||
nl.add("template.base.dir", getFile("velocity").getAbsolutePath());
|
||||
vrw.init(nl);
|
||||
SolrQueryRequest req = req(VelocityResponseWriter.TEMPLATE,"file");
|
||||
SolrQueryResponse rsp = new SolrQueryResponse();
|
||||
StringWriter buf = new StringWriter();
|
||||
vrw.write(buf, req, rsp);
|
||||
assertEquals("testing", buf.toString());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSolrResourceLoaderTemplate() throws Exception {
|
||||
assertEquals("0", h.query(req("q","*:*", "wt","velocity","v.template","numFound")));
|
||||
assertEquals("0", h.query(req("q","*:*", "wt","velocity",VelocityResponseWriter.TEMPLATE,"numFound")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMacros() throws Exception {
|
||||
// tests that a macro in a custom macros.vm is visible
|
||||
assertEquals("test_macro_SUCCESS", h.query(req("q","*:*", "wt","velocity",VelocityResponseWriter.TEMPLATE,"test_macro_visible")));
|
||||
|
||||
// tests that a builtin (_macros.vm) macro, #url_root in this case, can be overridden in a custom macros.vm
|
||||
// the macro is also defined in VM_global_library.vm, which should also be overridden by macros.vm
|
||||
assertEquals("Loaded from: macros.vm", h.query(req("q","*:*", "wt","velocity",VelocityResponseWriter.TEMPLATE,"test_macro_overridden")));
|
||||
|
||||
// tests that macros defined in VM_global_library.vm are visible. This file was where macros in pre-5.0 versions were defined
|
||||
assertEquals("legacy_macro_SUCCESS", h.query(req("q","*:*", "wt","velocity",VelocityResponseWriter.TEMPLATE,"test_macro_legacy_support")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInitProps() throws Exception {
|
||||
// The test init properties file turns off being able to use $foreach.index (the implicit loop counter)
|
||||
// The foreach.vm template uses $!foreach.index, with ! suppressing the literal "$foreach.index" output
|
||||
|
||||
assertEquals("01", h.query(req("q","*:*", "wt","velocity",VelocityResponseWriter.TEMPLATE,"foreach")));
|
||||
assertEquals("", h.query(req("q","*:*", "wt","velocityWithInitProps",VelocityResponseWriter.TEMPLATE,"foreach")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLocaleFeature() throws Exception {
|
||||
assertEquals("Color", h.query(req("q", "*:*", "wt", "velocity", VelocityResponseWriter.TEMPLATE, "locale",
|
||||
VelocityResponseWriter.LOCALE,"en_US")));
|
||||
assertEquals("Colour", h.query(req("q", "*:*", "wt", "velocity", VelocityResponseWriter.TEMPLATE, "locale",
|
||||
VelocityResponseWriter.LOCALE,"en_UK")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLayoutFeature() throws Exception {
|
||||
assertEquals("{{{0}}}", h.query(req("q","*:*", "wt","velocity",
|
||||
VelocityResponseWriter.TEMPLATE,"numFound", VelocityResponseWriter.LAYOUT,"layout")));
|
||||
|
||||
// even with v.layout specified, layout can be disabled explicitly
|
||||
assertEquals("0", h.query(req("q","*:*", "wt","velocity",
|
||||
VelocityResponseWriter.TEMPLATE,"numFound",
|
||||
VelocityResponseWriter.LAYOUT,"layout",
|
||||
VelocityResponseWriter.LAYOUT_ENABLED,"false")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testJSONWrapper() throws Exception {
|
||||
assertEquals("foo({\"result\":\"0\"})", h.query(req("q", "*:*", "wt", "velocity",
|
||||
VelocityResponseWriter.TEMPLATE, "numFound",
|
||||
VelocityResponseWriter.JSON,"foo")));
|
||||
|
||||
// Now with layout, for good measure
|
||||
assertEquals("foo({\"result\":\"{{{0}}}\"})", h.query(req("q", "*:*", "wt", "velocity",
|
||||
VelocityResponseWriter.TEMPLATE, "numFound",
|
||||
VelocityResponseWriter.JSON,"foo",
|
||||
VelocityResponseWriter.LAYOUT,"layout")));
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -0,0 +1,18 @@
|
|||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
color=Color
|
|
@ -0,0 +1,18 @@
|
|||
#
|
||||
# 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.
|
||||
#
|
||||
|
||||
color=Colour
|
|
@ -1727,8 +1727,9 @@
|
|||
<!--
|
||||
Custom response writers can be declared as needed...
|
||||
-->
|
||||
<queryResponseWriter name="velocity" class="solr.VelocityResponseWriter" startup="lazy"/>
|
||||
|
||||
<queryResponseWriter name="velocity" class="solr.VelocityResponseWriter" startup="lazy">
|
||||
<str name="template.base.dir">${velocity.template.base.dir:}</str>
|
||||
</queryResponseWriter>
|
||||
|
||||
<!-- XSLT response writer transforms the XML output by any xslt file found
|
||||
in Solr's conf/xslt directory. Changes to xslt files are checked for
|
||||
|
|
|
@ -1698,8 +1698,9 @@
|
|||
<!--
|
||||
Custom response writers can be declared as needed...
|
||||
-->
|
||||
<queryResponseWriter name="velocity" class="solr.VelocityResponseWriter" startup="lazy"/>
|
||||
|
||||
<queryResponseWriter name="velocity" class="solr.VelocityResponseWriter" startup="lazy">
|
||||
<str name="template.base.dir">${velocity.template.base.dir:}</str>
|
||||
</queryResponseWriter>
|
||||
|
||||
<!-- XSLT response writer transforms the XML output by any xslt file found
|
||||
in Solr's conf/xslt directory. Changes to xslt files are checked for
|
||||
|
|
|
@ -1695,8 +1695,9 @@
|
|||
<!--
|
||||
Custom response writers can be declared as needed...
|
||||
-->
|
||||
<queryResponseWriter name="velocity" class="solr.VelocityResponseWriter" startup="lazy"/>
|
||||
|
||||
<queryResponseWriter name="velocity" class="solr.VelocityResponseWriter" startup="lazy">
|
||||
<str name="template.base.dir">${velocity.template.base.dir:}</str>
|
||||
</queryResponseWriter>
|
||||
|
||||
<!-- XSLT response writer transforms the XML output by any xslt file found
|
||||
in Solr's conf/xslt directory. Changes to xslt files are checked for
|
||||
|
|
|
@ -1726,8 +1726,9 @@
|
|||
<!--
|
||||
Custom response writers can be declared as needed...
|
||||
-->
|
||||
<queryResponseWriter name="velocity" class="solr.VelocityResponseWriter" startup="lazy"/>
|
||||
|
||||
<queryResponseWriter name="velocity" class="solr.VelocityResponseWriter" startup="lazy">
|
||||
<str name="template.base.dir">${velocity.template.base.dir:}</str>
|
||||
</queryResponseWriter>
|
||||
|
||||
<!-- XSLT response writer transforms the XML output by any xslt file found
|
||||
in Solr's conf/xslt directory. Changes to xslt files are checked for
|
||||
|
|
|
@ -1704,8 +1704,9 @@
|
|||
<!--
|
||||
Custom response writers can be declared as needed...
|
||||
-->
|
||||
<queryResponseWriter name="velocity" class="solr.VelocityResponseWriter" startup="lazy"/>
|
||||
|
||||
<queryResponseWriter name="velocity" class="solr.VelocityResponseWriter" startup="lazy">
|
||||
<str name="template.base.dir">${velocity.template.base.dir:}</str>
|
||||
</queryResponseWriter>
|
||||
|
||||
<!-- XSLT response writer transforms the XML output by any xslt file found
|
||||
in Solr's conf/xslt directory. Changes to xslt files are checked for
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
{"params":{
|
||||
"query":{
|
||||
"defType":"edismax",
|
||||
"q.alt":"*:*",
|
||||
"rows":"10",
|
||||
"fl":"*,score",
|
||||
"":{"v":0}
|
||||
},
|
||||
"facets":{
|
||||
"facet":"on",
|
||||
"facet.mincount": "1",
|
||||
"":{"v":0}
|
||||
},
|
||||
"velocity":{
|
||||
"wt": "velocity",
|
||||
"v.template":"browse",
|
||||
"v.layout": "layout",
|
||||
"":{"v":0}
|
||||
}
|
||||
}}
|
|
@ -860,24 +860,9 @@
|
|||
(SearchHandler) can be registered multiple times with different
|
||||
names (and different init parameters)
|
||||
-->
|
||||
<requestHandler name="/browse" class="solr.SearchHandler">
|
||||
<requestHandler name="/browse" class="solr.SearchHandler" useParams="query,facets,velocity,browse">
|
||||
<lst name="defaults">
|
||||
<str name="echoParams">explicit</str>
|
||||
|
||||
<!-- VelocityResponseWriter settings -->
|
||||
<str name="wt">velocity</str>
|
||||
<str name="v.template">browse</str>
|
||||
<str name="v.layout">layout</str>
|
||||
|
||||
<!-- Query settings -->
|
||||
<str name="defType">edismax</str>
|
||||
<str name="q.alt">*:*</str>
|
||||
<str name="rows">10</str>
|
||||
<str name="fl">*,score</str>
|
||||
|
||||
<!-- Faceting defaults -->
|
||||
<str name="facet">on</str>
|
||||
<str name="facet.mincount">1</str>
|
||||
</lst>
|
||||
</requestHandler>
|
||||
|
||||
|
@ -1541,8 +1526,9 @@
|
|||
<!--
|
||||
Custom response writers can be declared as needed...
|
||||
-->
|
||||
<queryResponseWriter name="velocity" class="solr.VelocityResponseWriter" startup="lazy"/>
|
||||
|
||||
<queryResponseWriter name="velocity" class="solr.VelocityResponseWriter" startup="lazy">
|
||||
<str name="template.base.dir">${velocity.template.base.dir:}</str>
|
||||
</queryResponseWriter>
|
||||
|
||||
<!-- XSLT response writer transforms the XML output by any xslt file found
|
||||
in Solr's conf/xslt directory. Changes to xslt files are checked for
|
||||
|
|
|
@ -1762,7 +1762,9 @@
|
|||
<!--
|
||||
Custom response writers can be declared as needed...
|
||||
-->
|
||||
<queryResponseWriter name="velocity" class="solr.VelocityResponseWriter" startup="lazy"/>
|
||||
<queryResponseWriter name="velocity" class="solr.VelocityResponseWriter" startup="lazy">
|
||||
<str name="template.base.dir">${velocity.template.base.dir:}</str>
|
||||
</queryResponseWriter>
|
||||
|
||||
|
||||
<!-- XSLT response writer transforms the XML output by any xslt file found
|
||||
|
|
Loading…
Reference in New Issue