diff --git a/lucene/spatial/src/java/org/apache/lucene/spatial/util/ShapeAreaValueSource.java b/lucene/spatial/src/java/org/apache/lucene/spatial/util/ShapeAreaValueSource.java index c5eb5904252..c7b7253fbce 100644 --- a/lucene/spatial/src/java/org/apache/lucene/spatial/util/ShapeAreaValueSource.java +++ b/lucene/spatial/src/java/org/apache/lucene/spatial/util/ShapeAreaValueSource.java @@ -17,6 +17,9 @@ package org.apache.lucene.spatial.util; * limitations under the License. */ +import java.io.IOException; +import java.util.Map; + import com.spatial4j.core.context.SpatialContext; import com.spatial4j.core.shape.Shape; import org.apache.lucene.index.LeafReaderContext; @@ -26,9 +29,6 @@ import org.apache.lucene.queries.function.docvalues.DoubleDocValues; import org.apache.lucene.search.Explanation; import org.apache.lucene.search.IndexSearcher; -import java.io.IOException; -import java.util.Map; - /** * The area of a Shape retrieved from a ValueSource via * {@link org.apache.lucene.queries.function.FunctionValues#objectVal(int)}. @@ -41,11 +41,13 @@ public class ShapeAreaValueSource extends ValueSource { private final ValueSource shapeValueSource; private final SpatialContext ctx;//not part of identity; should be associated with shapeValueSource indirectly private final boolean geoArea; + private double multiplier; - public ShapeAreaValueSource(ValueSource shapeValueSource, SpatialContext ctx, boolean geoArea) { + public ShapeAreaValueSource(ValueSource shapeValueSource, SpatialContext ctx, boolean geoArea, double multiplier) { this.shapeValueSource = shapeValueSource; this.ctx = ctx; this.geoArea = geoArea; + this.multiplier = multiplier; } @Override @@ -70,7 +72,7 @@ public class ShapeAreaValueSource extends ValueSource { return 0;//or NaN? //This part of Spatial4j API is kinda weird. Passing null means 2D area, otherwise geo // assuming ctx.isGeo() - return shape.getArea( geoArea ? ctx : null ); + return shape.getArea( geoArea ? ctx : null ) * multiplier; } @Override diff --git a/lucene/spatial/src/test/org/apache/lucene/spatial/bbox/TestBBoxStrategy.java b/lucene/spatial/src/test/org/apache/lucene/spatial/bbox/TestBBoxStrategy.java index b708b5cd0d5..1a189f71a1c 100644 --- a/lucene/spatial/src/test/org/apache/lucene/spatial/bbox/TestBBoxStrategy.java +++ b/lucene/spatial/src/test/org/apache/lucene/spatial/bbox/TestBBoxStrategy.java @@ -19,6 +19,13 @@ package org.apache.lucene.spatial.bbox; import java.io.IOException; +import com.carrotsearch.randomizedtesting.annotations.Repeat; +import com.spatial4j.core.context.SpatialContext; +import com.spatial4j.core.context.SpatialContextFactory; +import com.spatial4j.core.distance.DistanceUtils; +import com.spatial4j.core.shape.Rectangle; +import com.spatial4j.core.shape.Shape; +import com.spatial4j.core.shape.impl.RectangleImpl; import org.apache.lucene.document.FieldType; import org.apache.lucene.index.DocValuesType; import org.apache.lucene.index.IndexOptions; @@ -30,13 +37,6 @@ import org.apache.lucene.spatial.query.SpatialOperation; import org.apache.lucene.spatial.util.ShapeAreaValueSource; import org.junit.Ignore; import org.junit.Test; -import com.carrotsearch.randomizedtesting.annotations.Repeat; -import com.spatial4j.core.context.SpatialContext; -import com.spatial4j.core.context.SpatialContextFactory; -import com.spatial4j.core.distance.DistanceUtils; -import com.spatial4j.core.shape.Rectangle; -import com.spatial4j.core.shape.Shape; -import com.spatial4j.core.shape.impl.RectangleImpl; public class TestBBoxStrategy extends RandomSpatialOpStrategyTestCase { @@ -301,9 +301,11 @@ public class TestBBoxStrategy extends RandomSpatialOpStrategyTestCase { adoc("100", ctx.makeRectangle(0, 20, 40, 80)); adoc("999", (Shape) null); commit(); - checkValueSource(new ShapeAreaValueSource(bboxStrategy.makeShapeValueSource(), ctx, false), + checkValueSource(new ShapeAreaValueSource(bboxStrategy.makeShapeValueSource(), ctx, false, 1.0), new float[]{800f, 0f}, 0f); - checkValueSource(new ShapeAreaValueSource(bboxStrategy.makeShapeValueSource(), ctx, true),//geo + checkValueSource(new ShapeAreaValueSource(bboxStrategy.makeShapeValueSource(), ctx, true, 1.0),//geo new float[]{391.93f, 0f}, 0.01f); + checkValueSource(new ShapeAreaValueSource(bboxStrategy.makeShapeValueSource(), ctx, true, 2.0), + new float[]{783.86f, 0f}, 0.01f); // testing with a different multiplier } } diff --git a/solr/CHANGES.txt b/solr/CHANGES.txt index f0b83783796..57b267c760d 100644 --- a/solr/CHANGES.txt +++ b/solr/CHANGES.txt @@ -138,6 +138,13 @@ Upgrading from Solr 4.x * SolrServer and associated classes have been deprecated. Applications using SolrJ should use the equivalent SolrClient classes instead. +* Spatial fields originating from Solr 4 (e.g. SpatialRecursivePrefixTreeFieldType, BBoxField) + have the 'units' attribute deprecated, now replaced with 'distanceUnits'. If you change it to + a unit other than 'degrees' (or if you don't specify it, which will default to kilometers if + geo=true), then be sure to update maxDistErr as it's in those units. If you keep units=degrees + then it should be backwards compatible but you'll get a deprecation warning on startup. See + SOLR-6797. + Detailed Change List ---------------------- @@ -271,6 +278,12 @@ New Features * SOLR-6761: Ability to ignore commit and/or optimize requests from clients when running in SolrCloud mode using the IgnoreCommitOptimizeUpdateProcessorFactory. (Timothy Potter) +* SOLR-6797: Spatial fields that used to require units=degrees like + SpatialRecursivePrefixTreeFieldType (RPT) now take distanceUnits=degrees|kilometers|miles + instead. It is applied to nearly all distance measurements involving the field: maxDistErr, + distErr, d, geodist, score=distance|area|area2d. score now accepts these units as well. It does + NOT affect distances embedded in WKT strings like BUFFER(POINT(200 10),0.2)). + (Ishan Chattopadhyaya, David Smiley) Bug Fixes ---------------------- diff --git a/solr/contrib/morphlines-core/src/test-files/solr/minimr/conf/schema.xml b/solr/contrib/morphlines-core/src/test-files/solr/minimr/conf/schema.xml index aa7fe8f7312..5a865e1a470 100644 --- a/solr/contrib/morphlines-core/src/test-files/solr/minimr/conf/schema.xml +++ b/solr/contrib/morphlines-core/src/test-files/solr/minimr/conf/schema.xml @@ -579,7 +579,7 @@ http://wiki.apache.org/solr/SolrAdaptersForLuceneSpatial4 --> + geo="true" distErrPct="0.025" maxDistErr="0.001" distanceUnits="kilometers" /> + geo="true" distErrPct="0.025" maxDistErr="0.001" distanceUnits="kilometers" /> + geo="true" distErrPct="0.025" maxDistErr="0.00001" distanceUnits="kilometers" /> diff --git a/solr/core/src/test/org/apache/solr/schema/SpatialRPTFieldTypeTest.java b/solr/core/src/test/org/apache/solr/schema/SpatialRPTFieldTypeTest.java new file mode 100644 index 00000000000..07a6f8f7bd4 --- /dev/null +++ b/solr/core/src/test/org/apache/solr/schema/SpatialRPTFieldTypeTest.java @@ -0,0 +1,275 @@ +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.File; +import java.nio.file.Files; +import java.util.HashMap; +import java.util.Map; + +import org.apache.commons.io.FileUtils; +import org.apache.solr.core.AbstractBadConfigTestBase; +import org.junit.After; +import org.junit.Before; + +public class SpatialRPTFieldTypeTest extends AbstractBadConfigTestBase { + + private static File tmpSolrHome; + private static File tmpConfDir; + + private static final String collection = "collection1"; + private static final String confDir = collection + "/conf"; + + @Before + private void initManagedSchemaCore() throws Exception { + tmpSolrHome = createTempDir().toFile(); + tmpConfDir = new File(tmpSolrHome, confDir); + File testHomeConfDir = new File(TEST_HOME(), confDir); + FileUtils.copyFileToDirectory(new File(testHomeConfDir, "solrconfig-managed-schema.xml"), tmpConfDir); + FileUtils.copyFileToDirectory(new File(testHomeConfDir, "solrconfig-basic.xml"), tmpConfDir); + FileUtils.copyFileToDirectory(new File(testHomeConfDir, "solrconfig.snippet.randomindexconfig.xml"), tmpConfDir); + FileUtils.copyFileToDirectory(new File(testHomeConfDir, "schema-one-field-no-dynamic-field.xml"), tmpConfDir); + FileUtils.copyFileToDirectory(new File(testHomeConfDir, "schema-one-field-no-dynamic-field-unique-key.xml"), tmpConfDir); + FileUtils.copyFileToDirectory(new File(testHomeConfDir, "schema-minimal.xml"), tmpConfDir); + FileUtils.copyFileToDirectory(new File(testHomeConfDir, "schema_codec.xml"), tmpConfDir); + FileUtils.copyFileToDirectory(new File(testHomeConfDir, "schema-bm25.xml"), tmpConfDir); + + // initCore will trigger an upgrade to managed schema, since the solrconfig has + // + System.setProperty("managed.schema.mutable", "false"); + System.setProperty("enable.update.log", "false"); + initCore("solrconfig-managed-schema.xml", "schema-minimal.xml", tmpSolrHome.getPath()); + } + + @After + private void afterClass() throws Exception { + deleteCore(); + System.clearProperty("managed.schema.mutable"); + System.clearProperty("enable.update.log"); + } + + final String INDEXED_COORDINATES = "25,82"; + final String QUERY_COORDINATES = "24,81"; + final String DISTANCE_DEGREES = "1.3520328"; + final String DISTANCE_KILOMETERS = "150.33939"; + final String DISTANCE_MILES = "93.416565"; + + public void testUnitsDegrees() throws Exception { // test back compat behaviour + setupRPTField("degrees", null, "true"); + + assertU(adoc("str", "X", "geo", INDEXED_COORDINATES)); + assertU(commit()); + String q; + + q = "geo:{!geofilt score=distance filter=false sfield=geo pt="+QUERY_COORDINATES+" d=1000}"; + assertQ(req("q", q, "fl", "*,score"), "//result/doc/float[@name='score'][.='"+DISTANCE_DEGREES+"']"); + + q = "geo:{!geofilt score=degrees filter=false sfield=geo pt="+QUERY_COORDINATES+" d=1000}"; + assertQ(req("q", q, "fl", "*,score"), "//result/doc/float[@name='score'][.='"+DISTANCE_DEGREES+"']"); + + q = "geo:{!geofilt score=kilometers filter=false sfield=geo pt="+QUERY_COORDINATES+" d=1000}"; + assertQ(req("q", q, "fl", "*,score"), "//result/doc/float[@name='score'][.='"+DISTANCE_KILOMETERS+"']"); + + q = "geo:{!geofilt score=miles filter=false sfield=geo pt="+QUERY_COORDINATES+" d=1000}"; + assertQ(req("q", q, "fl", "*,score"), "//result/doc/float[@name='score'][.='"+DISTANCE_MILES+"']"); + } + + public void testUnitsNonDegrees() throws Exception { + try { + setupRPTField("kilometers", null, "true"); + fail("Expected exception for deprecated units parameter."); + } catch (Exception ex) { + if(!ex.getMessage().startsWith("units parameter is deprecated")) + throw ex; + } + } + + public void testDistanceUnitsDegrees() throws Exception { + setupRPTField(null, "degrees", "true"); + + assertU(adoc("str", "X", "geo", INDEXED_COORDINATES)); + assertU(commit()); + String q; + + q = "geo:{!geofilt score=distance filter=false sfield=geo pt="+QUERY_COORDINATES+" d=1000}"; + assertQ(req("q", q, "fl", "*,score"), "//result/doc/float[@name='score'][.='"+DISTANCE_DEGREES+"']"); + + q = "geo:{!geofilt score=degrees filter=false sfield=geo pt="+QUERY_COORDINATES+" d=1000}"; + assertQ(req("q", q, "fl", "*,score"), "//result/doc/float[@name='score'][.='"+DISTANCE_DEGREES+"']"); + + q = "geo:{!geofilt score=kilometers filter=false sfield=geo pt="+QUERY_COORDINATES+" d=1000}"; + assertQ(req("q", q, "fl", "*,score"), "//result/doc/float[@name='score'][.='"+DISTANCE_KILOMETERS+"']"); + + q = "geo:{!geofilt score=miles filter=false sfield=geo pt="+QUERY_COORDINATES+" d=1000}"; + assertQ(req("q", q, "fl", "*,score"), "//result/doc/float[@name='score'][.='"+DISTANCE_MILES+"']"); + } + + public void testDistanceUnitsKilometers() throws Exception { + setupRPTField(null, "kilometers", "true"); + + assertU(adoc("str", "X", "geo", INDEXED_COORDINATES)); + assertU(commit()); + String q; + + q = "geo:{!geofilt score=distance filter=false sfield=geo pt="+QUERY_COORDINATES+" d=1000}"; + assertQ(req("q", q, "fl", "*,score"), "//result/doc/float[@name='score'][.='"+DISTANCE_KILOMETERS+"']"); + + q = "geo:{!geofilt score=degrees filter=false sfield=geo pt="+QUERY_COORDINATES+" d=1000}"; + assertQ(req("q", q, "fl", "*,score"), "//result/doc/float[@name='score'][.='"+DISTANCE_DEGREES+"']"); + + q = "geo:{!geofilt score=kilometers filter=false sfield=geo pt="+QUERY_COORDINATES+" d=1000}"; + assertQ(req("q", q, "fl", "*,score"), "//result/doc/float[@name='score'][.='"+DISTANCE_KILOMETERS+"']"); + + q = "geo:{!geofilt score=miles filter=false sfield=geo pt="+QUERY_COORDINATES+" d=1000}"; + assertQ(req("q", q, "fl", "*,score"), "//result/doc/float[@name='score'][.='"+DISTANCE_MILES+"']"); + } + + public void testBothUnitsAndDistanceUnits() throws Exception { // distanceUnits should take precedence + try { + setupRPTField("degrees", "kilometers", "true"); + fail("Expected exception for deprecated units parameter."); + } catch (Exception ex) { + if(!ex.getMessage().startsWith("units parameter is deprecated")) + throw ex; + } + } + + public void testJunkValuesForDistanceUnits() throws Exception { + try { + setupRPTField(null, "rose", "true"); + fail("Expected exception for bad value of distanceUnits."); + } catch (Exception ex) { + if(!ex.getMessage().startsWith("Must specify distanceUnits as one of")) + throw ex; + } + } + + public void testMaxDistErrConversion() throws Exception { + deleteCore(); + File managedSchemaFile = new File(tmpConfDir, "managed-schema"); + Files.delete(managedSchemaFile.toPath()); // Delete managed-schema so it won't block parsing a new schema + System.setProperty("managed.schema.mutable", "true"); + initCore("solrconfig-managed-schema.xml", "schema-one-field-no-dynamic-field.xml", tmpSolrHome.getPath()); + + String fieldName = "new_text_field"; + assertNull("Field '" + fieldName + "' is present in the schema", + h.getCore().getLatestSchema().getFieldOrNull(fieldName)); + + IndexSchema oldSchema = h.getCore().getLatestSchema(); + + SpatialRecursivePrefixTreeFieldType rptFieldType = new SpatialRecursivePrefixTreeFieldType(); + Map rptMap = new HashMap(); + + rptFieldType.setTypeName("location_rpt"); + rptMap.put("geo", "true"); + + // test km + rptMap.put("distanceUnits", "kilometers"); + rptMap.put("maxDistErr", "0.001"); // 1 meter + rptFieldType.init(oldSchema, rptMap); + assertEquals(11, rptFieldType.grid.getMaxLevels()); + + // test miles + rptMap.put("distanceUnits", "miles"); + rptMap.put("maxDistErr", "0.001"); + rptFieldType.init(oldSchema, rptMap); + assertEquals(10, rptFieldType.grid.getMaxLevels()); + + // test degrees + rptMap.put("distanceUnits", "degrees"); + rptMap.put("maxDistErr", "0.001"); + rptFieldType.init(oldSchema, rptMap); + assertEquals(8, rptFieldType.grid.getMaxLevels()); + } + + public void testGeoDistanceFunctionWithBackCompat() throws Exception { + setupRPTField("degrees", null, "true"); + + assertU(adoc("str", "X", "geo", "1,2")); + assertU(commit()); + + // geodist() should return in km + assertJQ(req("defType","func", + "q","geodist(3,4)", + "sfield","geo", + "fl","score") + , 1e-5 + ,"/response/docs/[0]/score==314.4033" + ); + } + + public void testGeoDistanceFunctionWithKilometers() throws Exception { + setupRPTField(null, "kilometers", "true"); + + assertU(adoc("str", "X", "geo", "1,2")); + assertU(commit()); + + assertJQ(req("defType","func", + "q","geodist(3,4)", + "sfield","geo", + "fl","score") + , 1e-5 + ,"/response/docs/[0]/score==314.4033" + ); + } + + public void testGeoDistanceFunctionWithMiles() throws Exception { + setupRPTField(null, "miles", "true"); + + assertU(adoc("str", "X", "geo", "1,2")); + assertU(commit()); + + assertJQ(req("defType","func", + "q","geodist(3,4)", + "sfield","geo", + "fl","score") + , 1e-5 + ,"/response/docs/[0]/score==195.36115" + ); + } + + private void setupRPTField(String units, String distanceUnits, String geo) throws Exception { + deleteCore(); + File managedSchemaFile = new File(tmpConfDir, "managed-schema"); + Files.delete(managedSchemaFile.toPath()); // Delete managed-schema so it won't block parsing a new schema + System.setProperty("managed.schema.mutable", "true"); + initCore("solrconfig-managed-schema.xml", "schema-one-field-no-dynamic-field.xml", tmpSolrHome.getPath()); + + String fieldName = "new_text_field"; + assertNull("Field '" + fieldName + "' is present in the schema", + h.getCore().getLatestSchema().getFieldOrNull(fieldName)); + + IndexSchema oldSchema = h.getCore().getLatestSchema(); + + SpatialRecursivePrefixTreeFieldType rptFieldType = new SpatialRecursivePrefixTreeFieldType(); + Map rptMap = new HashMap(); + if(units!=null) + rptMap.put("units", units); + if(distanceUnits!=null) + rptMap.put("distanceUnits", distanceUnits); + if(geo!=null) + rptMap.put("geo", geo); + rptFieldType.init(oldSchema, rptMap); + rptFieldType.setTypeName("location_rpt"); + SchemaField newField = new SchemaField("geo", rptFieldType, SchemaField.STORED | SchemaField.INDEXED, null); + IndexSchema newSchema = oldSchema.addField(newField); + + h.getCore().setLatestSchema(newSchema); + + assertU(delQ("*:*")); + } +} diff --git a/solr/core/src/test/org/apache/solr/search/TestSolr4Spatial.java b/solr/core/src/test/org/apache/solr/search/TestSolr4Spatial.java index 12c57254a19..690ad742d21 100644 --- a/solr/core/src/test/org/apache/solr/search/TestSolr4Spatial.java +++ b/solr/core/src/test/org/apache/solr/search/TestSolr4Spatial.java @@ -17,6 +17,9 @@ package org.apache.solr.search; * limitations under the License. */ +import java.text.ParseException; +import java.util.Arrays; + import com.carrotsearch.randomizedtesting.RandomizedTest; import com.carrotsearch.randomizedtesting.annotations.ParametersFactory; import com.spatial4j.core.context.SpatialContext; @@ -33,9 +36,6 @@ import org.junit.Before; import org.junit.BeforeClass; import org.junit.Test; -import java.text.ParseException; -import java.util.Arrays; - /** * Test Solr 4's new spatial capabilities from the new Lucene spatial module. Don't thoroughly test it here because * Lucene spatial has its own tests. Some of these tests were ported from Solr 3 spatial tests. @@ -110,19 +110,20 @@ public class TestSolr4Spatial extends SolrTestCaseJ4 { @Test public void testIntersectFilter() throws Exception { setupDocs(); + //Try some edge cases - checkHits(fieldName, "1,1", 175, 3, 5, 6, 7); - checkHits(fieldName, "0,179.8", 200, 2, 8, 9); - checkHits(fieldName, "89.8, 50", 200, 2, 10, 11);//this goes over the north pole - checkHits(fieldName, "-89.8, 50", 200, 2, 12, 13);//this goes over the south pole + checkHits(fieldName, "1,1", 175, DistanceUtils.EARTH_MEAN_RADIUS_KM, 3, 5, 6, 7); + checkHits(fieldName, "0,179.8", 200, DistanceUtils.EARTH_MEAN_RADIUS_KM, 2, 8, 9); + checkHits(fieldName, "89.8, 50", 200, DistanceUtils.EARTH_MEAN_RADIUS_KM, 2, 10, 11);//this goes over the north pole + checkHits(fieldName, "-89.8, 50", 200, DistanceUtils.EARTH_MEAN_RADIUS_KM, 2, 12, 13);//this goes over the south pole //try some normal cases - checkHits(fieldName, "33.0,-80.0", 300, 2); + checkHits(fieldName, "33.0,-80.0", 300, DistanceUtils.EARTH_MEAN_RADIUS_KM, 2); //large distance - checkHits(fieldName, "1,1", 5000, 3, 5, 6, 7); + checkHits(fieldName, "1,1", 5000, DistanceUtils.EARTH_MEAN_RADIUS_KM, 3, 5, 6, 7); //Because we are generating a box based on the west/east longitudes and the south/north latitudes, which then //translates to a range query, which is slightly more inclusive. Thus, even though 0.0 is 15.725 kms away, //it will be included, b/c of the box calculation. - checkHits(fieldName, false, "0.1,0.1", 15, 2, 5, 6); + checkHits(fieldName, false, "0.1,0.1", 15, DistanceUtils.EARTH_MEAN_RADIUS_KM, 2, 5, 6); //try some more clearIndex(); @@ -133,18 +134,18 @@ public class TestSolr4Spatial extends SolrTestCaseJ4 { assertU(adoc("id", "17", fieldName, "44.043900,-95.436643")); assertU(commit()); - checkHits(fieldName, "0,0", 1000, 1, 14); - checkHits(fieldName, "0,0", 2000, 2, 14, 15); - checkHits(fieldName, false, "0,0", 3000, 3, 14, 15, 16); - checkHits(fieldName, "0,0", 3001, 3, 14, 15, 16); - checkHits(fieldName, "0,0", 3000.1, 3, 14, 15, 16); + checkHits(fieldName, "0,0", 1000, DistanceUtils.EARTH_MEAN_RADIUS_KM, 1, 14); + checkHits(fieldName, "0,0", 2000, DistanceUtils.EARTH_MEAN_RADIUS_KM, 2, 14, 15); + checkHits(fieldName, false, "0,0", 3000, DistanceUtils.EARTH_MEAN_RADIUS_KM, 3, 14, 15, 16); + checkHits(fieldName, "0,0", 3001, DistanceUtils.EARTH_MEAN_RADIUS_KM, 3, 14, 15, 16); + checkHits(fieldName, "0,0", 3000.1, DistanceUtils.EARTH_MEAN_RADIUS_KM, 3, 14, 15, 16); //really fine grained distance and reflects some of the vagaries of how we are calculating the box - checkHits(fieldName, "43.517030,-96.789603", 109, 0); + checkHits(fieldName, "43.517030,-96.789603", 109, DistanceUtils.EARTH_MEAN_RADIUS_KM, 0); //falls outside of the real distance, but inside the bounding box - checkHits(fieldName, true, "43.517030,-96.789603", 110, 0); - checkHits(fieldName, false, "43.517030,-96.789603", 110, 1, 17); + checkHits(fieldName, true, "43.517030,-96.789603", 110, DistanceUtils.EARTH_MEAN_RADIUS_KM, 0); + checkHits(fieldName, false, "43.517030,-96.789603", 110, DistanceUtils.EARTH_MEAN_RADIUS_KM, 1, 17); } @Test @@ -164,14 +165,14 @@ public class TestSolr4Spatial extends SolrTestCaseJ4 { @Test public void checkQueryEmptyIndex() throws ParseException { - checkHits(fieldName, "0,0", 100, 0);//doesn't error + checkHits(fieldName, "0,0", 100, DistanceUtils.EARTH_MEAN_RADIUS_KM, 0);//doesn't error } - private void checkHits(String fieldName, String pt, double distKM, int count, int ... docIds) throws ParseException { - checkHits(fieldName, true, pt, distKM, count, docIds); + private void checkHits(String fieldName, String pt, double distKM, double sphereRadius, int count, int ... docIds) throws ParseException { + checkHits(fieldName, true, pt, distKM, sphereRadius, count, docIds); } - private void checkHits(String fieldName, boolean exact, String ptStr, double distKM, int count, int ... docIds) throws ParseException { + private void checkHits(String fieldName, boolean exact, String ptStr, double distKM, double sphereRadius, int count, int ... docIds) throws ParseException { if (exact && fieldName.equalsIgnoreCase("bbox")) { return; // bbox field only supports rectangular query } @@ -217,7 +218,7 @@ public class TestSolr4Spatial extends SolrTestCaseJ4 { { assertQ(req( "fl", "id", "q", "*:*", "rows", "1000", - "fq", "{!" + (exact ? "geofilt" : "bbox") + " sfield=" + fieldName + " pt='" + ptStr + "' d=" + distKM + "}"), + "fq", "{!" + (exact ? "geofilt" : "bbox") + " sfield=" + fieldName + " pt='" + ptStr + "' d=" + distKM + " sphere_radius=" + sphereRadius + "}"), tests); } @@ -332,7 +333,7 @@ public class TestSolr4Spatial extends SolrTestCaseJ4 { "sfield=" + fieldName + " " + (score != null ? "score="+score : "") + " " + (filter != null ? "filter="+filter : "") + " " - + "pt=" + lat + "," + lon + " d=" + (dDEG * DistanceUtils.DEG_TO_KM) + "}"; + + "pt=" + lat + "," + lon + " d=" + (dDEG /* DistanceUtils.DEG_TO_KM*/) + "}"; } else { return "{! " + (score != null ? "score="+score : "") + " " diff --git a/solr/core/src/test/org/apache/solr/util/DistanceUnitsTest.java b/solr/core/src/test/org/apache/solr/util/DistanceUnitsTest.java new file mode 100644 index 00000000000..6b2b5a4d9bc --- /dev/null +++ b/solr/core/src/test/org/apache/solr/util/DistanceUnitsTest.java @@ -0,0 +1,29 @@ +package org.apache.solr.util; + +import com.spatial4j.core.distance.DistanceUtils; +import org.apache.lucene.util.LuceneTestCase; + +/* + * 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. + */ + +public class DistanceUnitsTest extends LuceneTestCase { + + public void testAddNewUnits() throws Exception { + DistanceUnits.addUnits("lightyears", 6.73430542e-12, 9.4605284e12 * DistanceUtils.KM_TO_DEG); + assertTrue(DistanceUnits.getSupportedUnits().contains("lightyears")); + } +} diff --git a/solr/example/example-DIH/solr/db/conf/schema.xml b/solr/example/example-DIH/solr/db/conf/schema.xml index d0611fb3f1f..d407c5d0746 100755 --- a/solr/example/example-DIH/solr/db/conf/schema.xml +++ b/solr/example/example-DIH/solr/db/conf/schema.xml @@ -697,7 +697,7 @@ http://wiki.apache.org/solr/SolrAdaptersForLuceneSpatial4 --> + geo="true" distErrPct="0.025" maxDistErr="0.001" distanceUnits="kilometers" /> + geo="true" distErrPct="0.025" maxDistErr="0.001" distanceUnits="kilometers" /> + geo="true" distErrPct="0.025" maxDistErr="0.001" distanceUnits="kilometers" /> + geo="true" distErrPct="0.025" maxDistErr="0.001" distanceUnits="kilometers" /> + geo="true" distErrPct="0.025" maxDistErr="0.001" distanceUnits="kilometers" /> diff --git a/solr/server/solr/configsets/basic_configs/conf/schema.xml b/solr/server/solr/configsets/basic_configs/conf/schema.xml index c0be84efc73..f5612a530c7 100755 --- a/solr/server/solr/configsets/basic_configs/conf/schema.xml +++ b/solr/server/solr/configsets/basic_configs/conf/schema.xml @@ -504,7 +504,7 @@ http://wiki.apache.org/solr/SolrAdaptersForLuceneSpatial4 --> + geo="true" distErrPct="0.025" maxDistErr="0.001" distanceUnits="kilometers" /> + geo="true" distErrPct="0.025" maxDistErr="0.001" distanceUnits="kilometers" /> + geo="true" distErrPct="0.025" maxDistErr="0.001" distanceUnits="kilometers" />