You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by rm...@apache.org on 2013/01/07 19:33:13 UTC

svn commit: r1429935 - in /lucene/dev/trunk/solr: ./ core/src/java/org/apache/solr/handler/ core/src/java/org/apache/solr/handler/component/ core/src/java/org/apache/solr/response/ core/src/java/org/apache/solr/search/ core/src/test/org/apache/solr/req...

Author: rmuir
Date: Mon Jan  7 18:33:12 2013
New Revision: 1429935

URL: http://svn.apache.org/viewvc?rev=1429935&view=rev
Log:
SOLR-4226: Extract fl parsing code out of ReturnFields constructor

Added:
    lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/SolrReturnFields.java   (with props)
Modified:
    lucene/dev/trunk/solr/CHANGES.txt
    lucene/dev/trunk/solr/core/src/java/org/apache/solr/handler/MoreLikeThisHandler.java
    lucene/dev/trunk/solr/core/src/java/org/apache/solr/handler/component/QueryComponent.java
    lucene/dev/trunk/solr/core/src/java/org/apache/solr/handler/component/RealTimeGetComponent.java
    lucene/dev/trunk/solr/core/src/java/org/apache/solr/handler/component/TermVectorComponent.java
    lucene/dev/trunk/solr/core/src/java/org/apache/solr/response/SolrQueryResponse.java
    lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/ReturnFields.java
    lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/SolrIndexSearcher.java
    lucene/dev/trunk/solr/core/src/test/org/apache/solr/request/JSONWriterTest.java
    lucene/dev/trunk/solr/core/src/test/org/apache/solr/request/TestBinaryResponseWriter.java
    lucene/dev/trunk/solr/core/src/test/org/apache/solr/response/TestCSVResponseWriter.java
    lucene/dev/trunk/solr/core/src/test/org/apache/solr/search/ReturnFieldsTest.java

Modified: lucene/dev/trunk/solr/CHANGES.txt
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/CHANGES.txt?rev=1429935&r1=1429934&r2=1429935&view=diff
==============================================================================
--- lucene/dev/trunk/solr/CHANGES.txt (original)
+++ lucene/dev/trunk/solr/CHANGES.txt Mon Jan  7 18:33:12 2013
@@ -558,6 +558,9 @@ Other Changes
 * SOLR-4254: Harden the 'leader requests replica to recover' code path.
   (Mark Miller, yonik)
 
+* SOLR-4226: Extract fl parsing code out of ReturnFields constructor.
+  (Ryan Ernst via Robert Muir)
+
 ==================  4.0.0 ==================
 
 Versions of Major Components

Modified: lucene/dev/trunk/solr/core/src/java/org/apache/solr/handler/MoreLikeThisHandler.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/java/org/apache/solr/handler/MoreLikeThisHandler.java?rev=1429935&r1=1429934&r2=1429935&view=diff
==============================================================================
--- lucene/dev/trunk/solr/core/src/java/org/apache/solr/handler/MoreLikeThisHandler.java (original)
+++ lucene/dev/trunk/solr/core/src/java/org/apache/solr/handler/MoreLikeThisHandler.java Mon Jan  7 18:33:12 2013
@@ -76,7 +76,7 @@ public class MoreLikeThisHandler extends
     SolrParams params = req.getParams();
 
     // Set field flags
-    ReturnFields returnFields = new ReturnFields( req );
+    ReturnFields returnFields = new SolrReturnFields( req );
     rsp.setReturnFields( returnFields );
     int flags = 0;
     if (returnFields.wantsScore()) {

Modified: lucene/dev/trunk/solr/core/src/java/org/apache/solr/handler/component/QueryComponent.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/java/org/apache/solr/handler/component/QueryComponent.java?rev=1429935&r1=1429934&r2=1429935&view=diff
==============================================================================
--- lucene/dev/trunk/solr/core/src/java/org/apache/solr/handler/component/QueryComponent.java (original)
+++ lucene/dev/trunk/solr/core/src/java/org/apache/solr/handler/component/QueryComponent.java Mon Jan  7 18:33:12 2013
@@ -93,7 +93,7 @@ public class QueryComponent extends Sear
     SolrQueryResponse rsp = rb.rsp;
 
     // Set field flags    
-    ReturnFields returnFields = new ReturnFields( req );
+    ReturnFields returnFields = new SolrReturnFields( req );
     rsp.setReturnFields( returnFields );
     int flags = 0;
     if (returnFields.wantsScore()) {

Modified: lucene/dev/trunk/solr/core/src/java/org/apache/solr/handler/component/RealTimeGetComponent.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/java/org/apache/solr/handler/component/RealTimeGetComponent.java?rev=1429935&r1=1429934&r2=1429935&view=diff
==============================================================================
--- lucene/dev/trunk/solr/core/src/java/org/apache/solr/handler/component/RealTimeGetComponent.java (original)
+++ lucene/dev/trunk/solr/core/src/java/org/apache/solr/handler/component/RealTimeGetComponent.java Mon Jan  7 18:33:12 2013
@@ -56,6 +56,7 @@ import org.apache.solr.schema.IndexSchem
 import org.apache.solr.schema.SchemaField;
 import org.apache.solr.search.ReturnFields;
 import org.apache.solr.search.SolrIndexSearcher;
+import org.apache.solr.search.SolrReturnFields;
 import org.apache.solr.update.DocumentBuilder;
 import org.apache.solr.update.PeerSync;
 import org.apache.solr.update.UpdateLog;
@@ -72,7 +73,7 @@ public class RealTimeGetComponent extend
   @Override
   public void prepare(ResponseBuilder rb) throws IOException {
     // Set field flags
-    ReturnFields returnFields = new ReturnFields( rb.req );
+    ReturnFields returnFields = new SolrReturnFields( rb.req );
     rb.rsp.setReturnFields( returnFields );
   }
 

Modified: lucene/dev/trunk/solr/core/src/java/org/apache/solr/handler/component/TermVectorComponent.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/java/org/apache/solr/handler/component/TermVectorComponent.java?rev=1429935&r1=1429934&r2=1429935&view=diff
==============================================================================
--- lucene/dev/trunk/solr/core/src/java/org/apache/solr/handler/component/TermVectorComponent.java (original)
+++ lucene/dev/trunk/solr/core/src/java/org/apache/solr/handler/component/TermVectorComponent.java Mon Jan  7 18:33:12 2013
@@ -36,6 +36,7 @@ import org.apache.solr.search.ReturnFiel
 import org.apache.solr.search.DocList;
 import org.apache.solr.search.DocListAndSet;
 import org.apache.solr.search.SolrIndexSearcher;
+import org.apache.solr.search.SolrReturnFields;
 import org.apache.solr.util.SolrPluginUtils;
 import org.apache.solr.util.plugin.SolrCoreAware;
 
@@ -111,7 +112,7 @@ public class TermVectorComponent extends
         (1 == fldLst.length && 0 == fldLst[0].length())) {
 
       // no tv.fl, parse the main fl
-      ReturnFields rf = new ReturnFields
+      ReturnFields rf = new SolrReturnFields
         (params.getParams(CommonParams.FL), rb.req);
 
       if (rf.wantsAllFields()) {

Modified: lucene/dev/trunk/solr/core/src/java/org/apache/solr/response/SolrQueryResponse.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/java/org/apache/solr/response/SolrQueryResponse.java?rev=1429935&r1=1429934&r2=1429935&view=diff
==============================================================================
--- lucene/dev/trunk/solr/core/src/java/org/apache/solr/response/SolrQueryResponse.java (original)
+++ lucene/dev/trunk/solr/core/src/java/org/apache/solr/response/SolrQueryResponse.java Mon Jan  7 18:33:12 2013
@@ -20,6 +20,7 @@ package org.apache.solr.response;
 import org.apache.solr.common.util.NamedList;
 import org.apache.solr.common.util.SimpleOrderedMap;
 import org.apache.solr.search.ReturnFields;
+import org.apache.solr.search.SolrReturnFields;
 
 import java.util.*;
 
@@ -123,7 +124,7 @@ public class SolrQueryResponse {
    */
   public ReturnFields getReturnFields() {
     if( returnFields == null ) {
-      returnFields = new ReturnFields(); // by default return everything
+      returnFields = new SolrReturnFields(); // by default return everything
     }
     return returnFields;
   }

Modified: lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/ReturnFields.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/ReturnFields.java?rev=1429935&r1=1429934&r2=1429935&view=diff
==============================================================================
--- lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/ReturnFields.java (original)
+++ lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/ReturnFields.java Mon Jan  7 18:33:12 2013
@@ -18,25 +18,7 @@ package org.apache.solr.search;
 
 import java.util.*;
 
-import org.apache.commons.io.FilenameUtils;
-import org.apache.lucene.queries.function.FunctionQuery;
-import org.apache.lucene.queries.function.ValueSource;
-import org.apache.lucene.queries.function.valuesource.QueryValueSource;
-import org.apache.lucene.search.Query;
-import org.apache.solr.common.SolrException;
-import org.apache.solr.common.params.CommonParams;
-import org.apache.solr.common.params.MapSolrParams;
-import org.apache.solr.common.params.SolrParams;
-import org.apache.solr.common.util.NamedList;
-import org.apache.solr.request.SolrQueryRequest;
 import org.apache.solr.response.transform.DocTransformer;
-import org.apache.solr.response.transform.DocTransformers;
-import org.apache.solr.response.transform.RenameFieldTransformer;
-import org.apache.solr.response.transform.ScoreAugmenter;
-import org.apache.solr.response.transform.TransformerFactory;
-import org.apache.solr.response.transform.ValueSourceAugmenter;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
 
 /**
  * A class representing the return fields
@@ -44,376 +26,24 @@ import org.slf4j.LoggerFactory;
  *
  * @since solr 4.0
  */
-public class ReturnFields
-{
-  static final Logger log = LoggerFactory.getLogger( ReturnFields.class );
+public abstract class ReturnFields {
+  /**
+   * Set of field names with their exact names from the lucene index.
+   * <p>
+   * Class such as ResponseWriters pass this to {@link SolrIndexSearcher#doc(int, Set)}.
+   * @return Set of field names or <code>null</code> (all fields).
+   */
+  public abstract Set<String> getLuceneFieldNames();
+
+  /** Returns <code>true</code> if the specified field should be returned. */
+  public abstract boolean wantsField(String name);
 
-  // Special Field Keys
-  public static final String SCORE = "score";
+  /** Returns <code>true</code> if all fields should be returned. */
+  public abstract boolean wantsAllFields();
 
-  private final List<String> globs = new ArrayList<String>(1);
-  
-  // The lucene field names to request from the SolrIndexSearcher
-  // Order is important for CSVResponseWriter
-  private final Set<String> fields = new LinkedHashSet<String>();
-  
-  // Field names that are OK to include in the response.
-  // This will include pseudo fields, lucene fields, and matching globs
-  private Set<String> okFieldNames = new HashSet<String>(); 
+  /** Returns <code>true</code> if the score should be returned. */
+  public abstract boolean wantsScore();
 
-  // The list of explicitly requested fields
-  private Set<String> reqFieldNames = null;
-  
-  private DocTransformer transformer;
-  private boolean _wantsScore = false;
-  private boolean _wantsAllFields = false;
-
-  public ReturnFields() {
-    _wantsAllFields = true;
-  }
-
-  public ReturnFields(SolrQueryRequest req) {
-    this( req.getParams().getParams(CommonParams.FL), req );
-  }
-
-  public ReturnFields(String fl, SolrQueryRequest req) {
-//    this( (fl==null)?null:SolrPluginUtils.split(fl), req );
-    if( fl == null ) {
-      parseFieldList((String[])null, req);
-    }
-    else {
-      if( fl.trim().length() == 0 ) {
-        // legacy thing to support fl='  ' => fl=*,score!
-        // maybe time to drop support for this?
-        // See ConvertedLegacyTest
-        _wantsScore = true;
-        _wantsAllFields = true;
-        transformer = new ScoreAugmenter(SCORE);
-      }
-      else {
-        parseFieldList( new String[]{fl}, req);
-      }
-    }
-  }
-
-  public ReturnFields(String[] fl, SolrQueryRequest req) {
-    parseFieldList(fl, req);
-  }
-
-  private void parseFieldList(String[] fl, SolrQueryRequest req) {
-    _wantsScore = false;
-    _wantsAllFields = false;
-    if (fl == null || fl.length == 0 || fl.length == 1 && fl[0].length()==0) {
-      _wantsAllFields = true;
-      return;
-    }
-
-    NamedList<String> rename = new NamedList<String>();
-    DocTransformers augmenters = new DocTransformers();
-    for (String fieldList : fl) {
-      add(fieldList,rename,augmenters,req);
-    }
-    for( int i=0; i<rename.size(); i++ ) {
-      String from = rename.getName(i);
-      String to = rename.getVal(i);
-      okFieldNames.add( to );
-      boolean copy = (reqFieldNames!=null && reqFieldNames.contains(from));
-      if(!copy) {
-        // Check that subsequent copy/rename requests have the field they need to copy
-        for(int j=i+1; j<rename.size(); j++) {
-          if(from.equals(rename.getName(j))) {
-            rename.setName(j, to); // copy from the current target
-            if(reqFieldNames==null) {
-              reqFieldNames = new HashSet<String>();
-            }
-            reqFieldNames.add(to); // don't rename our current target
-          }
-        }
-      }
-      augmenters.addTransformer( new RenameFieldTransformer( from, to, copy ) );     
-    }
-
-    if( !_wantsAllFields ) {
-      if( !globs.isEmpty() ) {
-        // TODO??? need to fill up the fields with matching field names in the index
-        // and add them to okFieldNames?
-        // maybe just get all fields?
-        // this would disable field selection optimization... i think thatis OK
-        fields.clear(); // this will get all fields, and use wantsField to limit
-      }
-      okFieldNames.addAll( fields );
-    }
-
-    if( augmenters.size() == 1 ) {
-      transformer = augmenters.getTransformer(0);
-    }
-    else if( augmenters.size() > 1 ) {
-      transformer = augmenters;
-    }
-  }
-
-
-  // like getId, but also accepts dashes for legacy fields
-  String getFieldName(QueryParsing.StrParser sp) {
-    sp.eatws();
-    int id_start = sp.pos;
-    char ch;
-    if (sp.pos < sp.end && (ch = sp.val.charAt(sp.pos)) != '$' && Character.isJavaIdentifierStart(ch)) {
-      sp.pos++;
-      while (sp.pos < sp.end) {
-        ch = sp.val.charAt(sp.pos);
-        if (!Character.isJavaIdentifierPart(ch) && ch != '.' && ch != '-') {
-          break;
-        }
-        sp.pos++;
-      }
-      return sp.val.substring(id_start, sp.pos);
-    }
-
-    return null;
-  }
-
-  private void add(String fl, NamedList<String> rename, DocTransformers augmenters, SolrQueryRequest req) {
-    if( fl == null ) {
-      return;
-    }
-    try {
-      QueryParsing.StrParser sp = new QueryParsing.StrParser(fl);
-
-      for(;;) {
-        sp.opt(',');
-        sp.eatws();
-        if (sp.pos >= sp.end) break;
-
-        int start = sp.pos;
-
-        // short circuit test for a really simple field name
-        String key = null;
-        String field = getFieldName(sp);
-        char ch = sp.ch();
-
-        if (field != null) {
-          if (sp.opt(':')) {
-            // this was a key, not a field name
-            key = field;
-            field = null;
-            sp.eatws();
-            start = sp.pos;
-          } else {
-            if (Character.isWhitespace(ch) || ch == ',' || ch==0) {
-              addField( field, key, augmenters, req );
-              continue;
-            }
-            // an invalid field name... reset the position pointer to retry
-            sp.pos = start;
-            field = null;
-          }
-        }
-
-        if (key != null) {
-          // we read "key : "
-          field = sp.getId(null);
-          ch = sp.ch();
-          if (field != null && (Character.isWhitespace(ch) || ch == ',' || ch==0)) {
-            rename.add(field, key);
-            addField( field, key, augmenters, req );
-            continue;
-          }
-          // an invalid field name... reset the position pointer to retry
-          sp.pos = start;
-          field = null;
-        }
-
-        if (field == null) {
-          // We didn't find a simple name, so let's see if it's a globbed field name.
-          // Globbing only works with field names of the recommended form (roughly like java identifiers)
-
-          field = sp.getGlobbedId(null);
-          ch = sp.ch();
-          if (field != null && (Character.isWhitespace(ch) || ch == ',' || ch==0)) {
-            // "*" looks and acts like a glob, but we give it special treatment
-            if ("*".equals(field)) {
-              _wantsAllFields = true;
-            } else {
-              globs.add(field);
-            }
-            continue;
-          }
-
-          // an invalid glob
-          sp.pos = start;
-        }
-
-        String funcStr = sp.val.substring(start);
-
-        // Is it an augmenter of the form [augmenter_name foo=1 bar=myfield]?
-        // This is identical to localParams syntax except it uses [] instead of {!}
-
-        if (funcStr.startsWith("[")) {
-          Map<String,String> augmenterArgs = new HashMap<String,String>();
-          int end = QueryParsing.parseLocalParams(funcStr, 0, augmenterArgs, req.getParams(), "[", ']');
-          sp.pos += end;
-          
-          // [foo] is short for [type=foo] in localParams syntax
-          String augmenterName = augmenterArgs.remove("type"); 
-          String disp = key;
-          if( disp == null ) {
-            disp = '['+augmenterName+']';
-          }
-
-          TransformerFactory factory = req.getCore().getTransformerFactory( augmenterName );
-          if( factory != null ) {
-            MapSolrParams augmenterParams = new MapSolrParams( augmenterArgs );
-            augmenters.addTransformer( factory.create(disp, augmenterParams, req) );
-          }
-          else {
-            // unknown transformer?
-          }
-          addField(field, disp, augmenters, req);
-          continue;
-        }
-
-
-        // let's try it as a function instead
-        QParser parser = QParser.getParser(funcStr, FunctionQParserPlugin.NAME, req);
-        Query q = null;
-        ValueSource vs = null;
-
-        try {
-          if (parser instanceof FunctionQParser) {
-            FunctionQParser fparser = (FunctionQParser)parser;
-            fparser.setParseMultipleSources(false);
-            fparser.setParseToEnd(false);
-
-            q = fparser.getQuery();
-
-            if (fparser.localParams != null) {
-              if (fparser.valFollowedParams) {
-                // need to find the end of the function query via the string parser
-                int leftOver = fparser.sp.end - fparser.sp.pos;
-                sp.pos = sp.end - leftOver;   // reset our parser to the same amount of leftover
-              } else {
-                // the value was via the "v" param in localParams, so we need to find
-                // the end of the local params themselves to pick up where we left off
-                sp.pos = start + fparser.localParamsEnd;
-              }
-            } else {
-              // need to find the end of the function query via the string parser
-              int leftOver = fparser.sp.end - fparser.sp.pos;
-              sp.pos = sp.end - leftOver;   // reset our parser to the same amount of leftover
-            }
-          } else {
-            // A QParser that's not for function queries.
-            // It must have been specified via local params.
-            q = parser.getQuery();
-
-            assert parser.getLocalParams() != null;
-            sp.pos = start + parser.localParamsEnd;
-          }
-
-
-          if (q instanceof FunctionQuery) {
-            vs = ((FunctionQuery)q).getValueSource();
-          } else {
-            vs = new QueryValueSource(q, 0.0f);
-          }
-
-          if (key==null) {
-            SolrParams localParams = parser.getLocalParams();
-            if (localParams != null) {
-              key = localParams.get("key");
-            }
-            if (key == null) {
-              // use the function name itself as the field name
-              key = sp.val.substring(start, sp.pos);
-            }
-          }
-
-
-          if (key==null) {
-            key = funcStr;
-          }
-          okFieldNames.add( key );
-          okFieldNames.add( funcStr );
-          augmenters.addTransformer( new ValueSourceAugmenter( key, parser, vs ) );
-        }
-        catch (SyntaxError e) {
-          // try again, simple rules for a field name with no whitespace
-          sp.pos = start;
-          field = sp.getSimpleString();
-
-          if (req.getSchema().getFieldOrNull(field) != null) {
-            // OK, it was an oddly named field
-            fields.add(field);
-            if( key != null ) {
-              rename.add(field, key);
-            }
-          } else {
-            throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "Error parsing fieldname: " + e.getMessage(), e);
-          }
-        }
-
-       // end try as function
-
-      } // end for(;;)
-    } catch (SyntaxError e) {
-      throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "Error parsing fieldname", e);
-    }
-  }
-
-  private void addField( String field, String key, DocTransformers augmenters, SolrQueryRequest req )
-  {
-    if(key==null) {
-      if(reqFieldNames==null) {
-        reqFieldNames = new HashSet<String>();
-      }
-      reqFieldNames.add(field);
-    }
-    
-    fields.add(field); // need to put in the map to maintain order for things like CSVResponseWriter
-    okFieldNames.add( field );
-    okFieldNames.add( key );
-    // a valid field name
-    if(SCORE.equals(field)) {
-      _wantsScore = true;
-
-      String disp = (key==null) ? field : key;
-      augmenters.addTransformer( new ScoreAugmenter( disp ) );
-    }
-  }
-
-  public Set<String> getLuceneFieldNames()
-  {
-    return (_wantsAllFields || fields.isEmpty()) ? null : fields;
-  }
-
-  public boolean wantsAllFields()
-  {
-    return _wantsAllFields;
-  }
-
-  public boolean wantsScore()
-  {
-    return _wantsScore;
-  }
-
-  public boolean wantsField( String name )
-  {
-    if( _wantsAllFields || okFieldNames.contains( name ) ){
-      return true;
-    }
-    for( String s : globs ) {
-      // TODO something better?
-      if( FilenameUtils.wildcardMatch( name, s ) ) {
-        okFieldNames.add(name); // Don't calculate it again
-        return true;
-      }
-    }
-    return false;
-  }
-
-  public DocTransformer getTransformer()
-  {
-    return transformer;
-  }
+  /** Returns the DocTransformer used to modify documents, or <code>null</code> */
+  public abstract DocTransformer getTransformer();
 }

Modified: lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/SolrIndexSearcher.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/SolrIndexSearcher.java?rev=1429935&r1=1429934&r2=1429935&view=diff
==============================================================================
--- lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/SolrIndexSearcher.java (original)
+++ lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/SolrIndexSearcher.java Mon Jan  7 18:33:12 2013
@@ -565,8 +565,8 @@ public class SolrIndexSearcher extends I
 
   /**
    * Retrieve the {@link Document} instance corresponding to the document id.
-   *
-   * Note: The document will have all fields accessable, but if a field
+   * <p>
+   * Note: The document will have all fields accessible, but if a field
    * filter is provided, only the provided fields will be loaded (the 
    * remainder will be available lazily).
    */

Added: lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/SolrReturnFields.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/SolrReturnFields.java?rev=1429935&view=auto
==============================================================================
--- lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/SolrReturnFields.java (added)
+++ lucene/dev/trunk/solr/core/src/java/org/apache/solr/search/SolrReturnFields.java Mon Jan  7 18:33:12 2013
@@ -0,0 +1,421 @@
+/*
+ * 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.search;
+
+import org.apache.commons.io.FilenameUtils;
+import org.apache.lucene.queries.function.FunctionQuery;
+import org.apache.lucene.queries.function.ValueSource;
+import org.apache.lucene.queries.function.valuesource.QueryValueSource;
+import org.apache.lucene.search.Query;
+import org.apache.solr.common.SolrException;
+import org.apache.solr.common.params.CommonParams;
+import org.apache.solr.common.params.MapSolrParams;
+import org.apache.solr.common.params.SolrParams;
+import org.apache.solr.common.util.NamedList;
+import org.apache.solr.request.SolrQueryRequest;
+import org.apache.solr.response.transform.DocTransformer;
+import org.apache.solr.response.transform.DocTransformers;
+import org.apache.solr.response.transform.RenameFieldTransformer;
+import org.apache.solr.response.transform.ScoreAugmenter;
+import org.apache.solr.response.transform.TransformerFactory;
+import org.apache.solr.response.transform.ValueSourceAugmenter;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.LinkedHashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * The default implementation of return fields parsing for Solr.
+ */
+public class SolrReturnFields extends ReturnFields {
+  // Special Field Keys
+  public static final String SCORE = "score";
+
+  private final List<String> globs = new ArrayList<String>(1);
+
+  // The lucene field names to request from the SolrIndexSearcher
+  // Order is important for CSVResponseWriter
+  private final Set<String> fields = new LinkedHashSet<String>();
+
+  // Field names that are OK to include in the response.
+  // This will include pseudo fields, lucene fields, and matching globs
+  private Set<String> okFieldNames = new HashSet<String>();
+
+  // The list of explicitly requested fields
+  private Set<String> reqFieldNames = null;
+  
+  protected DocTransformer transformer;
+  protected boolean _wantsScore = false;
+  protected boolean _wantsAllFields = false;
+
+  public SolrReturnFields() {
+    _wantsAllFields = true;
+  }
+
+  public SolrReturnFields(SolrQueryRequest req) {
+    this( req.getParams().getParams(CommonParams.FL), req );
+  }
+
+  public SolrReturnFields(String fl, SolrQueryRequest req) {
+//    this( (fl==null)?null:SolrPluginUtils.split(fl), req );
+    if( fl == null ) {
+      parseFieldList((String[])null, req);
+    }
+    else {
+      if( fl.trim().length() == 0 ) {
+        // legacy thing to support fl='  ' => fl=*,score!
+        // maybe time to drop support for this?
+        // See ConvertedLegacyTest
+        _wantsScore = true;
+        _wantsAllFields = true;
+        transformer = new ScoreAugmenter(SCORE);
+      }
+      else {
+        parseFieldList( new String[]{fl}, req);
+      }
+    }
+  }
+
+  public SolrReturnFields(String[] fl, SolrQueryRequest req) {
+    parseFieldList(fl, req);
+  }
+
+  private void parseFieldList(String[] fl, SolrQueryRequest req) {
+    _wantsScore = false;
+    _wantsAllFields = false;
+    if (fl == null || fl.length == 0 || fl.length == 1 && fl[0].length()==0) {
+      _wantsAllFields = true;
+      return;
+    }
+
+    NamedList<String> rename = new NamedList<String>();
+    DocTransformers augmenters = new DocTransformers();
+    for (String fieldList : fl) {
+      add(fieldList,rename,augmenters,req);
+    }
+    for( int i=0; i<rename.size(); i++ ) {
+      String from = rename.getName(i);
+      String to = rename.getVal(i);
+      okFieldNames.add( to );
+      boolean copy = (reqFieldNames!=null && reqFieldNames.contains(from));
+      if(!copy) {
+        // Check that subsequent copy/rename requests have the field they need to copy
+        for(int j=i+1; j<rename.size(); j++) {
+          if(from.equals(rename.getName(j))) {
+            rename.setName(j, to); // copy from the current target
+            if(reqFieldNames==null) {
+              reqFieldNames = new HashSet<String>();
+            }
+            reqFieldNames.add(to); // don't rename our current target
+          }
+        }
+      }
+      augmenters.addTransformer( new RenameFieldTransformer( from, to, copy ) );
+    }
+
+    if( !_wantsAllFields ) {
+      if( !globs.isEmpty() ) {
+        // TODO??? need to fill up the fields with matching field names in the index
+        // and add them to okFieldNames?
+        // maybe just get all fields?
+        // this would disable field selection optimization... i think thatis OK
+        fields.clear(); // this will get all fields, and use wantsField to limit
+      }
+      okFieldNames.addAll( fields );
+    }
+
+    if( augmenters.size() == 1 ) {
+      transformer = augmenters.getTransformer(0);
+    }
+    else if( augmenters.size() > 1 ) {
+      transformer = augmenters;
+    }
+  }
+
+  // like getId, but also accepts dashes for legacy fields
+  String getFieldName(QueryParsing.StrParser sp) {
+    sp.eatws();
+    int id_start = sp.pos;
+    char ch;
+    if (sp.pos < sp.end && (ch = sp.val.charAt(sp.pos)) != '$' && Character.isJavaIdentifierStart(ch)) {
+      sp.pos++;
+      while (sp.pos < sp.end) {
+        ch = sp.val.charAt(sp.pos);
+        if (!Character.isJavaIdentifierPart(ch) && ch != '.' && ch != '-') {
+          break;
+        }
+        sp.pos++;
+      }
+      return sp.val.substring(id_start, sp.pos);
+    }
+
+    return null;
+  }
+
+  private void add(String fl, NamedList<String> rename, DocTransformers augmenters, SolrQueryRequest req) {
+    if( fl == null ) {
+      return;
+    }
+    try {
+      QueryParsing.StrParser sp = new QueryParsing.StrParser(fl);
+
+      for(;;) {
+        sp.opt(',');
+        sp.eatws();
+        if (sp.pos >= sp.end) break;
+
+        int start = sp.pos;
+
+        // short circuit test for a really simple field name
+        String key = null;
+        String field = getFieldName(sp);
+        char ch = sp.ch();
+
+        if (field != null) {
+          if (sp.opt(':')) {
+            // this was a key, not a field name
+            key = field;
+            field = null;
+            sp.eatws();
+            start = sp.pos;
+          } else {
+            if (Character.isWhitespace(ch) || ch == ',' || ch==0) {
+              addField( field, key, augmenters, req );
+              continue;
+            }
+            // an invalid field name... reset the position pointer to retry
+            sp.pos = start;
+            field = null;
+          }
+        }
+
+        if (key != null) {
+          // we read "key : "
+          field = sp.getId(null);
+          ch = sp.ch();
+          if (field != null && (Character.isWhitespace(ch) || ch == ',' || ch==0)) {
+            rename.add(field, key);
+            addField( field, key, augmenters, req );
+            continue;
+          }
+          // an invalid field name... reset the position pointer to retry
+          sp.pos = start;
+          field = null;
+        }
+
+        if (field == null) {
+          // We didn't find a simple name, so let's see if it's a globbed field name.
+          // Globbing only works with field names of the recommended form (roughly like java identifiers)
+
+          field = sp.getGlobbedId(null);
+          ch = sp.ch();
+          if (field != null && (Character.isWhitespace(ch) || ch == ',' || ch==0)) {
+            // "*" looks and acts like a glob, but we give it special treatment
+            if ("*".equals(field)) {
+              _wantsAllFields = true;
+            } else {
+              globs.add(field);
+            }
+            continue;
+          }
+
+          // an invalid glob
+          sp.pos = start;
+        }
+
+        String funcStr = sp.val.substring(start);
+
+        // Is it an augmenter of the form [augmenter_name foo=1 bar=myfield]?
+        // This is identical to localParams syntax except it uses [] instead of {!}
+
+        if (funcStr.startsWith("[")) {
+          Map<String,String> augmenterArgs = new HashMap<String,String>();
+          int end = QueryParsing.parseLocalParams(funcStr, 0, augmenterArgs, req.getParams(), "[", ']');
+          sp.pos += end;
+
+          // [foo] is short for [type=foo] in localParams syntax
+          String augmenterName = augmenterArgs.remove("type");
+          String disp = key;
+          if( disp == null ) {
+            disp = '['+augmenterName+']';
+          }
+
+          TransformerFactory factory = req.getCore().getTransformerFactory( augmenterName );
+          if( factory != null ) {
+            MapSolrParams augmenterParams = new MapSolrParams( augmenterArgs );
+            augmenters.addTransformer( factory.create(disp, augmenterParams, req) );
+          }
+          else {
+            // unknown transformer?
+          }
+          addField(field, disp, augmenters, req);
+          continue;
+        }
+
+
+        // let's try it as a function instead
+        QParser parser = QParser.getParser(funcStr, FunctionQParserPlugin.NAME, req);
+        Query q = null;
+        ValueSource vs = null;
+
+        try {
+          if (parser instanceof FunctionQParser) {
+            FunctionQParser fparser = (FunctionQParser)parser;
+            fparser.setParseMultipleSources(false);
+            fparser.setParseToEnd(false);
+
+            q = fparser.getQuery();
+
+            if (fparser.localParams != null) {
+              if (fparser.valFollowedParams) {
+                // need to find the end of the function query via the string parser
+                int leftOver = fparser.sp.end - fparser.sp.pos;
+                sp.pos = sp.end - leftOver;   // reset our parser to the same amount of leftover
+              } else {
+                // the value was via the "v" param in localParams, so we need to find
+                // the end of the local params themselves to pick up where we left off
+                sp.pos = start + fparser.localParamsEnd;
+              }
+            } else {
+              // need to find the end of the function query via the string parser
+              int leftOver = fparser.sp.end - fparser.sp.pos;
+              sp.pos = sp.end - leftOver;   // reset our parser to the same amount of leftover
+            }
+          } else {
+            // A QParser that's not for function queries.
+            // It must have been specified via local params.
+            q = parser.getQuery();
+
+            assert parser.getLocalParams() != null;
+            sp.pos = start + parser.localParamsEnd;
+          }
+
+
+          if (q instanceof FunctionQuery) {
+            vs = ((FunctionQuery)q).getValueSource();
+          } else {
+            vs = new QueryValueSource(q, 0.0f);
+          }
+
+          if (key==null) {
+            SolrParams localParams = parser.getLocalParams();
+            if (localParams != null) {
+              key = localParams.get("key");
+            }
+            if (key == null) {
+              // use the function name itself as the field name
+              key = sp.val.substring(start, sp.pos);
+            }
+          }
+
+
+          if (key==null) {
+            key = funcStr;
+          }
+          okFieldNames.add( key );
+          okFieldNames.add( funcStr );
+          augmenters.addTransformer( new ValueSourceAugmenter( key, parser, vs ) );
+        }
+        catch (SyntaxError e) {
+          // try again, simple rules for a field name with no whitespace
+          sp.pos = start;
+          field = sp.getSimpleString();
+
+          if (req.getSchema().getFieldOrNull(field) != null) {
+            // OK, it was an oddly named field
+            fields.add(field);
+            if( key != null ) {
+              rename.add(field, key);
+            }
+          } else {
+            throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "Error parsing fieldname: " + e.getMessage(), e);
+          }
+        }
+
+        // end try as function
+
+      } // end for(;;)
+    } catch (SyntaxError e) {
+      throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "Error parsing fieldname", e);
+    }
+  }
+
+  private void addField(String field, String key, DocTransformers augmenters, SolrQueryRequest req)
+  {
+    if(key==null) {
+      if(reqFieldNames==null) {
+        reqFieldNames = new HashSet<String>();
+      }
+      reqFieldNames.add(field);
+    }
+
+    fields.add(field); // need to put in the map to maintain order for things like CSVResponseWriter
+    okFieldNames.add( field );
+    okFieldNames.add( key );
+    // a valid field name
+    if(SCORE.equals(field)) {
+      _wantsScore = true;
+
+      String disp = (key==null) ? field : key;
+      augmenters.addTransformer( new ScoreAugmenter( disp ) );
+    }
+  }
+
+  @Override
+  public Set<String> getLuceneFieldNames()
+  {
+    return (_wantsAllFields || fields.isEmpty()) ? null : fields;
+  }
+
+  @Override
+  public boolean wantsField(String name)
+  {
+    if( _wantsAllFields || okFieldNames.contains( name ) ){
+      return true;
+    }
+    for( String s : globs ) {
+      // TODO something better?
+      if( FilenameUtils.wildcardMatch(name, s) ) {
+        okFieldNames.add(name); // Don't calculate it again
+        return true;
+      }
+    }
+    return false;
+  }
+  
+  @Override
+  public boolean wantsAllFields()
+  {
+    return _wantsAllFields;
+  }
+
+  @Override
+  public boolean wantsScore()
+  {
+    return _wantsScore;
+  }
+
+  @Override
+  public DocTransformer getTransformer()
+  {
+    return transformer;
+  }
+}

Modified: lucene/dev/trunk/solr/core/src/test/org/apache/solr/request/JSONWriterTest.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/test/org/apache/solr/request/JSONWriterTest.java?rev=1429935&r1=1429934&r2=1429935&view=diff
==============================================================================
--- lucene/dev/trunk/solr/core/src/test/org/apache/solr/request/JSONWriterTest.java (original)
+++ lucene/dev/trunk/solr/core/src/test/org/apache/solr/request/JSONWriterTest.java Mon Jan  7 18:33:12 2013
@@ -34,6 +34,7 @@ import org.apache.solr.response.PythonRe
 import org.apache.solr.response.QueryResponseWriter;
 import org.apache.solr.response.RubyResponseWriter;
 import org.apache.solr.response.SolrQueryResponse;
+import org.apache.solr.search.SolrReturnFields;
 import org.junit.BeforeClass;
 import org.junit.Test;
 
@@ -105,7 +106,7 @@ public class JSONWriterTest extends Solr
     SolrQueryResponse rsp = new SolrQueryResponse();
     JSONResponseWriter w = new JSONResponseWriter();
 
-    ReturnFields returnFields = new ReturnFields(req);
+    ReturnFields returnFields = new SolrReturnFields(req);
     rsp.setReturnFields(returnFields);
 
     StringWriter buf = new StringWriter();

Modified: lucene/dev/trunk/solr/core/src/test/org/apache/solr/request/TestBinaryResponseWriter.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/test/org/apache/solr/request/TestBinaryResponseWriter.java?rev=1429935&r1=1429934&r2=1429935&view=diff
==============================================================================
--- lucene/dev/trunk/solr/core/src/test/org/apache/solr/request/TestBinaryResponseWriter.java (original)
+++ lucene/dev/trunk/solr/core/src/test/org/apache/solr/request/TestBinaryResponseWriter.java Mon Jan  7 18:33:12 2013
@@ -30,6 +30,7 @@ import org.apache.solr.response.BinaryQu
 import org.apache.solr.response.BinaryResponseWriter.Resolver;
 import org.apache.solr.response.SolrQueryResponse;
 import org.apache.solr.search.ReturnFields;
+import org.apache.solr.search.SolrReturnFields;
 import org.apache.solr.util.AbstractSolrTestCase;
 import org.junit.BeforeClass;
 
@@ -81,7 +82,7 @@ public class TestBinaryResponseWriter ex
     in.addField("ddd_s", "ddd");
     in.addField("eee_s", "eee");    
 
-    Resolver r = new Resolver(req, new ReturnFields(req));
+    Resolver r = new Resolver(req, new SolrReturnFields(req));
     Object o = r.resolve(in, new JavaBinCodec());
 
     assertNotNull("obj is null", o);

Modified: lucene/dev/trunk/solr/core/src/test/org/apache/solr/response/TestCSVResponseWriter.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/test/org/apache/solr/response/TestCSVResponseWriter.java?rev=1429935&r1=1429934&r2=1429935&view=diff
==============================================================================
--- lucene/dev/trunk/solr/core/src/test/org/apache/solr/response/TestCSVResponseWriter.java (original)
+++ lucene/dev/trunk/solr/core/src/test/org/apache/solr/response/TestCSVResponseWriter.java Mon Jan  7 18:33:12 2013
@@ -23,6 +23,7 @@ import org.apache.solr.common.SolrDocume
 import org.apache.solr.common.util.DateUtil;
 import org.apache.solr.request.SolrQueryRequest;
 import org.apache.solr.search.ReturnFields;
+import org.apache.solr.search.SolrReturnFields;
 import org.junit.*;
 
 import java.io.StringWriter;
@@ -145,19 +146,19 @@ public class TestCSVResponseWriter exten
     rsp.add("response", sdl);
     QueryResponseWriter w = new CSVResponseWriter();
     
-    rsp.setReturnFields( new ReturnFields("id,foo_s", req) );
+    rsp.setReturnFields( new SolrReturnFields("id,foo_s", req) );
     StringWriter buf = new StringWriter();
     w.write(buf, req, rsp);
     assertEquals("id,foo_s\n1,hi\n2,\n", buf.toString());
 
     // try scores
-    rsp.setReturnFields( new ReturnFields("id,score,foo_s", req) );
+    rsp.setReturnFields( new SolrReturnFields("id,score,foo_s", req) );
     buf = new StringWriter();
     w.write(buf, req, rsp);
     assertEquals("id,score,foo_s\n1,2.718,hi\n2,89.83,\n", buf.toString());
 
     // get field values from docs... should be ordered and not include score unless requested
-    rsp.setReturnFields( new ReturnFields("*", req) );
+    rsp.setReturnFields( new SolrReturnFields("*", req) );
     buf = new StringWriter();
     w.write(buf, req, rsp);
     assertEquals("id,foo_i,foo_s,foo_l,foo_b,foo_f,foo_d,foo_dt,v_ss,v2_ss\n" +
@@ -167,14 +168,14 @@ public class TestCSVResponseWriter exten
     
 
     // get field values and scores - just check that the scores are there... we don't guarantee where
-    rsp.setReturnFields( new ReturnFields("*,score", req) );
+    rsp.setReturnFields( new SolrReturnFields("*,score", req) );
     buf = new StringWriter();
     w.write(buf, req, rsp);
     String s = buf.toString();
     assertTrue(s.indexOf("score") >=0 && s.indexOf("2.718") > 0 && s.indexOf("89.83") > 0 );
     
     // Test field globs
-    rsp.setReturnFields( new ReturnFields("id,foo*", req) );
+    rsp.setReturnFields( new SolrReturnFields("id,foo*", req) );
     buf = new StringWriter();
     w.write(buf, req, rsp);
     assertEquals("id,foo_i,foo_s,foo_l,foo_b,foo_f,foo_d,foo_dt\n" +
@@ -182,7 +183,7 @@ public class TestCSVResponseWriter exten
         "2,,,,,,,\n",
       buf.toString());
 
-    rsp.setReturnFields( new ReturnFields("id,*_d*", req) );
+    rsp.setReturnFields( new SolrReturnFields("id,*_d*", req) );
     buf = new StringWriter();
     w.write(buf, req, rsp);
     assertEquals("id,foo_d,foo_dt\n" +

Modified: lucene/dev/trunk/solr/core/src/test/org/apache/solr/search/ReturnFieldsTest.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/test/org/apache/solr/search/ReturnFieldsTest.java?rev=1429935&r1=1429934&r2=1429935&view=diff
==============================================================================
--- lucene/dev/trunk/solr/core/src/test/org/apache/solr/search/ReturnFieldsTest.java (original)
+++ lucene/dev/trunk/solr/core/src/test/org/apache/solr/search/ReturnFieldsTest.java Mon Jan  7 18:33:12 2013
@@ -97,7 +97,7 @@ public class ReturnFieldsTest extends So
 
   @Test
   public void testSeparators() {
-    ReturnFields rf = new ReturnFields( req("fl", "id name test subject score") );
+    ReturnFields rf = new SolrReturnFields( req("fl", "id name test subject score") );
     assertTrue( rf.wantsScore() );
     assertTrue( rf.wantsField( "id" ) );
     assertTrue( rf.wantsField( "name" ) );
@@ -108,7 +108,7 @@ public class ReturnFieldsTest extends So
     assertFalse( rf.wantsField( "xxx" ) );
     assertTrue( rf.getTransformer() instanceof ScoreAugmenter);
 
-    rf = new ReturnFields( req("fl", "id,name,test,subject,score") );
+    rf = new SolrReturnFields( req("fl", "id,name,test,subject,score") );
     assertTrue( rf.wantsScore() );
     assertTrue( rf.wantsField( "id" ) );
     assertTrue( rf.wantsField( "name" ) );
@@ -119,7 +119,7 @@ public class ReturnFieldsTest extends So
     assertFalse( rf.wantsField( "xxx" ) );
     assertTrue( rf.getTransformer() instanceof ScoreAugmenter);
 
-    rf = new ReturnFields( req("fl", "id,name test,subject score") );
+    rf = new SolrReturnFields( req("fl", "id,name test,subject score") );
     assertTrue( rf.wantsScore() );
     assertTrue( rf.wantsField( "id" ) );
     assertTrue( rf.wantsField( "name" ) );
@@ -130,7 +130,7 @@ public class ReturnFieldsTest extends So
     assertFalse( rf.wantsField( "xxx" ) );
     assertTrue( rf.getTransformer() instanceof ScoreAugmenter);
 
-    rf = new ReturnFields( req("fl", "id, name  test , subject,score") );
+    rf = new SolrReturnFields( req("fl", "id, name  test , subject,score") );
     assertTrue( rf.wantsScore() );
     assertTrue( rf.wantsField( "id" ) );
     assertTrue( rf.wantsField( "name" ) );
@@ -144,20 +144,20 @@ public class ReturnFieldsTest extends So
 
   @Test
   public void testWilcards() {
-    ReturnFields rf = new ReturnFields( req("fl", "*") );
+    ReturnFields rf = new SolrReturnFields( req("fl", "*") );
     assertFalse( rf.wantsScore() );
     assertTrue( rf.wantsField( "xxx" ) );
     assertTrue( rf.wantsAllFields() );
     assertNull( rf.getTransformer() );
 
-    rf = new ReturnFields( req("fl", " * ") );
+    rf = new SolrReturnFields( req("fl", " * ") );
     assertFalse( rf.wantsScore() );
     assertTrue( rf.wantsField( "xxx" ) );
     assertTrue( rf.wantsAllFields() );
     assertNull( rf.getTransformer() );
 
     // Check that we want wildcards
-    rf = new ReturnFields( req("fl", "id,aaa*,*bbb") );
+    rf = new SolrReturnFields( req("fl", "id,aaa*,*bbb") );
     assertTrue( rf.wantsField( "id" ) );
     assertTrue( rf.wantsField( "aaaxxx" ) );
     assertFalse(rf.wantsField("xxxaaa"));
@@ -169,7 +169,7 @@ public class ReturnFieldsTest extends So
 
   @Test
   public void testManyParameters() {
-    ReturnFields rf = new ReturnFields( req("fl", "id name", "fl", "test subject", "fl", "score") );
+    ReturnFields rf = new SolrReturnFields( req("fl", "id name", "fl", "test subject", "fl", "score") );
     assertTrue( rf.wantsScore() );
     assertTrue( rf.wantsField( "id" ) );
     assertTrue( rf.wantsField( "name" ) );
@@ -183,7 +183,7 @@ public class ReturnFieldsTest extends So
 
   @Test
   public void testFunctions() {
-    ReturnFields rf = new ReturnFields( req("fl", "id sum(1,1)") );
+    ReturnFields rf = new SolrReturnFields( req("fl", "id sum(1,1)") );
     assertFalse(rf.wantsScore());
     assertTrue( rf.wantsField( "id" ) );
     assertFalse( rf.wantsAllFields() );
@@ -194,41 +194,41 @@ public class ReturnFieldsTest extends So
 
   @Test
   public void testTransformers() {
-    ReturnFields rf = new ReturnFields( req("fl", "[explain]") );
+    ReturnFields rf = new SolrReturnFields( req("fl", "[explain]") );
     assertFalse( rf.wantsScore() );
     assertFalse(rf.wantsField("id"));
     assertFalse(rf.wantsAllFields());
     assertEquals( "[explain]", rf.getTransformer().getName() );
 
-    rf = new ReturnFields( req("fl", "[shard],id") );
+    rf = new SolrReturnFields( req("fl", "[shard],id") );
     assertFalse( rf.wantsScore() );
     assertTrue(rf.wantsField("id"));
     assertFalse(rf.wantsField("xxx"));
     assertFalse(rf.wantsAllFields());
     assertEquals( "[shard]", rf.getTransformer().getName() );
 
-    rf = new ReturnFields( req("fl", "[docid]") );
+    rf = new SolrReturnFields( req("fl", "[docid]") );
     assertFalse( rf.wantsScore() );
     assertFalse( rf.wantsField( "id" ) );
     assertFalse(rf.wantsField("xxx"));
     assertFalse(rf.wantsAllFields());
     assertEquals( "[docid]", rf.getTransformer().getName() );
 
-    rf = new ReturnFields( req("fl", "mydocid:[docid]") );
+    rf = new SolrReturnFields( req("fl", "mydocid:[docid]") );
     assertFalse( rf.wantsScore() );
     assertFalse( rf.wantsField( "id" ) );
     assertFalse(rf.wantsField("xxx"));
     assertFalse(rf.wantsAllFields());
     assertEquals( "mydocid", rf.getTransformer().getName() );
 
-    rf = new ReturnFields( req("fl", "[docid][shard]") );
+    rf = new SolrReturnFields( req("fl", "[docid][shard]") );
     assertFalse( rf.wantsScore() );
     assertFalse(rf.wantsField("xxx"));
     assertFalse(rf.wantsAllFields());
     assertTrue( rf.getTransformer() instanceof DocTransformers);
     assertEquals(2, ((DocTransformers)rf.getTransformer()).size());
 
-    rf = new ReturnFields( req("fl", "[xxxxx]") );
+    rf = new SolrReturnFields( req("fl", "[xxxxx]") );
     assertFalse( rf.wantsScore() );
     assertFalse( rf.wantsField( "id" ) );
     assertFalse(rf.wantsAllFields());
@@ -237,7 +237,7 @@ public class ReturnFieldsTest extends So
 
   @Test
   public void testAliases() {
-    ReturnFields rf = new ReturnFields( req("fl", "newId:id newName:name newTest:test newSubject:subject") );
+    ReturnFields rf = new SolrReturnFields( req("fl", "newId:id newName:name newTest:test newSubject:subject") );
     assertTrue(rf.wantsField("id"));
     assertTrue(rf.wantsField("name"));
     assertTrue(rf.wantsField("test"));
@@ -249,7 +249,7 @@ public class ReturnFieldsTest extends So
     assertFalse(rf.wantsField("xxx"));
     assertFalse(rf.wantsAllFields());
 
-    rf = new ReturnFields( req("fl", "newId:id newName:name newTest:test newSubject:subject score") );
+    rf = new SolrReturnFields( req("fl", "newId:id newName:name newTest:test newSubject:subject score") );
     assertTrue(rf.wantsField("id"));
     assertTrue(rf.wantsField("name"));
     assertTrue(rf.wantsField("test"));
@@ -268,7 +268,7 @@ public class ReturnFieldsTest extends So
   // the simplest case of fl=foo-bar to work
   @Test
   public void testHyphenInFieldName() {
-    ReturnFields rf = new ReturnFields(req("fl", "id-test"));
+    ReturnFields rf = new SolrReturnFields(req("fl", "id-test"));
     assertFalse(rf.wantsScore());
     assertTrue(rf.wantsField("id-test"));
     assertFalse(rf.wantsField("xxx"));
@@ -277,20 +277,20 @@ public class ReturnFieldsTest extends So
 
   @Test
   public void testTrailingDotInFieldName() {
-    ReturnFields rf = new ReturnFields(req("fl", "id.test"));
+    ReturnFields rf = new SolrReturnFields(req("fl", "id.test"));
     assertFalse(rf.wantsScore());
     assertTrue(rf.wantsField("id.test"));
     assertFalse(rf.wantsField("xxx"));
     assertFalse(rf.wantsAllFields());
 
-    rf = new ReturnFields(req("fl", "test:id.test"));
+    rf = new SolrReturnFields(req("fl", "test:id.test"));
     assertFalse(rf.wantsScore());
     assertTrue(rf.wantsField("id.test"));
     assertTrue(rf.wantsField("test"));
     assertFalse(rf.wantsField("xxx"));
     assertFalse(rf.wantsAllFields());
 
-    rf = new ReturnFields(req("fl", "test.id:id.test"));
+    rf = new SolrReturnFields(req("fl", "test.id:id.test"));
     assertFalse(rf.wantsScore());
     assertTrue(rf.wantsField("id.test"));
     assertTrue(rf.wantsField("test.id"));
@@ -300,7 +300,7 @@ public class ReturnFieldsTest extends So
 
   @Test
   public void testTrailingDollarInFieldName() {
-    ReturnFields rf = new ReturnFields(req("fl", "id$test"));
+    ReturnFields rf = new SolrReturnFields(req("fl", "id$test"));
     assertFalse(rf.wantsScore());
     assertTrue(rf.wantsField("id$test"));
     assertFalse(rf.wantsField("xxx"));
@@ -325,7 +325,7 @@ public class ReturnFieldsTest extends So
         _TestUtil.randomWhitespace(r, 0, 3);
 
       final String fl = id + (r.nextBoolean() ? "" : ",") + foo_i;
-      ReturnFields rf = new ReturnFields(req("fl", fl));
+      ReturnFields rf = new SolrReturnFields(req("fl", fl));
 
       assertFalse("score ("+fl+")", rf.wantsScore());