blob: 271b8140eeec644a5e748d7c5f6698b5843fc48f [file] [log] [blame]
Index: contrib/spatial/src/java/org/apache/lucene/spatial/geohash/GeoHashDistanceFilter.java
===================================================================
--- contrib/spatial/src/java/org/apache/lucene/spatial/geohash/GeoHashDistanceFilter.java (revision 833867)
+++ contrib/spatial/src/java/org/apache/lucene/spatial/geohash/GeoHashDistanceFilter.java Wed Jan 20 14:35:46 CET 2010
@@ -31,8 +31,10 @@
/** <p><font color="red"><b>NOTE:</b> This API is still in
* flux and might change in incompatible ways in the next
* release.</font>
+ *
+ * @deprecated This class has been replaced by DistanceFilter and GeoHashPointDecoder. It will be removed.
*/
-
+@Deprecated
public class GeoHashDistanceFilter extends DistanceFilter {
/**
Index: contrib/spatial/src/java/org/apache/lucene/spatial/distance/GeoHashPointDecoder.java
===================================================================
--- contrib/spatial/src/java/org/apache/lucene/spatial/distance/GeoHashPointDecoder.java Wed Jan 20 15:06:23 CET 2010
+++ contrib/spatial/src/java/org/apache/lucene/spatial/distance/GeoHashPointDecoder.java Wed Jan 20 15:06:23 CET 2010
@@ -0,0 +1,65 @@
+/** Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.lucene.spatial.distance;
+
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.search.FieldCache;
+import org.apache.lucene.spatial.geohash.GeoHashUtils;
+import org.apache.lucene.spatial.geometry.Point;
+
+import java.io.IOException;
+
+/**
+ * PointDecoder which uses {@link org.apache.lucene.spatial.geohash.GeoHashUtils#decode(String)} to decode the point
+ * data for a document.
+ */
+public class GeoHashPointDecoder implements PointDecoder {
+
+ private final String geoHashField;
+
+ /**
+ * Creates a new GeoHashPointDecoder that decodes the data taken from the given field
+ *
+ * @param geoHashField Name of the field in documents where the encoded geohash data is
+ */
+ public GeoHashPointDecoder(String geoHashField) {
+ this.geoHashField = geoHashField;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Point getPoint(int docId, IndexReader reader) throws IOException {
+ double[] coords = GeoHashUtils.decode(getGeoHash(docId, reader));
+ return new Point(coords[0], coords[1]);
+ }
+
+ // ================================================= Helper Methods ================================================
+
+ /**
+ * Retrieves the geohash for the document through the IndexReader
+ *
+ * @param docId ID of the document whose geohash is to be retrieved
+ * @param reader IndexReader through which the hash can be retrieved
+ * @return Geohash for the document
+ * @throws IOException Can be thrown while interacting with the index
+ */
+ String getGeoHash(int docId, IndexReader reader) throws IOException {
+ FieldCache.StringIndex stringIndex = FieldCache.DEFAULT.getStringIndex(reader, geoHashField);
+ return stringIndex.lookup[stringIndex.order[docId]];
+ }
+}
Index: contrib/spatial/src/test/org/apache/lucene/spatial/distance/GeoHashPointDecoderTest.java
===================================================================
--- contrib/spatial/src/test/org/apache/lucene/spatial/distance/GeoHashPointDecoderTest.java Wed Jan 20 15:08:50 CET 2010
+++ contrib/spatial/src/test/org/apache/lucene/spatial/distance/GeoHashPointDecoderTest.java Wed Jan 20 15:08:50 CET 2010
@@ -0,0 +1,49 @@
+/** Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.lucene.spatial.distance;
+
+import junit.framework.TestCase;
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.spatial.geometry.Point;
+
+import java.io.IOException;
+
+/**
+ * Tests for {@link org.apache.lucene.spatial.distance.GeoHashPointDecoder}
+ */
+public class GeoHashPointDecoderTest extends TestCase {
+
+ /**
+ * Pass condition: The geohash for the document is retrieved and decoded into a Point
+ *
+ * @throws IOException Can be thrown while interacting with the index
+ */
+ public void testGetPoint() throws IOException {
+ GeoHashPointDecoder pointDecoder = new GeoHashPointDecoder("geohash") {
+
+ @Override
+ String getGeoHash(int docId, IndexReader reader) throws IOException {
+ return "ezs42e44yx96";
+ }
+ };
+
+ Point point = pointDecoder.getPoint(0, null);
+
+ assertEquals(42.6, point.getX(), 0.00001D);
+ assertEquals(-5.6, point.getY(), 0.00001D);
+ }
+}
Index: contrib/spatial/src/java/org/apache/lucene/spatial/distance/SimpleDocumentDistanceSource.java
===================================================================
--- contrib/spatial/src/java/org/apache/lucene/spatial/distance/SimpleDocumentDistanceSource.java Wed Jan 20 15:08:44 CET 2010
+++ contrib/spatial/src/java/org/apache/lucene/spatial/distance/SimpleDocumentDistanceSource.java Wed Jan 20 15:08:44 CET 2010
@@ -0,0 +1,53 @@
+/** Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.lucene.spatial.distance;
+
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.spatial.geometry.DistanceUnits;
+import org.apache.lucene.spatial.geometry.GeoDistanceCalculator;
+import org.apache.lucene.spatial.geometry.Point;
+
+import java.io.IOException;
+
+/**
+ * Simple implementation of {@link DocumentDistanceSource} which calculates the distance eachtime without any caching.
+ */
+public class SimpleDocumentDistanceSource implements DocumentDistanceSource {
+
+ private final PointDecoder pointDecoder;
+ private final GeoDistanceCalculator distanceCalculator;
+
+ /**
+ * Creates a new SimpleDocumentDistanceSource which uses the given PointDecoder to retrieve the Points for documents,
+ * and the given GeoDistanceCalculator to calculate the distance
+ *
+ * @param pointDecoder PointDecoder to use to retrieve Points for documents
+ * @param distanceCalculator GeoDistanceCalculator to use to calculate the actual distances
+ */
+ public SimpleDocumentDistanceSource(PointDecoder pointDecoder, GeoDistanceCalculator distanceCalculator) {
+ this.pointDecoder = pointDecoder;
+ this.distanceCalculator = distanceCalculator;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public double getDocumentDistance(int docId, Point src, IndexReader indexReader, DistanceUnits units) throws IOException {
+ Point docPoint = pointDecoder.getPoint(docId, indexReader);
+ return distanceCalculator.calculate(src.getX(), src.getY(), docPoint.getX(), docPoint.getY(), units);
+ }
+}
Index: contrib/spatial/src/java/org/apache/lucene/spatial/distance/NoOpDistanceFilter.java
===================================================================
--- contrib/spatial/src/java/org/apache/lucene/spatial/distance/NoOpDistanceFilter.java Sun Jan 17 15:37:16 CET 2010
+++ contrib/spatial/src/java/org/apache/lucene/spatial/distance/NoOpDistanceFilter.java Sun Jan 17 15:37:16 CET 2010
@@ -0,0 +1,44 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.lucene.spatial.distance;
+
+import org.apache.lucene.index.IndexReader;
+
+import java.io.IOException;
+import java.util.BitSet;
+import java.util.Collections;
+import java.util.Map;
+
+/**
+ * Implementation of {@link DistanceFilter} that does no actual filtering. This means that there can always be a DistanceFilter
+ * instantiated, but that the actual process of filtering documents by their distance, which is a computationally expensive
+ * process, doesn't always have to occur.
+ *
+ * <p><font color="red"><b>NOTE:</b> This API is still in flux and might change in incompatible ways in the next release.</font>
+ */
+public class NoOpDistanceFilter implements DistanceFilter {
+
+ /**
+ * Executes no filtering. Simply returns the given BitSet
+ * <p/>
+ * {@inheritDoc}
+ */
+ public BitSet bits(IndexReader reader, BitSet bits) throws IOException {
+ return bits;
+ }
+}
Index: contrib/spatial/src/test/org/apache/lucene/spatial/distance/TestThreadedDistanceFilter.java
===================================================================
--- contrib/spatial/src/test/org/apache/lucene/spatial/distance/TestThreadedDistanceFilter.java Wed Jan 20 15:05:00 CET 2010
+++ contrib/spatial/src/test/org/apache/lucene/spatial/distance/TestThreadedDistanceFilter.java Wed Jan 20 15:05:00 CET 2010
@@ -0,0 +1,264 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.lucene.spatial.distance;
+
+import junit.framework.TestCase;
+import org.apache.lucene.analysis.standard.StandardAnalyzer;
+import org.apache.lucene.document.Document;
+import org.apache.lucene.document.Field;
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.index.IndexWriter;
+import org.apache.lucene.spatial.geometry.ArcGeoDistanceCalculator;
+import org.apache.lucene.spatial.geometry.DistanceUnits;
+import org.apache.lucene.spatial.geometry.GeoDistanceCalculator;
+import org.apache.lucene.spatial.geometry.Point;
+import org.apache.lucene.store.Directory;
+import org.apache.lucene.store.RAMDirectory;
+import org.apache.lucene.util.Version;
+
+import java.io.IOException;
+import java.util.*;
+import java.util.concurrent.*;
+
+/**
+ * Tests for {@link TestThreadedDistanceFilter}
+ */
+public class TestThreadedDistanceFilter extends TestCase {
+
+ private final List<Point> testPoints = Arrays.asList(new Point(4.53, 30.61), new Point(4.51, 31.01), new Point(5.69, 40.89));
+
+ private final PointDecoder pointDecoder = new PointDecoder() {
+
+ public Point getPoint(int docId, IndexReader reader) throws IOException {
+ return testPoints.get(docId);
+ }
+ };
+
+ private Directory directory;
+
+ /**
+ * Creates a new in-memory Directory containing an index with a document for each point in {@link #testPoints}
+ *
+ * @throws IOException Can be thrown creating the index
+ */
+ public void setUp() throws IOException {
+ directory = new RAMDirectory();
+ IndexWriter indexWriter = new IndexWriter(directory, new StandardAnalyzer(Version.LUCENE_CURRENT), IndexWriter.MaxFieldLength.UNLIMITED);
+ for (int i = 0; i < testPoints.size(); i++) {
+ addDocument(indexWriter, i);
+ }
+ indexWriter.commit();
+ indexWriter.close();
+ }
+
+ /**
+ * Closes the Directory
+ *
+ * @throws IOException Can be thrown closing the Directory
+ */
+ public void tearDown() throws IOException {
+ directory.close();
+ }
+
+ /**
+ * Adds a Document with a single field "id" with the given id as its value
+ *
+ * @param indexWriter IndexWriter to write the document too
+ * @param id ID that the document will have
+ * @throws IOException Can be thrown while writing document to the index
+ */
+ private void addDocument(IndexWriter indexWriter, int id) throws IOException {
+ Document document = new Document();
+ document.add(new Field("id", String.valueOf(id), Field.Store.YES, Field.Index.ANALYZED));
+ indexWriter.addDocument(document);
+ }
+
+ /**
+ * Pass condition: The latitude/longitude values have to be passed to the GeoDistanceCalculator in the correct order
+ *
+ * @throws IOException Can be thrown reading from the index
+ */
+ public void testGeoCalculation_correctArgumentPassing() throws IOException {
+ StubGeoDistanceCalculator distanceCalculator = new StubGeoDistanceCalculator();
+ ExecutorService executorService = Executors.newFixedThreadPool(1);
+
+ DocumentDistanceSource distanceSource = new SimpleDocumentDistanceSource(pointDecoder, distanceCalculator);
+
+ ThreadedDistanceFilter filter =
+ new ThreadedDistanceFilter(new Point(4.51, 31.01), 10, DistanceUnits.KILOMETERS, distanceSource, executorService, 1);
+
+ IndexReader indexReader = IndexReader.open(directory);
+ BitSet bitSet = new BitSet(1);
+ bitSet.set(0);
+ filter.bits(indexReader, bitSet);
+
+ assertEquals(4.51, distanceCalculator.getSourceLatitude(), 0);
+ assertEquals(31.01, distanceCalculator.getSourceLongitude(), 0);
+ assertEquals(4.53, distanceCalculator.getTargetLatitude(), 0);
+ assertEquals(30.61, distanceCalculator.getTargetLongitude(), 0);
+
+ indexReader.close();
+ }
+
+ /**
+ * Pass condition: 2 threads with the correct start and end indexes are created based on the BitSet given in the method
+ * call, and the number of threads that were requested.
+ *
+ * @throws java.io.IOException Can be thrown while reading from the index
+ */
+ public void testBits() throws IOException {
+ ExecutorService executorService = new ThreadPoolExecutor(2, 6, 8, TimeUnit.SECONDS, new LinkedBlockingQueue<Runnable>());
+ DocumentDistanceSource distanceSource = new SimpleDocumentDistanceSource(pointDecoder, new ArcGeoDistanceCalculator());
+
+ BitSet bitSet = new BitSet(13);
+ bitSet.set(2, 6);
+ bitSet.set(8, 9);
+ bitSet.set(11);
+
+ final List<IterateCallInfo> infoList = Collections.synchronizedList(new ArrayList<IterateCallInfo>());
+ ThreadedDistanceFilter distanceFilter = new ThreadedDistanceFilter(new Point(4.52, 30.81), 30, DistanceUnits.MILES, distanceSource, executorService, 2) {
+
+ @Override
+ protected IterationResult iterate(BitSet originalBitSet, int start, int end, int size, IndexReader reader) {
+ infoList.add(new IterateCallInfo(start, end, size));
+ return new IterationResult(new BitSet());
+ }
+ };
+
+ IndexReader indexReader = IndexReader.open(directory);
+
+ distanceFilter.bits(indexReader, bitSet);
+
+ assertEquals(2, infoList.size());
+
+ Collections.sort(infoList, new Comparator<IterateCallInfo>() {
+ public int compare(IterateCallInfo info1, IterateCallInfo info2) {
+ return info1.getStart() - info2.getStart();
+ }
+ });
+
+ IterateCallInfo callInfo = infoList.get(0);
+ assertEquals(0, callInfo.getStart());
+ assertEquals(6, callInfo.getEnd());
+ assertEquals(6, callInfo.getSize());
+
+ callInfo = infoList.get(1);
+ assertEquals(6, callInfo.getStart());
+ assertEquals(12, callInfo.getEnd());
+ assertEquals(6, callInfo.getSize());
+
+ indexReader.close();
+ }
+
+ /**
+ * Pass condition: Of the 3 documents with points, 1 is outside of the radius and should be filtered out, 1 has been
+ * deleted so it should also be filtered out, leaving just 1 that passes the filter. Its distance should be recorded
+ * in the calculated distances of the filter
+ *
+ * @throws IOException Can be thrown when interactin with the index
+ */
+ public void testIterate() throws IOException {
+ IndexReader indexReader = IndexReader.open(directory, false);
+ indexReader.deleteDocument(1);
+
+ BitSet bitSet = new BitSet(2);
+ bitSet.set(0);
+ bitSet.set(1);
+ bitSet.set(2);
+
+ MapCachingDocumentDistanceSource distanceSource = new MapCachingDocumentDistanceSource(new SimpleDocumentDistanceSource(pointDecoder, new ArcGeoDistanceCalculator()));
+
+ ThreadedDistanceFilter distanceFilter = new ThreadedDistanceFilter(new Point(4.52, 30.81), 30, DistanceUnits.MILES, distanceSource, null, 0);
+
+ ThreadedDistanceFilter.IterationResult result = distanceFilter.iterate(bitSet, 0, 3, 3, indexReader);
+
+ assertNotNull(result);
+
+ BitSet resultingBitSet = result.getBitSet();
+ assertNotNull(resultingBitSet);
+ assertTrue(resultingBitSet.get(0));
+ assertFalse(resultingBitSet.get(1));
+ assertFalse(resultingBitSet.get(2));
+
+ assertEquals(2, distanceSource.getCachedDistances().size());
+
+ indexReader.close();
+ }
+
+ // ================================================= Inner Classes =================================================
+
+ private static class IterateCallInfo {
+
+ private final int start;
+ private final int end;
+ private final int size;
+
+ private IterateCallInfo(int start, int end, int size) {
+ this.start = start;
+ this.end = end;
+ this.size = size;
+ }
+
+ public int getStart() {
+ return start;
+ }
+
+ public int getEnd() {
+ return end;
+ }
+
+ public int getSize() {
+ return size;
+ }
+ }
+
+ /**
+ * Dummy implementation. Used for unit test to assure that the given arguments to a distance calculator are given correctly .
+ */
+ class StubGeoDistanceCalculator implements GeoDistanceCalculator {
+
+ private double sourceLatitude;
+ private double sourceLongitude;
+ private double targetLatitude;
+ private double targetLongitude;
+
+ public double calculate(double sourceLatitude, double sourceLongitude, double targetLatitude, double targetLongitude, DistanceUnits unit) {
+ this.sourceLatitude = sourceLatitude;
+ this.sourceLongitude = sourceLongitude;
+ this.targetLatitude = targetLatitude;
+ this.targetLongitude = targetLongitude;
+ return 0.0;
+ }
+
+ public double getSourceLatitude() {
+ return sourceLatitude;
+ }
+
+ public double getSourceLongitude() {
+ return sourceLongitude;
+ }
+
+ public double getTargetLatitude() {
+ return targetLatitude;
+ }
+
+ public double getTargetLongitude() {
+ return targetLongitude;
+ }
+ }
+}
Index: contrib/spatial/src/java/org/apache/lucene/spatial/distance/LatLngPointDecoder.java
===================================================================
--- contrib/spatial/src/java/org/apache/lucene/spatial/distance/LatLngPointDecoder.java Wed Jan 20 15:08:24 CET 2010
+++ contrib/spatial/src/java/org/apache/lucene/spatial/distance/LatLngPointDecoder.java Wed Jan 20 15:08:24 CET 2010
@@ -0,0 +1,66 @@
+/** Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.lucene.spatial.distance;
+
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.search.FieldCache;
+import org.apache.lucene.spatial.geometry.Point;
+
+import java.io.IOException;
+
+/**
+ * PointDecoder that uses latitude and longitude data that is stored in two separate fields.
+ */
+public class LatLngPointDecoder implements PointDecoder {
+
+ private final String latField;
+ private final String lngField;
+
+ /**
+ * Creates a new LatLngPointDecoder that uses the data found in the given latitude and longitude fields
+ *
+ * @param latField Name of the field in documents containing laitutde data
+ * @param lngField Name of the field in documents containing longitude data
+ */
+ public LatLngPointDecoder(String latField, String lngField) {
+ this.latField = latField;
+ this.lngField = lngField;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Point getPoint(int docId, IndexReader reader) throws IOException {
+ double[] latValues = getFieldValues(latField, reader);
+ double[] lngValues = getFieldValues(lngField, reader);
+ return new Point(latValues[docId], lngValues[docId]);
+ }
+
+ // ================================================= Helper Methods ================================================
+
+ /**
+ * Gets the values for the given field from the IndexReader
+ *
+ * @param field Field whose values are to be retrieved
+ * @param indexReader IndexReader from where the values are to be retrieved
+ * @return Values for the field
+ * @throws IOException Can be thrown while interacting with the index
+ */
+ double[] getFieldValues(String field, IndexReader indexReader) throws IOException {
+ return FieldCache.DEFAULT.getDoubles(indexReader, field, FieldCache.NUMERIC_UTILS_DOUBLE_PARSER);
+ }
+}
Index: contrib/spatial/src/java/org/apache/lucene/spatial/distance/PointDecoder.java
===================================================================
--- contrib/spatial/src/java/org/apache/lucene/spatial/distance/PointDecoder.java Wed Jan 20 15:08:44 CET 2010
+++ contrib/spatial/src/java/org/apache/lucene/spatial/distance/PointDecoder.java Wed Jan 20 15:08:44 CET 2010
@@ -0,0 +1,38 @@
+/** Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.lucene.spatial.distance;
+
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.spatial.geometry.Point;
+
+import java.io.IOException;
+
+/**
+ * PointDecoders decode the representation of a point for a document into instances of {@link org.apache.lucene.spatial.geometry.Point}
+ */
+public interface PointDecoder {
+
+ /**
+ * Retrieves the Point for the document with the given ID
+ *
+ * @param docId ID of the document whose point is to be retrieved
+ * @param reader IndexReader for communicating with the index
+ * @return Point representation of the decoded data
+ * @throws IOException Can be thrown while interacting with the index
+ */
+ Point getPoint(int docId, IndexReader reader) throws IOException;
+}
Index: contrib/spatial/src/java/org/apache/lucene/spatial/distance/DistanceFilter.java
===================================================================
--- contrib/spatial/src/java/org/apache/lucene/spatial/distance/DistanceFilter.java Sun Jan 17 15:33:25 CET 2010
+++ contrib/spatial/src/java/org/apache/lucene/spatial/distance/DistanceFilter.java Sun Jan 17 15:33:25 CET 2010
@@ -0,0 +1,44 @@
+/** Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.lucene.spatial.distance;
+
+import org.apache.lucene.index.IndexReader;
+
+import java.io.IOException;
+import java.util.BitSet;
+import java.util.Map;
+
+/**
+ * DistanceFilter is responsible for filtering out documents from an existing BitSet, based on their calculated distance
+ * from the central point. Because the costing of calculating distances for documents is relatively high, this filter
+ * uses an existing BitSet, which will have been created another filter previously. As such, this is technically not
+ * a Lucene Filter.
+ *
+ * <p><font color="red"><b>NOTE:</b> This API is still in flux and might change in incompatible ways in the next release.</font>
+ */
+public interface DistanceFilter {
+
+ /**
+ * Filters the documents from the given IndexReader who have bits set in the given BitSet.
+ *
+ * @param reader IndexReader from where the documents will be read from
+ * @param bits BitSet containing bits indicating which documents should be considered to be filtered out
+ * @return BitSet with bits set representing those documents that passed the filter
+ * @throws java.io.IOException Can be thrown while reading from the IndexReader
+ */
+ BitSet bits(IndexReader reader, BitSet bits) throws IOException;
+}
Index: contrib/spatial/src/java/org/apache/lucene/spatial/distance/MapCachingDocumentDistanceSource.java
===================================================================
--- contrib/spatial/src/java/org/apache/lucene/spatial/distance/MapCachingDocumentDistanceSource.java Wed Jan 20 15:08:29 CET 2010
+++ contrib/spatial/src/java/org/apache/lucene/spatial/distance/MapCachingDocumentDistanceSource.java Wed Jan 20 15:08:29 CET 2010
@@ -0,0 +1,69 @@
+/** Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.lucene.spatial.distance;
+
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.spatial.geometry.DistanceUnits;
+import org.apache.lucene.spatial.geometry.Point;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Implementation of {@link DocumentDistanceSource} which caches calculated distances in a Map. Note, the caching does
+ * not take into account the IndexReader, nor does it apply any multi-thread synchronisation.
+ */
+public class MapCachingDocumentDistanceSource implements DocumentDistanceSource {
+
+ private final DocumentDistanceSource source;
+ private final Map<Integer, Double> cachedDistances = new HashMap<Integer, Double>();
+
+ /**
+ * Creates a new MapCachingDocumentDistanceSource that caches distances from the given DocumentDistanceSource
+ *
+ * @param source DocumentDistanceSource whose calculated distances will be cached
+ */
+ public MapCachingDocumentDistanceSource(DocumentDistanceSource source) {
+ this.source = source;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public double getDocumentDistance(int docId, Point src, IndexReader indexReader, DistanceUnits units) throws IOException {
+ Double cachedDistance = cachedDistances.get(docId);
+ if (cachedDistance != null) {
+ return cachedDistance;
+ }
+
+ double calculatedDistance = source.getDocumentDistance(docId, src, indexReader, units);
+ cachedDistances.put(docId, calculatedDistance);
+ return calculatedDistance;
+ }
+
+ // ================================================= Getters / Setters =============================================
+
+ /**
+ * Returns the cached distances
+ *
+ * @return Cached distances
+ */
+ public Map<Integer, Double> getCachedDistances() {
+ return cachedDistances;
+ }
+}
Index: contrib/spatial/src/java/org/apache/lucene/spatial/distance/ThreadedDistanceFilter.java
===================================================================
--- contrib/spatial/src/java/org/apache/lucene/spatial/distance/ThreadedDistanceFilter.java Sun Jan 17 15:35:42 CET 2010
+++ contrib/spatial/src/java/org/apache/lucene/spatial/distance/ThreadedDistanceFilter.java Sun Jan 17 15:35:42 CET 2010
@@ -0,0 +1,175 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.lucene.spatial.distance;
+
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.spatial.geometry.DistanceUnits;
+import org.apache.lucene.spatial.geometry.Point;
+
+import java.io.IOException;
+import java.util.*;
+import java.util.concurrent.Callable;
+import java.util.concurrent.ExecutionException;
+import java.util.concurrent.ExecutorService;
+import java.util.concurrent.Future;
+
+/**
+ * Implementation of {@link DistanceFilter} that uses multiple threads to iterate in parallel, over the BitSet to be filtered.
+ * <p/>
+ * To manage the threads, an ExecutorService is used, which allows users of this class to have fine grained control over
+ * how many threads should be created, and what to do when there isn't any threads left in the pool.
+ *
+ * <p><font color="red"><b>NOTE:</b> This API is still in flux and might change in incompatible ways in the next release.</font>
+ */
+public class ThreadedDistanceFilter implements DistanceFilter {
+
+ private final Point centralPoint;
+ private final double radius;
+ private final DistanceUnits unit;
+ private final DocumentDistanceSource documentDistanceSource;
+
+ private final ExecutorService executorService;
+ private final int threadCount;
+
+ /**
+ * Creates a new ThreadedDistanceFilter that will filter out documents that are outside of the given radius of the
+ * central point defined by the given latitude and longitude
+ *
+ * @param centralPoint Point at the centre of the search
+ * @param radius Radius that documents must be within from the central point to pass the filter
+ * @param unit Unit of distance the radius is in
+ * @param distanceSource DocumentDistanceSource from where the distances for documents from the central point, will be
+ * retrieved from
+ * @param executorService ExecutorService which will manage the execution of the threads
+ * @param threadCount Number of threads that the filter should try to split its work across
+ */
+ public ThreadedDistanceFilter(
+ Point centralPoint,
+ double radius,
+ DistanceUnits unit,
+ DocumentDistanceSource distanceSource,
+ ExecutorService executorService,
+ int threadCount) {
+ this.centralPoint = centralPoint;
+ this.radius = radius;
+ this.unit = unit;
+ this.documentDistanceSource = distanceSource;
+ this.executorService = executorService;
+ this.threadCount = threadCount;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public BitSet bits(final IndexReader reader, final BitSet bits) throws IOException {
+ int maxLength = bits.length();
+ int threadSize = maxLength / threadCount;
+ List<Callable<IterationResult>> tasks = new ArrayList<Callable<IterationResult>>();
+
+ for (int i = 0; i < threadCount; i++) {
+ final int start = i * threadSize;
+ // if the last batch of documents has been reached, then maxLength should be end
+ final int end = (i == threadCount - 1) ? maxLength : Math.min((i + 1) * threadSize, maxLength);
+ tasks.add(new Callable<IterationResult>() {
+ public IterationResult call() throws Exception {
+ return iterate(bits, start, end, end - start, reader);
+ }
+ });
+ }
+
+ BitSet result = new BitSet(bits.cardinality());
+
+ try {
+ List<Future<IterationResult>> results = executorService.invokeAll(tasks);
+ for (Future<IterationResult> resultFuture : results) {
+ IterationResult iterationResult = resultFuture.get();
+ result.or(iterationResult.getBitSet());
+ }
+ } catch (InterruptedException ie) {
+ throw new RuntimeException("InterruptedException thrown while executing tasks", ie);
+ } catch (ExecutionException ee) {
+ throw new RuntimeException("ExecutionException thrown while retrieving results of tasks", ee);
+ }
+
+ return result;
+ }
+
+ // ================================================ Helper Methods =================================================
+
+ /**
+ * Iterates over the set bits in the given BitSet from the given start to end range, calculating the distance of the
+ * documents and determining which are within the distance radius of the central point.
+ *
+ * @param originalBitSet BitSet which has bits set identifying which documents should be checked to see if their
+ * distance falls within the radius
+ * @param start Index in the BitSet that the method will start at
+ * @param end Index in the BitSet that the method will stop at
+ * @param size Size the the resulting BitSet should be created at (most likely end - start)
+ * @param reader IndexReader for checking if the document has been deleted
+ * @return IterationResult containing all the results of the method.
+ * @throws IOException Can be thrown while interacting with the index
+ */
+ protected IterationResult iterate(BitSet originalBitSet, int start, int end, int size, IndexReader reader) throws IOException {
+ BitSet bitSet = new BitSet(size);
+
+ int docId = originalBitSet.nextSetBit(start);
+ while (docId != -1 && docId < end) {
+ if (reader.isDeleted(docId)) {
+ docId = originalBitSet.nextSetBit(docId + 1);
+ continue;
+ }
+
+ double distance = documentDistanceSource.getDocumentDistance(docId, centralPoint, reader, unit);
+ if (distance < radius) {
+ bitSet.set(docId);
+ }
+
+ docId = originalBitSet.nextSetBit(docId + 1);
+ }
+ return new IterationResult(bitSet);
+ }
+
+ // ================================================= Inner Classes =================================================
+
+ /**
+ * Wrapper of the results from {@link ThreadedDistanceFilter#iterate(BitSet, int, int, int, IndexReader)}.
+ * This allows the method to operate in almost total isolation in separate threads.
+ */
+ protected class IterationResult {
+
+ private BitSet bitSet;
+
+ /**
+ * Creates a new IterationResult that wraps the given BitSet
+ *
+ * @param bitSet BitSet to wrap
+ */
+ public IterationResult(BitSet bitSet) {
+ this.bitSet = bitSet;
+ }
+
+ /**
+ * Returns the wrapped BitSet
+ *
+ * @return Wrapped BitSet
+ */
+ public BitSet getBitSet() {
+ return bitSet;
+ }
+ }
+}
Index: contrib/spatial/src/java/org/apache/lucene/spatial/tier/LatLongDistanceFilter.java
===================================================================
--- contrib/spatial/src/java/org/apache/lucene/spatial/tier/LatLongDistanceFilter.java (revision 833867)
+++ contrib/spatial/src/java/org/apache/lucene/spatial/tier/LatLongDistanceFilter.java Fri Dec 18 14:53:45 CET 2009
@@ -29,7 +29,10 @@
* <p><font color="red"><b>NOTE:</b> This API is still in
* flux and might change in incompatible ways in the next
* release.</font>
+ *
+ * @deprecated This class has been made replaced by DistanceFilter and LatLngLocationDataSetFactory. It will be removed.
*/
+@Deprecated
public class LatLongDistanceFilter extends DistanceFilter {
/**
Index: contrib/spatial/src/java/org/apache/lucene/spatial/tier/DistanceFilter.java
===================================================================
--- contrib/spatial/src/java/org/apache/lucene/spatial/tier/DistanceFilter.java (revision 833867)
+++ contrib/spatial/src/java/org/apache/lucene/spatial/tier/DistanceFilter.java Fri Dec 18 14:53:40 CET 2009
@@ -29,7 +29,10 @@
* <p><font color="red"><b>NOTE:</b> This API is still in
* flux and might change in incompatible ways in the next
* release.</font>
+ *
+ * @deprecated This class has been replaced by DistanceFilter and its implementations. It will be removed.
*/
+@Deprecated
public abstract class DistanceFilter extends Filter {
final protected Filter startingFilter;
Index: contrib/spatial/src/test/org/apache/lucene/spatial/distance/LatLngPointDecoderTest.java
===================================================================
--- contrib/spatial/src/test/org/apache/lucene/spatial/distance/LatLngPointDecoderTest.java Wed Jan 20 15:08:54 CET 2010
+++ contrib/spatial/src/test/org/apache/lucene/spatial/distance/LatLngPointDecoderTest.java Wed Jan 20 15:08:54 CET 2010
@@ -0,0 +1,55 @@
+/** Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.lucene.spatial.distance;
+
+import junit.framework.TestCase;
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.spatial.geometry.Point;
+
+import java.io.IOException;
+
+/**
+ * Tests for {@link org.apache.lucene.spatial.distance.LatLngPointDecoder}
+ */
+public class LatLngPointDecoderTest extends TestCase {
+
+ /**
+ * Pass condition: The latitude and longitude for document 0 is correctly decoded from the lat and lng fields
+ *
+ * @throws IOException Can be thrown while interacting with the index
+ */
+ public void testGetPoint() throws IOException {
+ LatLngPointDecoder pointDecoder = new LatLngPointDecoder("lat", "lng") {
+
+ @Override
+ double[] getFieldValues(String field, IndexReader indexReader) {
+ if ("lat".equals(field)) {
+ return new double[]{40.3, 54.2};
+ } else if ("lng".equals(field)) {
+ return new double[]{3.21, 9.65};
+ } else {
+ throw new IllegalArgumentException("Unexpected field name: " + field);
+ }
+ }
+ };
+
+ Point point = pointDecoder.getPoint(0, null);
+
+ assertEquals(40.3, point.getX(), 0D);
+ assertEquals(3.21, point.getY(), 0D);
+ }
+}
Index: contrib/spatial/src/java/org/apache/lucene/spatial/distance/DocumentDistanceSource.java
===================================================================
--- contrib/spatial/src/java/org/apache/lucene/spatial/distance/DocumentDistanceSource.java Wed Jan 20 15:05:37 CET 2010
+++ contrib/spatial/src/java/org/apache/lucene/spatial/distance/DocumentDistanceSource.java Wed Jan 20 15:05:37 CET 2010
@@ -0,0 +1,25 @@
+package org.apache.lucene.spatial.distance;
+
+import org.apache.lucene.index.IndexReader;
+import org.apache.lucene.spatial.geometry.DistanceUnits;
+import org.apache.lucene.spatial.geometry.Point;
+
+import java.io.IOException;
+
+/**
+ * Single access point for anything wishing to retrieve the distance a document is from a point.
+ */
+public interface DocumentDistanceSource {
+
+ /**
+ * Retrieves the distance the document with the given id is from the given Point.
+ *
+ * @param docId ID of the document whose distance is to be retrieved
+ * @param src Point from where the distance to the document is to be calculated
+ * @param indexReader IndexReader for interacting with the index
+ * @param units DistanceUnits that the distance should be returned in
+ * @return Distance the document is from the point in the defined units
+ * @throws IOException Can be thrown while interacting with the index
+ */
+ double getDocumentDistance(int docId, Point src, IndexReader indexReader, DistanceUnits units) throws IOException;
+}