From 0bb2e730418972418075fb39006d2decf0b400fc Mon Sep 17 00:00:00 2001 From: Grant Ingersoll Date: Sun, 29 Aug 2010 13:16:56 +0000 Subject: [PATCH] SOLR-1665: Add more control to debug via options for timings, etc. git-svn-id: https://svn.apache.org/repos/asf/lucene/dev/trunk@990577 13f79535-47bb-0310-9956-ffa450edef68 --- solr/CHANGES.txt | 3 +- .../solr/common/params/CommonParams.java | 22 +- .../solr/handler/MoreLikeThisHandler.java | 53 ++-- .../handler/component/DebugComponent.java | 14 +- .../component/QueryElevationComponent.java | 6 +- .../handler/component/ResponseBuilder.java | 101 ++++--- .../solr/handler/component/SearchHandler.java | 15 +- .../org/apache/solr/util/SolrPluginUtils.java | 247 +++++++----------- .../apache/solr/BasicFunctionalityTest.java | 4 +- .../apache/solr/DisMaxRequestHandlerTest.java | 7 +- .../apache/solr/TestDistributedSearch.java | 3 +- .../handler/component/DebugComponentTest.java | 142 ++++++++++ .../apache/solr/search/TestQueryTypes.java | 3 +- 13 files changed, 397 insertions(+), 223 deletions(-) create mode 100644 solr/src/test/org/apache/solr/handler/component/DebugComponentTest.java diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt index b98f3bc081d..7b98885e777 100644 --- a/solr/CHANGES.txt +++ b/solr/CHANGES.txt @@ -249,7 +249,8 @@ New Features * SOLR-2059: Add "types" attribute to WordDelimiterFilterFactory, which allows you to customize how WordDelimiterFilter tokenizes text with a configuration file. (Peter Karich, rmuir) - + +* SOLR-1665: Add debug component options for timings, results and query info only (gsingers, hossman, yonik) Optimizations ---------------------- diff --git a/solr/src/common/org/apache/solr/common/params/CommonParams.java b/solr/src/common/org/apache/solr/common/params/CommonParams.java index e5bd28cec0d..c981c4ce9ff 100755 --- a/solr/src/common/org/apache/solr/common/params/CommonParams.java +++ b/solr/src/common/org/apache/solr/common/params/CommonParams.java @@ -58,8 +58,28 @@ public interface CommonParams { /** default query field */ public static final String DF = "df"; - /** whether to include debug data */ + /** whether to include debug data for all components pieces, including doing explains*/ public static final String DEBUG_QUERY = "debugQuery"; + + /** + * Whether to provide debug info for specific items. + * + * @see #DEBUG_QUERY + */ + public static final String DEBUG = "debug"; + + /** + * {@link #DEBUG} value indicating an interest in debug output related to timing + */ + public static final String TIMING = "timing"; + /** + * {@link #DEBUG} value indicating an interest in debug output related to the results (explains) + */ + public static final String RESULTS = "results"; + /** + * {@link #DEBUG} value indicating an interest in debug output related to the Query (parsing, etc.) + */ + public static final String QUERY = "query"; /** * boolean indicating whether score explanations should structured (true), diff --git a/solr/src/java/org/apache/solr/handler/MoreLikeThisHandler.java b/solr/src/java/org/apache/solr/handler/MoreLikeThisHandler.java index 74c7d0531d9..5a685c5fec1 100644 --- a/solr/src/java/org/apache/solr/handler/MoreLikeThisHandler.java +++ b/solr/src/java/org/apache/solr/handler/MoreLikeThisHandler.java @@ -23,9 +23,12 @@ import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; import java.util.Comparator; + +import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.regex.Pattern; import org.apache.lucene.document.Document; @@ -56,6 +59,7 @@ import org.apache.solr.search.DocList; import org.apache.solr.search.DocListAndSet; import org.apache.solr.search.QueryParsing; import org.apache.solr.search.SolrIndexSearcher; + import org.apache.solr.util.SolrPluginUtils; /** @@ -192,24 +196,41 @@ public class MoreLikeThisHandler extends RequestHandlerBase rsp.add( "facet_counts", f.getFacetCounts() ); } } - - // Copied from StandardRequestHandler... perhaps it should be added to doStandardDebug? - try { - NamedList dbg = SolrPluginUtils.doStandardDebug(req, q, mlt.mltquery, mltDocs.docList ); - if (null != dbg) { - if (null != filters) { - dbg.add("filter_queries",req.getParams().getParams(CommonParams.FQ)); - List fqs = new ArrayList(filters.size()); - for (Query fq : filters) { - fqs.add(QueryParsing.toString(fq, req.getSchema())); - } - dbg.add("parsed_filter_queries",fqs); + boolean dbg = req.getParams().getBool(CommonParams.DEBUG_QUERY, false); + + boolean dbgQuery = false, dbgResults = false; + if (dbg == false){//if it's true, we are doing everything anyway. + String[] dbgParams = req.getParams().getParams(CommonParams.DEBUG); + for (int i = 0; i < dbgParams.length; i++) { + if (dbgParams[i].equals(CommonParams.QUERY)){ + dbgQuery = true; + } else if (dbgParams[i].equals(CommonParams.RESULTS)){ + dbgResults = true; } - rsp.add("debug", dbg); } - } catch (Exception e) { - SolrException.logOnce(SolrCore.log, "Exception during debug", e); - rsp.add("exception_during_debug", SolrException.toStr(e)); + } else { + dbgQuery = true; + dbgResults = true; + } + // Copied from StandardRequestHandler... perhaps it should be added to doStandardDebug? + if (dbg == true) { + try { + NamedList dbgInfo = SolrPluginUtils.doStandardDebug(req, q, mlt.mltquery, mltDocs.docList, dbgQuery, dbgResults); + if (null != dbgInfo) { + if (null != filters) { + dbgInfo.add("filter_queries",req.getParams().getParams(CommonParams.FQ)); + List fqs = new ArrayList(filters.size()); + for (Query fq : filters) { + fqs.add(QueryParsing.toString(fq, req.getSchema())); + } + dbgInfo.add("parsed_filter_queries",fqs); + } + rsp.add("debug", dbgInfo); + } + } catch (Exception e) { + SolrException.logOnce(SolrCore.log, "Exception during debug", e); + rsp.add("exception_during_debug", SolrException.toStr(e)); + } } } diff --git a/solr/src/java/org/apache/solr/handler/component/DebugComponent.java b/solr/src/java/org/apache/solr/handler/component/DebugComponent.java index 2d958f18692..8ed4e2089a9 100644 --- a/solr/src/java/org/apache/solr/handler/component/DebugComponent.java +++ b/solr/src/java/org/apache/solr/handler/component/DebugComponent.java @@ -18,7 +18,8 @@ package org.apache.solr.handler.component; import static org.apache.solr.common.params.CommonParams.FQ; -import org.apache.solr.common.params.HighlightParams; + +import org.apache.solr.common.params.CommonParams; import java.io.IOException; import java.net.URL; @@ -51,8 +52,9 @@ public class DebugComponent extends SearchComponent public void process(ResponseBuilder rb) throws IOException { if( rb.isDebug() ) { + NamedList stdinfo = SolrPluginUtils.doStandardDebug( rb.req, - rb.getQueryString(), rb.getQuery(), rb.getResults().docList); + rb.getQueryString(), rb.getQuery(), rb.getResults().docList, rb.isDebugQuery(), rb.isDebugResults()); NamedList info = rb.getDebugInfo(); if( info == null ) { @@ -63,12 +65,12 @@ public class DebugComponent extends SearchComponent info.addAll( stdinfo ); } - if (rb.getQparser() != null) { + if (rb.isDebugQuery() && rb.getQparser() != null) { rb.getQparser().addDebugInfo(rb.getDebugInfo()); } if (null != rb.getDebugInfo() ) { - if (null != rb.getFilters() ) { + if (rb.isDebugQuery() && null != rb.getFilters() ) { info.add("filter_queries",rb.req.getParams().getParams(FQ)); List fqs = new ArrayList(rb.getFilters().size()); for (Query fq : rb.getFilters()) { @@ -90,9 +92,9 @@ public class DebugComponent extends SearchComponent // Turn on debug to get explain only when retrieving fields if ((sreq.purpose & ShardRequest.PURPOSE_GET_FIELDS) != 0) { sreq.purpose |= ShardRequest.PURPOSE_GET_DEBUG; - sreq.params.set("debugQuery", "true"); + sreq.params.set(CommonParams.DEBUG_QUERY, "true"); } else { - sreq.params.set("debugQuery", "false"); + sreq.params.set(CommonParams.DEBUG_QUERY, "false"); } } diff --git a/solr/src/java/org/apache/solr/handler/component/QueryElevationComponent.java b/solr/src/java/org/apache/solr/handler/component/QueryElevationComponent.java index 1d004dc0634..bf687262a83 100644 --- a/solr/src/java/org/apache/solr/handler/component/QueryElevationComponent.java +++ b/solr/src/java/org/apache/solr/handler/component/QueryElevationComponent.java @@ -30,7 +30,9 @@ import java.util.List; import java.util.Map; import java.util.WeakHashMap; +import org.apache.solr.common.params.CommonParams; import org.apache.solr.common.params.QueryElevationParams; + import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -406,7 +408,9 @@ public class QueryElevationComponent extends SearchComponent implements SolrCore SimpleOrderedMap dbg = new SimpleOrderedMap(); dbg.add( "q", qstr ); dbg.add( "match", match ); - rb.addDebugInfo( "queryBoosting", dbg ); + if (rb.isDebugQuery()) { + rb.addDebugInfo("queryBoosting", dbg ); + } } } diff --git a/solr/src/java/org/apache/solr/handler/component/ResponseBuilder.java b/solr/src/java/org/apache/solr/handler/component/ResponseBuilder.java index bd9a36e911b..1660f698b1f 100644 --- a/solr/src/java/org/apache/solr/handler/component/ResponseBuilder.java +++ b/solr/src/java/org/apache/solr/handler/component/ResponseBuilder.java @@ -26,11 +26,15 @@ import org.apache.solr.request.SolrQueryRequest; import org.apache.solr.response.SolrQueryResponse; import org.apache.solr.search.DocListAndSet; import org.apache.solr.search.QParser; -import org.apache.solr.search.SortSpec; import org.apache.solr.search.SolrIndexSearcher; +import org.apache.solr.search.SortSpec; +import org.apache.solr.util.SolrPluginUtils; +import java.util.Collections; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; /** * This class is experimental and will be changing in the future. @@ -38,8 +42,9 @@ import java.util.Map; * @version $Id$ * @since solr 1.3 */ -public class ResponseBuilder -{ +public class ResponseBuilder { + + public SolrQueryRequest req; public SolrQueryResponse rsp; public boolean doHighlights; @@ -50,7 +55,8 @@ public class ResponseBuilder private boolean needDocList = false; private boolean needDocSet = false; private int fieldFlags = 0; - private boolean debug = false; + //private boolean debug = false; + private boolean debugTimings, debugQuery, debugResults; private QParser qparser = null; private String queryString = null; @@ -76,20 +82,21 @@ public class ResponseBuilder public static final String SHARDS = "shards"; public static final String IDS = "ids"; - /*** - public static final String NUMDOCS = "nd"; - public static final String DOCFREQS = "tdf"; - public static final String TERMS = "terms"; - public static final String EXTRACT_QUERY_TERMS = "eqt"; - public static final String LOCAL_SHARD = "local"; - public static final String DOC_QUERY = "dq"; - ***/ + /** + * public static final String NUMDOCS = "nd"; + * public static final String DOCFREQS = "tdf"; + * public static final String TERMS = "terms"; + * public static final String EXTRACT_QUERY_TERMS = "eqt"; + * public static final String LOCAL_SHARD = "local"; + * public static final String DOC_QUERY = "dq"; + * * + */ - public static int STAGE_START = 0; - public static int STAGE_PARSE_QUERY = 1000; - public static int STAGE_EXECUTE_QUERY = 2000; - public static int STAGE_GET_FIELDS = 3000; - public static int STAGE_DONE = Integer.MAX_VALUE; + public static int STAGE_START = 0; + public static int STAGE_PARSE_QUERY = 1000; + public static int STAGE_EXECUTE_QUERY = 2000; + public static int STAGE_GET_FIELDS = 3000; + public static int STAGE_DONE = Integer.MAX_VALUE; public int stage; // What stage is this current request at? @@ -102,15 +109,15 @@ public class ResponseBuilder public int getShardNum(String shard) { - for (int i=0; i getDebugInfo() { @@ -272,23 +305,23 @@ public class ResponseBuilder */ public SolrIndexSearcher.QueryCommand getQueryCommand() { SolrIndexSearcher.QueryCommand cmd = new SolrIndexSearcher.QueryCommand(); - cmd.setQuery( getQuery() ) - .setFilterList( getFilters() ) - .setSort( getSortSpec().getSort() ) - .setOffset( getSortSpec().getOffset() ) - .setLen( getSortSpec().getCount() ) - .setFlags( getFieldFlags() ) - .setNeedDocSet( isNeedDocSet() ); + cmd.setQuery(getQuery()) + .setFilterList(getFilters()) + .setSort(getSortSpec().getSort()) + .setOffset(getSortSpec().getOffset()) + .setLen(getSortSpec().getCount()) + .setFlags(getFieldFlags()) + .setNeedDocSet(isNeedDocSet()); return cmd; } /** * Sets results from a SolrIndexSearcher.QueryResult. */ - public void setResult( SolrIndexSearcher.QueryResult result ) { - setResults( result.getDocListAndSet() ); - if( result.isPartialResults() ) { - rsp.getResponseHeader().add( "partialResults", Boolean.TRUE ); + public void setResult(SolrIndexSearcher.QueryResult result) { + setResults(result.getDocListAndSet()); + if (result.isPartialResults()) { + rsp.getResponseHeader().add("partialResults", Boolean.TRUE); } } } diff --git a/solr/src/java/org/apache/solr/handler/component/SearchHandler.java b/solr/src/java/org/apache/solr/handler/component/SearchHandler.java index 2817d2cf64b..eb5103cf9e6 100644 --- a/solr/src/java/org/apache/solr/handler/component/SearchHandler.java +++ b/solr/src/java/org/apache/solr/handler/component/SearchHandler.java @@ -20,7 +20,6 @@ package org.apache.solr.handler.component; import org.apache.solr.handler.RequestHandlerBase; 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.common.params.CommonParams; import org.apache.solr.common.params.ModifiableSolrParams; import org.apache.solr.common.params.ShardParams; @@ -32,7 +31,8 @@ import org.apache.solr.client.solrj.SolrRequest; import org.apache.solr.client.solrj.SolrResponse; import org.apache.solr.client.solrj.request.QueryRequest; import org.apache.solr.client.solrj.impl.CommonsHttpSolrServer; -import org.apache.solr.client.solrj.impl.BinaryResponseParser; + +import org.apache.solr.util.SolrPluginUtils; import org.apache.solr.util.plugin.SolrCoreAware; import org.apache.solr.core.SolrCore; import org.apache.lucene.queryParser.ParseException; @@ -175,7 +175,11 @@ public class SearchHandler extends RequestHandlerBase implements SolrCoreAware rb.req = req; rb.rsp = rsp; rb.components = components; - rb.setDebug(req.getParams().getBool(CommonParams.DEBUG_QUERY, false)); + boolean dbg = req.getParams().getBool(CommonParams.DEBUG_QUERY, false); + rb.setDebug(dbg); + if (dbg == false){//if it's true, we are doing everything anyway. + SolrPluginUtils.getDebugInterests(req.getParams().getParams(CommonParams.DEBUG), rb); + } final RTimer timer = rb.isDebug() ? new RTimer() : null; @@ -218,10 +222,9 @@ public class SearchHandler extends RequestHandlerBase implements SolrCoreAware timer.stop(); // add the timing info - if( rb.getDebugInfo() == null ) { - rb.setDebugInfo( new SimpleOrderedMap() ); + if (rb.isDebugTimings()) { + rb.addDebugInfo("timing", timer.asNamedList() ); } - rb.getDebugInfo().add( "timing", timer.asNamedList() ); } } else { diff --git a/solr/src/java/org/apache/solr/util/SolrPluginUtils.java b/solr/src/java/org/apache/solr/util/SolrPluginUtils.java index 048efa6c3ac..ff54f6f209a 100644 --- a/solr/src/java/org/apache/solr/util/SolrPluginUtils.java +++ b/solr/src/java/org/apache/solr/util/SolrPluginUtils.java @@ -22,7 +22,6 @@ import org.apache.lucene.queryParser.ParseException; import org.apache.lucene.queryParser.QueryParser; import org.apache.lucene.search.*; import org.apache.lucene.search.BooleanClause.Occur; -import org.apache.lucene.index.LogByteSizeMergePolicy; import org.apache.solr.common.SolrDocument; import org.apache.solr.common.SolrDocumentList; import org.apache.solr.common.SolrException; @@ -34,6 +33,7 @@ import org.apache.solr.common.util.NamedList; import org.apache.solr.common.util.SimpleOrderedMap; import org.apache.solr.common.util.StrUtils; import org.apache.solr.core.SolrCore; +import org.apache.solr.handler.component.ResponseBuilder; import org.apache.solr.highlight.SolrHighlighter; import org.apache.solr.request.SolrQueryRequest; import org.apache.solr.response.SolrQueryResponse; @@ -61,7 +61,7 @@ import java.lang.reflect.InvocationTargetException; *

:TODO: refactor StandardRequestHandler to use these utilities

* *

:TODO: Many "standard" functionality methods are not cognisant of - * default parameter settings. + * default parameter settings. */ public class SolrPluginUtils { final static Logger log = LoggerFactory.getLogger( SolrPluginUtils.class ); @@ -75,7 +75,7 @@ public class SolrPluginUtils { public static void setDefaults(SolrQueryRequest req, SolrParams defaults) { setDefaults(req, defaults, null, null); } - + /** * Set default-ish params on a SolrQueryRequest. * @@ -89,7 +89,7 @@ public class SolrPluginUtils { */ public static void setDefaults(SolrQueryRequest req, SolrParams defaults, SolrParams appends, SolrParams invariants) { - + SolrParams p = req.getParams(); if (defaults != null) { p = new DefaultSolrParams(p,defaults); @@ -120,16 +120,16 @@ public class SolrPluginUtils { throws IOException { return (null == f) ? s.getDocSet(q).size() : s.numDocs(q,f); - + } - + /** * Returns the param, or the default if it's empty or not specified. * @deprecated use SolrParam.get(String,String) */ public static String getParam(SolrQueryRequest req, String param, String def) { - + String v = req.getParam(param); // Note: parameters passed but given only white-space value are // considered equivalent to passing nothing for that parameter. @@ -138,7 +138,7 @@ public class SolrPluginUtils { } return v; } - + /** * Treats the param value as a Number, returns the default if nothing is * there or if it's not a number. @@ -146,7 +146,7 @@ public class SolrPluginUtils { */ public static Number getNumberParam(SolrQueryRequest req, String param, Number def) { - + Number r = def; String v = req.getParam(param); if (null == v || "".equals(v.trim())) { @@ -159,23 +159,23 @@ public class SolrPluginUtils { } return r; } - + /** - * Treats parameter value as a boolean. The string 'false' is false; + * Treats parameter value as a boolean. The string 'false' is false; * any other non-empty string is true. * @deprecated use SolrParam.getBool(String,boolean) */ public static boolean getBooleanParam(SolrQueryRequest req, - String param, boolean def) { + String param, boolean def) { String v = req.getParam(param); if (null == v || "".equals(v.trim())) { return def; } return !"false".equals(v.trim()); } - + private final static Pattern splitList=Pattern.compile(",| "); - + /** Split a value that may contain a comma, space of bar separated list. */ public static String[] split(String value){ return splitList.split(value.trim(), 0); @@ -226,7 +226,7 @@ public class SolrPluginUtils { *

    *
  • Locates the document-retrieval costs in one spot, which helps * detailed performance measurement
  • - * + * *
  • Determines a priori what fields will be needed to be fetched by * various subtasks, like response writing and highlighting. This * minimizes the chance that many needed fields will be loaded lazily. @@ -253,13 +253,13 @@ public class SolrPluginUtils { // add highlight fields SolrHighlighter highligher = req.getCore().getHighlighter(); if(highligher.isHighlightingEnabled(req.getParams())) { - for(String field: highligher.getHighlightFields(query, req, null)) - fieldFilter.add(field); + for(String field: highligher.getHighlightFields(query, req, null)) + fieldFilter.add(field); } // fetch unique key if one exists. SchemaField keyField = req.getSearcher().getSchema().getUniqueKeyField(); if(null != keyField) - fieldFilter.add(keyField.getName()); + fieldFilter.add(keyField.getName()); } // get documents @@ -269,85 +269,26 @@ public class SolrPluginUtils { } } - /** - *

    - * Returns a NamedList containing many "standard" pieces of debugging - * information. - *

    - * - *
      - *
    • rawquerystring - the 'q' param exactly as specified by the client - *
    • - *
    • querystring - the 'q' param after any preprocessing done by the plugin - *
    • - *
    • parsedquery - the main query executed formated by the Solr - * QueryParsing utils class (which knows about field types) - *
    • - *
    • parsedquery_toString - the main query executed formated by it's - * own toString method (in case it has internal state Solr - * doesn't know about) - *
    • - *
    • expain - the list of score explanations for each document in - * results against query. - *
    • - *
    • otherQuery - the query string specified in 'explainOther' query param. - *
    • - *
    • explainOther - the list of score explanations for each document in - * results against 'otherQuery' - *
    • - *
    - * - * @param req the request we are dealing with - * @param userQuery the users query as a string, after any basic - * preprocessing has been done - * @param query the query built from the userQuery - * (and perhaps other clauses) that identifies the main - * result set of the response. - * @param results the main result set of the response - * @deprecated Use doStandardDebug(SolrQueryRequest,String,Query,DocList) with setDefaults - */ - public static NamedList doStandardDebug(SolrQueryRequest req, - String userQuery, - Query query, - DocList results, - org.apache.solr.util.CommonParams params) - throws IOException { - - String debug = getParam(req, CommonParams.DEBUG_QUERY, params.debugQuery); - NamedList dbg = null; - if (debug!=null) { - dbg = new SimpleOrderedMap(); - - /* userQuery may have been pre-processes .. expose that */ - dbg.add("rawquerystring", req.getQueryString()); - dbg.add("querystring", userQuery); - - /* QueryParsing.toString isn't perfect, use it to see converted - * values, use regular toString to see any attributes of the - * underlying Query it may have missed. - */ - dbg.add("parsedquery",QueryParsing.toString(query, req.getSchema())); - dbg.add("parsedquery_toString", query.toString()); - - dbg.add("explain", getExplainList - (query, results, req.getSearcher(), req.getSchema())); - String otherQueryS = req.getParam("explainOther"); - if (otherQueryS != null && otherQueryS.length() > 0) { - DocList otherResults = doSimpleQuery - (otherQueryS,req.getSearcher(), req.getSchema(),0,10); - dbg.add("otherQuery",otherQueryS); - dbg.add("explainOther", getExplainList - (query, otherResults, - req.getSearcher(), - req.getSchema())); + public static Set getDebugInterests(String[] params, ResponseBuilder rb){ + Set debugInterests = new HashSet(); + if (params != null) { + for (int i = 0; i < params.length; i++) { + if (params[i].equalsIgnoreCase("all") || params[i].equalsIgnoreCase("true")){ + rb.setDebug(true); + break; + //still might add others + } else if (params[i].equals(CommonParams.TIMING)){ + rb.setDebugTimings(true); + } else if (params[i].equals(CommonParams.QUERY)){ + rb.setDebugQuery(true); + } else if (params[i].equals(CommonParams.RESULTS)){ + rb.setDebugResults(true); + } } } - - return dbg; + return debugInterests; } - - /** *

    * Returns a NamedList containing many "standard" pieces of debugging @@ -383,55 +324,59 @@ public class SolrPluginUtils { * (and perhaps other clauses) that identifies the main * result set of the response. * @param results the main result set of the response + * @return The debug info + * @throws java.io.IOException if there was an IO error */ public static NamedList doStandardDebug(SolrQueryRequest req, String userQuery, Query query, - DocList results) + DocList results, boolean dbgQuery, boolean dbgResults) throws IOException { - String debug = req.getParams().get(CommonParams.DEBUG_QUERY); - NamedList dbg = null; - if (debug!=null) { - dbg = new SimpleOrderedMap(); - SolrIndexSearcher searcher = req.getSearcher(); - IndexSchema schema = req.getSchema(); + dbg = new SimpleOrderedMap(); - boolean explainStruct - = req.getParams().getBool(CommonParams.EXPLAIN_STRUCT,false); - - /* userQuery may have been pre-processes .. expose that */ + SolrIndexSearcher searcher = req.getSearcher(); + IndexSchema schema = req.getSchema(); + + boolean explainStruct + = req.getParams().getBool(CommonParams.EXPLAIN_STRUCT, false); + + /* userQuery may have been pre-processes .. expose that */ + if (dbgQuery) { dbg.add("rawquerystring", req.getParams().get(CommonParams.Q)); dbg.add("querystring", userQuery); /* QueryParsing.toString isn't perfect, use it to see converted - * values, use regular toString to see any attributes of the - * underlying Query it may have missed. - */ - dbg.add("parsedquery",QueryParsing.toString(query, schema)); + * values, use regular toString to see any attributes of the + * underlying Query it may have missed. + */ + dbg.add("parsedquery", QueryParsing.toString(query, schema)); dbg.add("parsedquery_toString", query.toString()); + } - NamedList explain - = getExplanations(query, results, searcher, schema); + if (dbgResults) { + NamedList explain + = getExplanations(query, results, searcher, schema); dbg.add("explain", explainStruct ? - explanationsToNamedLists(explain) : + explanationsToNamedLists(explain) : explanationsToStrings(explain)); String otherQueryS = req.getParams().get(CommonParams.EXPLAIN_OTHER); if (otherQueryS != null && otherQueryS.length() > 0) { DocList otherResults = doSimpleQuery - (otherQueryS,req.getSearcher(), req.getSchema(),0,10); - dbg.add("otherQuery",otherQueryS); + (otherQueryS, req.getSearcher(), req.getSchema(), 0, 10); + dbg.add("otherQuery", otherQueryS); NamedList explainO - = getExplanations(query, otherResults, searcher, schema); + = getExplanations(query, otherResults, searcher, schema); dbg.add("explainOther", explainStruct ? - explanationsToNamedLists(explainO) : + explanationsToNamedLists(explainO) : explanationsToStrings(explainO)); } } + return dbg; } @@ -456,11 +401,11 @@ public class SolrPluginUtils { return out; } - + public static NamedList> explanationsToNamedLists (NamedList explanations) { - NamedList> out + NamedList> out = new SimpleOrderedMap>(); for (Map.Entry entry : explanations) { out.add(entry.getKey(), explanationToNamedList(entry.getValue())); @@ -475,11 +420,11 @@ public class SolrPluginUtils { * @param docs The Documents you want explained relative that query */ public static NamedList getExplanations - (Query query, - DocList docs, - SolrIndexSearcher searcher, + (Query query, + DocList docs, + SolrIndexSearcher searcher, IndexSchema schema) throws IOException { - + NamedList explainList = new SimpleOrderedMap(); DocIterator iterator = docs.iterator(); for (int i=0; i parseFuncs(IndexSchema s, String in) throws ParseException { - + Map ff = parseFieldBoosts(in); List funcs = new ArrayList(ff.keySet().size()); for (String f : ff.keySet()) { @@ -610,7 +555,7 @@ public class SolrPluginUtils { return funcs; } - + /** * Checks the number of optional clauses in the query, and compares it * with the specification string to determine the proper value to use. @@ -662,11 +607,11 @@ public class SolrPluginUtils { static int calculateMinShouldMatch(int optionalClauseCount, String spec) { int result = optionalClauseCount; - + if (-1 < spec.indexOf("<")) { /* we have conditional spec(s) */ - + for (String s : spec.trim().split(" ")) { String[] parts = s.split("<"); int upperBound = (new Integer(parts[0])).intValue(); @@ -694,10 +639,10 @@ public class SolrPluginUtils { return (optionalClauseCount < result ? optionalClauseCount : (result < 0 ? 0 : result)); - + } - - + + /** * Recursively walks the "from" query pulling out sub-queries and * adding them to the "to" query. @@ -711,17 +656,17 @@ public class SolrPluginUtils { public static void flattenBooleanQuery(BooleanQuery to, BooleanQuery from) { for (BooleanClause clause : (List)from.clauses()) { - + Query cq = clause.getQuery(); cq.setBoost(cq.getBoost() * from.getBoost()); - + if (cq instanceof BooleanQuery && !clause.isRequired() && !clause.isProhibited()) { - + /* we can recurse */ flattenBooleanQuery(to, (BooleanQuery)cq); - + } else { to.add(clause); } @@ -753,7 +698,7 @@ public class SolrPluginUtils { private final static Pattern DANGLING_OP_PATTERN = Pattern.compile( "\\s+[-+\\s]+$" ); // Pattern to detect consecutive + and/or - operators // \s+[+-](?:\s*[+-]+)+ - private final static Pattern CONSECUTIVE_OP_PATTERN = Pattern.compile( "\\s+[+-](?:\\s*[+-]+)+" ); + private final static Pattern CONSECUTIVE_OP_PATTERN = Pattern.compile( "\\s+[+-](?:\\s*[+-]+)+" ); /** * Strips operators that are used illegally, otherwise reuturns it's @@ -844,9 +789,9 @@ public class SolrPluginUtils { */ protected Query getFieldQuery(String field, String queryText, boolean quoted) throws ParseException { - + if (aliases.containsKey(field)) { - + Alias a = aliases.get(field); DisjunctionMaxQuery q = new DisjunctionMaxQuery(a.tie); @@ -854,7 +799,7 @@ public class SolrPluginUtils { * in which case we should return null */ boolean ok = false; - + for (String f : a.fields.keySet()) { Query sub = getFieldQuery(f,queryText,quoted); @@ -876,7 +821,7 @@ public class SolrPluginUtils { } } } - + } /** @@ -906,7 +851,7 @@ public class SolrPluginUtils { SolrCore.log.warn("Invalid sort \""+sort+"\" was specified, ignoring", sortE); return null; } - + return ss; } @@ -923,8 +868,8 @@ public class SolrPluginUtils { * * @return null if no queries are generated */ - public static List parseQueryStrings(SolrQueryRequest req, - String[] queries) throws ParseException { + public static List parseQueryStrings(SolrQueryRequest req, + String[] queries) throws ParseException { if (null == queries || 0 == queries.length) return null; List out = new ArrayList(queries.length); for (String q : queries) { @@ -971,9 +916,9 @@ public class SolrPluginUtils { * @since solr 1.4 */ public static SolrDocumentList docListToSolrDocumentList( - DocList docs, - SolrIndexSearcher searcher, - Set fields, + DocList docs, + SolrIndexSearcher searcher, + Set fields, Map ids ) throws IOException { DocumentBuilder db = new DocumentBuilder(searcher.getSchema()); @@ -1011,12 +956,12 @@ public class SolrPluginUtils { /** - * Given a SolrQueryResponse replace the DocList if it is in the result. + * Given a SolrQueryResponse replace the DocList if it is in the result. * Otherwise add it to the response - * + * * @since solr 1.4 */ - public static void addOrReplaceResults(SolrQueryResponse rsp, SolrDocumentList docs) + public static void addOrReplaceResults(SolrQueryResponse rsp, SolrDocumentList docs) { NamedList vals = rsp.getValues(); int idx = vals.indexOf( "response", 0 ); @@ -1029,7 +974,7 @@ public class SolrPluginUtils { vals.add( "response", docs ); } } - + public static void invokeSetters(Object bean, NamedList initArgs) { if (initArgs == null) return; Class clazz = bean.getClass(); @@ -1042,7 +987,7 @@ public class SolrPluginUtils { Method method = null; try { for (Method m : methods) { - if (m.getName().equals(setterName) && m.getParameterTypes().length == 1) { + if (m.getName().equals(setterName) && m.getParameterTypes().length == 1) { method = m; break; } diff --git a/solr/src/test/org/apache/solr/BasicFunctionalityTest.java b/solr/src/test/org/apache/solr/BasicFunctionalityTest.java index 390bd1f6a11..a5762565e7d 100644 --- a/solr/src/test/org/apache/solr/BasicFunctionalityTest.java +++ b/solr/src/test/org/apache/solr/BasicFunctionalityTest.java @@ -271,7 +271,7 @@ public class BasicFunctionalityTest extends SolrTestCaseJ4 { assertQ(req("text:hello") ,"//*[@numFound='2']" ); - String resp = h.query(lrf.makeRequest("q", "text:hello", "debugQuery", "true")); + String resp = h.query(lrf.makeRequest("q", "text:hello", CommonParams.DEBUG_QUERY, "true")); //System.out.println(resp); // second doc ranked first assertTrue( resp.indexOf("\"2\"") < resp.indexOf("\"1\"") ); @@ -290,7 +290,7 @@ public class BasicFunctionalityTest extends SolrTestCaseJ4 { assertQ(req("text:hello"), "//*[@numFound='2']" ); - String resp = h.query(lrf.makeRequest("q", "text:hello", "debugQuery", "true")); + String resp = h.query(lrf.makeRequest("q", "text:hello", CommonParams.DEBUG_QUERY, "true")); //System.out.println(resp); // second doc ranked first assertTrue( resp.indexOf("\"2\"") < resp.indexOf("\"1\"") ); diff --git a/solr/src/test/org/apache/solr/DisMaxRequestHandlerTest.java b/solr/src/test/org/apache/solr/DisMaxRequestHandlerTest.java index 2756a0f7be5..1ccdbc329bc 100644 --- a/solr/src/test/org/apache/solr/DisMaxRequestHandlerTest.java +++ b/solr/src/test/org/apache/solr/DisMaxRequestHandlerTest.java @@ -17,6 +17,7 @@ package org.apache.solr; +import org.apache.solr.common.params.CommonParams; import org.junit.BeforeClass; import org.junit.Test; @@ -120,7 +121,7 @@ public class DisMaxRequestHandlerTest extends SolrTestCaseJ4 { ,"version", "2.0" ,"bq", "subject:hell^400" ,"bq", "subject:cool^4" - ,"debugQuery", "true" + , CommonParams.DEBUG_QUERY, "true" ) ,"//*[@numFound='3']" ,"//result/doc[1]/int[@name='id'][.='666']" @@ -179,7 +180,7 @@ public class DisMaxRequestHandlerTest extends SolrTestCaseJ4 { ,"qt", "dismax" ,"version", "2.0" ,"bq", "subject:hell OR subject:cool" - ,"debugQuery", "true" + ,CommonParams.DEBUG_QUERY, "true" )); assertTrue(p.matcher(resp).find()); assertFalse(p_bool.matcher(resp).find()); @@ -189,7 +190,7 @@ public class DisMaxRequestHandlerTest extends SolrTestCaseJ4 { ,"version", "2.0" ,"bq", "subject:hell OR subject:cool" ,"bq","" - ,"debugQuery", "true" + ,CommonParams.DEBUG_QUERY, "true" )); assertTrue(p.matcher(resp).find()); assertTrue(p_bool.matcher(resp).find()); diff --git a/solr/src/test/org/apache/solr/TestDistributedSearch.java b/solr/src/test/org/apache/solr/TestDistributedSearch.java index a995e45c8b4..eeff016f2d2 100755 --- a/solr/src/test/org/apache/solr/TestDistributedSearch.java +++ b/solr/src/test/org/apache/solr/TestDistributedSearch.java @@ -20,6 +20,7 @@ package org.apache.solr; import junit.framework.TestCase; import org.apache.solr.client.solrj.SolrServerException; +import org.apache.solr.common.params.CommonParams; /** * TODO? perhaps use: @@ -133,7 +134,7 @@ public class TestDistributedSearch extends BaseDistributedSearchTestCase { handle.put("time", SKIPVAL); query("q","now their fox sat had put","fl","*,score", - "debugQuery", "true"); + CommonParams.DEBUG_QUERY, "true"); // TODO: This test currently fails because debug info is obtained only // on shards with matches. diff --git a/solr/src/test/org/apache/solr/handler/component/DebugComponentTest.java b/solr/src/test/org/apache/solr/handler/component/DebugComponentTest.java new file mode 100644 index 00000000000..ae35d4aea04 --- /dev/null +++ b/solr/src/test/org/apache/solr/handler/component/DebugComponentTest.java @@ -0,0 +1,142 @@ +package org.apache.solr.handler.component; +/** + * 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.solr.SolrTestCaseJ4; +import org.apache.solr.common.params.CommonParams; +import org.junit.BeforeClass; +import org.junit.Test; + + +/** + * + * + **/ +public class DebugComponentTest extends SolrTestCaseJ4 { + @BeforeClass + public static void beforeClass() throws Exception { + initCore("solrconfig.xml", "schema.xml"); + assertU(adoc("id", "1", "title", "this is a title.")); + assertU(adoc("id", "2", "title", "this is another title.")); + assertU(adoc("id", "3", "title", "Mary had a little lamb.")); + assertU(commit()); + + } + + @Test + public void testBasicInterface() throws Exception { + //make sure the basics are in place + assertQ(req("q", "*:*", CommonParams.DEBUG_QUERY, "true"), + "//str[@name='rawquerystring']='*:*'", + "//str[@name='querystring']='*:*'", + "//str[@name='parsedquery']='MatchAllDocsQuery(*:*)'", + "//str[@name='parsedquery_toString']='*:*'", + "count(//lst[@name='explain']/*)=3", + "//lst[@name='explain']/str[@name='1']", + "//lst[@name='explain']/str[@name='2']", + "//lst[@name='explain']/str[@name='3']", + "//str[@name='QParser']",// make sure the QParser is specified + "count(//lst[@name='timing']/*)=3", //should be three pieces to timings + "//lst[@name='timing']/double[@name='time']", //make sure we have a time value, but don't specify it's result + "count(//lst[@name='prepare']/*)>0", + "//lst[@name='prepare']/double[@name='time']", + "count(//lst[@name='process']/*)>0", + "//lst[@name='process']/double[@name='time']" + ); + } + + // Test the ability to specify which pieces to include + + @Test + public void testPerItemInterface() throws Exception { + //Same as debugQuery = true + assertQ(req("q", "*:*", "debug", "true"), + "//str[@name='rawquerystring']='*:*'", + "//str[@name='querystring']='*:*'", + "//str[@name='parsedquery']='MatchAllDocsQuery(*:*)'", + "//str[@name='parsedquery_toString']='*:*'", + "//str[@name='QParser']",// make sure the QParser is specified + "count(//lst[@name='explain']/*)=3", + "//lst[@name='explain']/str[@name='1']", + "//lst[@name='explain']/str[@name='2']", + "//lst[@name='explain']/str[@name='3']", + "count(//lst[@name='timing']/*)=3", //should be three pieces to timings + "//lst[@name='timing']/double[@name='time']", //make sure we have a time value, but don't specify it's result + "count(//lst[@name='prepare']/*)>0", + "//lst[@name='prepare']/double[@name='time']", + "count(//lst[@name='process']/*)>0", + "//lst[@name='process']/double[@name='time']" + ); + //timing only + assertQ(req("q", "*:*", "debug", CommonParams.TIMING), + "count(//str[@name='rawquerystring'])=0", + "count(//str[@name='querystring'])=0", + "count(//str[@name='parsedquery'])=0", + "count(//str[@name='parsedquery_toString'])=0", + "count(//lst[@name='explain']/*)=0", + "count(//str[@name='QParser'])=0",// make sure the QParser is specified + "count(//lst[@name='timing']/*)=3", //should be three pieces to timings + "//lst[@name='timing']/double[@name='time']", //make sure we have a time value, but don't specify it's result + "count(//lst[@name='prepare']/*)>0", + "//lst[@name='prepare']/double[@name='time']", + "count(//lst[@name='process']/*)>0", + "//lst[@name='process']/double[@name='time']" + ); + //query only + assertQ(req("q", "*:*", "debug", CommonParams.QUERY), + "//str[@name='rawquerystring']='*:*'", + "//str[@name='querystring']='*:*'", + "//str[@name='parsedquery']='MatchAllDocsQuery(*:*)'", + "//str[@name='parsedquery_toString']='*:*'", + "count(//lst[@name='explain']/*)=0", + "//str[@name='QParser']",// make sure the QParser is specified + "count(//lst[@name='timing']/*)=0" + + ); + + //explains + assertQ(req("q", "*:*", "debug", CommonParams.RESULTS), + "count(//str[@name='rawquerystring'])=0", + "count(//str[@name='querystring'])=0", + "count(//str[@name='parsedquery'])=0", + "count(//str[@name='parsedquery_toString'])=0", + "count(//lst[@name='explain']/*)=3", + "//lst[@name='explain']/str[@name='1']", + "//lst[@name='explain']/str[@name='2']", + "//lst[@name='explain']/str[@name='3']", + "count(//str[@name='QParser'])=0",// make sure the QParser is specified + "count(//lst[@name='timing']/*)=0" + ); + + assertQ(req("q", "*:*", "debug", CommonParams.RESULTS, + "debug", CommonParams.QUERY), + "//str[@name='rawquerystring']='*:*'", + "//str[@name='querystring']='*:*'", + "//str[@name='parsedquery']='MatchAllDocsQuery(*:*)'", + "//str[@name='parsedquery_toString']='*:*'", + "//str[@name='QParser']",// make sure the QParser is specified + + "count(//lst[@name='explain']/*)=3", + "//lst[@name='explain']/str[@name='1']", + "//lst[@name='explain']/str[@name='2']", + "//lst[@name='explain']/str[@name='3']", + + "count(//lst[@name='timing']/*)=0" + ); + + } +} diff --git a/solr/src/test/org/apache/solr/search/TestQueryTypes.java b/solr/src/test/org/apache/solr/search/TestQueryTypes.java index b88aa556a60..8160f1095d4 100755 --- a/solr/src/test/org/apache/solr/search/TestQueryTypes.java +++ b/solr/src/test/org/apache/solr/search/TestQueryTypes.java @@ -16,6 +16,7 @@ */ package org.apache.solr.search; +import org.apache.solr.common.params.CommonParams; import org.apache.solr.util.AbstractSolrTestCase; public class TestQueryTypes extends AbstractSolrTestCase { @@ -262,7 +263,7 @@ public class TestQueryTypes extends AbstractSolrTestCase { ,"qf","v_t" ,"bf","sqrt(v_f)^100 log(sum(v_f,1))^50" ,"bq","{!prefix f=v_t}he" - ,"debugQuery","on" + , CommonParams.DEBUG_QUERY,"on" ) ,"//result[@numFound='2']" );