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/05/21 15:04:34 UTC
svn commit: r1680862 - in /lucene/dev/trunk:
lucene/spatial/src/java/org/apache/lucene/spatial/composite/ solr/
solr/core/src/java/org/apache/solr/handler/component/
solr/core/src/java/org/apache/solr/schema/
solr/core/src/test-files/solr/collection1/c...
Author: dsmiley
Date: Thu May 21 13:04:33 2015
New Revision: 1680862
URL: http://svn.apache.org/r1680862
Log:
SOLR-7379: Spatial RptWithGeometrySpatialField (based on CompositeSpatialStrategy)
Added:
lucene/dev/trunk/solr/core/src/java/org/apache/solr/schema/RptWithGeometrySpatialField.java (with props)
lucene/dev/trunk/solr/core/src/test-files/solr/collection1/conf/solrconfig-spatial.xml (with props)
Modified:
lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/composite/CompositeSpatialStrategy.java
lucene/dev/trunk/solr/CHANGES.txt
lucene/dev/trunk/solr/core/src/java/org/apache/solr/handler/component/SpatialHeatmapFacets.java
lucene/dev/trunk/solr/core/src/test-files/solr/collection1/conf/schema-spatial.xml
lucene/dev/trunk/solr/core/src/test/org/apache/solr/search/TestSolr4Spatial2.java
Modified: lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/composite/CompositeSpatialStrategy.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/composite/CompositeSpatialStrategy.java?rev=1680862&r1=1680861&r2=1680862&view=diff
==============================================================================
--- lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/composite/CompositeSpatialStrategy.java (original)
+++ lucene/dev/trunk/lucene/spatial/src/java/org/apache/lucene/spatial/composite/CompositeSpatialStrategy.java Thu May 21 13:04:33 2015
@@ -62,6 +62,18 @@ public class CompositeSpatialStrategy ex
this.geometryStrategy = geometryStrategy;
}
+ public RecursivePrefixTreeStrategy getIndexStrategy() {
+ return indexStrategy;
+ }
+
+ public SerializedDVStrategy getGeometryStrategy() {
+ return geometryStrategy;
+ }
+
+ public boolean isOptimizePredicates() {
+ return optimizePredicates;
+ }
+
/** Set to false to NOT use optimized search predicates that avoid checking the geometry sometimes. Only useful for
* benchmarking. */
public void setOptimizePredicates(boolean optimizePredicates) {
Modified: lucene/dev/trunk/solr/CHANGES.txt
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/CHANGES.txt?rev=1680862&r1=1680861&r2=1680862&view=diff
==============================================================================
--- lucene/dev/trunk/solr/CHANGES.txt (original)
+++ lucene/dev/trunk/solr/CHANGES.txt Thu May 21 13:04:33 2015
@@ -212,6 +212,10 @@ New Features
* SOLR-7274: Pluggable authentication module in Solr. This defines an interface and a mechanism to create,
load, and use an Authentication plugin. (Noble Paul, Ishan Chattopadhyaya, Gregory Chanan, Anshum Gupta)
+* SOLR-7379: (experimental) New spatial RptWithGeometrySpatialField, based on CompositeSpatialStrategy,
+ which blends RPT indexes for speed with serialized geometry for accuracy. Includes a Lucene segment based
+ in-memory shape cache. (David Smiley)
+
Bug Fixes
----------------------
Modified: lucene/dev/trunk/solr/core/src/java/org/apache/solr/handler/component/SpatialHeatmapFacets.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/java/org/apache/solr/handler/component/SpatialHeatmapFacets.java?rev=1680862&r1=1680861&r2=1680862&view=diff
==============================================================================
--- lucene/dev/trunk/solr/core/src/java/org/apache/solr/handler/component/SpatialHeatmapFacets.java (original)
+++ lucene/dev/trunk/solr/core/src/java/org/apache/solr/handler/component/SpatialHeatmapFacets.java Thu May 21 13:04:33 2015
@@ -47,10 +47,12 @@ import org.apache.solr.common.util.Named
import org.apache.solr.common.util.SimpleOrderedMap;
import org.apache.solr.schema.AbstractSpatialPrefixTreeFieldType;
import org.apache.solr.schema.FieldType;
+import org.apache.solr.schema.RptWithGeometrySpatialField;
import org.apache.solr.schema.SchemaField;
import org.apache.solr.schema.SpatialRecursivePrefixTreeFieldType;
import org.apache.solr.search.DocSet;
import org.apache.solr.search.QueryParsing;
+import org.apache.solr.util.DistanceUnits;
import org.apache.solr.util.SpatialUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@@ -75,13 +77,24 @@ public class SpatialHeatmapFacets {
//get the strategy from the field type
final SchemaField schemaField = rb.req.getSchema().getField(fieldName);
final FieldType type = schemaField.getType();
- if (!(type instanceof AbstractSpatialPrefixTreeFieldType)) {
+
+ final PrefixTreeStrategy strategy;
+ final DistanceUnits distanceUnits;
+ // note: the two instanceof conditions is not ideal, versus one. If we start needing to add more then refactor.
+ if ((type instanceof AbstractSpatialPrefixTreeFieldType)) {
+ AbstractSpatialPrefixTreeFieldType rptType = (AbstractSpatialPrefixTreeFieldType) type;
+ strategy = (PrefixTreeStrategy) rptType.getStrategy(fieldName);
+ distanceUnits = rptType.getDistanceUnits();
+ } else if (type instanceof RptWithGeometrySpatialField) {
+ RptWithGeometrySpatialField rptSdvType = (RptWithGeometrySpatialField) type;
+ strategy = rptSdvType.getStrategy(fieldName).getIndexStrategy();
+ distanceUnits = rptSdvType.getDistanceUnits();
+ } else {
//FYI we support the term query one too but few people use that one
throw new SolrException(SolrException.ErrorCode.BAD_REQUEST, "heatmap field needs to be of type "
- + SpatialRecursivePrefixTreeFieldType.class);
+ + SpatialRecursivePrefixTreeFieldType.class + " or " + RptWithGeometrySpatialField.class);
}
- AbstractSpatialPrefixTreeFieldType rptType = (AbstractSpatialPrefixTreeFieldType) type;
- final PrefixTreeStrategy strategy = (PrefixTreeStrategy) rptType.getStrategy(fieldName);
+
final SpatialContext ctx = strategy.getSpatialContext();
//get the bbox (query Rectangle)
@@ -106,7 +119,7 @@ public class SpatialHeatmapFacets {
final Double distErrObj = params.getFieldDouble(fieldKey, FacetParams.FACET_HEATMAP_DIST_ERR);
if (distErrObj != null) {
// convert distErr units based on configured units
- spatialArgs.setDistErr(distErrObj * rptType.getDistanceUnits().multiplierFromThisUnitToDegrees());
+ spatialArgs.setDistErr(distErrObj * distanceUnits.multiplierFromThisUnitToDegrees());
}
spatialArgs.setDistErrPct(params.getFieldDouble(fieldKey, FacetParams.FACET_HEATMAP_DIST_ERR_PCT));
double distErr = spatialArgs.resolveDistErr(ctx, DEFAULT_DIST_ERR_PCT);
Added: lucene/dev/trunk/solr/core/src/java/org/apache/solr/schema/RptWithGeometrySpatialField.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/java/org/apache/solr/schema/RptWithGeometrySpatialField.java?rev=1680862&view=auto
==============================================================================
--- lucene/dev/trunk/solr/core/src/java/org/apache/solr/schema/RptWithGeometrySpatialField.java (added)
+++ lucene/dev/trunk/solr/core/src/java/org/apache/solr/schema/RptWithGeometrySpatialField.java Thu May 21 13:04:33 2015
@@ -0,0 +1,249 @@
+package org.apache.solr.schema;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.io.IOException;
+import java.lang.ref.WeakReference;
+import java.util.Map;
+
+import com.spatial4j.core.context.SpatialContext;
+import com.spatial4j.core.shape.Shape;
+import com.spatial4j.core.shape.jts.JtsGeometry;
+import org.apache.lucene.analysis.Analyzer;
+import org.apache.lucene.index.LeafReaderContext;
+import org.apache.lucene.queries.function.FunctionValues;
+import org.apache.lucene.queries.function.ValueSource;
+import org.apache.lucene.search.Explanation;
+import org.apache.lucene.spatial.composite.CompositeSpatialStrategy;
+import org.apache.lucene.spatial.prefix.RecursivePrefixTreeStrategy;
+import org.apache.lucene.spatial.query.SpatialArgsParser;
+import org.apache.lucene.spatial.serialized.SerializedDVStrategy;
+import org.apache.solr.common.SolrException;
+import org.apache.solr.core.SolrCore;
+import org.apache.solr.request.SolrRequestInfo;
+import org.apache.solr.search.SolrCache;
+
+/** A Solr Spatial FieldType based on {@link CompositeSpatialStrategy}.
+ * @lucene.experimental */
+public class RptWithGeometrySpatialField extends AbstractSpatialFieldType<CompositeSpatialStrategy> {
+
+ public static final String DEFAULT_DIST_ERR_PCT = "0.15";
+
+ private SpatialRecursivePrefixTreeFieldType rptFieldType;
+ private SolrCore core;
+
+ @Override
+ protected void init(IndexSchema schema, Map<String, String> args) {
+ // Do NOT call super.init(); instead we delegate to an RPT field. Admittedly this is error prone.
+
+ //TODO Move this check to a call from AbstractSpatialFieldType.createFields() so the type can declare
+ // if it supports multi-valued or not. It's insufficient here; we can't see if you set multiValued on the field.
+ if (isMultiValued()) {
+ throw new SolrException(SolrException.ErrorCode.SERVER_ERROR, "Not capable of multiValued: " + getTypeName());
+ }
+
+ // Choose a better default distErrPct if not configured
+ if (args.containsKey(SpatialArgsParser.DIST_ERR_PCT) == false) {
+ args.put(SpatialArgsParser.DIST_ERR_PCT, DEFAULT_DIST_ERR_PCT);
+ }
+
+ rptFieldType = new SpatialRecursivePrefixTreeFieldType();
+ rptFieldType.setTypeName(getTypeName());
+ rptFieldType.properties = properties;
+ rptFieldType.init(schema, args);
+ rptFieldType.argsParser = argsParser = newSpatialArgsParser();
+ this.ctx = rptFieldType.ctx;
+ this.distanceUnits = rptFieldType.distanceUnits;
+ this.units = rptFieldType.units;
+ }
+
+ @Override
+ protected CompositeSpatialStrategy newSpatialStrategy(String fieldName) {
+ // We use the same field name for both sub-strategies knowing there will be no conflict for these two
+
+ RecursivePrefixTreeStrategy rptStrategy = rptFieldType.newSpatialStrategy(fieldName);
+
+ SerializedDVStrategy geomStrategy = new CachingSerializedDVStrategy(ctx, fieldName);
+
+ return new CompositeSpatialStrategy(fieldName, rptStrategy, geomStrategy);
+ }
+
+ @Override
+ public Analyzer getQueryAnalyzer() {
+ return rptFieldType.getQueryAnalyzer();
+ }
+
+ @Override
+ public Analyzer getIndexAnalyzer() {
+ return rptFieldType.getIndexAnalyzer();
+ }
+
+ // Most of the complexity of this field type is below, which is all about caching the shapes in a SolrCache
+
+ private static class CachingSerializedDVStrategy extends SerializedDVStrategy {
+ public CachingSerializedDVStrategy(SpatialContext ctx, String fieldName) {
+ super(ctx, fieldName);
+ }
+
+ @Override
+ public ValueSource makeShapeValueSource() {
+ return new CachingShapeValuesource(super.makeShapeValueSource(), getFieldName());
+ }
+ }
+
+ private static class CachingShapeValuesource extends ValueSource {
+
+ private final ValueSource targetValueSource;
+ private final String fieldName;
+
+ private CachingShapeValuesource(ValueSource targetValueSource, String fieldName) {
+ this.targetValueSource = targetValueSource;
+ this.fieldName = fieldName;
+ }
+
+ @Override
+ public String description() {
+ return "cache(" + targetValueSource.description() + ")";
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ CachingShapeValuesource that = (CachingShapeValuesource) o;
+
+ if (!targetValueSource.equals(that.targetValueSource)) return false;
+ return fieldName.equals(that.fieldName);
+
+ }
+
+ @Override
+ public int hashCode() {
+ int result = targetValueSource.hashCode();
+ result = 31 * result + fieldName.hashCode();
+ return result;
+ }
+
+ @SuppressWarnings("unchecked")
+ @Override
+ public FunctionValues getValues(Map context, LeafReaderContext readerContext) throws IOException {
+ final FunctionValues targetFuncValues = targetValueSource.getValues(context, readerContext);
+ // The key is a pair of leaf reader with a docId relative to that reader. The value is a Map from field to Shape.
+ final SolrCache<PerSegCacheKey,Shape> cache =
+ SolrRequestInfo.getRequestInfo().getReq().getSearcher().getCache(CACHE_KEY_PREFIX + fieldName);
+ if (cache == null) {
+ return targetFuncValues; // no caching; no configured cache
+ }
+
+ return new FunctionValues() {
+ int docId = -1;
+ Shape shape = null;
+
+ private void setShapeFromDoc(int doc) {
+ if (docId == doc) {
+ return;
+ }
+ docId = doc;
+ //lookup in cache
+ PerSegCacheKey key = new PerSegCacheKey(readerContext.reader().getCoreCacheKey(), doc);
+ shape = cache.get(key);
+ if (shape == null) {
+ shape = (Shape) targetFuncValues.objectVal(doc);
+ if (shape != null) {
+ cache.put(key, shape);
+ }
+ } else {
+ //optimize shape on a cache hit if possible. This must be thread-safe and it is.
+ if (shape instanceof JtsGeometry) {
+ ((JtsGeometry) shape).index(); // TODO would be nice if some day we didn't have to cast
+ }
+ }
+ }
+
+ // Use the cache for exists & objectVal;
+
+ @Override
+ public boolean exists(int doc) {
+ setShapeFromDoc(doc);
+ return shape != null;
+ }
+
+ @Override
+ public Object objectVal(int doc) {
+ setShapeFromDoc(doc);
+ return shape;
+ }
+
+ @Override
+ public Explanation explain(int doc) {
+ return targetFuncValues.explain(doc);
+ }
+
+ @Override
+ public String toString(int doc) {
+ return targetFuncValues.toString(doc);
+ }
+ };
+
+ }
+
+ }
+
+ public static final String CACHE_KEY_PREFIX = "perSegSpatialFieldCache_";//then field name
+
+ // Used in a SolrCache for the key
+ private static class PerSegCacheKey {
+ final WeakReference<Object> segCoreKeyRef;
+ final int docId;
+ final int hashCode;//cached because we can't necessarily compute after construction
+
+ private PerSegCacheKey(Object segCoreKey, int docId) {
+ this.segCoreKeyRef = new WeakReference<>(segCoreKey);
+ this.docId = docId;
+ this.hashCode = segCoreKey.hashCode() * 31 + docId;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ PerSegCacheKey that = (PerSegCacheKey) o;
+
+ if (docId != that.docId) return false;
+
+ //compare by referent not reference
+ Object segCoreKey = segCoreKeyRef.get();
+ if (segCoreKey == null) {
+ return false;
+ }
+ return segCoreKey.equals(that.segCoreKeyRef.get());
+ }
+
+ @Override
+ public int hashCode() {
+ return hashCode;
+ }
+
+ @Override
+ public String toString() {
+ return "Key{seg=" + segCoreKeyRef.get() + ", docId=" + docId + '}';
+ }
+ }
+}
Modified: lucene/dev/trunk/solr/core/src/test-files/solr/collection1/conf/schema-spatial.xml
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/test-files/solr/collection1/conf/schema-spatial.xml?rev=1680862&r1=1680861&r2=1680862&view=diff
==============================================================================
--- lucene/dev/trunk/solr/core/src/test-files/solr/collection1/conf/schema-spatial.xml (original)
+++ lucene/dev/trunk/solr/core/src/test-files/solr/collection1/conf/schema-spatial.xml Thu May 21 13:04:33 2015
@@ -50,6 +50,8 @@
<fieldType name="pointvector" class="solr.SpatialPointVectorFieldType"
numberType="tdouble" distanceUnits="degrees"/>
+ <fieldType name="srptgeom" class="solr.RptWithGeometrySpatialField" />
+
<fieldType name="bbox" class="solr.BBoxField"
numberType="tdoubleDV" distanceUnits="degrees" storeSubFields="false"/>
</types>
@@ -64,6 +66,7 @@
<field name="srpt_packedquad" type="srpt_packedquad" multiValued="true" />
<field name="stqpt_geohash" type="stqpt_geohash" multiValued="true" />
<field name="pointvector" type="pointvector" />
+ <field name="srptgeom" type="srptgeom" />
<field name="bbox" type="bbox" />
<dynamicField name="bboxD_*" type="bbox" indexed="true" />
Added: lucene/dev/trunk/solr/core/src/test-files/solr/collection1/conf/solrconfig-spatial.xml
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/test-files/solr/collection1/conf/solrconfig-spatial.xml?rev=1680862&view=auto
==============================================================================
--- lucene/dev/trunk/solr/core/src/test-files/solr/collection1/conf/solrconfig-spatial.xml (added)
+++ lucene/dev/trunk/solr/core/src/test-files/solr/collection1/conf/solrconfig-spatial.xml Thu May 21 13:04:33 2015
@@ -0,0 +1,36 @@
+<?xml version="1.0" ?>
+
+<!--
+ Licensed to the Apache Software Foundation (ASF) under one or more
+ contributor license agreements. See the NOTICE file distributed with
+ this work for additional information regarding copyright ownership.
+ The ASF licenses this file to You under the Apache License, Version 2.0
+ (the "License"); you may not use this file except in compliance with
+ the License. You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+
+<!-- a basic solrconfig that tests NoOpRegenerator -->
+<config>
+ <luceneMatchVersion>${tests.luceneMatchVersion:LATEST}</luceneMatchVersion>
+ <dataDir>${solr.data.dir:}</dataDir>
+ <xi:include href="solrconfig.snippet.randomindexconfig.xml" xmlns:xi="http://www.w3.org/2001/XInclude"/>
+ <directoryFactory name="DirectoryFactory" class="${solr.directoryFactory:solr.RAMDirectoryFactory}"/>
+ <requestHandler name="standard" class="solr.StandardRequestHandler" />
+ <requestHandler name="/update" class="solr.UpdateRequestHandler" />
+ <query>
+ <cache name="perSegSpatialFieldCache_srptgeom"
+ class="solr.LRUCache"
+ size="3"
+ initialSize="0"
+ autowarmCount="100%"
+ regenerator="solr.NoOpRegenerator"/>
+ </query>
+</config>
\ No newline at end of file
Modified: lucene/dev/trunk/solr/core/src/test/org/apache/solr/search/TestSolr4Spatial2.java
URL: http://svn.apache.org/viewvc/lucene/dev/trunk/solr/core/src/test/org/apache/solr/search/TestSolr4Spatial2.java?rev=1680862&r1=1680861&r2=1680862&view=diff
==============================================================================
--- lucene/dev/trunk/solr/core/src/test/org/apache/solr/search/TestSolr4Spatial2.java (original)
+++ lucene/dev/trunk/solr/core/src/test/org/apache/solr/search/TestSolr4Spatial2.java Thu May 21 13:04:33 2015
@@ -19,6 +19,8 @@ package org.apache.solr.search;
import org.apache.solr.SolrTestCaseJ4;
import org.apache.solr.common.SolrException;
+import org.apache.solr.common.params.FacetParams;
+import org.apache.solr.request.SolrQueryRequest;
import org.junit.Before;
import org.junit.BeforeClass;
import org.junit.Test;
@@ -28,7 +30,7 @@ public class TestSolr4Spatial2 extends S
@BeforeClass
public static void beforeClass() throws Exception {
- initCore("solrconfig-basic.xml", "schema-spatial.xml");
+ initCore("solrconfig-spatial.xml", "schema-spatial.xml");
}
@Override
@@ -72,19 +74,19 @@ public class TestSolr4Spatial2 extends S
);
//area2D
- assertJQ(req("q", "{!field f="+fieldName+" filter=false score=area2D}" +
+ assertJQ(req("q", "{!field f=" + fieldName + " filter=false score=area2D}" +
"Intersects(ENVELOPE(0,0,12,12))",//pt
"fl", "id,score",
"debug", "results"),//explain info
- "/response/docs/[0]/id=='1'" ,
+ "/response/docs/[0]/id=='1'",
"/response/docs/[0]/score==" + (30f * 5f) + "]"//150
);
//area (not 2D)
- assertJQ(req("q", "{!field f="+fieldName+" filter=false score=area}" +
+ assertJQ(req("q", "{!field f=" + fieldName + " filter=false score=area}" +
"Intersects(ENVELOPE(0,0,12,12))",//pt
"fl", "id,score",
"debug", "results"),//explain info
- "/response/docs/[0]/id=='1'" ,
+ "/response/docs/[0]/id=='1'",
"/response/docs/[0]/score==" + 146.39793f + "]"//a bit less than 150
);
}
@@ -94,8 +96,53 @@ public class TestSolr4Spatial2 extends S
String fieldName = "bbox";
assertQEx("expect friendly error message",
"area2D",
- req("{!field f="+fieldName+" filter=false score=bogus}Intersects(ENVELOPE(0,0,12,12))"),
+ req("{!field f=" + fieldName + " filter=false score=bogus}Intersects(ENVELOPE(0,0,12,12))"),
SolrException.ErrorCode.BAD_REQUEST);
}
+ @Test
+ public void testRptWithGeometryField() throws Exception {
+ String fieldName = "srptgeom"; //note: fails with "srpt_geohash" because it's not as precise
+ assertU(adoc("id", "0", fieldName, "ENVELOPE(-10, 20, 15, 10)"));
+ assertU(adoc("id", "1", fieldName, "BUFFER(POINT(-10 15), 5)"));//circle at top-left corner
+ assertU(optimize());// one segment.
+ assertU(commit());
+
+ // Search to the edge but not quite touching the indexed envelope of id=0. It requires geom validation to
+ // eliminate id=0. id=1 is found and doesn't require validation. cache=false means no query cache.
+ final SolrQueryRequest sameReq = req(
+ "q", "{!cache=false field f=" + fieldName + "}Intersects(ENVELOPE(-20, -10.0001, 30, 15.0001))",
+ "sort", "id asc");
+ assertJQ(sameReq, "/response/numFound==1", "/response/docs/[0]/id=='1'");
+
+ // The tricky thing is verifying the cache works correctly...
+
+ SolrCache cache = (SolrCache) h.getCore().getInfoRegistry().get("perSegSpatialFieldCache_srptgeom");
+ assertEquals("1", cache.getStatistics().get("cumulative_inserts").toString());
+ assertEquals("0", cache.getStatistics().get("cumulative_hits").toString());
+
+ // Repeat the query earlier
+ assertJQ(sameReq, "/response/numFound==1", "/response/docs/[0]/id=='1'");
+ assertEquals("1", cache.getStatistics().get("cumulative_hits").toString());
+
+ assertEquals("1 segment",
+ 1, ((SolrIndexSearcher) h.getCore().getInfoRegistry().get("searcher")).getRawReader().leaves().size());
+ // add new segment
+ assertU(adoc("id", "3"));
+ assertU(commit()); // sometimes merges (to one seg), sometimes won't
+ boolean newSeg =
+ (((SolrIndexSearcher)h.getCore().getInfoRegistry().get("searcher")).getRawReader().leaves().size() > 1);
+
+ // can still find the same document
+ assertJQ(sameReq, "/response/numFound==1", "/response/docs/[0]/id=='1'");
+
+ // when there are new segments, we accumulate another hit. This tests the cache was not blown away on commit.
+ assertEquals(newSeg ? "2" : "1", cache.getStatistics().get("cumulative_hits").toString());
+
+ // Now try to see if heatmaps work:
+ assertJQ(req("q", "*:*", "facet", "true", FacetParams.FACET_HEATMAP, fieldName, "json.nl", "map"),
+ "/facet_counts/facet_heatmaps/" + fieldName + "/minX==-180.0");
+
+ }
+
}