OK. So I think I fixed the whitespace problem. Thanks for explaining the problem with interfaces -- that makes sense now. I assume that EventListener and RequestHandler are interfaces because they've been thought long and hard about and are not going to change?

My first try at the patch was just to include the public methods, which are the ones you list:
.initialize(Config)
.isHighlightEnabled(SolrParams)
.doHighlighting(...)
.getHighlightFields(...)
I discovered that the unit tests call the formatters and fragmenters directly so in the interface version I made public get methods for these. Now that we're using an abstract class I am able to just include these as is - so no changes to HighlighterTest this time. But speaking of unit tests... Anecdotally I know that the SolrCore changes allow the highlighter to be configured (my custom highlighter). I guess I should write a unit test that ensures this works. I'll do that now.

After doing some thinking I decided to leave the default implementation of isHighlightingEnabled(SolrParams), and getHighlightFields(...) in the abstract class because both methods deal with reading parameters. I can't think of a use case of a highlighter that wouldn't use this or at worst ignore/override this method. Is this a reasonable decision?

I wasn't sure what to do with the logger, so I left it in the AbstractSolrHighlighter.

Thoughts?
Tricia

p.s.  I've attached the updated patch since JIRA appears to be down.

Mike Klaas wrote:
Hi Tricia,

I don't suggest removing whitespace by hand--the best way to avoid the situation is to make sure that your editor does not automatically edit the whitespace of a file (some good-intentioned editors do this).

The maintenance concern with interfaces is that if we want to add a method to the highlighter contract, doing so will break all custom implementors of the interface. If it is an abstract class that must be subclassed, we can add a default implementation to the new method without breaking everyone's code.

As for the contract, I was going to review the proposed patch to see what the interface consists of, but I think that all that is required is:

.initialize(Config)
.isHighlightEnabled(SolrParams)
.doHighlighting(...)
.getHighlightFields(...)

(which is probably what you had in your patch already---JIRA seems down at the moment so I can't check).

cheers,
-Mike

On 5-Mar-08, at 2:49 PM, Tricia Williams wrote:

Thanks to Grant and Mike for the feedback! It is much appreciated. Is there a quick and easy way to check for unnecessary whitespace changes? It isn't that hard for me to go through the patch by hand to find and remove them, but if there is an easier way I'm happy to hear it.

I had taken the suggestion that Eli gave somewhat literally and made SolrHighlighter an interface like RequestHandler. In the SolrCore there are three existing objects that are configured: SolrEventListener, SolrRequestHandler, and UpdateHandler. The first two are interfaces and the third is an abstract class. While I'm sure the maintenance concern is legitimate, I'm not sure what the maintenance concern is - could someone elaborate?

I'll take your advice and make an AbstractSolrHighlighter that SolrHighlighter (and my custom highlighter) extends. I noticed that some of the other configurable objects implement SolrInfoMBean. Is this something that the SolrHighlighter/AbstractSolrHighlighter should also do?

Thanks,
Tricia

Mike Klaas (JIRA) wrote:
[ https://issues.apache.org/jira/browse/SOLR-386?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=12574337#action_12574337 ]
Mike Klaas commented on SOLR-386:
---------------------------------

Hi Tricia,

I'm not sure that I would ever use SolrHighlighter overriding (if I had the need, I probably would just make a separate search component). However, several people want this functionality and it has relatively low implementation/maintenance cost.

There are a few things that need to be done to get the patch committed. First, the unnecessary whitespace changes in SolrCore shouldn't be in the diff (it makes it really hard to see the changes, and might make it hard to apply/revert). Also, I'm skeptical of using interfaces for this type of thing, for maintenance reasons. Perhaps we could move to one of the approaches that Grant suggested?

Thanks again for the contribution, and sorry it took so long




Add confuguration to specify SolrHighlighter implementation
-----------------------------------------------------------

               Key: SOLR-386
               URL: https://issues.apache.org/jira/browse/SOLR-386
           Project: Solr
        Issue Type: Improvement
        Components: highlighter
  Affects Versions: 1.3
          Reporter: Eli Levine
          Assignee: Mike Klaas
Attachments: SOLR-386-SolrHighlighter.patch, SOLR-386-SolrHighlighter.patch, SOLR-386-SolrHighlighter.patch, SOLR-386-SolrHighlighter.patch


It would be great if SolrCore allowed the highlighter class to be configurable. A good way would be to add a +class+ attribute to the <highlighting> element in solrconfig.xml, similar to how the RequestHandler instance is initialized in SorCore.







Index: CHANGES.txt
===================================================================
--- CHANGES.txt	(revision 634072)
+++ CHANGES.txt	(working copy)
@@ -541,6 +541,11 @@
 35. SOLR-249: Deprecated SolrException( int, ... ) constructors in favor 
     of constructors that takes an ErrorCode enum.  This will ensure that
     all SolrExceptions use a valid HTTP status code. (ryan)
+    
+xx. SOLR-386: Abstracted SolrHighlighter to AbstractSolrHighlighter and  
+	extended in SolrHighlighter.  Adjusted SolrCore and solrconfig.xml so 
+	that highlighter is configurable via a class attribute.  Allows users 
+	to use their own highlighter implementation. (Tricia Williams) 
         
 Changes in runtime behavior
  1. Highlighting using DisMax will only pick up terms from the main 
Index: src/test/test-files/solr/conf/solrconfig.xml
===================================================================
--- src/test/test-files/solr/conf/solrconfig.xml	(revision 634072)
+++ src/test/test-files/solr/conf/solrconfig.xml	(working copy)
@@ -318,7 +318,7 @@
   </requestHandler>
   
 
-  <highlighting>
+  <highlighting class="org.apache.solr.highlight.SolrHighlighter">
    <!-- Configure the standard fragmenter -->
    <fragmenter name="gap" class="org.apache.solr.highlight.GapFragmenter" default="true">
     <lst name="defaults">
Index: src/java/org/apache/solr/highlight/AbstractSolrHighlighter.java
===================================================================
--- src/java/org/apache/solr/highlight/AbstractSolrHighlighter.java	(revision 0)
+++ src/java/org/apache/solr/highlight/AbstractSolrHighlighter.java	(revision 0)
@@ -0,0 +1,95 @@
+package org.apache.solr.highlight;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.logging.Logger;
+
+import org.apache.lucene.search.Query;
+import org.apache.lucene.search.highlight.Formatter;
+import org.apache.lucene.search.highlight.Fragmenter;
+import org.apache.lucene.search.highlight.Highlighter;
+import org.apache.lucene.search.highlight.QueryScorer;
+import org.apache.solr.common.SolrException;
+import org.apache.solr.common.params.HighlightParams;
+import org.apache.solr.common.params.SolrParams;
+import org.apache.solr.common.util.NamedList;
+import org.apache.solr.core.Config;
+import org.apache.solr.request.SolrQueryRequest;
+import org.apache.solr.search.DocList;
+import org.apache.solr.util.SolrPluginUtils;
+
+public abstract class AbstractSolrHighlighter 
+{
+	public static Logger log = Logger.getLogger(AbstractSolrHighlighter.class.getName());
+
+	// Thread safe registry
+	protected final Map<String,SolrFormatter> formatters = 
+		Collections.synchronizedMap( new HashMap<String, SolrFormatter>() );
+
+	// Thread safe registry
+	protected final Map<String,SolrFragmenter> fragmenters = 
+		Collections.synchronizedMap( new HashMap<String, SolrFragmenter>() );
+
+	public abstract void initalize( final Config config );
+
+
+	/**
+	 * Check whether Highlighting is enabled for this request.
+	 * @param params The params controlling Highlighting
+	 * @return <code>true</code> if highlighting enabled, <code>false</code> if not.
+	 */
+	public boolean isHighlightingEnabled(SolrParams params) {
+		return params.getBool(HighlightParams.HIGHLIGHT, false);
+	}
+
+	/**
+	 * Return a String array of the fields to be highlighted.
+	 * Falls back to the programatic defaults, or the default search field if the list of fields
+	 * is not specified in either the handler configuration or the request.
+	 * @param query The current Query
+	 * @param request The current SolrQueryRequest
+	 * @param defaultFields Programmatic default highlight fields, used if nothing is specified in the handler config or the request.
+	 */
+	public String[] getHighlightFields(Query query, SolrQueryRequest request, String[] defaultFields) {
+		String fields[] = request.getParams().getParams(HighlightParams.FIELDS);
+
+		// if no fields specified in the request, or the handler, fall back to programmatic default, or default search field.
+		if(emptyArray(fields)) {
+			// use default search field if highlight fieldlist not specified.
+			if (emptyArray(defaultFields)) {
+				String defaultSearchField = request.getSchema().getSolrQueryParser(null).getField();
+				fields = null == defaultSearchField ? new String[]{} : new String[]{defaultSearchField};
+			}  
+			else {
+				fields = defaultFields;
+			}
+		}
+		else if (fields.length == 1) {
+			// if there's a single request/handler value, it may be a space/comma separated list
+			fields = SolrPluginUtils.split(fields[0]);
+		}
+
+		return fields;
+	}
+
+	protected boolean emptyArray(String[] arr) {
+		return (arr == null || arr.length == 0 || arr[0] == null || arr[0].trim().length() == 0);
+	}
+
+	/**
+	 * Generates a list of Highlighted query fragments for each item in a list
+	 * of documents, or returns null if highlighting is disabled.
+	 *
+	 * @param docs query results
+	 * @param query the query
+	 * @param req the current request
+	 * @param defaultFields default list of fields to summarize
+	 *
+	 * @return NamedList containing a NamedList for each document, which in 
+	 * turns contains sets (field, summary) pairs.
+	 */
+	@SuppressWarnings("unchecked")
+	public abstract NamedList<Object> doHighlighting(DocList docs, Query query, SolrQueryRequest req, String[] defaultFields) throws IOException;
+}
Index: src/java/org/apache/solr/highlight/SolrHighlighter.java
===================================================================
--- src/java/org/apache/solr/highlight/SolrHighlighter.java	(revision 634072)
+++ src/java/org/apache/solr/highlight/SolrHighlighter.java	(working copy)
@@ -62,18 +62,9 @@
  * 
  * @since solr 1.3
  */
-public class SolrHighlighter 
+public class SolrHighlighter extends AbstractSolrHighlighter
 {
-  public static Logger log = Logger.getLogger(SolrHighlighter.class.getName());
   
-  // Thread safe registry
-  protected final Map<String,SolrFormatter> formatters = 
-    Collections.synchronizedMap( new HashMap<String, SolrFormatter>() );
-
-  // Thread safe registry
-  protected final Map<String,SolrFragmenter> fragmenters = 
-    Collections.synchronizedMap( new HashMap<String, SolrFragmenter>() );
-  
   public void initalize( final Config config )
   {
     formatters.clear();
@@ -102,15 +93,6 @@
   
   
   /**
-   * Check whether Highlighting is enabled for this request.
-   * @param params The params controlling Highlighting
-   * @return <code>true</code> if highlighting enabled, <code>false</code> if not.
-   */
-  public boolean isHighlightingEnabled(SolrParams params) {
-    return params.getBool(HighlightParams.HIGHLIGHT, false);
-  }
-  
-  /**
    * Return a Highlighter appropriate for this field.
    * @param query The current Query
    * @param fieldName The name of the field
@@ -145,40 +127,6 @@
   }
   
   /**
-   * Return a String array of the fields to be highlighted.
-   * Falls back to the programatic defaults, or the default search field if the list of fields
-   * is not specified in either the handler configuration or the request.
-   * @param query The current Query
-   * @param request The current SolrQueryRequest
-   * @param defaultFields Programmatic default highlight fields, used if nothing is specified in the handler config or the request.
-   */
-  public String[] getHighlightFields(Query query, SolrQueryRequest request, String[] defaultFields) {
-     String fields[] = request.getParams().getParams(HighlightParams.FIELDS);
-     
-     // if no fields specified in the request, or the handler, fall back to programmatic default, or default search field.
-     if(emptyArray(fields)) {
-        // use default search field if highlight fieldlist not specified.
-        if (emptyArray(defaultFields)) {
-           String defaultSearchField = request.getSchema().getDefaultSearchFieldName();
-           fields = null == defaultSearchField ? new String[]{} : new String[]{defaultSearchField};
-        }  
-        else {
-           fields = defaultFields;
-        }
-     }
-     else if (fields.length == 1) {
-        // if there's a single request/handler value, it may be a space/comma separated list
-        fields = SolrPluginUtils.split(fields[0]);
-     }
-     
-     return fields;
-  }
-  
-  protected boolean emptyArray(String[] arr) {
-     return (arr == null || arr.length == 0 || arr[0] == null || arr[0].trim().length() == 0);
-  }
-  
-  /**
    * Return the max number of snippets for this field. If this has not
    * been configured for this field, fall back to the configured default
    * or the solr default.
Index: src/java/org/apache/solr/core/SolrCore.java
===================================================================
--- src/java/org/apache/solr/core/SolrCore.java	(revision 634072)
+++ src/java/org/apache/solr/core/SolrCore.java	(working copy)
@@ -305,8 +305,11 @@
   private UpdateHandler createUpdateHandler(String className) {
     return createInstance(className, UpdateHandler.class, "Update Handler");
   }
-
   
+  private SolrHighlighter createHighlighter(String className) {
+	return createInstance(className, SolrHighlighter.class, "Highlighter");
+  }
+  
   /** 
    * @return the last core initialized.  If you are using multiple cores, 
    * this is not a function to use.
@@ -379,8 +382,9 @@
       reqHandlers = new RequestHandlers(this);
       reqHandlers.initHandlersFromConfig( solrConfig );
   
-      // TODO? could select the highlighter implementation
-      highlighter = new SolrHighlighter();
+      highlighter = createHighlighter(
+    		  solrConfig.get("highlighting/@class", SolrHighlighter.class.getName())
+      );
       highlighter.initalize( solrConfig );
       
       try {

Reply via email to