You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by ds...@apache.org on 2015/02/10 04:23:19 UTC

svn commit: r1658617 - in /lucene/dev/branches/branch_5x: ./ solr/ solr/core/ solr/core/src/java/org/apache/solr/handler/component/ solr/core/src/java/org/apache/solr/request/ solr/core/src/java/org/apache/solr/util/ solr/core/src/test/org/apache/solr/...

Author: dsmiley
Date: Tue Feb 10 03:23:18 2015
New Revision: 1658617

URL: http://svn.apache.org/r1658617
Log:
SOLR-7005: New facet.heatmap on spatial RPT fields

Added:
    lucene/dev/branches/branch_5x/solr/core/src/java/org/apache/solr/handler/component/SpatialHeatmapFacets.java
      - copied, changed from r1658614, lucene/dev/trunk/solr/core/src/java/org/apache/solr/handler/component/SpatialHeatmapFacets.java
    lucene/dev/branches/branch_5x/solr/core/src/test/org/apache/solr/handler/component/SpatialHeatmapFacetsTest.java
      - copied unchanged from r1658614, lucene/dev/trunk/solr/core/src/test/org/apache/solr/handler/component/SpatialHeatmapFacetsTest.java
Modified:
    lucene/dev/branches/branch_5x/   (props changed)
    lucene/dev/branches/branch_5x/solr/   (props changed)
    lucene/dev/branches/branch_5x/solr/CHANGES.txt   (contents, props changed)
    lucene/dev/branches/branch_5x/solr/core/   (props changed)
    lucene/dev/branches/branch_5x/solr/core/src/java/org/apache/solr/handler/component/FacetComponent.java
    lucene/dev/branches/branch_5x/solr/core/src/java/org/apache/solr/request/SimpleFacets.java
    lucene/dev/branches/branch_5x/solr/core/src/java/org/apache/solr/util/SpatialUtils.java
    lucene/dev/branches/branch_5x/solr/core/src/test/org/apache/solr/TestGroupingSearch.java
    lucene/dev/branches/branch_5x/solr/solrj/   (props changed)
    lucene/dev/branches/branch_5x/solr/solrj/src/java/org/apache/solr/common/params/FacetParams.java

Modified: lucene/dev/branches/branch_5x/solr/CHANGES.txt
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_5x/solr/CHANGES.txt?rev=1658617&r1=1658616&r2=1658617&view=diff
==============================================================================
--- lucene/dev/branches/branch_5x/solr/CHANGES.txt (original)
+++ lucene/dev/branches/branch_5x/solr/CHANGES.txt Tue Feb 10 03:23:18 2015
@@ -53,10 +53,13 @@ New Features
   for setting 'highlight' and 'allTermsRequired' in the suggester configuration.
   (Boon Low, Varun Thacker via Tomás Fernández Löbbe)
 
-* SOLR-7083:Support managing all named components in solrconfig such as
+* SOLR-7083: Support managing all named components in solrconfig such as
   requestHandler, queryParser, queryResponseWriter, valueSourceParser,
   transformer, queryConverter (Noble Paul)
 
+* SOLR-7005: Spatial 2D heatmap faceting on RPT fields via new facet.heatmap with PNG and
+  2D int array formats. (David Smiley)
+
 Bug Fixes
 ----------------------
 

Modified: lucene/dev/branches/branch_5x/solr/core/src/java/org/apache/solr/handler/component/FacetComponent.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_5x/solr/core/src/java/org/apache/solr/handler/component/FacetComponent.java?rev=1658617&r1=1658616&r2=1658617&view=diff
==============================================================================
--- lucene/dev/branches/branch_5x/solr/core/src/java/org/apache/solr/handler/component/FacetComponent.java (original)
+++ lucene/dev/branches/branch_5x/solr/core/src/java/org/apache/solr/handler/component/FacetComponent.java Tue Feb 10 03:23:18 2015
@@ -51,8 +51,7 @@ import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
 /**
- * TODO!
- *
+ * Computes facets -- aggregations with counts of terms or ranges over the whole search results.
  *
  * @since solr 1.3
  */
@@ -66,9 +65,9 @@ public class FacetComponent extends Sear
   private static final String PIVOT_REFINE_PREFIX = "{!"+PivotFacet.REFINE_PARAM+"=";
 
   /**
-   * incrememented counter used to track the values being refined in a given request.  
+   * Incremented counter used to track the values being refined in a given request.
    * This counter is used in conjunction with {@link PivotFacet#REFINE_PARAM} to identify
-   * which refinement values are associated with which pivots
+   * which refinement values are associated with which pivots.
    */
   int pivotRefinementCounter = 0;
 
@@ -214,6 +213,7 @@ public class FacetComponent extends Sear
           shardsRefineRequest.params.set(FacetParams.FACET, "true");
           shardsRefineRequest.params.remove(FacetParams.FACET_FIELD);
           shardsRefineRequest.params.remove(FacetParams.FACET_QUERY);
+          //TODO remove interval faceting, and ranges and heatmap too?
 
           for (int i = 0; i < distribFieldFacetRefinements.size();) {
             String facetCommand = distribFieldFacetRefinements.get(i++);
@@ -318,6 +318,8 @@ public class FacetComponent extends Sear
       modifyRequestForRangeFacets(sreq, fi);
       
       modifyRequestForPivotFacets(rb, sreq, fi.pivotFacets);
+
+      SpatialHeatmapFacets.distribModifyRequest(sreq, fi.heatmapFacets);
       
       sreq.params.remove(FacetParams.FACET_MINCOUNT);
       sreq.params.remove(FacetParams.FACET_OFFSET);
@@ -332,17 +334,11 @@ public class FacetComponent extends Sear
   // we must get all the range buckets back in order to have coherent lists at the end, see SOLR-6154
   private void modifyRequestForRangeFacets(ShardRequest sreq, FacetInfo fi) {
     // Collect all the range fields.
-    if (sreq.params.getParams(FacetParams.FACET_RANGE) == null) {
-      return;
-    }
-    List<String> rangeFields = new ArrayList<>();
-    for (String field : sreq.params.getParams(FacetParams.FACET_RANGE)) {
-      rangeFields.add(field);
-    }
-
-    for (String field : rangeFields) {
-      sreq.params.remove("f." + field + ".facet.mincount");
-      sreq.params.add("f." + field + ".facet.mincount", "0");
+    final String[] fields = sreq.params.getParams(FacetParams.FACET_RANGE);
+    if (fields != null) {
+      for (String field : fields) {
+        sreq.params.set("f." + field + ".facet.mincount", "0");
+      }
     }
   }
 
@@ -550,6 +546,9 @@ public class FacetComponent extends Sear
       // refinement reqs still needed (below) once we've considered every shard
       doDistribPivots(rb, shardNum, facet_counts);
 
+      // Distributed facet_heatmaps
+      SpatialHeatmapFacets.distribHandleResponse(fi.heatmapFacets, facet_counts);
+
     } // end for-each-response-in-shard-request...
     
     // refine each pivot based on the new shard data
@@ -1046,6 +1045,8 @@ public class FacetComponent extends Sear
     facet_counts.add("facet_dates", fi.dateFacets);
     facet_counts.add("facet_ranges", fi.rangeFacets);
     facet_counts.add("facet_intervals", fi.intervalFacets);
+    facet_counts.add(SpatialHeatmapFacets.RESPONSE_KEY,
+        SpatialHeatmapFacets.distribFinish(fi.heatmapFacets, rb));
 
     if (fi.pivotFacets != null && fi.pivotFacets.size() > 0) {
       facet_counts.add(PIVOT_KEY, createPivotFacetOutput(rb));
@@ -1112,6 +1113,7 @@ public class FacetComponent extends Sear
       = new SimpleOrderedMap<>();
     public SimpleOrderedMap<PivotFacet> pivotFacets
       = new SimpleOrderedMap<>();
+    public LinkedHashMap<String,SpatialHeatmapFacets.HeatmapFacet> heatmapFacets;
 
     void parse(SolrParams params, ResponseBuilder rb) {
       queryFacets = new LinkedHashMap<>();
@@ -1142,6 +1144,8 @@ public class FacetComponent extends Sear
           pivotFacets.add(pf.getKey(), pf);
         }
       }
+
+      heatmapFacets = SpatialHeatmapFacets.distribParse(params, rb);
     }
   }
   

Copied: lucene/dev/branches/branch_5x/solr/core/src/java/org/apache/solr/handler/component/SpatialHeatmapFacets.java (from r1658614, lucene/dev/trunk/solr/core/src/java/org/apache/solr/handler/component/SpatialHeatmapFacets.java)
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_5x/solr/core/src/java/org/apache/solr/handler/component/SpatialHeatmapFacets.java?p2=lucene/dev/branches/branch_5x/solr/core/src/java/org/apache/solr/handler/component/SpatialHeatmapFacets.java&p1=lucene/dev/trunk/solr/core/src/java/org/apache/solr/handler/component/SpatialHeatmapFacets.java&r1=1658614&r2=1658617&rev=1658617&view=diff
==============================================================================
--- lucene/dev/trunk/solr/core/src/java/org/apache/solr/handler/component/SpatialHeatmapFacets.java (original)
+++ lucene/dev/branches/branch_5x/solr/core/src/java/org/apache/solr/handler/component/SpatialHeatmapFacets.java Tue Feb 10 03:23:18 2015
@@ -187,7 +187,7 @@ public class SpatialHeatmapFacets {
       public List<Integer> get(final int rowIdx) {//top-down remember; the heatmap.counts is bottom up
         //check if all zeroes and return null if so
         boolean hasNonZero = false;
-        int y = rows - rowIdx - 1;//flip direction for 'y'
+        final int y = rows - rowIdx - 1;//flip direction for 'y'
         for (int c = 0; c < columns; c++) {
           if (counts[c * rows + y] > 0) {
             hasNonZero = true;

Modified: lucene/dev/branches/branch_5x/solr/core/src/java/org/apache/solr/request/SimpleFacets.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_5x/solr/core/src/java/org/apache/solr/request/SimpleFacets.java?rev=1658617&r1=1658616&r2=1658617&view=diff
==============================================================================
--- lucene/dev/branches/branch_5x/solr/core/src/java/org/apache/solr/request/SimpleFacets.java (original)
+++ lucene/dev/branches/branch_5x/solr/core/src/java/org/apache/solr/request/SimpleFacets.java Tue Feb 10 03:23:18 2015
@@ -72,6 +72,7 @@ import org.apache.solr.common.util.Named
 import org.apache.solr.common.util.SimpleOrderedMap;
 import org.apache.solr.common.util.StrUtils;
 import org.apache.solr.handler.component.ResponseBuilder;
+import org.apache.solr.handler.component.SpatialHeatmapFacets;
 import org.apache.solr.request.IntervalFacets.FacetInterval;
 import org.apache.solr.schema.BoolField;
 import org.apache.solr.schema.DateRangeField;
@@ -259,7 +260,7 @@ public class SimpleFacets {
       facetResponse.add("facet_dates", getFacetDateCounts());
       facetResponse.add("facet_ranges", getFacetRangeCounts());
       facetResponse.add("facet_intervals", getFacetIntervalCounts());
-
+      facetResponse.add(SpatialHeatmapFacets.RESPONSE_KEY, getHeatmapCounts());
     } catch (IOException e) {
       throw new SolrException(ErrorCode.SERVER_ERROR, e);
     } catch (SyntaxError e) {
@@ -1519,5 +1520,22 @@ public class SimpleFacets {
     return res;
   }
 
+  private NamedList getHeatmapCounts() throws IOException, SyntaxError {
+    final NamedList<Object> resOuter = new SimpleOrderedMap<>();
+    String[] unparsedFields = rb.req.getParams().getParams(FacetParams.FACET_HEATMAP);
+    if (unparsedFields == null || unparsedFields.length == 0) {
+      return resOuter;
+    }
+    if (params.getBool(GroupParams.GROUP_FACET, false)) {
+      throw new SolrException(SolrException.ErrorCode.BAD_REQUEST,
+          "Heatmaps can't be used with " + GroupParams.GROUP_FACET);
+    }
+    for (String unparsedField : unparsedFields) {
+      parseParams(FacetParams.FACET_HEATMAP, unparsedField); // populates facetValue, rb, params, docs
+
+      resOuter.add(key, SpatialHeatmapFacets.getHeatmapForField(key, facetValue, rb, params, docs));
+    }
+    return resOuter;
+  }
 }
 

Modified: lucene/dev/branches/branch_5x/solr/core/src/java/org/apache/solr/util/SpatialUtils.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_5x/solr/core/src/java/org/apache/solr/util/SpatialUtils.java?rev=1658617&r1=1658616&r2=1658617&view=diff
==============================================================================
--- lucene/dev/branches/branch_5x/solr/core/src/java/org/apache/solr/util/SpatialUtils.java (original)
+++ lucene/dev/branches/branch_5x/solr/core/src/java/org/apache/solr/util/SpatialUtils.java Tue Feb 10 03:23:18 2015
@@ -17,9 +17,13 @@ package org.apache.solr.util;
  * limitations under the License.
  */
 
+import java.text.ParseException;
+
 import com.spatial4j.core.context.SpatialContext;
 import com.spatial4j.core.exception.InvalidShapeException;
 import com.spatial4j.core.shape.Point;
+import com.spatial4j.core.shape.Rectangle;
+import com.spatial4j.core.shape.Shape;
 import org.apache.solr.common.SolrException;
 
 /** Utility methods pertaining to spatial. */
@@ -27,6 +31,28 @@ public class SpatialUtils {
 
   private SpatialUtils() {}
 
+  /**
+   * Parses a 'geom' parameter (might also be used to parse shapes for indexing). {@code geomStr} can either be WKT or
+   * a rectangle-range syntax (see {@link #parseRectangle(String, com.spatial4j.core.context.SpatialContext)}.
+   */
+  public static Shape parseGeomSolrException(String geomStr, SpatialContext ctx) {
+    if (geomStr.length() == 0) {
+      throw new IllegalArgumentException("0-length geometry string");
+    }
+    char c = geomStr.charAt(0);
+    if (c == '[' || c == '{') {
+      return parseRectangeSolrException(geomStr, ctx);
+    }
+    //TODO parse a raw point?
+    try {
+      return ctx.readShapeFromWkt(geomStr);
+    } catch (ParseException e) {
+      throw new SolrException(SolrException.ErrorCode.BAD_REQUEST,
+          "Expecting WKT or '[minPoint TO maxPoint]': " + e, e);
+    }
+
+  }
+
   /** Parses either "lat, lon" (spaces optional on either comma side) or "x y" style formats. Spaces can be basically
    * anywhere.  And not any whitespace, just the space char.
    *
@@ -37,8 +63,6 @@ public class SpatialUtils {
    */
   public static Point parsePoint(String str, SpatialContext ctx) throws InvalidShapeException {
     //note we don't do generic whitespace, just a literal space char detection
-    //TODO: decide on if we should pick one format decided by ctx.isGeo()
-    //          Perhaps 5x use isGeo; 4x use either?
     try {
       double x, y;
       str = str.trim();//TODO use findIndexNotSpace instead?
@@ -89,4 +113,53 @@ public class SpatialUtils {
       throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, message, e);
     }
   }
+
+  /**
+   * Parses {@code str} in the format of '[minPoint TO maxPoint]' where {@code minPoint} is the lower left corner
+   * and maxPoint is the upper-right corner of the bounding box.  Both corners may optionally be wrapped with a quote
+   * and then it's parsed via {@link #parsePoint(String, com.spatial4j.core.context.SpatialContext)}.
+   * @param str Non-null; may *not* have leading or trailing spaces
+   * @param ctx Non-null
+   * @return the Rectangle
+   * @throws InvalidShapeException If for any reason there was a problem parsing the string or creating the rectangle.
+   */
+  public static Rectangle parseRectangle(String str, SpatialContext ctx) throws InvalidShapeException {
+    //note we don't do generic whitespace, just a literal space char detection
+    try {
+      int toIdx = str.indexOf(" TO ");
+      if (toIdx == -1 || str.charAt(0) != '[' || str.charAt(str.length() - 1) != ']') {
+        throw new InvalidShapeException("expecting '[bottomLeft TO topRight]'");
+      }
+      String leftPart = unwrapQuotes(str.substring(1, toIdx).trim());
+      String rightPart = unwrapQuotes(str.substring(toIdx + " TO ".length(), str.length() - 1).trim());
+      return ctx.makeRectangle(parsePoint(leftPart, ctx), parsePoint(rightPart, ctx));
+    } catch (InvalidShapeException e) {
+      throw e;
+    } catch (Exception e) {
+      throw new InvalidShapeException(e.toString(), e);
+    }
+  }
+
+  /**
+   * Calls {@link #parseRectangle(String, com.spatial4j.core.context.SpatialContext)} and wraps the exception with
+   * {@link org.apache.solr.common.SolrException} with a helpful message.
+   */
+  public static Rectangle parseRectangeSolrException(String externalVal, SpatialContext ctx) throws SolrException {
+    try {
+      return parseRectangle(externalVal, ctx);
+    } catch (InvalidShapeException e) {
+      String message = e.getMessage();
+      if (!message.contains(externalVal))
+        message = "Can't parse rectangle '" + externalVal + "' because: " + message;
+      throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, message, e);
+    }
+  }
+
+  private static String unwrapQuotes(String str) {
+    if (str.length() >= 2 && str.charAt(0) == '\"' && str.charAt(str.length()-1) == '\"') {
+      return str.substring(1, str.length()-1);
+    }
+    return str;
+  }
+
 }

Modified: lucene/dev/branches/branch_5x/solr/core/src/test/org/apache/solr/TestGroupingSearch.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_5x/solr/core/src/test/org/apache/solr/TestGroupingSearch.java?rev=1658617&r1=1658616&r2=1658617&view=diff
==============================================================================
--- lucene/dev/branches/branch_5x/solr/core/src/test/org/apache/solr/TestGroupingSearch.java (original)
+++ lucene/dev/branches/branch_5x/solr/core/src/test/org/apache/solr/TestGroupingSearch.java Tue Feb 10 03:23:18 2015
@@ -17,9 +17,20 @@
 
 package org.apache.solr;
 
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+
 import org.apache.lucene.index.LogDocMergePolicy;
-import org.noggit.JSONUtil;
-import org.noggit.ObjectBuilder;
 import org.apache.solr.client.solrj.impl.BinaryResponseParser;
 import org.apache.solr.common.params.CommonParams;
 import org.apache.solr.common.params.GroupParams;
@@ -32,25 +43,15 @@ import org.apache.solr.schema.IndexSchem
 import org.junit.Before;
 import org.junit.BeforeClass;
 import org.junit.Test;
-
-import java.io.ByteArrayInputStream;
-import java.io.ByteArrayOutputStream;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.Collections;
-import java.util.Comparator;
-import java.util.HashMap;
-import java.util.LinkedHashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.TreeMap;
+import org.noggit.JSONUtil;
+import org.noggit.ObjectBuilder;
 
 public class TestGroupingSearch extends SolrTestCaseJ4 {
 
   public static final String FOO_STRING_FIELD = "foo_s1";
   public static final String SMALL_STRING_FIELD = "small_s1";
   public static final String SMALL_INT_FIELD = "small_i";
+  static final String EMPTY_FACETS = "'facet_dates':{},'facet_ranges':{},'facet_intervals':{},'facet_heatmaps':{}";
 
   @BeforeClass
   public static void beforeTests() throws Exception {
@@ -317,7 +318,7 @@ public class TestGroupingSearch extends
     assertJQ(
         req,
         "/grouped=={'value1_s1':{'matches':5,'groups':[{'groupValue':'1','doclist':{'numFound':3,'start':0,'docs':[{'id':'1'}]}}]}}",
-        "/facet_counts=={'facet_queries':{},'facet_fields':{'value3_s1':['a',3,'b',2]},'facet_dates':{},'facet_ranges':{},'facet_intervals':{}}"
+        "/facet_counts=={'facet_queries':{},'facet_fields':{'value3_s1':['a',3,'b',2]}," + EMPTY_FACETS + "}"
     );
 
     // Facet counts based on groups
@@ -326,7 +327,7 @@ public class TestGroupingSearch extends
     assertJQ(
         req,
         "/grouped=={'value1_s1':{'matches':5,'groups':[{'groupValue':'1','doclist':{'numFound':3,'start':0,'docs':[{'id':'1'}]}}]}}",
-        "/facet_counts=={'facet_queries':{},'facet_fields':{'value3_s1':['a',1,'b',1]},'facet_dates':{},'facet_ranges':{},'facet_intervals':{}}"
+        "/facet_counts=={'facet_queries':{},'facet_fields':{'value3_s1':['a',1,'b',1]}," + EMPTY_FACETS + "}"
     );
 
     // Facet counts based on groups and with group.func. This should trigger FunctionAllGroupHeadsCollector
@@ -335,7 +336,7 @@ public class TestGroupingSearch extends
     assertJQ(
         req,
         "/grouped=={'strdist(1,value1_s1,edit)':{'matches':5,'groups':[{'groupValue':1.0,'doclist':{'numFound':3,'start':0,'docs':[{'id':'1'}]}}]}}",
-        "/facet_counts=={'facet_queries':{},'facet_fields':{'value3_s1':['a',1,'b',1]},'facet_dates':{},'facet_ranges':{},'facet_intervals':{}}"
+        "/facet_counts=={'facet_queries':{},'facet_fields':{'value3_s1':['a',1,'b',1]}," + EMPTY_FACETS + "}"
     );
 
     // Facet counts based on groups without sort on an int field.
@@ -344,7 +345,7 @@ public class TestGroupingSearch extends
     assertJQ(
         req,
         "/grouped=={'value4_i':{'matches':5,'groups':[{'groupValue':1,'doclist':{'numFound':3,'start':0,'docs':[{'id':'1'}]}}]}}",
-        "/facet_counts=={'facet_queries':{},'facet_fields':{'value3_s1':['a',1,'b',1]},'facet_dates':{},'facet_ranges':{},'facet_intervals':{}}"
+        "/facet_counts=={'facet_queries':{},'facet_fields':{'value3_s1':['a',1,'b',1]}," + EMPTY_FACETS + "}"
     );
 
     // Multi select facets AND group.truncate=true
@@ -353,7 +354,7 @@ public class TestGroupingSearch extends
     assertJQ(
         req,
         "/grouped=={'value4_i':{'matches':2,'groups':[{'groupValue':2,'doclist':{'numFound':2,'start':0,'docs':[{'id':'3'}]}}]}}",
-        "/facet_counts=={'facet_queries':{},'facet_fields':{'value3_s1':['a',1,'b',1]},'facet_dates':{},'facet_ranges':{},'facet_intervals':{}}"
+        "/facet_counts=={'facet_queries':{},'facet_fields':{'value3_s1':['a',1,'b',1]}," + EMPTY_FACETS + "}"
     );
 
     // Multi select facets AND group.truncate=false
@@ -362,7 +363,7 @@ public class TestGroupingSearch extends
     assertJQ(
         req,
         "/grouped=={'value4_i':{'matches':2,'groups':[{'groupValue':2,'doclist':{'numFound':2,'start':0,'docs':[{'id':'3'}]}}]}}",
-        "/facet_counts=={'facet_queries':{},'facet_fields':{'value3_s1':['a',3,'b',2]},'facet_dates':{},'facet_ranges':{},'facet_intervals':{}}"
+        "/facet_counts=={'facet_queries':{},'facet_fields':{'value3_s1':['a',3,'b',2]}," + EMPTY_FACETS + "}"
     );
 
     // Multi select facets AND group.truncate=true
@@ -371,7 +372,7 @@ public class TestGroupingSearch extends
     assertJQ(
         req,
         "/grouped=={'sub(value4_i,1)':{'matches':2,'groups':[{'groupValue':1.0,'doclist':{'numFound':2,'start':0,'docs':[{'id':'3'}]}}]}}",
-        "/facet_counts=={'facet_queries':{},'facet_fields':{'value3_s1':['a',1,'b',1]},'facet_dates':{},'facet_ranges':{},'facet_intervals':{}}"
+        "/facet_counts=={'facet_queries':{},'facet_fields':{'value3_s1':['a',1,'b',1]}," + EMPTY_FACETS + "}"
     );
   }
 
@@ -394,7 +395,7 @@ public class TestGroupingSearch extends
     assertJQ(
         req,
         "/grouped=={'cat_sI':{'matches':2,'groups':[{'groupValue':'a','doclist':{'numFound':1,'start':0,'docs':[{'id':'5'}]}}]}}",
-        "/facet_counts=={'facet_queries':{'LW1':2,'LM1':2,'LM3':2},'facet_fields':{},'facet_dates':{},'facet_ranges':{},'facet_intervals':{}}"
+        "/facet_counts=={'facet_queries':{'LW1':2,'LM1':2,'LM3':2},'facet_fields':{}," + EMPTY_FACETS + "}"
     );
   }
 

Modified: lucene/dev/branches/branch_5x/solr/solrj/src/java/org/apache/solr/common/params/FacetParams.java
URL: http://svn.apache.org/viewvc/lucene/dev/branches/branch_5x/solr/solrj/src/java/org/apache/solr/common/params/FacetParams.java?rev=1658617&r1=1658616&r2=1658617&view=diff
==============================================================================
--- lucene/dev/branches/branch_5x/solr/solrj/src/java/org/apache/solr/common/params/FacetParams.java (original)
+++ lucene/dev/branches/branch_5x/solr/solrj/src/java/org/apache/solr/common/params/FacetParams.java Tue Feb 10 03:23:18 2015
@@ -17,11 +17,11 @@
 
 package org.apache.solr.common.params;
 
-import org.apache.solr.common.SolrException;
-
 import java.util.EnumSet;
 import java.util.Locale;
 
+import org.apache.solr.common.SolrException;
+
 /**
  * Facet parameters
  */
@@ -282,6 +282,41 @@ public interface FacetParams {
    */
   public static final String FACET_INTERVAL_SET = FACET_INTERVAL + ".set";
 
+  /** A spatial RPT field to generate a 2D "heatmap" (grid of facet counts) on. Just like the other faceting types,
+   * this may include a 'key' or local-params to facet multiple times.  All parameters with this suffix can be
+   * overridden on a per-field basis. */
+  public static final String FACET_HEATMAP = "facet.heatmap";
+
+  /** The format of the heatmap: either png or ints2D (default). */
+  public static final String FACET_HEATMAP_FORMAT = FACET_HEATMAP + ".format";
+
+  /** The region the heatmap should minimally enclose.  It defaults to the world if not set.  The format can either be
+   * a minimum to maximum point range format: <pre>["-150 10" TO "-100 30"]</pre> (the first is bottom-left and second
+   * is bottom-right, both of which are parsed as points are parsed).  OR, any WKT can be provided and it's bounding
+   * box will be taken. */
+  public static final String FACET_HEATMAP_GEOM = FACET_HEATMAP + ".geom";
+
+  /** Specify the heatmap grid level explicitly, instead of deriving it via distErr or distErrPct. */
+  public static final String FACET_HEATMAP_LEVEL = FACET_HEATMAP + ".gridLevel";
+
+  /** Used to determine the heatmap grid level to compute, defaulting to 0.15.  It has the same interpretation of
+   * distErrPct when searching on RPT, but relative to the shape in 'bbox'.  It's a fraction (not a %) of the radius of
+   * the shape that grid squares must fit into without exceeding. &gt; 0 and &lt;= 0.5.
+   * Mutually exclusive with distErr &amp; gridLevel. */
+  public static final String FACET_HEATMAP_DIST_ERR_PCT = FACET_HEATMAP + ".distErrPct";
+
+  /** Used to determine the heatmap grid level to compute (optional). It has the same interpretation of maxDistErr or
+   * distErr with RPT.  It's an absolute distance (in units of what's specified on the field type) that a grid square
+   * must maximally fit into (width &amp; height).  It can be used to to more explicitly specify the maximum grid square
+   * size without knowledge of what particular grid levels translate to.  This can in turn be used with
+   * knowledge of the size of 'bbox' to get a target minimum number of grid cells.
+   * Mutually exclusive with distErrPct &amp; gridLevel. */
+  public static final String FACET_HEATMAP_DIST_ERR = FACET_HEATMAP + ".distErr";
+
+  /** The maximum number of cells (grid squares) the client is willing to handle. If this limit would be exceeded, we
+   * throw an error instead.  Defaults to 100k. */
+  public static final String FACET_HEATMAP_MAX_CELLS = FACET_HEATMAP + ".maxCells";
+
   /**
    * An enumeration of the legal values for {@link #FACET_RANGE_OTHER} and {@link #FACET_DATE_OTHER} ...
    * <ul>