SOLR-281 -- building a SearchComponent registry with components initialized via the standard plugin utils method.

git-svn-id: https://svn.apache.org/repos/asf/lucene/solr/trunk@602341 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Ryan McKinley 2007-12-08 07:27:49 +00:00
parent c0d2db9a89
commit 072e526155
14 changed files with 324 additions and 84 deletions

View File

@ -288,19 +288,6 @@
</requestHandler>
<requestHandler name="/search" class="solr.SearchHandler">
<lst name="defaults">
<str name="echoParams">explicit</str>
</lst>
<arr name="components">
<str>org.apache.solr.handler.component.QueryComponent</str>
<str>org.apache.solr.handler.component.FacetComponent</str>
<str>org.apache.solr.handler.component.MoreLikeThisComponent</str>
<str>org.apache.solr.handler.component.HighlightComponent</str>
<str>org.apache.solr.handler.component.DebugComponent</str>
</arr>
</requestHandler>
<!-- DisMaxRequestHandler allows easy searching across multiple fields
for simple user-entered phrases.
see http://wiki.apache.org/solr/DisMaxRequestHandler
@ -439,6 +426,52 @@
</lst>
</requestHandler>
<!--
Search components are registered to SolrCore and used by Search Handlers
By default, the following components are avaliable:
<searchComponent name="query" class="org.apache.solr.handler.component.QueryComponent" />
<searchComponent name="facet" class="org.apache.solr.handler.component.FacetComponent" />
<searchComponent name="mlt" class="org.apache.solr.handler.component.MoreLikeThisComponent" />
<searchComponent name="highlight" class="org.apache.solr.handler.component.HighlightComponent" />
<searchComponent name="debug" class="org.apache.solr.handler.component.DebugComponent" />
If you register a searchComponent to one of the standard names, that will be used instead.
-->
<requestHandler name="/search" class="solr.SearchHandler">
<lst name="defaults">
<str name="echoParams">explicit</str>
</lst>
<!--
By default, this will register the following components:
<arr name="components">
<str>query</str>
<str>facet</str>
<str>mlt</str>
<str>highlight</str>
<str>debug</str>
</arr>
To insert handlers before or after the 'standard' components, use:
<arr name="first-components">
<str>first</str>
</arr>
<arr name="last-components">
<str>last</str>
</arr>
-->
</requestHandler>
<!-- Update request handler.
Note: Since solr1.1 requestHandlers requires a valid content type header if posted in

View File

@ -51,13 +51,16 @@ public class Config {
this( null, name, is, prefix );
}
public Config(String instanceDir, String name) throws ParserConfigurationException, IOException, SAXException
public Config(SolrResourceLoader loader, String name) throws ParserConfigurationException, IOException, SAXException
{
this( new SolrResourceLoader( instanceDir ), name, null, null );
this( loader, name, null, null );
}
public Config(SolrResourceLoader loader, String name, InputStream is, String prefix) throws ParserConfigurationException, IOException, SAXException
{
if( loader == null ) {
loader = new SolrResourceLoader( null );
}
this.loader = loader;
this.name = name;
this.prefix = prefix;

View File

@ -44,6 +44,12 @@ import org.apache.solr.common.util.DOMUtil;
import org.apache.solr.common.util.NamedList;
import org.apache.solr.common.util.SimpleOrderedMap;
import org.apache.solr.handler.PingRequestHandler;
import org.apache.solr.handler.component.DebugComponent;
import org.apache.solr.handler.component.FacetComponent;
import org.apache.solr.handler.component.HighlightComponent;
import org.apache.solr.handler.component.MoreLikeThisComponent;
import org.apache.solr.handler.component.QueryComponent;
import org.apache.solr.handler.component.SearchComponent;
import org.apache.solr.highlight.SolrHighlighter;
import org.apache.solr.request.JSONResponseWriter;
import org.apache.solr.request.PythonResponseWriter;
@ -86,6 +92,7 @@ public final class SolrCore {
private final long startTime;
private final RequestHandlers reqHandlers;
private final SolrHighlighter highlighter;
private final Map<String,SearchComponent> searchComponents;
private final Map<String,UpdateRequestProcessorFactory> updateProcessors;
private final Map<String,SolrInfoMBean> infoRegistry = new java.util.HashMap<String,SolrInfoMBean>();
@ -310,6 +317,8 @@ public final class SolrCore {
initWriters();
initQParsers();
this.searchComponents = loadSearchComponents( config );
// Processors initialized before the handlers
updateProcessors = loadUpdateProcessors();
reqHandlers = new RequestHandlers(this);
@ -465,6 +474,37 @@ public final class SolrCore {
return reqHandlers.register(handlerName,handler);
}
/**
* Register the default search components
*/
private static Map<String, SearchComponent> loadSearchComponents( SolrConfig config )
{
Map<String, SearchComponent> components = new HashMap<String, SearchComponent>();
components.put( QueryComponent.COMPONENT_NAME, new QueryComponent() );
components.put( FacetComponent.COMPONENT_NAME, new FacetComponent() );
components.put( MoreLikeThisComponent.COMPONENT_NAME, new MoreLikeThisComponent() );
components.put( HighlightComponent.COMPONENT_NAME, new HighlightComponent() );
components.put( DebugComponent.COMPONENT_NAME, new DebugComponent() );
String xpath = "searchComponent";
NamedListPluginLoader<SearchComponent> loader = new NamedListPluginLoader<SearchComponent>( xpath, components );
loader.load( config.getResourceLoader(), (NodeList)config.evaluate( xpath, XPathConstants.NODESET ) );
return components;
}
/**
* @return a Search Component registered to a given name. Throw an exception if the component is undefined
*/
public SearchComponent getSearchComponent( String name )
{
SearchComponent component = searchComponents.get( name );
if( component == null ) {
throw new SolrException( SolrException.ErrorCode.BAD_REQUEST,
"Unknown Search Component: "+name );
}
return component;
}
////////////////////////////////////////////////////////////////////////////////
// Update Handler

View File

@ -18,18 +18,25 @@
package org.apache.solr.handler;
import org.apache.lucene.queryParser.ParseException;
import org.apache.solr.common.SolrException;
import org.apache.solr.common.params.CommonParams;
import org.apache.solr.common.util.NamedList;
import org.apache.solr.common.util.RTimer;
import org.apache.solr.common.util.SimpleOrderedMap;
import org.apache.solr.core.SolrCore;
import org.apache.solr.handler.component.DebugComponent;
import org.apache.solr.handler.component.FacetComponent;
import org.apache.solr.handler.component.HighlightComponent;
import org.apache.solr.handler.component.MoreLikeThisComponent;
import org.apache.solr.handler.component.QueryComponent;
import org.apache.solr.handler.component.ResponseBuilder;
import org.apache.solr.handler.component.SearchComponent;
import org.apache.solr.request.SolrQueryRequest;
import org.apache.solr.request.SolrQueryResponse;
import org.apache.solr.util.plugin.SolrCoreAware;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.logging.Logger;
@ -37,41 +44,82 @@ import java.util.logging.Logger;
*
* Refer SOLR-281
*
* @since solr 1.3
*/
public class SearchHandler extends RequestHandlerBase
public class SearchHandler extends RequestHandlerBase implements SolrCoreAware
{
private static final String RESPONSE_BUILDER_CONTEXT_KEY = "ResponseBuilder";
static final String RESPONSE_BUILDER_CONTEXT_KEY = "ResponseBuilder";
static final String INIT_COMPONENTS = "components";
static final String INIT_FISRT_COMPONENTS = "first-components";
static final String INIT_LAST_COMPONENTS = "last-components";
protected static Logger log = Logger.getLogger(SearchHandler.class.getName());
protected Collection<SearchComponent> components;
protected List<SearchComponent> components = null;
protected NamedList initArgs = null;
@Override
public void init(NamedList args) {
super.init( args );
initComponents(args);
initArgs = args;
}
// TODO: should there be a way to append components from solrconfig w/o having to
// know the complete standard list (which may expand over time?)
protected void initComponents(NamedList args){
if( args != null ) {
try {
Object declaredComponents = args.get("components");
if (declaredComponents != null && declaredComponents instanceof List) {
List list = (List) declaredComponents;
components = new ArrayList<SearchComponent>(list.size());
for(Object c : list){
// TODO: an init() with args for components?
SearchComponent comp = (SearchComponent) Class.forName((String) c).newInstance();
components.add(comp);
log.info("Adding component:"+comp);
}
}
} catch (Exception e) {
throw new RuntimeException(e);
protected List<String> getDefaultComponets()
{
ArrayList<String> names = new ArrayList<String>(5);
names.add( QueryComponent.COMPONENT_NAME );
names.add( FacetComponent.COMPONENT_NAME );
names.add( MoreLikeThisComponent.COMPONENT_NAME );
names.add( HighlightComponent.COMPONENT_NAME );
names.add( DebugComponent.COMPONENT_NAME );
return names;
}
/**
* Initialize the components based on name
*/
@SuppressWarnings("unchecked")
public void inform(SolrCore core)
{
Object declaredComponents = initArgs.get(INIT_COMPONENTS);
List<String> first = (List<String>) initArgs.get(INIT_FISRT_COMPONENTS);
List<String> last = (List<String>) initArgs.get(INIT_LAST_COMPONENTS);
List<String> list = null;
if( declaredComponents == null ) {
// Use the default component list
list = getDefaultComponets();
if( first != null ) {
List<String> clist = first;
clist.addAll( list );
list = clist;
}
if( last != null ) {
list.addAll( last );
}
}
else {
list = (List<String>)declaredComponents;
if( first != null || last != null ) {
throw new SolrException( SolrException.ErrorCode.SERVER_ERROR,
"First/Last components only valid if you do not declare 'components'");
}
}
// Build the component list
components = new ArrayList<SearchComponent>( list.size() );
for(String c : list){
SearchComponent comp = core.getSearchComponent( c );
components.add(comp);
log.info("Adding component:"+comp);
}
}
public List<SearchComponent> getComponents() {
return components;
}
public static ResponseBuilder getResponseBuilder(SolrQueryRequest req)
@ -79,14 +127,23 @@ public class SearchHandler extends RequestHandlerBase
return (ResponseBuilder) req.getContext().get( RESPONSE_BUILDER_CONTEXT_KEY );
}
//---------------------------------------------------------------------------------------
// SolrRequestHandler
//---------------------------------------------------------------------------------------
@Override
public void handleRequestBody(SolrQueryRequest req, SolrQueryResponse rsp) throws IOException, ParseException, InstantiationException, IllegalAccessException
{
ResponseBuilder builder = new ResponseBuilder();
req.getContext().put( RESPONSE_BUILDER_CONTEXT_KEY, builder );
// The semantics of debugging vs not debugging are different enough that
// it makes sense to have two control loops
if( components == null ) {
throw new SolrException( SolrException.ErrorCode.SERVER_ERROR,
"SearchHandler not initialized properly. No components registered." );
}
// The semantics of debugging vs not debugging are distinct enough
// to justify two control loops
if( !req.getParams().getBool( CommonParams.DEBUG_QUERY, false ) ) {
// Prepare
for( SearchComponent c : components ) {
@ -122,14 +179,13 @@ public class SearchHandler extends RequestHandlerBase
timer.stop();
// add the timing info
if( builder.getDebugInfo() == null ) {
builder.setDebugInfo( new SimpleOrderedMap<Object>() );
}
builder.getDebugInfo().add( "timing", timer.asNamedList() );
builder.addDebugInfo( "timing", timer.asNamedList() );
}
}
//////////////////////// SolrInfoMBeans methods //////////////////////
//---------------------------------------------------------------------------------------
// SolrInfoMBeans
//---------------------------------------------------------------------------------------
@Override
public String getDescription() {

View File

@ -19,15 +19,6 @@ package org.apache.solr.handler;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import org.apache.solr.common.util.NamedList;
import org.apache.solr.handler.component.DebugComponent;
import org.apache.solr.handler.component.FacetComponent;
import org.apache.solr.handler.component.HighlightComponent;
import org.apache.solr.handler.component.MoreLikeThisComponent;
import org.apache.solr.handler.component.QueryComponent;
import org.apache.solr.handler.component.SearchComponent;
/**
* @version $Id$
@ -48,18 +39,8 @@ import org.apache.solr.handler.component.SearchComponent;
* </ul>
*
*/
public class StandardRequestHandler extends SearchHandler {
@Override
protected void initComponents(NamedList args) {
components = new ArrayList<SearchComponent>(5);
components.add( new QueryComponent() );
components.add( new FacetComponent() );
components.add( new MoreLikeThisComponent() );
components.add( new HighlightComponent() );
components.add( new DebugComponent() );
}
public class StandardRequestHandler extends SearchHandler
{
//////////////////////// SolrInfoMBeans methods //////////////////////
@Override

View File

@ -24,8 +24,8 @@ import java.net.URL;
import java.util.ArrayList;
import java.util.List;
import org.apache.lucene.queryParser.ParseException;
import org.apache.lucene.search.Query;
import org.apache.solr.common.util.NamedList;
import org.apache.solr.handler.SearchHandler;
import org.apache.solr.request.SolrQueryRequest;
import org.apache.solr.request.SolrQueryResponse;
@ -40,19 +40,31 @@ import org.apache.solr.util.SolrPluginUtils;
*/
public class DebugComponent extends SearchComponent
{
public static final String COMPONENT_NAME = "debug";
@Override
public void prepare(SolrQueryRequest req, SolrQueryResponse rsp) throws IOException, ParseException
public void prepare(SolrQueryRequest req, SolrQueryResponse rsp) throws IOException
{
}
@SuppressWarnings("unchecked")
@Override
public void process(SolrQueryRequest req, SolrQueryResponse rsp) throws IOException
{
ResponseBuilder builder = SearchHandler.getResponseBuilder( req );
if( builder.isDebug() ) {
builder.setDebugInfo( SolrPluginUtils.doStandardDebug( req,
builder.getQueryString(), builder.getQuery(), builder.getResults().docList) );
NamedList stdinfo = SolrPluginUtils.doStandardDebug( req,
builder.getQueryString(), builder.getQuery(), builder.getResults().docList);
NamedList info = builder.getDebugInfo();
if( info == null ) {
builder.setDebugInfo( stdinfo );
info = stdinfo;
}
else {
info.addAll( stdinfo );
}
if (builder.getQparser() != null) {
builder.getQparser().addDebugInfo(builder.getDebugInfo());
@ -60,12 +72,12 @@ public class DebugComponent extends SearchComponent
if (null != builder.getDebugInfo() ) {
if (null != builder.getFilters() ) {
builder.getDebugInfo().add("filter_queries",req.getParams().getParams(FQ));
info.add("filter_queries",req.getParams().getParams(FQ));
List<String> fqs = new ArrayList<String>(builder.getFilters().size());
for (Query fq : builder.getFilters()) {
fqs.add(QueryParsing.toString(fq, req.getSchema()));
}
builder.getDebugInfo().add("parsed_filter_queries",fqs);
info.add("parsed_filter_queries",fqs);
}
// Add this directly here?

View File

@ -20,7 +20,6 @@ package org.apache.solr.handler.component;
import java.io.IOException;
import java.net.URL;
import org.apache.lucene.queryParser.ParseException;
import org.apache.solr.common.params.FacetParams;
import org.apache.solr.common.params.SolrParams;
import org.apache.solr.handler.SearchHandler;
@ -36,8 +35,10 @@ import org.apache.solr.request.SolrQueryResponse;
*/
public class FacetComponent extends SearchComponent
{
public static final String COMPONENT_NAME = "facet";
@Override
public void prepare(SolrQueryRequest req, SolrQueryResponse rsp) throws IOException, ParseException
public void prepare(SolrQueryRequest req, SolrQueryResponse rsp) throws IOException
{
SolrParams params = req.getParams();
if (params.getBool(FacetParams.FACET,false)) {

View File

@ -17,7 +17,6 @@
package org.apache.solr.handler.component;
import org.apache.lucene.queryParser.ParseException;
import org.apache.solr.common.SolrException;
import org.apache.solr.common.params.CommonParams;
import org.apache.solr.common.params.SolrParams;
@ -36,9 +35,13 @@ import java.net.URL;
* @version $Id$
* @since solr 1.3
*/
public class HighlightComponent extends SearchComponent {
public class HighlightComponent extends SearchComponent
{
public static final String COMPONENT_NAME = "highlight";
@Override
public void prepare(SolrQueryRequest req, SolrQueryResponse rsp) throws IOException, ParseException {
public void prepare(SolrQueryRequest req, SolrQueryResponse rsp)
{
}

View File

@ -20,7 +20,6 @@ package org.apache.solr.handler.component;
import java.io.IOException;
import java.net.URL;
import org.apache.lucene.queryParser.ParseException;
import org.apache.solr.common.params.MoreLikeThisParams;
import org.apache.solr.common.params.SolrParams;
import org.apache.solr.common.util.NamedList;
@ -39,8 +38,10 @@ import org.apache.solr.search.SolrIndexSearcher;
*/
public class MoreLikeThisComponent extends SearchComponent
{
public static final String COMPONENT_NAME = "mlt";
@Override
public void prepare(SolrQueryRequest req, SolrQueryResponse rsp) throws IOException, ParseException
public void prepare(SolrQueryRequest req, SolrQueryResponse rsp) throws IOException
{
}

View File

@ -40,6 +40,8 @@ import java.util.List;
*/
public class QueryComponent extends SearchComponent
{
public static final String COMPONENT_NAME = "query";
@Override
public void prepare(SolrQueryRequest req, SolrQueryResponse rsp) throws IOException, ParseException
{

View File

@ -20,6 +20,7 @@ package org.apache.solr.handler.component;
import org.apache.lucene.search.Query;
import org.apache.solr.common.util.NamedList;
import org.apache.solr.common.util.RTimer;
import org.apache.solr.common.util.SimpleOrderedMap;
import org.apache.solr.search.DocListAndSet;
import org.apache.solr.search.QParser;
import org.apache.solr.search.SortSpec;
@ -51,6 +52,23 @@ public class ResponseBuilder
private Query highlightQuery = null;
//-------------------------------------------------------------------------
//-------------------------------------------------------------------------
/**
* Utility function to add debugging info. This will make sure a valid
* debugInfo exists before adding to it.
*/
public void addDebugInfo( String name, Object val )
{
if( debugInfo == null ) {
debugInfo = new SimpleOrderedMap<Object>();
}
debugInfo.add( name, val );
}
//-------------------------------------------------------------------------
//-------------------------------------------------------------------------
public boolean isDebug() {

View File

@ -26,6 +26,7 @@ import org.apache.solr.common.util.SimpleOrderedMap;
import org.apache.solr.core.SolrInfoMBean;
import org.apache.solr.request.SolrQueryRequest;
import org.apache.solr.request.SolrQueryResponse;
import org.apache.solr.util.plugin.NamedListInitializedPlugin;
/**
* TODO!
@ -33,11 +34,18 @@ import org.apache.solr.request.SolrQueryResponse;
* @version $Id$
* @since solr 1.3
*/
public abstract class SearchComponent implements SolrInfoMBean
public abstract class SearchComponent implements SolrInfoMBean, NamedListInitializedPlugin
{
public abstract void prepare( SolrQueryRequest req, SolrQueryResponse rsp ) throws IOException, ParseException;
public abstract void process( SolrQueryRequest req, SolrQueryResponse rsp ) throws IOException;
//////////////////////// NamedListInitializedPlugin methods //////////////////////
public void init( NamedList args )
{
// By default do nothing
}
//////////////////////// SolrInfoMBeans methods //////////////////////
public String getName() {

View File

@ -39,6 +39,11 @@ public class SortSpec
this.num=num;
}
public void setSort( Sort s )
{
sort = s;
}
/**
* Gets the Lucene Sort object, or null for the default sort
* by score descending.

View File

@ -0,0 +1,77 @@
/**
* 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.
*/
package org.apache.solr.handler;
import java.util.ArrayList;
import java.util.List;
import org.apache.solr.common.util.NamedList;
import org.apache.solr.core.SolrCore;
import org.apache.solr.handler.component.FacetComponent;
import org.apache.solr.handler.component.MoreLikeThisComponent;
import org.apache.solr.handler.component.SearchComponent;
import org.apache.solr.util.AbstractSolrTestCase;
public class SearchHandlerTest extends AbstractSolrTestCase
{
@Override public String getSchemaFile() { return "schema.xml"; }
@Override public String getSolrConfigFile() { return "solrconfig.xml"; }
@SuppressWarnings("unchecked")
public void testInitalization()
{
SolrCore core = h.getCore();
// Build an explicit list
//-----------------------------------------------
List<String> names0 = new ArrayList<String>();
names0.add( MoreLikeThisComponent.COMPONENT_NAME );
NamedList args = new NamedList();
args.add( SearchHandler.INIT_COMPONENTS, names0 );
SearchHandler handler = new SearchHandler();
handler.init( args );
handler.inform( core );
assertEquals( 1, handler.getComponents().size() );
assertEquals( core.getSearchComponent( MoreLikeThisComponent.COMPONENT_NAME ),
handler.getComponents().get( 0 ) );
// First/Last list
//-----------------------------------------------
names0 = new ArrayList<String>();
names0.add( MoreLikeThisComponent.COMPONENT_NAME );
List<String> names1 = new ArrayList<String>();
names1.add( FacetComponent.COMPONENT_NAME );
args = new NamedList();
args.add( SearchHandler.INIT_FISRT_COMPONENTS, names0 );
args.add( SearchHandler.INIT_LAST_COMPONENTS, names1 );
handler = new SearchHandler();
handler.init( args );
handler.inform( core );
List<SearchComponent> comps = handler.getComponents();
assertEquals( 2+handler.getDefaultComponets().size(), comps.size() );
assertEquals( core.getSearchComponent( MoreLikeThisComponent.COMPONENT_NAME ), comps.get( 0 ) );
assertEquals( core.getSearchComponent( FacetComponent.COMPONENT_NAME ), comps.get( comps.size()-1 ) );
}
}