lucene-solr-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From markrmil...@apache.org
Subject svn commit: r890809 [2/2] - in /lucene/solr/branches/cloud: ./ contrib/dataimporthandler/ contrib/dataimporthandler/src/extras/ contrib/dataimporthandler/src/extras/main/ contrib/dataimporthandler/src/extras/main/java/ contrib/dataimporthandler/src/ext...
Date Tue, 15 Dec 2009 14:49:20 GMT
Modified: lucene/solr/branches/cloud/src/java/org/apache/solr/search/QueryParsing.java
URL: http://svn.apache.org/viewvc/lucene/solr/branches/cloud/src/java/org/apache/solr/search/QueryParsing.java?rev=890809&r1=890808&r2=890809&view=diff
==============================================================================
--- lucene/solr/branches/cloud/src/java/org/apache/solr/search/QueryParsing.java (original)
+++ lucene/solr/branches/cloud/src/java/org/apache/solr/search/QueryParsing.java Tue Dec 15 14:49:18 2009
@@ -20,7 +20,18 @@
 import org.apache.lucene.index.Term;
 import org.apache.lucene.queryParser.ParseException;
 import org.apache.lucene.queryParser.QueryParser;
-import org.apache.lucene.search.*;
+import org.apache.lucene.search.BooleanClause;
+import org.apache.lucene.search.BooleanQuery;
+import org.apache.lucene.search.ConstantScoreQuery;
+import org.apache.lucene.search.FuzzyQuery;
+import org.apache.lucene.search.NumericRangeQuery;
+import org.apache.lucene.search.PrefixQuery;
+import org.apache.lucene.search.Query;
+import org.apache.lucene.search.Sort;
+import org.apache.lucene.search.SortField;
+import org.apache.lucene.search.TermQuery;
+import org.apache.lucene.search.TermRangeQuery;
+import org.apache.lucene.search.WildcardQuery;
 import org.apache.solr.common.SolrException;
 import org.apache.solr.common.params.MapSolrParams;
 import org.apache.solr.common.params.SolrParams;
@@ -30,6 +41,7 @@
 import org.apache.solr.schema.IndexSchema;
 import org.apache.solr.schema.SchemaField;
 import org.apache.solr.search.function.FunctionQuery;
+import org.apache.solr.search.function.ValueSource;
 
 import java.io.IOException;
 import java.util.ArrayList;
@@ -53,26 +65,28 @@
   public static final char LOCALPARAM_END = '}';
   public static final String DOCID = "_docid_";
 
-  /** 
-   * Helper utility for parsing a query using the Lucene QueryParser syntax. 
-   * @param qs query expression in standard Lucene syntax
+  /**
+   * Helper utility for parsing a query using the Lucene QueryParser syntax.
+   *
+   * @param qs     query expression in standard Lucene syntax
    * @param schema used for default operator (overridden by params) and passed to the query parser for field format analysis information
    */
   public static Query parseQuery(String qs, IndexSchema schema) {
     return parseQuery(qs, null, schema);
   }
 
-  /** 
-   * Helper utility for parsing a query using the Lucene QueryParser syntax. 
-   * @param qs query expression in standard Lucene syntax
+  /**
+   * Helper utility for parsing a query using the Lucene QueryParser syntax.
+   *
+   * @param qs           query expression in standard Lucene syntax
    * @param defaultField default field used for unqualified search terms in the query expression
-   * @param schema used for default operator (overridden by params) and passed to the query parser for field format analysis information
+   * @param schema       used for default operator (overridden by params) and passed to the query parser for field format analysis information
    */
   public static Query parseQuery(String qs, String defaultField, IndexSchema schema) {
     try {
       Query query = schema.getSolrQueryParser(defaultField).parse(qs);
 
-      if (SolrCore.log.isTraceEnabled() ) {
+      if (SolrCore.log.isTraceEnabled()) {
         SolrCore.log.trace("After QueryParser:" + query);
       }
 
@@ -80,16 +94,17 @@
 
     } catch (ParseException e) {
       SolrCore.log(e);
-      throw new SolrException( SolrException.ErrorCode.BAD_REQUEST,"Error parsing Lucene query",e);
+      throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "Error parsing Lucene query", e);
     }
   }
 
   /**
-   * Helper utility for parsing a query using the Lucene QueryParser syntax. 
-   * @param qs query expression in standard Lucene syntax
+   * Helper utility for parsing a query using the Lucene QueryParser syntax.
+   *
+   * @param qs           query expression in standard Lucene syntax
    * @param defaultField default field used for unqualified search terms in the query expression
-   * @param params used to determine the default operator, overriding the schema specified operator
-   * @param schema used for default operator (overridden by params) and passed to the query parser for field format analysis information
+   * @param params       used to determine the default operator, overriding the schema specified operator
+   * @param schema       used for default operator (overridden by params) and passed to the query parser for field format analysis information
    */
   public static Query parseQuery(String qs, String defaultField, SolrParams params, IndexSchema schema) {
     try {
@@ -100,7 +115,7 @@
       }
       Query query = parser.parse(qs);
 
-      if (SolrCore.log.isTraceEnabled() ) {
+      if (SolrCore.log.isTraceEnabled()) {
         SolrCore.log.trace("After QueryParser:" + query);
       }
 
@@ -108,38 +123,38 @@
 
     } catch (ParseException e) {
       SolrCore.log(e);
-      throw new SolrException( SolrException.ErrorCode.BAD_REQUEST,"Query parsing error: " + e.getMessage(),e);
+      throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "Query parsing error: " + e.getMessage(), e);
     }
   }
 
 
   // note to self: something needs to detect infinite recursion when parsing queries
-  static int parseLocalParams(String txt, int start, Map<String,String> target, SolrParams params) throws ParseException {
-    int off=start;
-    if (!txt.startsWith(LOCALPARAM_START,off)) return start;
-    StrParser p = new StrParser(txt,start,txt.length());
-    p.pos+=2; // skip over "{!"
+  static int parseLocalParams(String txt, int start, Map<String, String> target, SolrParams params) throws ParseException {
+    int off = start;
+    if (!txt.startsWith(LOCALPARAM_START, off)) return start;
+    StrParser p = new StrParser(txt, start, txt.length());
+    p.pos += 2; // skip over "{!"
 
-    for(;;) {
+    for (; ;) {
       /*
       if (p.pos>=txt.length()) {
         throw new ParseException("Missing '}' parsing local params '" + txt + '"');
       }
       */
       char ch = p.peek();
-      if (ch==LOCALPARAM_END) {
-        return p.pos+1;
+      if (ch == LOCALPARAM_END) {
+        return p.pos + 1;
       }
 
       String id = p.getId();
-      if (id.length()==0) {
+      if (id.length() == 0) {
         throw new ParseException("Expected identifier '}' parsing local params '" + txt + '"');
 
       }
-      String val=null;
+      String val = null;
 
       ch = p.peek();
-      if (ch!='=') {
+      if (ch != '=') {
         // single word... treat {!func} as type=func for easy lookup
         val = id;
         id = TYPE;
@@ -147,25 +162,25 @@
         // saw equals, so read value
         p.pos++;
         ch = p.peek();
-        if (ch=='\"' || ch=='\'') {
+        if (ch == '\"' || ch == '\'') {
           val = p.getQuotedString();
-        } else if (ch=='$') {
+        } else if (ch == '$') {
           p.pos++;
           // dereference parameter
           String pname = p.getId();
-          if (params!=null) {
+          if (params != null) {
             val = params.get(pname);
           }
         } else {
           // read unquoted literal ended by whitespace or '}'
           // there is no escaping.
           int valStart = p.pos;
-          for (;;) {
+          for (; ;) {
             if (p.pos >= p.end) {
-              throw new ParseException("Missing end to unquoted value starting at " + valStart + " str='" + txt +"'");
+              throw new ParseException("Missing end to unquoted value starting at " + valStart + " str='" + txt + "'");
             }
             char c = p.val.charAt(p.pos);
-            if (c==LOCALPARAM_END || Character.isWhitespace(c)) {
+            if (c == LOCALPARAM_END || Character.isWhitespace(c)) {
               val = p.val.substring(valStart, p.pos);
               break;
             }
@@ -173,40 +188,39 @@
           }
         }
       }
-      if (target != null) target.put(id,val);
+      if (target != null) target.put(id, val);
     }
   }
 
   /**
-   *  "foo" returns null
-   *  "{!prefix f=myfield}yes" returns type="prefix",f="myfield",v="yes"
-   *  "{!prefix f=myfield v=$p}" returns type="prefix",f="myfield",v=params.get("p")
+   * "foo" returns null
+   * "{!prefix f=myfield}yes" returns type="prefix",f="myfield",v="yes"
+   * "{!prefix f=myfield v=$p}" returns type="prefix",f="myfield",v=params.get("p")
    */
   public static SolrParams getLocalParams(String txt, SolrParams params) throws ParseException {
-    if (txt==null || !txt.startsWith(LOCALPARAM_START)) {
-      return null;      
+    if (txt == null || !txt.startsWith(LOCALPARAM_START)) {
+      return null;
     }
-    Map<String,String> localParams = new HashMap<String,String>();
+    Map<String, String> localParams = new HashMap<String, String>();
     int start = QueryParsing.parseLocalParams(txt, 0, localParams, params);
 
     String val;
     if (start >= txt.length()) {
       // if the rest of the string is empty, check for "v" to provide the value
       val = localParams.get(V);
-      val = val==null ? "" : val;
+      val = val == null ? "" : val;
     } else {
       val = txt.substring(start);
     }
-    localParams.put(V,val);
+    localParams.put(V, val);
     return new MapSolrParams(localParams);
   }
 
 
-  private static Pattern sortSep = Pattern.compile(",");
 
   /**
    * Returns null if the sortSpec is the standard sort desc.
-   *
+   * <p/>
    * <p>
    * The form of the sort specification string currently parsed is:
    * </p>
@@ -218,72 +232,153 @@
    * Examples:
    * <pre>
    *   score desc               #normal sort by score (will return null)
-   *   weight bottom            #sort by weight ascending 
+   *   weight bottom            #sort by weight ascending
    *   weight desc              #sort by weight descending
    *   height desc,weight desc  #sort by height descending, and use weight descending to break any ties
    *   height desc,weight asc   #sort by height descending, using weight ascending as a tiebreaker
    * </pre>
-   *
    */
   public static Sort parseSort(String sortSpec, IndexSchema schema) {
-    if (sortSpec==null || sortSpec.length()==0) return null;
+    if (sortSpec == null || sortSpec.length() == 0) return null;
+    char[] chars = sortSpec.toCharArray();
+    int i = 0;
+    StringBuilder buffer = new StringBuilder(sortSpec.length());
+    String sort = null;
+    String order = null;
+    int functionDepth = 0;
+    boolean score = true;
+    List<SortField> lst = new ArrayList<SortField>(5);
+    boolean needOrder = false;
+    while (i < chars.length) {
+      if (Character.isWhitespace(chars[i]) && functionDepth == 0) {
+        if (buffer.length() == 0) {
+          //do nothing
+        } else {
+          if (needOrder == false) {
+            sort = buffer.toString().trim();
+            buffer.setLength(0);
+            needOrder = true;
+          } else {
+            order = buffer.toString().trim();
+            buffer.setLength(0);
+            needOrder = false;
+          }
+        }
+      } else if (chars[i] == '(' && functionDepth == 0) {
+        buffer.append(chars[i]);
+        functionDepth++;
+      } else if (chars[i] == ')' && functionDepth > 0) {
+        buffer.append(chars[i]);
+        functionDepth--;//close up one layer
+      } else if (chars[i] == ',' && functionDepth == 0) {//can either be a separator of sort declarations, or a separator in a function
+        //we have a separator between sort declarations,
+        // We may need an order still, but then evaluate it, as we should have everything we need
+        if (needOrder == true && buffer.length() > 0){
+          order = buffer.toString().trim();
+          buffer.setLength(0);
+          needOrder = false;
+        }
+        score = processSort(schema, sort, order, lst);
+        sort = null;
+        order = null;
+        buffer.setLength(0);//get ready for the next one, if there is one
+      } else if (chars[i] == ',' && functionDepth > 0) {
+        //we are in a function
+        buffer.append(chars[i]);
+      } else {
+        //just a regular old char, add it to the buffer
+        buffer.append(chars[i]);
+      }
+      i++;
+    }
+    if (buffer.length() > 0 && needOrder){//see if we have anything left, at most it should be an order
+      order = buffer.toString().trim();
+      buffer.setLength(0);
+      needOrder = false;
+    }
 
-    String[] parts = sortSep.split(sortSpec.trim());
-    if (parts.length == 0) return null;
+    //do some sanity checks
+    if (functionDepth != 0){
+      throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "Unable to parse sort spec, mismatched parentheses: " + sortSpec);
+    }
+    if (buffer.length() > 0){//there's something wrong, as everything should have been parsed by now
+      throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "Unable to parse sort spec: " + sortSpec);
+    }
+    if (needOrder == false && sort != null && sort.equals("") == false && order != null && order.equals("") == false){//handle the last declaration
+      score = processSort(schema, sort, order, lst);
+    }
+    //If the normal case (by score desc) do nothing
+    if (lst.size() == 1 && score == true && lst.get(0).getReverse() == false) {
+      return null; // do normal scoring...
+    }
+    return new Sort((SortField[]) lst.toArray(new SortField[lst.size()]));
+  }
 
-    SortField[] lst = new SortField[parts.length];
-    for( int i=0; i<parts.length; i++ ) {
-      String part = parts[i].trim();
-      boolean top=true;
-        
-      int idx = part.indexOf( ' ' );
-      if( idx > 0 ) {
-        String order = part.substring( idx+1 ).trim();
-      	if( "desc".equals( order ) || "top".equals(order) ) {
-      	  top = true;
-      	}
-      	else if ("asc".equals(order) || "bottom".equals(order)) {
-      	  top = false;
-      	}
-      	else {
-      	  throw new SolrException( SolrException.ErrorCode.BAD_REQUEST, "Unknown sort order: "+order);
-      	}
-      	part = part.substring( 0, idx ).trim();
-      }
-      else {
-        throw new SolrException( SolrException.ErrorCode.BAD_REQUEST, "Missing sort order." );
+  private static boolean processSort(IndexSchema schema, String sort, String order, List<SortField> lst) {
+    boolean score = false;
+    if (sort != null && order != null) {
+      boolean top = true;
+      if ("desc".equals(order) || "top".equals(order)) {
+        top = true;
+      } else if ("asc".equals(order) || "bottom".equals(order)) {
+        top = false;
+      } else {
+        throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "Unknown sort order: " + order);
       }
-    	
-      if( "score".equals(part) ) {
+      //we got the order, now deal with the sort
+      if ("score".equals(sort)) {
+        score = true;
         if (top) {
-          // If there is only one thing in the list, just do the regular thing...
-          if( parts.length == 1 ) {
-            return null; // do normal scoring...
-          }
-          lst[i] = SortField.FIELD_SCORE;
-        }
-        else {
-          lst[i] = new SortField(null, SortField.SCORE, true);
+          lst.add(SortField.FIELD_SCORE);
+        } else {
+          lst.add(new SortField(null, SortField.SCORE, true));
         }
-      } else if (DOCID.equals(part)) {
-        lst[i] = new SortField(null, SortField.DOC, top);
-      }
-      else {
+      } else if (DOCID.equals(sort)) {
+        lst.add(new SortField(null, SortField.DOC, top));
+      } else {
+        //See if we have a Field first, then see if it is a function, then throw an exception
         // getField could throw an exception if the name isn't found
         SchemaField f = null;
-        try{
-          f = schema.getField(part);
+        try {
+          f = schema.getField(sort);
         }
-        catch( SolrException e ){
-          throw new SolrException( SolrException.ErrorCode.BAD_REQUEST, "can not sort on undefined field: "+part, e );
+        catch (SolrException e) {
+          //Not an error just yet
         }
-        if (f == null || !f.indexed()){
-          throw new SolrException( SolrException.ErrorCode.BAD_REQUEST, "can not sort on unindexed field: "+part );
+        if (f != null) {
+          if (f == null || !f.indexed()) {
+            throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "can not sort on unindexed field: " + sort);
+          }
+          lst.add(f.getType().getSortField(f, top));
+        } else {
+          //See if we have a function:
+          FunctionQuery query = null;
+          try {
+            query = parseFunction(sort, schema);
+            if (query != null) {
+              ValueSource valueSource = query.getValueSource();
+              //We have a function query
+              try {
+                lst.add(valueSource.getSortField(top));
+              } catch (IOException e) {
+                throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "error getting the sort for this function: " + sort, e);
+              }
+            } else {
+              throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "can not sort on undefined function: " + sort);
+            }
+          } catch (ParseException e) {
+            throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "can not sort on undefined field or function: " + sort, e);
+          }
+
         }
-        lst[i] = f.getType().getSortField(f,top);
       }
+    } else if (sort == null) {//no sort value
+      throw new SolrException(SolrException.ErrorCode.BAD_REQUEST,
+              "Must declare sort field or function");
+    } else if (order == null) {
+      throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "Missing sort order: ");
     }
-    return new Sort(lst);
+    return score;
   }
 
 
@@ -295,15 +390,15 @@
     FieldType ft = null;
     ft = schema.getFieldTypeNoEx(name);
     out.append(name);
-    if (ft==null) {
-      out.append("(UNKNOWN FIELD "+name+')');
+    if (ft == null) {
+      out.append("(UNKNOWN FIELD " + name + ')');
     }
     out.append(':');
     return ft;
   }
 
   static void writeFieldVal(String val, FieldType ft, Appendable out, int flags) throws IOException {
-    if (ft!=null) {
+    if (ft != null) {
       try {
         out.append(ft.indexedToReadable(val));
       } catch (Exception e) {
@@ -315,23 +410,26 @@
       out.append(val);
     }
   }
-  /** @see #toString(Query,IndexSchema) */
+
+  /**
+   * @see #toString(Query,IndexSchema)
+   */
   public static void toString(Query query, IndexSchema schema, Appendable out, int flags) throws IOException {
-    boolean writeBoost=true;
+    boolean writeBoost = true;
 
     if (query instanceof TermQuery) {
-      TermQuery q = (TermQuery)query;
+      TermQuery q = (TermQuery) query;
       Term t = q.getTerm();
       FieldType ft = writeFieldName(t.field(), schema, out, flags);
       writeFieldVal(t.text(), ft, out, flags);
     } else if (query instanceof TermRangeQuery) {
-      TermRangeQuery q = (TermRangeQuery)query;
+      TermRangeQuery q = (TermRangeQuery) query;
       String fname = q.getField();
       FieldType ft = writeFieldName(fname, schema, out, flags);
-      out.append( q.includesLower() ? '[' : '{' );
+      out.append(q.includesLower() ? '[' : '{');
       String lt = q.getLowerTerm();
       String ut = q.getUpperTerm();
-      if (lt==null) {
+      if (lt == null) {
         out.append('*');
       } else {
         writeFieldVal(lt, ft, out, flags);
@@ -339,21 +437,21 @@
 
       out.append(" TO ");
 
-      if (ut==null) {
+      if (ut == null) {
         out.append('*');
       } else {
         writeFieldVal(ut, ft, out, flags);
       }
 
-      out.append( q.includesUpper() ? ']' : '}' );
+      out.append(q.includesUpper() ? ']' : '}');
     } else if (query instanceof NumericRangeQuery) {
-      NumericRangeQuery q = (NumericRangeQuery)query;
+      NumericRangeQuery q = (NumericRangeQuery) query;
       String fname = q.getField();
       FieldType ft = writeFieldName(fname, schema, out, flags);
-      out.append( q.includesMin() ? '[' : '{' );
+      out.append(q.includesMin() ? '[' : '{');
       Number lt = q.getMin();
       Number ut = q.getMax();
-      if (lt==null) {
+      if (lt == null) {
         out.append('*');
       } else {
         out.append(lt.toString());
@@ -361,29 +459,29 @@
 
       out.append(" TO ");
 
-      if (ut==null) {
+      if (ut == null) {
         out.append('*');
       } else {
         out.append(ut.toString());
       }
 
-      out.append( q.includesMax() ? ']' : '}' );
+      out.append(q.includesMax() ? ']' : '}');
     } else if (query instanceof BooleanQuery) {
-      BooleanQuery q = (BooleanQuery)query;
-      boolean needParens=false;
+      BooleanQuery q = (BooleanQuery) query;
+      boolean needParens = false;
 
       if (q.getBoost() != 1.0 || q.getMinimumNumberShouldMatch() != 0) {
-        needParens=true;
+        needParens = true;
       }
       if (needParens) {
         out.append('(');
       }
-      boolean first=true;
-      for (BooleanClause c : (List<BooleanClause>)q.clauses()) {
+      boolean first = true;
+      for (BooleanClause c : (List<BooleanClause>) q.clauses()) {
         if (!first) {
           out.append(' ');
         } else {
-          first=false;
+          first = false;
         }
 
         if (c.isProhibited()) {
@@ -392,12 +490,12 @@
           out.append('+');
         }
         Query subQuery = c.getQuery();
-        boolean wrapQuery=false;
+        boolean wrapQuery = false;
 
         // TODO: may need to put parens around other types
         // of queries too, depending on future syntax.
         if (subQuery instanceof BooleanQuery) {
-          wrapQuery=true;
+          wrapQuery = true;
         }
 
         if (wrapQuery) {
@@ -414,36 +512,36 @@
       if (needParens) {
         out.append(')');
       }
-      if (q.getMinimumNumberShouldMatch()>0) {
+      if (q.getMinimumNumberShouldMatch() > 0) {
         out.append('~');
         out.append(Integer.toString(q.getMinimumNumberShouldMatch()));
       }
 
     } else if (query instanceof PrefixQuery) {
-      PrefixQuery q = (PrefixQuery)query;
+      PrefixQuery q = (PrefixQuery) query;
       Term prefix = q.getPrefix();
       FieldType ft = writeFieldName(prefix.field(), schema, out, flags);
       out.append(prefix.text());
       out.append('*');
     } else if (query instanceof ConstantScorePrefixQuery) {
-      ConstantScorePrefixQuery q = (ConstantScorePrefixQuery)query;
+      ConstantScorePrefixQuery q = (ConstantScorePrefixQuery) query;
       Term prefix = q.getPrefix();
       FieldType ft = writeFieldName(prefix.field(), schema, out, flags);
       out.append(prefix.text());
       out.append('*');
     } else if (query instanceof WildcardQuery) {
       out.append(query.toString());
-      writeBoost=false;
+      writeBoost = false;
     } else if (query instanceof FuzzyQuery) {
       out.append(query.toString());
-      writeBoost=false;      
+      writeBoost = false;
     } else if (query instanceof ConstantScoreQuery) {
       out.append(query.toString());
-      writeBoost=false;
+      writeBoost = false;
     } else {
       out.append(query.getClass().getSimpleName()
-              + '(' + query.toString() + ')' );
-      writeBoost=false;
+              + '(' + query.toString() + ')');
+      writeBoost = false;
     }
 
     if (writeBoost && query.getBoost() != 1.0f) {
@@ -452,17 +550,17 @@
     }
 
   }
-  
+
   /**
-   * Formats a Query for debugging, using the IndexSchema to make 
+   * Formats a Query for debugging, using the IndexSchema to make
    * complex field types readable.
-   *
+   * <p/>
    * <p>
-   * The benefit of using this method instead of calling 
+   * The benefit of using this method instead of calling
    * <code>Query.toString</code> directly is that it knows about the data
-   * types of each field, so any field which is encoded in a particularly 
-   * complex way is still readable. The downside is that it only knows 
-   * about built in Query types, and will not be able to format custom 
+   * types of each field, so any field which is encoded in a particularly
+   * complex way is still readable. The downside is that it only knows
+   * about built in Query types, and will not be able to format custom
    * Query classes.
    * </p>
    */
@@ -486,7 +584,7 @@
     int end;
 
     public StrParser(String val) {
-      this(val,0,val.length());
+      this(val, 0, val.length());
     }
 
     public StrParser(String val, int start, int end) {
@@ -496,18 +594,18 @@
     }
 
     void eatws() {
-      while (pos<end && Character.isWhitespace(val.charAt(pos))) pos++;
+      while (pos < end && Character.isWhitespace(val.charAt(pos))) pos++;
     }
 
     void skip(int nChars) {
-      pos = Math.max(pos+nChars, end);
+      pos = Math.max(pos + nChars, end);
     }
 
     boolean opt(String s) {
       eatws();
-      int slen=s.length();
+      int slen = s.length();
       if (val.regionMatches(pos, s, 0, slen)) {
-        pos+=slen;
+        pos += slen;
         return true;
       }
       return false;
@@ -525,82 +623,82 @@
 
     void expect(String s) throws ParseException {
       eatws();
-      int slen=s.length();
+      int slen = s.length();
       if (val.regionMatches(pos, s, 0, slen)) {
-        pos+=slen;
+        pos += slen;
       } else {
-        throw new ParseException("Expected '"+s+"' at position " + pos + " in '"+val+"'");
+        throw new ParseException("Expected '" + s + "' at position " + pos + " in '" + val + "'");
       }
     }
 
     float getFloat() throws ParseException {
       eatws();
-      char[] arr = new char[end-pos];
+      char[] arr = new char[end - pos];
       int i;
-      for (i=0; i<arr.length; i++) {
+      for (i = 0; i < arr.length; i++) {
         char ch = val.charAt(pos);
-        if ( (ch>='0' && ch<='9')
-             || ch=='+' || ch=='-'
-             || ch=='.' || ch=='e' || ch=='E'
-        ) {
+        if ((ch >= '0' && ch <= '9')
+                || ch == '+' || ch == '-'
+                || ch == '.' || ch == 'e' || ch == 'E'
+                ) {
           pos++;
-          arr[i]=ch;
+          arr[i] = ch;
         } else {
           break;
         }
       }
 
-      return Float.parseFloat(new String(arr,0,i));
+      return Float.parseFloat(new String(arr, 0, i));
     }
 
     double getDouble() throws ParseException {
       eatws();
-      char[] arr = new char[end-pos];
+      char[] arr = new char[end - pos];
       int i;
-      for (i=0; i<arr.length; i++) {
+      for (i = 0; i < arr.length; i++) {
         char ch = val.charAt(pos);
-        if ( (ch>='0' && ch<='9')
-             || ch=='+' || ch=='-'
-             || ch=='.' || ch=='e' || ch=='E'
-        ) {
+        if ((ch >= '0' && ch <= '9')
+                || ch == '+' || ch == '-'
+                || ch == '.' || ch == 'e' || ch == 'E'
+                ) {
           pos++;
-          arr[i]=ch;
+          arr[i] = ch;
         } else {
           break;
         }
       }
 
-      return Double.parseDouble(new String(arr,0,i));
+      return Double.parseDouble(new String(arr, 0, i));
     }
 
     int getInt() throws ParseException {
       eatws();
-      char[] arr = new char[end-pos];
+      char[] arr = new char[end - pos];
       int i;
-      for (i=0; i<arr.length; i++) {
+      for (i = 0; i < arr.length; i++) {
         char ch = val.charAt(pos);
-        if ( (ch>='0' && ch<='9')
-             || ch=='+' || ch=='-'
-        ) {
+        if ((ch >= '0' && ch <= '9')
+                || ch == '+' || ch == '-'
+                ) {
           pos++;
-          arr[i]=ch;
+          arr[i] = ch;
         } else {
           break;
         }
       }
 
-      return Integer.parseInt(new String(arr,0,i));
+      return Integer.parseInt(new String(arr, 0, i));
     }
 
 
     String getId() throws ParseException {
       eatws();
-      int id_start=pos;
-      if (pos<end && Character.isJavaIdentifierStart(val.charAt(pos))) {
+      int id_start = pos;
+      if (pos < end && Character.isJavaIdentifierStart(val.charAt(pos))) {
         pos++;
-        while (pos<end) {
+        while (pos < end) {
           char ch = val.charAt(pos);
-          if (!Character.isJavaIdentifierPart(ch) && ch!='.') {
+          if (!Character.isJavaIdentifierPart(ch) && ch != '.') {
             break;
           }
           pos++;
@@ -614,35 +712,45 @@
     String getQuotedString() throws ParseException {
       eatws();
       char delim = peekChar();
-      if (!(delim=='\"' || delim=='\'')) {
+      if (!(delim == '\"' || delim == '\'')) {
         return null;
       }
       int val_start = ++pos;
       StringBuilder sb = new StringBuilder(); // needed for escaping
-      for(;;) {
-        if (pos>=end) {
-          throw new ParseException("Missing end quote for string at pos " + (val_start-1) + " str='"+val+"'");
+      for (; ;) {
+        if (pos >= end) {
+          throw new ParseException("Missing end quote for string at pos " + (val_start - 1) + " str='" + val + "'");
         }
         char ch = val.charAt(pos);
-        if (ch=='\\') {
+        if (ch == '\\') {
           pos++;
-          if (pos>=end) break; 
+          if (pos >= end) break;
           ch = val.charAt(pos);
-          switch(ch) {
-            case 'n' : ch='\n'; break;
-            case 't' : ch='\t'; break;
-            case 'r' : ch='\r'; break;
-            case 'b' : ch='\b'; break;
-            case 'f' : ch='\f'; break;
-            case 'u' :
-              if (pos+4 >= end) {
-                throw new ParseException("bad unicode escape \\uxxxx at pos" + (val_start-1) + " str='"+val+"'");                
+          switch (ch) {
+            case 'n':
+              ch = '\n';
+              break;
+            case 't':
+              ch = '\t';
+              break;
+            case 'r':
+              ch = '\r';
+              break;
+            case 'b':
+              ch = '\b';
+              break;
+            case 'f':
+              ch = '\f';
+              break;
+            case 'u':
+              if (pos + 4 >= end) {
+                throw new ParseException("bad unicode escape \\uxxxx at pos" + (val_start - 1) + " str='" + val + "'");
               }
-              ch = (char)Integer.parseInt(val.substring(pos+1, pos+5), 16);
+              ch = (char) Integer.parseInt(val.substring(pos + 1, pos + 5), 16);
               pos += 4;
               break;
           }
-        } else if (ch==delim) {
+        } else if (ch == delim) {
           pos++;  // skip over the quote
           break;
         }
@@ -656,12 +764,12 @@
     // next non-whitespace char
     char peek() {
       eatws();
-      return pos<end ? val.charAt(pos) : 0;
+      return pos < end ? val.charAt(pos) : 0;
     }
 
     // next char
     char peekChar() {
-      return pos<end ? val.charAt(pos) : 0;  
+      return pos < end ? val.charAt(pos) : 0;
     }
 
     public String toString() {
@@ -681,44 +789,44 @@
     return out;
   }
 
-  /** 
+  /**
    * Parse a function, returning a FunctionQuery
-   *
+   * <p/>
    * <p>
    * Syntax Examples....
    * </p>
-   *
+   * <p/>
    * <pre>
    * // Numeric fields default to correct type
    * // (ie: IntFieldSource or FloatFieldSource)
    * // Others use explicit ord(...) to generate numeric field value
    * myfield
-   *
+   * <p/>
    * // OrdFieldSource
    * ord(myfield)
-   *
+   * <p/>
    * // ReverseOrdFieldSource
    * rord(myfield)
-   *
+   * <p/>
    * // LinearFloatFunction on numeric field value
    * linear(myfield,1,2)
-   *
+   * <p/>
    * // MaxFloatFunction of LinearFloatFunction on numeric field value or constant
    * max(linear(myfield,1,2),100)
-   *
+   * <p/>
    * // ReciprocalFloatFunction on numeric field value
    * recip(myfield,1,2,3)
-   *
+   * <p/>
    * // ReciprocalFloatFunction on ReverseOrdFieldSource
    * recip(rord(myfield),1,2,3)
-   *
+   * <p/>
    * // ReciprocalFloatFunction on LinearFloatFunction on ReverseOrdFieldSource
    * recip(linear(rord(myfield),1,2),3,4,5)
    * </pre>
    */
   public static FunctionQuery parseFunction(String func, IndexSchema schema) throws ParseException {
     SolrCore core = SolrCore.getSolrCore();
-    return (FunctionQuery)(QParser.getParser(func,"func",new LocalSolrQueryRequest(core,new HashMap())).parse());
+    return (FunctionQuery) (QParser.getParser(func, "func", new LocalSolrQueryRequest(core, new HashMap())).parse());
     // return new FunctionQuery(parseValSource(new StrParser(func), schema));
   }
 

Modified: lucene/solr/branches/cloud/src/java/org/apache/solr/search/SolrCache.java
URL: http://svn.apache.org/viewvc/lucene/solr/branches/cloud/src/java/org/apache/solr/search/SolrCache.java?rev=890809&r1=890808&r2=890809&view=diff
==============================================================================
--- lucene/solr/branches/cloud/src/java/org/apache/solr/search/SolrCache.java (original)
+++ lucene/solr/branches/cloud/src/java/org/apache/solr/search/SolrCache.java Tue Dec 15 14:49:18 2009
@@ -30,7 +30,7 @@
  * 
  * @version $Id$
  */
-public interface SolrCache extends SolrInfoMBean {
+public interface SolrCache<K,V> extends SolrInfoMBean {
   public final static Logger log = LoggerFactory.getLogger(SolrCache.class);
 
 
@@ -82,10 +82,10 @@
   public int size();
 
   /** :TODO: copy from Map */
-  public Object put(Object key, Object value);
+  public V put(K key, V value);
 
   /** :TODO: copy from Map */
-  public Object get(Object key);
+  public V get(K key);
 
   /** :TODO: copy from Map */
   public void clear();
@@ -125,7 +125,7 @@
    * Warm this cache associated with <code>searcher</code> using the <code>old</code>
    * cache object.  <code>this</code> and <code>old</code> will have the same concrete type.
    */
-  void warm(SolrIndexSearcher searcher, SolrCache old) throws IOException;
+  void warm(SolrIndexSearcher searcher, SolrCache<K,V> old) throws IOException;
   // Q: an alternative to passing the searcher here would be to pass it in
   // init and have the cache implementation save it.
 

Modified: lucene/solr/branches/cloud/src/java/org/apache/solr/search/SolrIndexSearcher.java
URL: http://svn.apache.org/viewvc/lucene/solr/branches/cloud/src/java/org/apache/solr/search/SolrIndexSearcher.java?rev=890809&r1=890808&r2=890809&view=diff
==============================================================================
--- lucene/solr/branches/cloud/src/java/org/apache/solr/search/SolrIndexSearcher.java (original)
+++ lucene/solr/branches/cloud/src/java/org/apache/solr/search/SolrIndexSearcher.java Tue Dec 15 14:49:18 2009
@@ -67,10 +67,10 @@
   public final boolean enableLazyFieldLoading;
   
   private final boolean cachingEnabled;
-  private final SolrCache filterCache;
-  private final SolrCache queryResultCache;
-  private final SolrCache documentCache;
-  private final SolrCache fieldValueCache;
+  private final SolrCache<Query,DocSet> filterCache;
+  private final SolrCache<QueryResultKey,DocList> queryResultCache;
+  private final SolrCache<Integer,Document> documentCache;
+  private final SolrCache<String,Object> fieldValueCache;
 
   private final LuceneQueryOptimizer optimizer;
   

Modified: lucene/solr/branches/cloud/src/java/org/apache/solr/search/ValueSourceParser.java
URL: http://svn.apache.org/viewvc/lucene/solr/branches/cloud/src/java/org/apache/solr/search/ValueSourceParser.java?rev=890809&r1=890808&r2=890809&view=diff
==============================================================================
--- lucene/solr/branches/cloud/src/java/org/apache/solr/search/ValueSourceParser.java (original)
+++ lucene/solr/branches/cloud/src/java/org/apache/solr/search/ValueSourceParser.java Tue Dec 15 14:49:18 2009
@@ -32,13 +32,7 @@
 import org.apache.solr.schema.TrieDateField;
 import org.apache.solr.search.function.*;
 
-import org.apache.solr.search.function.distance.HaversineFunction;
-
-import org.apache.solr.search.function.distance.SquaredEuclideanFunction;
-import org.apache.solr.search.function.distance.VectorDistanceFunction;
-import org.apache.solr.search.function.distance.GeohashHaversineFunction;
-import org.apache.solr.search.function.distance.GeohashFunction;
-import org.apache.solr.search.function.distance.StringDistanceFunction;
+import org.apache.solr.search.function.distance.*;
 import org.apache.solr.util.plugin.NamedListInitializedPlugin;
 
 import java.io.IOException;
@@ -287,12 +281,12 @@
 
     addParser(new DoubleParser("rad") {
       public double func(int doc, DocValues vals) {
-        return Math.toRadians(vals.doubleVal(doc));
+        return vals.doubleVal(doc) * DistanceUtils.DEGREES_TO_RADIANS;
       }
     });
     addParser(new DoubleParser("deg") {
       public double func(int doc, DocValues vals) {
-        return Math.toDegrees(vals.doubleVal(doc));
+        return vals.doubleVal(doc) * DistanceUtils.RADIANS_TO_DEGREES;
       }
     });
     addParser(new DoubleParser("sqrt") {

Modified: lucene/solr/branches/cloud/src/java/org/apache/solr/search/function/ValueSource.java
URL: http://svn.apache.org/viewvc/lucene/solr/branches/cloud/src/java/org/apache/solr/search/function/ValueSource.java?rev=890809&r1=890808&r2=890809&view=diff
==============================================================================
--- lucene/solr/branches/cloud/src/java/org/apache/solr/search/function/ValueSource.java (original)
+++ lucene/solr/branches/cloud/src/java/org/apache/solr/search/function/ValueSource.java Tue Dec 15 14:49:18 2009
@@ -18,13 +18,19 @@
 package org.apache.solr.search.function;
 
 import org.apache.lucene.index.IndexReader;
-import org.apache.lucene.search.*;
-import org.apache.solr.search.function.DocValues;
+import org.apache.lucene.search.Explanation;
+import org.apache.lucene.search.FieldComparator;
+import org.apache.lucene.search.FieldComparatorSource;
+import org.apache.lucene.search.Scorer;
+import org.apache.lucene.search.Searcher;
+import org.apache.lucene.search.SortField;
 
 import java.io.IOException;
 import java.io.Serializable;
 import java.util.IdentityHashMap;
 import java.util.Map;
+import java.util.HashMap;
+import java.util.Collections;
 
 /**
  * Instantiates {@link org.apache.solr.search.function.DocValues} for a particular reader.
@@ -40,7 +46,8 @@
     return getValues(null, reader);
   }
 
-  /** Gets the values for this reader and the context that was previously
+  /**
+   * Gets the values for this reader and the context that was previously
    * passed to createWeight()
    */
   public DocValues getValues(Map context, IndexReader reader) throws IOException {
@@ -51,23 +58,113 @@
 
   public abstract int hashCode();
 
-  /** description of field, used in explain() */
+  /**
+   * description of field, used in explain()
+   */
   public abstract String description();
 
   public String toString() {
     return description();
   }
 
-  /** Implementations should propagate createWeight to sub-ValueSources which can optionally store
+  /**
+   * Get the SortField for this ValueSource.  Uses the {@link #getValues(java.util.Map, org.apache.lucene.index.IndexReader)}
+   * to populate the SortField.
+   * 
+   * @param reverse true if the order should be reversed.
+   * @return The {@link org.apache.lucene.search.SortField} for the ValueSource
+   * @throws IOException if there was a problem reading the values.
+   */
+  public SortField getSortField(boolean reverse) throws IOException {
+    //should we pass in the description for the field name?
+    //Hmm, Lucene is going to intern whatever we pass in, not sure I like that
+    //and we can't pass in null, either, as that throws an illegal arg. exception
+    return new SortField(description(), new ValueSourceComparatorSource(), reverse);
+  }
+
+
+  /**
+   * Implementations should propagate createWeight to sub-ValueSources which can optionally store
    * weight info in the context. The context object will be passed to getValues()
-   * where this info can be retrieved. */
+   * where this info can be retrieved.
+   */
   public void createWeight(Map context, Searcher searcher) throws IOException {
   }
 
-  /** Returns a new non-threadsafe context map. */
+  /**
+   * Returns a new non-threadsafe context map.
+   */
   public static Map newContext() {
     return new IdentityHashMap();
   }
+
+  class ValueSourceComparatorSource extends FieldComparatorSource {
+
+
+    public ValueSourceComparatorSource() {
+
+    }
+
+    public FieldComparator newComparator(String fieldname, int numHits,
+                                         int sortPos, boolean reversed) throws IOException {
+      return new ValueSourceComparator(numHits);
+    }
+  }
+
+  /**
+   * Implement a {@link org.apache.lucene.search.FieldComparator} that works
+   * off of the {@link org.apache.solr.search.function.DocValues} for a ValueSource
+   * instead of the normal Lucene FieldComparator that works off of a FieldCache.
+   */
+  class ValueSourceComparator extends FieldComparator {
+    private final double[] values;
+    private DocValues docVals;
+    private double bottom;
+
+    ValueSourceComparator(int numHits) {
+      values = new double[numHits];
+    }
+
+    public int compare(int slot1, int slot2) {
+      final double v1 = values[slot1];
+      final double v2 = values[slot2];
+      if (v1 > v2) {
+        return 1;
+      } else if (v1 < v2) {
+        return -1;
+      } else {
+        return 0;
+      }
+
+    }
+
+    public int compareBottom(int doc) {
+      final double v2 = docVals.doubleVal(doc);
+      if (bottom > v2) {
+        return 1;
+      } else if (bottom < v2) {
+        return -1;
+      } else {
+        return 0;
+      }
+    }
+
+    public void copy(int slot, int doc) {
+      values[slot] = docVals.doubleVal(doc);
+    }
+
+    public void setNextReader(IndexReader reader, int docBase) throws IOException {
+      docVals = getValues(Collections.emptyMap(), reader);
+    }
+
+    public void setBottom(final int bottom) {
+      this.bottom = values[bottom];
+    }
+
+    public Comparable value(int slot) {
+      return Double.valueOf(values[slot]);
+    }
+  }
 }
 
 
@@ -86,7 +183,9 @@
     setCheckDeletes(true);
   }
 
-  public IndexReader getReader() { return reader; }
+  public IndexReader getReader() {
+    return reader;
+  }
 
   public void setCheckDeletes(boolean checkDeletes) {
     this.checkDeletes = checkDeletes && reader.hasDeletions();
@@ -107,9 +206,9 @@
 
   @Override
   public int nextDoc() throws IOException {
-    for(;;) {
+    for (; ;) {
       doc++;
-      if (doc >= maxDoc) return doc=NO_MORE_DOCS;
+      if (doc >= maxDoc) return doc = NO_MORE_DOCS;
       if (matches(doc)) return doc;
     }
   }
@@ -117,7 +216,7 @@
   @Override
   public int advance(int target) throws IOException {
     // also works fine when target==NO_MORE_DOCS
-    doc = target-1;
+    doc = target - 1;
     return nextDoc();
   }
 
@@ -126,7 +225,7 @@
   }
 
   public boolean next() {
-    for(;;) {
+    for (; ;) {
       doc++;
       if (doc >= maxDoc) return false;
       if (matches(doc)) return true;
@@ -134,7 +233,7 @@
   }
 
   public boolean skipTo(int target) {
-    doc = target-1;
+    doc = target - 1;
     return next();
   }
 

Modified: lucene/solr/branches/cloud/src/java/org/apache/solr/search/function/distance/DistanceUtils.java
URL: http://svn.apache.org/viewvc/lucene/solr/branches/cloud/src/java/org/apache/solr/search/function/distance/DistanceUtils.java?rev=890809&r1=890808&r2=890809&view=diff
==============================================================================
--- lucene/solr/branches/cloud/src/java/org/apache/solr/search/function/distance/DistanceUtils.java (original)
+++ lucene/solr/branches/cloud/src/java/org/apache/solr/search/function/distance/DistanceUtils.java Tue Dec 15 14:49:18 2009
@@ -22,6 +22,9 @@
  *
  **/
 public class DistanceUtils {
+  public static final double DEGREES_TO_RADIANS = Math.PI / 180.0;
+  public static final double RADIANS_TO_DEGREES = 180.0 / Math.PI;
+
   /**
    * @see org.apache.solr.search.function.distance.HaversineFunction
    * 
@@ -46,4 +49,6 @@
     }
     return result;
   }
+
+
 }

Modified: lucene/solr/branches/cloud/src/solrj/org/apache/solr/client/solrj/SolrQuery.java
URL: http://svn.apache.org/viewvc/lucene/solr/branches/cloud/src/solrj/org/apache/solr/client/solrj/SolrQuery.java?rev=890809&r1=890808&r2=890809&view=diff
==============================================================================
--- lucene/solr/branches/cloud/src/solrj/org/apache/solr/client/solrj/SolrQuery.java (original)
+++ lucene/solr/branches/cloud/src/solrj/org/apache/solr/client/solrj/SolrQuery.java Tue Dec 15 14:49:18 2009
@@ -22,6 +22,7 @@
 import org.apache.solr.common.params.HighlightParams;
 import org.apache.solr.common.params.ModifiableSolrParams;
 import org.apache.solr.common.params.StatsParams;
+import org.apache.solr.common.params.TermsParams;
 
 import java.util.regex.Pattern;
 
@@ -54,7 +55,154 @@
     this.set(CommonParams.Q, q);
   }
 
+  /** enable/disable terms.  
+   * 
+   * @param b flag to indicate terms should be enabled. <br /> if b==false, removes all other terms parameters
+   * @return Current reference (<i>this</i>)
+   */
+  public SolrQuery setTerms(boolean b) {
+    if (b) {
+      this.set(TermsParams.TERMS, true);
+    } else {
+      this.remove(TermsParams.TERMS);
+      this.remove(TermsParams.TERMS_FIELD);
+      this.remove(TermsParams.TERMS_LOWER);
+      this.remove(TermsParams.TERMS_UPPER);
+      this.remove(TermsParams.TERMS_UPPER_INCLUSIVE);
+      this.remove(TermsParams.TERMS_LOWER_INCLUSIVE);
+      this.remove(TermsParams.TERMS_LIMIT);
+      this.remove(TermsParams.TERMS_PREFIX_STR);
+      this.remove(TermsParams.TERMS_MINCOUNT);
+      this.remove(TermsParams.TERMS_MAXCOUNT);
+      this.remove(TermsParams.TERMS_RAW);
+      this.remove(TermsParams.TERMS_SORT);
+      this.remove(TermsParams.TERMS_REGEXP_STR);
+      this.remove(TermsParams.TERMS_REGEXP_FLAG);
+    }
+    return this;
+  }
+  
+  public boolean getTerms() {
+    return this.getBool(TermsParams.TERMS, false);
+  }
+  
+  public SolrQuery addTermsField(String field) {
+    this.add(TermsParams.TERMS_FIELD, field);
+    return this;
+  }
+  
+  public String[] getTermsFields() {
+    return this.getParams(TermsParams.TERMS_FIELD);
+  }
+  
+  public SolrQuery setTermsLower(String lower) {
+    this.set(TermsParams.TERMS_LOWER, lower);
+    return this;
+  }
+  
+  public String getTermsLower() {
+    return this.get(TermsParams.TERMS_LOWER, "");
+  }
+  
+  public SolrQuery setTermsUpper(String upper) {
+    this.set(TermsParams.TERMS_UPPER, upper);
+    return this;
+  }
+  
+  public String getTermsUpper() {
+    return this.get(TermsParams.TERMS_UPPER, "");
+  }
+  
+  public SolrQuery setTermsUpperInclusive(boolean b) {
+    this.set(TermsParams.TERMS_UPPER_INCLUSIVE, b);
+    return this;
+  }
+  
+  public boolean getTermsUpperInclusive() {
+    return this.getBool(TermsParams.TERMS_UPPER_INCLUSIVE, false);
+  }
+  
+  public SolrQuery setTermsLowerInclusive(boolean b) {
+    this.set(TermsParams.TERMS_LOWER_INCLUSIVE, b);
+    return this;
+  }
+  
+  public boolean getTermsLowerInclusive() {
+    return this.getBool(TermsParams.TERMS_LOWER_INCLUSIVE, true);
+  }
+ 
+  public SolrQuery setTermsLimit(int limit) {
+    this.set(TermsParams.TERMS_LIMIT, limit);
+    return this;
+  }
+  
+  public int getTermsLimit() {
+    return this.getInt(TermsParams.TERMS_LIMIT, 10);
+  }
+ 
+  public SolrQuery setTermsMinCount(int cnt) {
+    this.set(TermsParams.TERMS_MINCOUNT, cnt);
+    return this;
+  }
+  
+  public int getTermsMinCount() {
+    return this.getInt(TermsParams.TERMS_MINCOUNT, 1);
+  }
 
+  public SolrQuery setTermsMaxCount(int cnt) {
+    this.set(TermsParams.TERMS_MAXCOUNT, cnt);
+    return this;
+  }
+  
+  public int getTermsMaxCount() {
+    return this.getInt(TermsParams.TERMS_MAXCOUNT, -1);
+  }
+  
+  public SolrQuery setTermsPrefix(String prefix) {
+    this.set(TermsParams.TERMS_PREFIX_STR, prefix);
+    return this;
+  }
+  
+  public String getTermsPrefix() {
+    return this.get(TermsParams.TERMS_PREFIX_STR, "");
+  }
+  
+  public SolrQuery setTermsRaw(boolean b) {
+    this.set(TermsParams.TERMS_RAW, b);
+    return this;
+  }
+  
+  public boolean getTermsRaw() {
+    return this.getBool(TermsParams.TERMS_RAW, false);
+  }
+ 
+  public SolrQuery setTermsSortString(String type) {
+    this.set(TermsParams.TERMS_SORT, type);
+    return this;
+  }
+  
+  public String getTermsSortString() {
+    return this.get(TermsParams.TERMS_SORT, TermsParams.TERMS_SORT_COUNT);
+  }
+
+  public SolrQuery setTermsRegex(String regex)  {
+    this.set(TermsParams.TERMS_REGEXP_STR, regex);
+    return this;
+  }
+
+  public String getTermsRegex() {
+    return this.get(TermsParams.TERMS_REGEXP_STR);
+  }
+
+  public SolrQuery setTermsRegexFlag(String flag) {
+    this.add(TermsParams.TERMS_REGEXP_FLAG, flag);
+    return this;
+  }
+
+  public String[] getTermsRegexFlags()  {
+    return this.getParams(TermsParams.TERMS_REGEXP_FLAG);
+  }
+     
   /** Add field(s) for facet computation.
    * 
    * @param fields Array of field names from the IndexSchema

Modified: lucene/solr/branches/cloud/src/solrj/org/apache/solr/client/solrj/impl/StreamingUpdateSolrServer.java
URL: http://svn.apache.org/viewvc/lucene/solr/branches/cloud/src/solrj/org/apache/solr/client/solrj/impl/StreamingUpdateSolrServer.java?rev=890809&r1=890808&r2=890809&view=diff
==============================================================================
--- lucene/solr/branches/cloud/src/solrj/org/apache/solr/client/solrj/impl/StreamingUpdateSolrServer.java (original)
+++ lucene/solr/branches/cloud/src/solrj/org/apache/solr/client/solrj/impl/StreamingUpdateSolrServer.java Tue Dec 15 14:49:18 2009
@@ -27,6 +27,7 @@
 import java.util.concurrent.locks.Lock;
 import java.util.concurrent.locks.ReentrantLock;
 
+import org.apache.commons.httpclient.HttpClient;
 import org.apache.commons.httpclient.HttpStatus;
 import org.apache.commons.httpclient.methods.PostMethod;
 import org.apache.commons.httpclient.methods.RequestEntity;
@@ -61,10 +62,26 @@
   final Queue<Runner> runners;
   volatile CountDownLatch lock = null;  // used to block everything
   final int threadCount;
-  
-  public StreamingUpdateSolrServer(String solrServerUrl, int queueSize, int threadCount ) throws MalformedURLException  {
-    super( solrServerUrl );
-    queue = new LinkedBlockingQueue<UpdateRequest>( queueSize );
+
+  /**
+   * Uses an internal MultiThreadedHttpConnectionManager to manage http connections
+   *
+   * @param solrServerUrl The solr server url
+   * @param queueSize     The buffer size before the documents are sent o the server
+   * @param threadCount   The number of backgtound threads used to empty the queue
+   * @throws MalformedURLException
+   */
+  public StreamingUpdateSolrServer(String solrServerUrl, int queueSize, int threadCount) throws MalformedURLException {
+    this(solrServerUrl, null, queueSize, threadCount);
+  }
+
+  /**
+   * Uses the supplied HttpClient to send documents to the solr server, the HttpClient should be instantiated using a
+   * MultiThreadedHttpConnectionManager.
+   */
+  public StreamingUpdateSolrServer(String solrServerUrl, HttpClient client, int queueSize, int threadCount) throws MalformedURLException {
+    super(solrServerUrl, client);
+    queue = new LinkedBlockingQueue<UpdateRequest>(queueSize);
     this.threadCount = threadCount;
     runners = new LinkedList<Runner>();
   }

Modified: lucene/solr/branches/cloud/src/solrj/org/apache/solr/client/solrj/response/QueryResponse.java
URL: http://svn.apache.org/viewvc/lucene/solr/branches/cloud/src/solrj/org/apache/solr/client/solrj/response/QueryResponse.java?rev=890809&r1=890808&r2=890809&view=diff
==============================================================================
--- lucene/solr/branches/cloud/src/solrj/org/apache/solr/client/solrj/response/QueryResponse.java (original)
+++ lucene/solr/branches/cloud/src/solrj/org/apache/solr/client/solrj/response/QueryResponse.java Tue Dec 15 14:49:18 2009
@@ -46,6 +46,7 @@
   private NamedList<Object> _highlightingInfo = null;
   private NamedList<Object> _spellInfo = null;
   private NamedList<Object> _statsInfo = null;
+  private NamedList<Object> _termsInfo = null;
 
   // Facet stuff
   private Map<String,Integer> _facetQuery = null;
@@ -59,6 +60,9 @@
   // SpellCheck Response
   private SpellCheckResponse _spellResponse = null;
 
+  // Terms Response
+  private TermsResponse _termsResponse = null;
+  
   // Field stats Response
   private Map<String,FieldStatsInfo> _fieldStatsInfo = null;
   
@@ -118,6 +122,10 @@
         _statsInfo = (NamedList<Object>) res.getVal( i );
         extractStatsInfo( _statsInfo );
       }
+      else if ( "terms".equals( n ) ) {
+        _termsInfo = (NamedList<Object>) res.getVal( i );
+        extractTermsInfo( _termsInfo );
+      }
     }
   }
 
@@ -125,6 +133,10 @@
     _spellResponse = new SpellCheckResponse(spellInfo);
   }
 
+  private void extractTermsInfo(NamedList<Object> termsInfo) {
+    _termsResponse = new TermsResponse(termsInfo);
+  }
+  
   private void extractStatsInfo(NamedList<Object> info) {
     if( info != null ) {
       _fieldStatsInfo = new HashMap<String, FieldStatsInfo>();
@@ -276,6 +288,10 @@
     return _spellResponse;
   }
 
+  public TermsResponse getTermsResponse() {
+    return _termsResponse;
+  }
+  
   /**
    * See also: {@link #getLimitingFacets()}
    */

Modified: lucene/solr/branches/cloud/src/test/org/apache/solr/client/solrj/MultiCoreExampleTestBase.java
URL: http://svn.apache.org/viewvc/lucene/solr/branches/cloud/src/test/org/apache/solr/client/solrj/MultiCoreExampleTestBase.java?rev=890809&r1=890808&r2=890809&view=diff
==============================================================================
--- lucene/solr/branches/cloud/src/test/org/apache/solr/client/solrj/MultiCoreExampleTestBase.java (original)
+++ lucene/solr/branches/cloud/src/test/org/apache/solr/client/solrj/MultiCoreExampleTestBase.java Tue Dec 15 14:49:18 2009
@@ -17,8 +17,6 @@
 
 package org.apache.solr.client.solrj;
 
-import java.io.File;
-
 import org.apache.solr.client.solrj.request.CoreAdminRequest;
 import org.apache.solr.client.solrj.request.QueryRequest;
 import org.apache.solr.client.solrj.request.UpdateRequest;
@@ -132,35 +130,6 @@
     long after = mcr.getStartTime( name ).getTime();
     assertTrue( "should have more recent time: "+after+","+before, after > before );
 
-    // test alias
-    CoreAdminRequest.aliasCore("core1","corefoo",coreadmin);
-    assertEquals( 1, getSolrCore1().query( new SolrQuery( "id:BBB" ) ).getResults().size() );
-    assertEquals( 1, getSolrCore("corefoo").query( new SolrQuery( "id:BBB" ) ).getResults().size() );
-
-    // test that reload affects aliases
-    CoreAdminRequest.reloadCore("core1", coreadmin);
-
-    // this is only an effective test for embedded, where we have
-    // direct access to the core container.
-    SolrCore c1 = cores.getCore("core1");
-    SolrCore c2 = cores.getCore("corefoo");
-    assertTrue(c1 == c2);
-    if (c1 != null) c1.close();
-    if (c2 != null) c2.close();
-
-    // retest core query
-    assertEquals( 1, getSolrCore1().query( new SolrQuery( "id:BBB" ) ).getResults().size() );
-
-    // test close
-    CoreAdminRequest.unloadCore("corefoo",coreadmin);
-    try {
-      getSolrCore("corefoo").query( new SolrQuery( "id:BBB" ) );
-      fail( "corefoo should be gone" );
-    }
-    catch( Exception ex ) {}
-    // aliased core should still work
-    assertEquals( 1, getSolrCore1().query( new SolrQuery( "id:BBB" ) ).getResults().size() );
-    
     // test move
     CoreAdminRequest.renameCore("core1","corea",coreadmin);
     CoreAdminRequest.renameCore("corea","coreb",coreadmin);

Modified: lucene/solr/branches/cloud/src/test/org/apache/solr/client/solrj/SolrQueryTest.java
URL: http://svn.apache.org/viewvc/lucene/solr/branches/cloud/src/test/org/apache/solr/client/solrj/SolrQueryTest.java?rev=890809&r1=890808&r2=890809&view=diff
==============================================================================
--- lucene/solr/branches/cloud/src/test/org/apache/solr/client/solrj/SolrQueryTest.java (original)
+++ lucene/solr/branches/cloud/src/test/org/apache/solr/client/solrj/SolrQueryTest.java Tue Dec 15 14:49:18 2009
@@ -143,4 +143,56 @@
     assertEquals( SolrQuery.ORDER.asc, SolrQuery.ORDER.desc.reverse() );
     assertEquals( SolrQuery.ORDER.desc, SolrQuery.ORDER.asc.reverse() );
   }
+  
+  public void testTerms() {
+    SolrQuery q = new SolrQuery();
+    
+    // check getters
+    assertEquals(false, q.getTerms());
+    assertEquals(null, q.getTermsFields());
+    assertEquals("", q.getTermsLower());
+    assertEquals("", q.getTermsUpper());
+    assertEquals(false, q.getTermsUpperInclusive());
+    assertEquals(true, q.getTermsLowerInclusive());
+    assertEquals(10, q.getTermsLimit());
+    assertEquals(1, q.getTermsMinCount());
+    assertEquals(-1, q.getTermsMaxCount());
+    assertEquals("", q.getTermsPrefix());
+    assertEquals(false, q.getTermsRaw());
+    assertEquals("count", q.getTermsSortString());
+    assertEquals(null, q.getTermsRegex());
+    assertEquals(null, q.getTermsRegexFlags());
+
+    // check setters
+    q.setTerms(true);
+    assertEquals(true, q.getTerms());
+    q.addTermsField("testfield");
+    assertEquals(1, q.getTermsFields().length);
+    assertEquals("testfield", q.getTermsFields()[0]);
+    q.setTermsLower("lower");
+    assertEquals("lower", q.getTermsLower());
+    q.setTermsUpper("upper");
+    assertEquals("upper", q.getTermsUpper());
+    q.setTermsUpperInclusive(true);
+    assertEquals(true, q.getTermsUpperInclusive());
+    q.setTermsLowerInclusive(false);
+    assertEquals(false, q.getTermsLowerInclusive());
+    q.setTermsLimit(5);
+    assertEquals(5, q.getTermsLimit());
+    q.setTermsMinCount(2);
+    assertEquals(2, q.getTermsMinCount());
+    q.setTermsMaxCount(5);
+    assertEquals(5, q.getTermsMaxCount());
+    q.setTermsPrefix("prefix");
+    assertEquals("prefix", q.getTermsPrefix());
+    q.setTermsRaw(true);
+    assertEquals(true, q.getTermsRaw());
+    q.setTermsSortString("index");
+    assertEquals("index", q.getTermsSortString());
+    q.setTermsRegex("a.*");
+    assertEquals("a.*", q.getTermsRegex());
+    q.setTermsRegexFlag("case_insensitive");
+    q.setTermsRegexFlag("multiline");
+    assertEquals(2, q.getTermsRegexFlags().length);
+  }
 }

Modified: lucene/solr/branches/cloud/src/test/org/apache/solr/handler/component/TermsComponentTest.java
URL: http://svn.apache.org/viewvc/lucene/solr/branches/cloud/src/test/org/apache/solr/handler/component/TermsComponentTest.java?rev=890809&r1=890808&r2=890809&view=diff
==============================================================================
--- lucene/solr/branches/cloud/src/test/org/apache/solr/handler/component/TermsComponentTest.java (original)
+++ lucene/solr/branches/cloud/src/test/org/apache/solr/handler/component/TermsComponentTest.java Tue Dec 15 14:49:18 2009
@@ -26,6 +26,7 @@
 import org.apache.solr.request.LocalSolrQueryRequest;
 import org.apache.solr.request.SolrQueryResponse;
 
+import java.util.regex.Pattern;
 
 /**
  *
@@ -210,6 +211,73 @@
     assertTrue("value is null and it shouldn't be", value != null);
   }
 
+  public void testRegexp() throws Exception {
+    SolrCore core = h.getCore();
+    TermsComponent tc = (TermsComponent) core.getSearchComponent("termsComp");
+    assertTrue("tc is null and it shouldn't be", tc != null);
+
+    ModifiableSolrParams params = new ModifiableSolrParams();
+    params.add(TermsParams.TERMS, "true");
+    params.add(TermsParams.TERMS_FIELD, "standardfilt");
+    params.add(TermsParams.TERMS_LOWER,  "bb");
+    params.add(TermsParams.TERMS_LOWER_INCLUSIVE, "false");
+    params.add(TermsParams.TERMS_REGEXP_STR, "b.*");
+    params.add(TermsParams.TERMS_UPPER, "bbbb");
+    params.add(TermsParams.TERMS_UPPER_INCLUSIVE, "true");
+    params.add(TermsParams.TERMS_LIMIT, String.valueOf(50));
+    SolrRequestHandler handler;
+    SolrQueryResponse rsp;
+    NamedList values;
+    NamedList terms;
+    handler = core.getRequestHandler("/terms");
+    assertTrue("handler is null and it shouldn't be", handler != null);
+    rsp = new SolrQueryResponse();
+    rsp.add("responseHeader", new SimpleOrderedMap());
+    handler.handleRequest(new LocalSolrQueryRequest(core, params), rsp);
+    values = rsp.getValues();
+    terms = (NamedList) ((NamedList) values.get("terms")).get("standardfilt");
+    assertEquals("terms Size: " + terms.size() + " is not: 1", 1, terms.size());
+  }
+
+  public void testRegexpFlagParsing() {
+      ModifiableSolrParams params = new ModifiableSolrParams();
+      params.add(TermsParams.TERMS_REGEXP_FLAG, "case_insensitive", "literal", "comments", "multiline", "unix_lines",
+              "unicode_case", "dotall", "canon_eq");
+      int flags = new TermsComponent().resolveRegexpFlags(params);
+      int expected = Pattern.CASE_INSENSITIVE | Pattern.LITERAL | Pattern.COMMENTS | Pattern.MULTILINE | Pattern.UNIX_LINES
+              | Pattern.UNICODE_CASE | Pattern.DOTALL | Pattern.CANON_EQ;
+      assertEquals(expected, flags);
+  }
+
+  public void testRegexpWithFlags() throws Exception {
+    SolrCore core = h.getCore();
+    TermsComponent tc = (TermsComponent) core.getSearchComponent("termsComp");
+    assertTrue("tc is null and it shouldn't be", tc != null);
+
+    ModifiableSolrParams params = new ModifiableSolrParams();
+    params.add(TermsParams.TERMS, "true");
+    params.add(TermsParams.TERMS_FIELD, "standardfilt");
+    params.add(TermsParams.TERMS_LOWER,  "bb");
+    params.add(TermsParams.TERMS_LOWER_INCLUSIVE, "false");
+    params.add(TermsParams.TERMS_REGEXP_STR, "B.*");
+    params.add(TermsParams.TERMS_REGEXP_FLAG, "case_insensitive");
+    params.add(TermsParams.TERMS_UPPER, "bbbb");
+    params.add(TermsParams.TERMS_UPPER_INCLUSIVE, "true");
+    params.add(TermsParams.TERMS_LIMIT, String.valueOf(50));
+    SolrRequestHandler handler;
+    SolrQueryResponse rsp;
+    NamedList values;
+    NamedList terms;
+    handler = core.getRequestHandler("/terms");
+    assertTrue("handler is null and it shouldn't be", handler != null);
+    rsp = new SolrQueryResponse();
+    rsp.add("responseHeader", new SimpleOrderedMap());
+    handler.handleRequest(new LocalSolrQueryRequest(core, params), rsp);
+    values = rsp.getValues();
+    terms = (NamedList) ((NamedList) values.get("terms")).get("standardfilt");
+    assertEquals("terms Size: " + terms.size() + " is not: 1", 1, terms.size());
+  }
+
   public void testSortCount() throws Exception {
     SolrCore core = h.getCore();
     TermsComponent tc = (TermsComponent) core.getSearchComponent("termsComp");

Modified: lucene/solr/branches/cloud/src/webapp/web/admin/index.jsp
URL: http://svn.apache.org/viewvc/lucene/solr/branches/cloud/src/webapp/web/admin/index.jsp?rev=890809&r1=890808&r2=890809&view=diff
==============================================================================
--- lucene/solr/branches/cloud/src/webapp/web/admin/index.jsp (original)
+++ lucene/solr/branches/cloud/src/webapp/web/admin/index.jsp Tue Dec 15 14:49:18 2009
@@ -58,10 +58,11 @@
 <%-- List the cores (that arent this one) so we can switch --%>
 <% org.apache.solr.core.CoreContainer cores = (org.apache.solr.core.CoreContainer)request.getAttribute("org.apache.solr.CoreContainer");
   if (cores!=null) {
-    Collection<String> names = cores.getCoreNames();
+    Collection<SolrCore> names = cores.getCores();
     if (names.size() > 1) {%><tr><td><strong>Cores:</strong><br></td><td><%
-    for (String name : names) {
-    %>[<a href="../../<%=name%>/admin/"><%=name%></a>]<%         
+    for (SolrCore name : names) {
+      if(name.equals(core.getName())) continue;
+    %>[<a href="../../<%=name.getName()%>/admin/"><%=name%></a>]<%         
   }%></td></tr><%
 }}%>
 

Modified: lucene/solr/branches/cloud/src/webapp/web/index.jsp
URL: http://svn.apache.org/viewvc/lucene/solr/branches/cloud/src/webapp/web/index.jsp?rev=890809&r1=890808&r2=890809&view=diff
==============================================================================
--- lucene/solr/branches/cloud/src/webapp/web/index.jsp (original)
+++ lucene/solr/branches/cloud/src/webapp/web/index.jsp Tue Dec 15 14:49:18 2009
@@ -1,3 +1,5 @@
+<%@ page import="org.apache.solr.core.SolrCore" %>
+<%@ page import="java.util.Collection" %>
 <%--
  Licensed to the Apache Software Foundation (ASF) under one or more
  contributor license agreements.  See the NOTICE file distributed with
@@ -29,11 +31,16 @@
 
 <% 
   org.apache.solr.core.CoreContainer cores = (org.apache.solr.core.CoreContainer)request.getAttribute("org.apache.solr.CoreContainer");
+  Collection<SolrCore> solrCores = cores.getCores();
   if( cores != null
-   && cores.getCores().size() > 0 // HACK! check that we have valid names...
-   && cores.getCores().iterator().next().getName().length() != 0 ) { 
-    for( org.apache.solr.core.SolrCore core : cores.getCores() ) {%>
-<a href="<%= core.getName() %>/admin/">Admin <%= core.getName() %></a><br/>
+   && solrCores.size() > 0 // HACK! check that we have valid names...
+   && solrCores.iterator().next().getName().length() != 0 ) {
+    for( org.apache.solr.core.SolrCore core : cores.getCores() ) {
+       String coreName = core.getName();
+      if("".equals(coreName) ) coreName =".";
+%>
+<a href="<%= coreName %>/admin/">Admin <%= core.getName() %></a>
+<br/>
 <% }} else { %>
 <a href="admin/">Solr Admin</a>
 <% } %>



Mime
View raw message