LUCENE-1504: switch to DocIdSet API and FilteredDocIdSet for contrib/spatial

git-svn-id: https://svn.apache.org/repos/asf/lucene/java/trunk@800892 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Michael McCandless 2009-08-04 18:01:57 +00:00
parent a4c2eae1d4
commit d79a369414
21 changed files with 376 additions and 880 deletions

View File

@ -28,5 +28,20 @@
<import file="../contrib-build.xml"/> <import file="../contrib-build.xml"/>
<property name="misc.jar" location="${common.dir}/build/contrib/misc/lucene-misc-${version}.jar"/>
<available property="memory.jar.present" type="file" file="${memory.jar}"/>
<path id="classpath">
<pathelement path="${lucene.jar}"/>
<pathelement path="${misc.jar}"/>
<pathelement path="${project.classpath}"/>
</path>
<target name="compile-core" depends="build-misc, common.compile-core" />
<target name="build-misc" unless="memory.jar.present">
<echo>Misc building dependency ${misc.jar}</echo>
<ant antfile="../miscellaneous/build.xml" target="default" inheritall="false" dir="../miscellaneous" />
</target>
</project> </project>

View File

@ -1,34 +0,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.
*/
package org.apache.lucene.spatial;
import java.io.IOException;
import java.util.BitSet;
import org.apache.lucene.index.CorruptIndexException;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.search.Filter;
/**
* Provide an optimized filter, by allowing the bitset from
* previous filters in the bitset to be used in the next part of the chain.
*
*/
public abstract class ISerialChainFilter extends Filter {
public abstract BitSet bits(IndexReader reader, BitSet bits) throws CorruptIndexException, IOException, Exception;
}

View File

@ -1,214 +0,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.
*/
package org.apache.lucene.spatial;
import java.io.IOException;
import java.util.BitSet;
import org.apache.lucene.index.CorruptIndexException;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.search.DocIdSet;
import org.apache.lucene.search.Filter;
import org.apache.lucene.util.DocIdBitSet;
/**
*
* Provide a serial chain filter, passing the bitset in with the
* index reader to each of the filters in an ordered fashion.
*
* Based off chain filter, but with some improvements to allow a narrowed down
* filtering. Traditional filter required iteration through an IndexReader.
*
* By implementing the ISerialChainFilter class, you can create a bits(IndexReader reader, BitSet bits)
* @see org.apache.lucene.search.ISerialChainFilter
*
*/
public class SerialChainFilter extends Filter {
/**
* $Id: SerialChainFilter.java 136 2008-12-17 16:16:38Z ryantxu $
*/
private static final long serialVersionUID = 1L;
private Filter chain[];
public static final int SERIALAND = 1;
public static final int SERIALOR = 2;
public static final int AND = 3; // regular filters may be used first
public static final int OR = 4; // regular filters may be used first
public static final int DEFAULT = SERIALOR;
private int actionType[];
public SerialChainFilter(Filter chain[]){
this.chain = chain;
this.actionType = new int[] {DEFAULT};
}
public SerialChainFilter(Filter chain[], int actionType[]){
this.chain= chain;
this.actionType = actionType;
}
/* (non-Javadoc)
* @see org.apache.lucene.search.Filter#bits(org.apache.lucene.index.IndexReader)
*/
@Override
public BitSet bits(IndexReader reader) throws IOException {
return ((DocIdBitSet)getDocIdSet(reader)).getBitSet();
}
/* (non-Javadoc)
* @see org.apache.lucene.search.Filter#getDocIdSet(org.apache.lucene.index.IndexReader)
*/
@Override
public DocIdSet getDocIdSet(IndexReader reader) throws CorruptIndexException, IOException {
BitSet bits = new BitSet(reader.maxDoc());
int chainSize = chain.length;
int actionSize = actionType.length;
int i = 0;
/**
* taken from ChainedFilter, first and on an empty bitset results in 0
*/
if (actionType[i] == AND){
try {
//System.out.println(chain[i] );
bits = (BitSet) ((DocIdBitSet)chain[i].getDocIdSet(reader)).getBitSet().clone();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
++i;
}
for( ; i < chainSize; i++) {
int action = (i < actionSize)? actionType[i]: DEFAULT;
//System.out.println(chain[i] + ": "+ action);
switch (action){
case (SERIALAND):
try {
bits.and(((ISerialChainFilter) chain[i]).bits(reader, bits));
} catch (CorruptIndexException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
break;
case (SERIALOR):
try {
bits.or(((ISerialChainFilter) chain[i]).bits(reader,bits));
} catch (Exception e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
break;
case (AND):
bits.and(chain[i].bits(reader));
break;
case (OR):
bits.or(((DocIdBitSet)chain[i].getDocIdSet(reader)).getBitSet());
break;
}
}
// System.out.println("++++++====================");
// new Exception().printStackTrace();
return new DocIdBitSet(bits);
}
/**
* @return the chain
*/
Filter[] getChain() {
return chain;
}
/**
* @return the actionType
*/
int[] getActionType() {
return actionType;
}
/**
* Returns true if <code>o</code> is equal to this.
*
* @see org.apache.lucene.search.RangeFilter#equals
*/
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof SerialChainFilter)) return false;
SerialChainFilter other = (SerialChainFilter) o;
if (this.chain.length != other.getChain().length ||
this.actionType.length != other.getActionType().length)
return false;
for (int i = 0; i < this.chain.length; i++) {
if (this.actionType[i] != other.getActionType()[i] ||
(!this.chain[i].equals(other.getChain()[i])))
return false;
}
return true;
}
/**
* Returns a hash code value for this object.
*
* @see org.apache.lucene.search.RangeFilter#hashCode
*/
@Override
public int hashCode() {
if (chain.length == 0)
return 0;
int h = chain[0].hashCode() ^ new Integer(actionType[0]).hashCode();
for (int i = 1; i < this.chain.length; i++) {
h ^= chain[i].hashCode();
h ^= new Integer(actionType[i]).hashCode();
}
return h;
}
@Override
public String toString() {
StringBuffer buf = new StringBuffer();
buf.append("SerialChainFilter(");
for (int i = 0; i < chain.length; i++) {
switch(actionType[i]) {
case (SERIALAND): buf.append("SERIALAND"); break;
case (SERIALOR): buf.append("SERIALOR"); break;
case (AND): buf.append("AND"); break;
case (OR): buf.append("OR"); break;
default: buf.append(actionType[i]);
}
buf.append(" " + chain[i].toString() + " ");
}
return buf.toString().trim() + ")";
}
}

View File

@ -18,17 +18,14 @@
package org.apache.lucene.spatial.geohash; package org.apache.lucene.spatial.geohash;
import java.io.IOException; import java.io.IOException;
import java.util.BitSet;
import java.util.HashMap;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.logging.Logger;
import org.apache.lucene.index.IndexReader; import org.apache.lucene.index.IndexReader;
import org.apache.lucene.search.FieldCache; import org.apache.lucene.search.FieldCache;
import org.apache.lucene.search.Filter;
import org.apache.lucene.search.DocIdSet;
import org.apache.lucene.search.FilteredDocIdSet;
import org.apache.lucene.spatial.tier.DistanceFilter; import org.apache.lucene.spatial.tier.DistanceFilter;
import org.apache.lucene.spatial.tier.DistanceUtils; import org.apache.lucene.spatial.tier.DistanceUtils;
import org.apache.lucene.spatial.tier.DistanceHandler.Precision;
@ -39,167 +36,62 @@ public class GeoHashDistanceFilter extends DistanceFilter {
*/ */
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
private double distance;
private double lat; private double lat;
private double lng; private double lng;
private String geoHashField; private String geoHashField;
private Logger log = Logger.getLogger(getClass().getName());
private Map<Integer,Double> distances = null;
private Precision precise = null;
int offset = 0;
int nextOffset;
/** /**
* Provide a distance filter based from a center point with a radius * Provide a distance filter based from a center point with a radius
* in miles * in miles
* @param startingFilter
* @param lat * @param lat
* @param lng * @param lng
* @param miles * @param miles
* @param latField
* @param lngField
*/ */
public GeoHashDistanceFilter(double lat, double lng, double miles, String geoHashField){ public GeoHashDistanceFilter(Filter startingFilter, double lat, double lng, double miles, String geoHashField) {
distance = miles; super(startingFilter, miles);
this.lat = lat; this.lat = lat;
this.lng = lng; this.lng = lng;
this.geoHashField = geoHashField; this.geoHashField = geoHashField;
}
public Map<Integer,Double> getDistances(){
return distances;
}
public Double getDistance(int docid){
return distances.get(docid);
} }
@Override @Override
public BitSet bits(IndexReader reader) throws IOException { public DocIdSet getDocIdSet(IndexReader reader) throws IOException {
/* Create a BitSet to store the result */ final String[] geoHashValues = FieldCache.DEFAULT.getStrings(reader, geoHashField);
int maxdocs = reader.numDocs();
BitSet bits = new BitSet(maxdocs);
setPrecision(maxdocs); final int docBase = nextDocBase;
// create an intermediate cache to avoid recomputing nextDocBase += reader.maxDoc();
// distances for the same point
// TODO: Why is this a WeakHashMap?
WeakHashMap<String,Double> cdistance = new WeakHashMap<String,Double>(maxdocs);
String[] geoHashCache = FieldCache.DEFAULT.getStrings(reader, geoHashField); return new FilteredDocIdSet(startingFilter.getDocIdSet(reader)) {
public boolean match(int doc) {
String geoHash = geoHashValues[doc];
/* store calculated distances for reuse by other components */
distances = new HashMap<Integer,Double>(maxdocs);
for (int i = 0 ; i < maxdocs; i++) {
String geoHash = geoHashCache[i];
double[] coords = GeoHashUtils.decode(geoHash); double[] coords = GeoHashUtils.decode(geoHash);
double x = coords[0]; double x = coords[0];
double y = coords[1]; double y = coords[1];
// round off lat / longs if necessary // round off lat / longs if necessary
// x = DistanceHandler.getPrecision(x, precise); // x = DistanceHandler.getPrecision(x, precise);
// y = DistanceHandler.getPrecision(y, precise); // y = DistanceHandler.getPrecision(y, precise);
Double cachedDistance = distanceLookupCache.get(geoHash);
Double cachedDistance = cdistance.get(geoHash);
double d; double d;
if(cachedDistance != null){ if (cachedDistance != null) {
d = cachedDistance.doubleValue(); d = cachedDistance.doubleValue();
} else { } else {
d = DistanceUtils.getInstance().getDistanceMi(lat, lng, x, y); d = DistanceUtils.getInstance().getDistanceMi(lat, lng, x, y);
cdistance.put(geoHash, d); distanceLookupCache.put(geoHash, d);
} }
distances.put(i, d);
if (d < distance){ if (d < distance){
bits.set(i); distances.put(doc+docBase, d);
} return true;
}
return bits;
}
@Override
public BitSet bits(IndexReader reader, BitSet bits) throws Exception {
/* Create a BitSet to store the result */
int size = bits.cardinality();
BitSet result = new BitSet(size);
/* create an intermediate cache to avoid recomputing
distances for the same point */
HashMap<String,Double> cdistance = new HashMap<String,Double>(size);
/* store calculated distances for reuse by other components */
offset += reader.maxDoc();
if (distances == null)
distances = new HashMap<Integer,Double>(size);
long start = System.currentTimeMillis();
String[] geoHashCache = FieldCache.DEFAULT.getStrings(reader, geoHashField);
/* loop over all set bits (hits from the boundary box filters) */
int i = bits.nextSetBit(0);
while (i >= 0){
// if we have a completed
// filter chain, lat / lngs can be retrived from
// memory rather than document base.
String geoHash = geoHashCache[i];
double[] coords = GeoHashUtils.decode(geoHash);
double x = coords[0];
double y = coords[1];
// round off lat / longs if necessary
// x = DistanceHandler.getPrecision(x, precise);
// y = DistanceHandler.getPrecision(y, precise);
Double cachedDistance = cdistance.get(geoHash);
double d;
if(cachedDistance != null){
d = cachedDistance.doubleValue();
} else { } else {
d = DistanceUtils.getInstance().getDistanceMi(lat, lng, x, y); return false;
//d = DistanceUtils.getLLMDistance(lat, lng, x, y);
cdistance.put(geoHash, d);
} }
distances.put(i, d);
if (d < distance){
result.set(i);
} }
i = bits.nextSetBit(i+1); };
}
long end = System.currentTimeMillis();
log.fine("Time taken : "+ (end - start) +
", results : "+ distances.size() +
", cached : "+ cdistance.size() +
", incoming size: "+ size);
cdistance = null;
nextOffset += offset;
return result;
} }
/** Returns true if <code>o</code> is equal to this. */ /** Returns true if <code>o</code> is equal to this. */
@ -209,7 +101,8 @@ public class GeoHashDistanceFilter extends DistanceFilter {
if (!(o instanceof GeoHashDistanceFilter)) return false; if (!(o instanceof GeoHashDistanceFilter)) return false;
GeoHashDistanceFilter other = (GeoHashDistanceFilter) o; GeoHashDistanceFilter other = (GeoHashDistanceFilter) o;
if (this.distance != other.distance || if (!this.startingFilter.equals(other.startingFilter) ||
this.distance != other.distance ||
this.lat != other.lat || this.lat != other.lat ||
this.lng != other.lng || this.lng != other.lng ||
!this.geoHashField.equals(other.geoHashField) ) { !this.geoHashField.equals(other.geoHashField) ) {
@ -222,26 +115,11 @@ public class GeoHashDistanceFilter extends DistanceFilter {
@Override @Override
public int hashCode() { public int hashCode() {
int h = new Double(distance).hashCode(); int h = new Double(distance).hashCode();
h ^= startingFilter.hashCode();
h ^= new Double(lat).hashCode(); h ^= new Double(lat).hashCode();
h ^= new Double(lng).hashCode(); h ^= new Double(lng).hashCode();
h ^= geoHashField.hashCode(); h ^= geoHashField.hashCode();
return h; return h;
} }
private void setPrecision(int maxDocs) {
precise = Precision.EXACT;
if (maxDocs > 1000 && distance > 10) {
precise = Precision.TWENTYFEET;
}
if (maxDocs > 10000 && distance > 10){
precise = Precision.TWOHUNDREDFEET;
}
}
public void setDistances(Map<Integer, Double> distances) {
this.distances = distances;
}
} }

View File

@ -46,9 +46,6 @@ public class CartesianPoint {
/** /**
* Return a new point translated in the x and y dimensions * Return a new point translated in the x and y dimensions
* @param i
* @param translation
* @return
*/ */
public CartesianPoint translate(int deltaX, int deltaY) { public CartesianPoint translate(int deltaX, int deltaY) {
return new CartesianPoint(this.x+deltaX, this.y+deltaY); return new CartesianPoint(this.x+deltaX, this.y+deltaY);

View File

@ -22,7 +22,6 @@ package org.apache.lucene.spatial.geometry;
* Abstract base lat-lng class which can manipulate fixed point or floating * Abstract base lat-lng class which can manipulate fixed point or floating
* point based coordinates. Instances are immutable. * point based coordinates. Instances are immutable.
* *
* @see FixedLatLngTest
* @see FloatLatLng * @see FloatLatLng
* *
*/ */
@ -62,8 +61,6 @@ public abstract class LatLng {
* The x dimension corresponds to latitude and y corresponds to longitude. * The x dimension corresponds to latitude and y corresponds to longitude.
* The translation starts with the normalized latlng and adds 180 to the latitude and * The translation starts with the normalized latlng and adds 180 to the latitude and
* 90 to the longitude (subject to fixed point scaling). * 90 to the longitude (subject to fixed point scaling).
*
* @return
*/ */
public CartesianPoint toCartesian() { public CartesianPoint toCartesian() {
LatLng ll=normalize(); LatLng ll=normalize();
@ -80,7 +77,6 @@ public abstract class LatLng {
/** /**
* The inverse of toCartesian(). Always returns a FixedLatLng. * The inverse of toCartesian(). Always returns a FixedLatLng.
* @param pt * @param pt
* @return
*/ */
public static LatLng fromCartesian(CartesianPoint pt) { public static LatLng fromCartesian(CartesianPoint pt) {
int lat=pt.getY() - 90 * FixedLatLng.SCALE_FACTOR_INT; int lat=pt.getY() - 90 * FixedLatLng.SCALE_FACTOR_INT;
@ -158,7 +154,6 @@ public abstract class LatLng {
/** /**
* Calculate the midpoint between this point an another. Respects fixed vs floating point * Calculate the midpoint between this point an another. Respects fixed vs floating point
* @param other * @param other
* @return
*/ */
public abstract LatLng calculateMidpoint(LatLng other); public abstract LatLng calculateMidpoint(LatLng other);
} }

View File

@ -56,9 +56,6 @@ public class Ellipse implements Geometry2D {
/** /**
* Constructor given bounding rectangle and a rotation. * Constructor given bounding rectangle and a rotation.
*
* @param
* @param
*/ */
public Ellipse(Point2D p1, Point2D p2, double angle) { public Ellipse(Point2D p1, Point2D p2, double angle) {
center = new Point2D(); center = new Point2D();

View File

@ -31,26 +31,22 @@ public interface Geometry2D {
/** /**
* Does the shape contain the given point * Does the shape contain the given point
* @param p * @param p
* @return
*/ */
public boolean contains(Point2D p); public boolean contains(Point2D p);
/** /**
* Return the area * Return the area
* @return
*/ */
public double area(); public double area();
/** /**
* Return the centroid * Return the centroid
* @return
*/ */
public Point2D centroid(); public Point2D centroid();
/** /**
* Returns information about how this shape intersects the given rectangle * Returns information about how this shape intersects the given rectangle
* @param r * @param r
* @return
*/ */
public IntersectCase intersect(Rectangle r); public IntersectCase intersect(Rectangle r);

View File

@ -41,7 +41,6 @@ public class LLRect {
/** /**
* Return the area in units of lat-lng squared. This is a contrived unit * Return the area in units of lat-lng squared. This is a contrived unit
* that only has value when comparing to something else. * that only has value when comparing to something else.
* @return
*/ */
public double area() { public double area() {
return Math.abs((ll.getLat()-ur.getLat()) * (ll.getLng()-ur.getLng())); return Math.abs((ll.getLat()-ur.getLat()) * (ll.getLng()-ur.getLng()));
@ -79,7 +78,6 @@ public class LLRect {
* @param center * @param center
* @param widthMi * @param widthMi
* @param heightMi * @param heightMi
* @return
*/ */
public static LLRect createBox(LatLng center, double widthMi, double heightMi) { public static LLRect createBox(LatLng center, double widthMi, double heightMi) {
double miplatdeg=DistanceApproximation.getMilesPerLngDeg(center.getLat()); double miplatdeg=DistanceApproximation.getMilesPerLngDeg(center.getLat());
@ -97,7 +95,6 @@ public class LLRect {
/** /**
* Returns a rectangle shape for the bounding box * Returns a rectangle shape for the bounding box
* @return
*/ */
public Rectangle toRectangle() { public Rectangle toRectangle() {
return new Rectangle(ll.getLng(), ll.getLat(), ur.getLng(), ur.getLat()); return new Rectangle(ll.getLng(), ll.getLat(), ur.getLng(), ur.getLat());

View File

@ -18,7 +18,6 @@
package org.apache.lucene.spatial.tier; package org.apache.lucene.spatial.tier;
import java.io.IOException; import java.io.IOException;
import java.util.BitSet;
import java.util.logging.Logger; import java.util.logging.Logger;
import org.apache.lucene.index.IndexReader; import org.apache.lucene.index.IndexReader;
@ -26,7 +25,9 @@ import org.apache.lucene.index.Term;
import org.apache.lucene.index.TermDocs; import org.apache.lucene.index.TermDocs;
import org.apache.lucene.index.TermEnum; import org.apache.lucene.index.TermEnum;
import org.apache.lucene.search.Filter; import org.apache.lucene.search.Filter;
import org.apache.lucene.search.DocIdSet;
import org.apache.lucene.util.NumericUtils; import org.apache.lucene.util.NumericUtils;
import org.apache.lucene.util.OpenBitSet;
@ -84,15 +85,15 @@ public class BoundaryBoxFilter extends Filter {
/** /**
* Returns a BitSet with true for documents which should be * Returns a DocIdSet with true for documents which should be
* permitted in search results, and false for those that should * permitted in search results, and false for those that should
* not. * not.
*/ */
@Override @Override
public BitSet bits(IndexReader reader) throws IOException { public DocIdSet getDocIdSet(IndexReader reader) throws IOException {
long start = System.currentTimeMillis(); long start = System.currentTimeMillis();
BitSet bits = new BitSet(reader.maxDoc()); OpenBitSet bits = new OpenBitSet(reader.maxDoc());
TermEnum enumerator = TermEnum enumerator =
(null != lowerTerm (null != lowerTerm
? reader.terms(new Term(fieldName, lowerTerm)) ? reader.terms(new Term(fieldName, lowerTerm))
@ -128,7 +129,7 @@ public class BoundaryBoxFilter extends Filter {
// we have a good term, find the docs // we have a good term, find the docs
termDocs.seek(enumerator.term()); termDocs.seek(enumerator.term());
while (termDocs.next()) { while (termDocs.next()) {
bits.set(termDocs.doc()); bits.fastSet(termDocs.doc());
} }
} }
} }

View File

@ -33,6 +33,12 @@ import org.apache.lucene.spatial.tier.projections.SinusoidalProjector;
*/ */
public class CartesianPolyFilterBuilder { public class CartesianPolyFilterBuilder {
// Finer granularity than 1 mile isn't accurate with
// standard java math. Also, there's already a 2nd
// precise filter, if needed, in DistanceQueryBuilder,
// that will make the filtering exact.
public static final double MILES_FLOOR = 1.0;
private IProjector projector = new SinusoidalProjector(); private IProjector projector = new SinusoidalProjector();
private Logger log = Logger.getLogger(getClass().getName()); private Logger log = Logger.getLogger(getClass().getName());
@ -42,10 +48,12 @@ public class CartesianPolyFilterBuilder {
this.tierPrefix = tierPrefix; this.tierPrefix = tierPrefix;
} }
public Shape getBoxShape(double latitude, double longitude, int miles) public Shape getBoxShape(double latitude, double longitude, double miles)
{ {
if (miles < MILES_FLOOR) {
miles = MILES_FLOOR;
}
Rectangle box = DistanceUtils.getInstance().getBoundary(latitude, longitude, miles); Rectangle box = DistanceUtils.getInstance().getBoundary(latitude, longitude, miles);
double latY = box.getMaxPoint().getY();//box.getY(); double latY = box.getMaxPoint().getY();//box.getY();
double latX = box.getMinPoint().getY() ; //box.getMaxY(); double latX = box.getMinPoint().getY() ; //box.getMaxY();
@ -104,7 +112,7 @@ public class CartesianPolyFilterBuilder {
return shape; return shape;
} }
public Filter getBoundingArea(double latitude, double longitude, int miles) public Filter getBoundingArea(double latitude, double longitude, double miles)
{ {
Shape shape = getBoxShape(latitude, longitude, miles); Shape shape = getBoxShape(latitude, longitude, miles);
return new CartesianShapeFilter(shape, shape.getTierId()); return new CartesianShapeFilter(shape, shape.getTierId());

View File

@ -17,7 +17,6 @@
package org.apache.lucene.spatial.tier; package org.apache.lucene.spatial.tier;
import java.io.IOException; import java.io.IOException;
import java.util.BitSet;
import java.util.List; import java.util.List;
import java.util.logging.Logger; import java.util.logging.Logger;
@ -25,7 +24,9 @@ import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.Term; import org.apache.lucene.index.Term;
import org.apache.lucene.index.TermDocs; import org.apache.lucene.index.TermDocs;
import org.apache.lucene.search.Filter; import org.apache.lucene.search.Filter;
import org.apache.lucene.search.DocIdSet;
import org.apache.lucene.util.NumericUtils; import org.apache.lucene.util.NumericUtils;
import org.apache.lucene.util.OpenBitSet;
public class CartesianShapeFilter extends Filter { public class CartesianShapeFilter extends Filter {
@ -43,10 +44,10 @@ public class CartesianShapeFilter extends Filter {
} }
@Override @Override
public BitSet bits(IndexReader reader) throws IOException { public DocIdSet getDocIdSet(IndexReader reader) throws IOException {
long start = System.currentTimeMillis(); long start = System.currentTimeMillis();
BitSet bits = new BitSet(reader.maxDoc()); OpenBitSet bits = new OpenBitSet(reader.maxDoc());
TermDocs termDocs = reader.termDocs(); TermDocs termDocs = reader.termDocs();
List<Double> area = shape.getArea(); List<Double> area = shape.getArea();
@ -62,7 +63,7 @@ public class CartesianShapeFilter extends Filter {
// iterate through all documents // iterate through all documents
// which have this boxId // which have this boxId
while (termDocs.next()) { while (termDocs.next()) {
bits.set(termDocs.doc()); bits.fastSet(termDocs.doc());
} }
} }
@ -70,5 +71,4 @@ public class CartesianShapeFilter extends Filter {
log.fine("BoundaryBox Time Taken: "+ (end - start) + " found: "+bits.cardinality()+" candidates"); log.fine("BoundaryBox Time Taken: "+ (end - start) + " found: "+bits.cardinality()+" candidates");
return bits; return bits;
} }
} }

View File

@ -16,38 +16,81 @@
package org.apache.lucene.spatial.tier; package org.apache.lucene.spatial.tier;
import java.io.IOException;
import java.util.BitSet;
import java.util.Map; import java.util.Map;
import java.util.WeakHashMap;
import java.util.HashMap;
import org.apache.lucene.index.IndexReader; import org.apache.lucene.search.Filter;
import org.apache.lucene.spatial.ISerialChainFilter; import org.apache.lucene.spatial.tier.DistanceHandler.Precision;
public abstract class DistanceFilter extends Filter {
public abstract class DistanceFilter extends ISerialChainFilter { final protected Filter startingFilter;
protected Precision precise;
protected Map<Integer,Double> distances;
protected double distance;
public DistanceFilter() { protected int nextDocBase;
super(); protected final WeakHashMap<String,Double> distanceLookupCache;
/** Filters the startingFilter by precise distance
* checking filter */
public DistanceFilter(Filter startingFilter, double distance) {
if (startingFilter == null) {
throw new IllegalArgumentException("please provide a non-null startingFilter; you can use QueryWrapperFilter(MatchAllDocsQuery) as a no-op filter");
}
this.startingFilter = startingFilter;
this.distance = distance;
// NOTE: neither of the distance filters use precision
// now - if we turn that on, we'll need to pass top
// reader into here
// setPrecision(reader.maxDoc());
/* store calculated distances for reuse by other components */
distances = new HashMap<Integer,Double>();
// create an intermediate cache to avoid recomputing
// distances for the same point
// TODO: Why is this a WeakHashMap?
distanceLookupCache = new WeakHashMap<String,Double>();
} }
public abstract Map<Integer,Double> getDistances(); public Map<Integer,Double> getDistances(){
return distances;
}
public abstract Double getDistance(int docid); public Double getDistance(int docid){
return distances.get(docid);
}
@Override public void setDistances(Map<Integer, Double> distances) {
public abstract BitSet bits(IndexReader reader) throws IOException; this.distances = distances;
}
@Override /** You must call this before re-using this DistanceFilter
public abstract BitSet bits(IndexReader reader, BitSet bits) throws Exception; * across searches */
public void reset() {
nextDocBase = 0;
}
/** Returns true if <code>o</code> is equal to this. */ /** Returns true if <code>o</code> is equal to this. */
@Override
public abstract boolean equals(Object o); public abstract boolean equals(Object o);
/** Returns a hash code value for this object.*/ /** Returns a hash code value for this object.*/
@Override
public abstract int hashCode(); public abstract int hashCode();
public abstract void setDistances(Map<Integer, Double> distances); /*
private void setPrecision(int maxDocs) {
precise = Precision.EXACT;
if (maxDocs > 1000 && distance > 10) {
precise = Precision.TWENTYFEET;
}
if (maxDocs > 10000 && distance > 10){
precise = Precision.TWOHUNDREDFEET;
}
}
*/
} }

View File

@ -21,8 +21,8 @@ import org.apache.lucene.search.ConstantScoreQuery;
import org.apache.lucene.search.Filter; import org.apache.lucene.search.Filter;
import org.apache.lucene.search.Query; import org.apache.lucene.search.Query;
import org.apache.lucene.search.QueryWrapperFilter; import org.apache.lucene.search.QueryWrapperFilter;
import org.apache.lucene.spatial.SerialChainFilter;
import org.apache.lucene.spatial.geohash.GeoHashDistanceFilter; import org.apache.lucene.spatial.geohash.GeoHashDistanceFilter;
import org.apache.lucene.misc.ChainedFilter;
public class DistanceQueryBuilder { public class DistanceQueryBuilder {
@ -31,39 +31,39 @@ public class DistanceQueryBuilder {
public BoundaryBoxFilter latFilter; public BoundaryBoxFilter latFilter;
public BoundaryBoxFilter lngFilter; public BoundaryBoxFilter lngFilter;
public DistanceFilter distanceFilter;
private final double lat; private final double lat;
private final double lng; private final double lng;
private final double miles; private final double miles;
private Filter cartesianFilter; private final Filter filter;
private boolean needPrecision = true; final DistanceFilter distanceFilter;
/** /**
* Create a distance query using * Create a distance query using
* a boundary box wrapper around a more precise * a boundary box wrapper around a more precise
* DistanceFilter. * DistanceFilter.
* *
* @see SerialChainFilter
* @param lat * @param lat
* @param lng * @param lng
* @param miles * @param miles
*/ */
public DistanceQueryBuilder (double lat, double lng, double miles, public DistanceQueryBuilder (double lat, double lng, double miles,
String latField, String lngField, String tierFieldPrefix,boolean needPrecise){ String latField, String lngField, String tierFieldPrefix, boolean needPrecise) {
this.lat = lat; this.lat = lat;
this.lng = lng; this.lng = lng;
this.miles = miles; this.miles = miles;
this.needPrecision = needPrecise;
CartesianPolyFilterBuilder cpf = new CartesianPolyFilterBuilder(tierFieldPrefix); CartesianPolyFilterBuilder cpf = new CartesianPolyFilterBuilder(tierFieldPrefix);
cartesianFilter = cpf.getBoundingArea(lat, lng, (int)miles); Filter cartesianFilter = cpf.getBoundingArea(lat, lng, miles);
/* create precise distance filter */ /* create precise distance filter */
if( needPrecise) if (needPrecise) {
distanceFilter = new LatLongDistanceFilter(lat, lng, miles, latField, lngField); filter = distanceFilter = new LatLongDistanceFilter(cartesianFilter, lat, lng, miles, latField, lngField);
} else {
filter = cartesianFilter;
distanceFilter = null;
}
} }
/** /**
@ -71,26 +71,27 @@ public class DistanceQueryBuilder {
* a boundary box wrapper around a more precise * a boundary box wrapper around a more precise
* DistanceFilter. * DistanceFilter.
* *
* @see SerialChainFilter
* @param lat * @param lat
* @param lng * @param lng
* @param miles * @param miles
*/ */
public DistanceQueryBuilder (double lat, double lng, double miles, public DistanceQueryBuilder (double lat, double lng, double miles,
String geoHashFieldPrefix, String tierFieldPrefix,boolean needPrecise){ String geoHashFieldPrefix, String tierFieldPrefix, boolean needPrecise){
this.lat = lat; this.lat = lat;
this.lng = lng; this.lng = lng;
this.miles = miles; this.miles = miles;
this.needPrecision = needPrecise;
CartesianPolyFilterBuilder cpf = new CartesianPolyFilterBuilder(tierFieldPrefix); CartesianPolyFilterBuilder cpf = new CartesianPolyFilterBuilder(tierFieldPrefix);
cartesianFilter = cpf.getBoundingArea(lat, lng, (int)miles); Filter cartesianFilter = cpf.getBoundingArea(lat, lng, miles);
/* create precise distance filter */ /* create precise distance filter */
if( needPrecise) if (needPrecise) {
distanceFilter = new GeoHashDistanceFilter(lat, lng, miles, geoHashFieldPrefix); filter = distanceFilter = new GeoHashDistanceFilter(cartesianFilter, lat, lng, miles, geoHashFieldPrefix);
} else {
filter = cartesianFilter;
distanceFilter = null;
}
} }
@ -98,50 +99,23 @@ public class DistanceQueryBuilder {
* Create a distance query using * Create a distance query using
* a boundary box wrapper around a more precise * a boundary box wrapper around a more precise
* DistanceFilter. * DistanceFilter.
*
* @see SerialChainFilter
* @param lat
* @param lng
* @param miles
*/ */
public Filter getFilter() { public Filter getFilter() {
Filter [] f; if (distanceFilter != null) {
int [] chain; distanceFilter.reset();
if (needPrecision){
f = new Filter[]{cartesianFilter, distanceFilter};
chain = new int[] {SerialChainFilter.AND,
SerialChainFilter.SERIALAND};
}else{
f= new Filter[]{cartesianFilter};
chain = new int[] {SerialChainFilter.AND};
} }
return new SerialChainFilter( f, chain ); return filter;
} }
public Filter getFilter(Query query) { public Filter getFilter(Query query) {
// Chain the Query (as filter) with our distance filter
if (distanceFilter != null) {
distanceFilter.reset();
}
QueryWrapperFilter qf = new QueryWrapperFilter(query); QueryWrapperFilter qf = new QueryWrapperFilter(query);
return new ChainedFilter(new Filter[] {qf, filter},
Filter [] f; ChainedFilter.AND);
int [] chain;
if (needPrecision){
f = new Filter[]{cartesianFilter, qf, distanceFilter};
chain = new int[] {SerialChainFilter.AND,
SerialChainFilter.AND,
SerialChainFilter.SERIALAND};
}else{
f= new Filter[]{cartesianFilter, qf};
chain = new int[] {SerialChainFilter.AND,
SerialChainFilter.AND};
} }
return new SerialChainFilter(f,chain);
}
// public Query getQuery() {
// return new ConstantScoreQuery(getFilter());
// }
public Query getQuery(Query query){ public Query getQuery(Query query){
return new ConstantScoreQuery(getFilter(query)); return new ConstantScoreQuery(getFilter(query));

View File

@ -71,19 +71,10 @@ public class DistanceSortSource implements SortComparatorSource {
public int compare(ScoreDoc aDoc, ScoreDoc bDoc) { public int compare(ScoreDoc aDoc, ScoreDoc bDoc) {
// if (this.distances == null) {
// distances = distanceFilter.getDistances();
// }
//System.out.println("comparing : "+ aDoc.doc+ " - "+ bDoc.doc);
try {
double a = distanceFilter.getDistance(aDoc.doc); double a = distanceFilter.getDistance(aDoc.doc);
double b = distanceFilter.getDistance(bDoc.doc); double b = distanceFilter.getDistance(bDoc.doc);
if (a > b) return 1; if (a > b) return 1;
if (a < b )return -1; if (a < b) return -1;
} catch (Exception e){
System.out.println(" Failed with sort with "+ aDoc.doc +" - "+bDoc.doc);
}
return 0; return 0;
} }

View File

@ -18,19 +18,11 @@
package org.apache.lucene.spatial.tier; package org.apache.lucene.spatial.tier;
import java.io.IOException; import java.io.IOException;
import java.util.BitSet;
import java.util.HashMap;
import java.util.Map;
import java.util.WeakHashMap;
import java.util.logging.Logger;
import org.apache.lucene.index.IndexReader; import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.TermDocs; import org.apache.lucene.search.FilteredDocIdSet;
import org.apache.lucene.search.FieldCache; import org.apache.lucene.search.FieldCache;
import org.apache.lucene.util.NumericUtils; import org.apache.lucene.search.Filter;
import org.apache.lucene.spatial.tier.DistanceHandler.Precision; import org.apache.lucene.search.DocIdSet;
public class LatLongDistanceFilter extends DistanceFilter { public class LatLongDistanceFilter extends DistanceFilter {
@ -40,192 +32,70 @@ public class LatLongDistanceFilter extends DistanceFilter {
*/ */
private static final long serialVersionUID = 1L; private static final long serialVersionUID = 1L;
double distance;
double lat; double lat;
double lng; double lng;
String latField; String latField;
String lngField; String lngField;
Logger log = Logger.getLogger(getClass().getName());
int nextOffset = 0; int nextOffset = 0;
Map<Integer,Double> distances = null;
private Precision precise = null;
/** /**
* Provide a distance filter based from a center point with a radius * Provide a distance filter based from a center point with a radius
* in miles * in miles.
* @param startingFilter Filter to start from
* @param lat * @param lat
* @param lng * @param lng
* @param miles * @param miles
* @param latField * @param latField
* @param lngField * @param lngField
*/ */
public LatLongDistanceFilter(double lat, double lng, double miles, String latField, String lngField){ public LatLongDistanceFilter(Filter startingFilter, double lat, double lng, double miles, String latField, String lngField) {
distance = miles; super(startingFilter, miles);
this.lat = lat; this.lat = lat;
this.lng = lng; this.lng = lng;
this.latField = latField; this.latField = latField;
this.lngField = lngField; this.lngField = lngField;
} }
public Map<Integer,Double> getDistances(){
return distances;
}
public Double getDistance(int docid){
return distances.get(docid);
}
@Override @Override
public BitSet bits(IndexReader reader) throws IOException { public DocIdSet getDocIdSet(IndexReader reader) throws IOException {
/* Create a BitSet to store the result */ final double[] latIndex = FieldCache.DEFAULT.getDoubles(reader, latField);
int maxdocs = reader.maxDoc(); final double[] lngIndex = FieldCache.DEFAULT.getDoubles(reader, lngField);
BitSet bits = new BitSet(maxdocs);
setPrecision(maxdocs); final int docBase = nextDocBase;
// create an intermediate cache to avoid recomputing nextDocBase += reader.maxDoc();
// distances for the same point
// TODO: Why is this a WeakHashMap?
WeakHashMap<String,Double> cdistance = new WeakHashMap<String,Double>(maxdocs);
long start = System.currentTimeMillis();
double[] latIndex = FieldCache.DEFAULT.getDoubles(reader, latField);
double[] lngIndex = FieldCache.DEFAULT.getDoubles(reader, lngField);
/* store calculated distances for reuse by other components */
distances = new HashMap<Integer,Double>(maxdocs);
if (distances == null){
distances = new HashMap<Integer,Double>();
}
TermDocs td = reader.termDocs(null);
while(td.next()) {
int doc = td.doc();
return new FilteredDocIdSet(startingFilter.getDocIdSet(reader)) {
protected boolean match(int doc) {
double x = latIndex[doc]; double x = latIndex[doc];
double y = lngIndex[doc]; double y = lngIndex[doc];
// round off lat / longs if necessary // round off lat / longs if necessary
// x = DistanceHandler.getPrecision(x, precise); // x = DistanceHandler.getPrecision(x, precise);
// y = DistanceHandler.getPrecision(y, precise); // y = DistanceHandler.getPrecision(y, precise);
String ck = new Double(x).toString()+","+new Double(y).toString();
Double cachedDistance = cdistance.get(ck);
String ck = Double.toString(x)+","+Double.toString(y);
Double cachedDistance = distanceLookupCache.get(ck);
double d; double d;
if (cachedDistance != null){
if(cachedDistance != null){
d = cachedDistance.doubleValue(); d = cachedDistance.doubleValue();
} else { } else {
d = DistanceUtils.getInstance().getDistanceMi(lat, lng, x, y); d = DistanceUtils.getInstance().getDistanceMi(lat, lng, x, y);
cdistance.put(ck, d); distanceLookupCache.put(ck, d);
} }
// why was i storing all distances again? if (d < distance) {
if (d < distance){ // Save distances, so they can be pulled for
bits.set(doc); // sorting after filtering is done:
distances.put(doc+ nextOffset, d); // include nextOffset for multi segment reader distances.put(doc+docBase, d);
} return true;
}
int size = bits.cardinality();
nextOffset += reader.maxDoc(); // this should be something that's part of indexReader
long end = System.currentTimeMillis();
log.fine("Bits 1: Time taken : "+ (end - start) +
", results : "+ distances.size() +
", cached : "+ cdistance.size() +
", incoming size: "+ size+
", nextOffset: "+ nextOffset);
return bits;
}
@Override
public BitSet bits(IndexReader reader, BitSet bits) throws Exception {
/* Create a BitSet to store the result */
int size = bits.cardinality();
BitSet result = new BitSet(size);
/* create an intermediate cache to avoid recomputing
distances for the same point */
HashMap<String,Double> cdistance = new HashMap<String,Double>(size);
if (distances == null){
distances = new HashMap<Integer,Double>();
}
long start = System.currentTimeMillis();
double[] latIndex = FieldCache.DEFAULT.getDoubles(reader, latField);
double[] lngIndex = FieldCache.DEFAULT.getDoubles(reader, lngField);
/* loop over all set bits (hits from the boundary box filters) */
int i = bits.nextSetBit(0);
while (i >= 0){
if (reader.isDeleted(i)) {
i = bits.nextSetBit(i+1);
continue;
}
double x,y;
// if we have a completed
// filter chain, lat / lngs can be retrived from
// memory rather than document base.
x = latIndex[i];
y = lngIndex[i];
// round off lat / longs if necessary
// x = DistanceHandler.getPrecision(x, precise);
// y = DistanceHandler.getPrecision(y, precise);
String ck = new Double(x).toString()+","+new Double(y).toString();
Double cachedDistance = cdistance.get(ck);
double d;
if(cachedDistance != null){
d = cachedDistance.doubleValue();
} else { } else {
d = DistanceUtils.getInstance().getDistanceMi(lat, lng, x, y); return false;
//d = DistanceUtils.getLLMDistance(lat, lng, x, y);
cdistance.put(ck, d);
} }
// why was i storing all distances again?
if (d < distance){
result.set(i);
int did = i + nextOffset;
distances.put(did, d); // include nextOffset for multi segment reader
} }
i = bits.nextSetBit(i+1); };
}
long end = System.currentTimeMillis();
nextOffset += reader.maxDoc(); // this should be something that's part of indexReader
log.fine("Time taken : "+ (end - start) +
", results : "+ distances.size() +
", cached : "+ cdistance.size() +
", incoming size: "+ size+
", nextOffset: "+ nextOffset);
cdistance = null;
return result;
} }
/** Returns true if <code>o</code> is equal to this. */ /** Returns true if <code>o</code> is equal to this. */
@ -235,7 +105,8 @@ public class LatLongDistanceFilter extends DistanceFilter {
if (!(o instanceof LatLongDistanceFilter)) return false; if (!(o instanceof LatLongDistanceFilter)) return false;
LatLongDistanceFilter other = (LatLongDistanceFilter) o; LatLongDistanceFilter other = (LatLongDistanceFilter) o;
if (this.distance != other.distance || if (!this.startingFilter.equals(other.startingFilter) ||
this.distance != other.distance ||
this.lat != other.lat || this.lat != other.lat ||
this.lng != other.lng || this.lng != other.lng ||
!this.latField.equals(other.latField) || !this.latField.equals(other.latField) ||
@ -249,28 +120,11 @@ public class LatLongDistanceFilter extends DistanceFilter {
@Override @Override
public int hashCode() { public int hashCode() {
int h = new Double(distance).hashCode(); int h = new Double(distance).hashCode();
h ^= startingFilter.hashCode();
h ^= new Double(lat).hashCode(); h ^= new Double(lat).hashCode();
h ^= new Double(lng).hashCode(); h ^= new Double(lng).hashCode();
h ^= latField.hashCode(); h ^= latField.hashCode();
h ^= lngField.hashCode(); h ^= lngField.hashCode();
return h; return h;
} }
public void setDistances(Map<Integer, Double> distances) {
this.distances = distances;
}
void setPrecision(int maxDocs) {
precise = Precision.EXACT;
if (maxDocs > 1000 && distance > 10) {
precise = Precision.TWENTYFEET;
}
if (maxDocs > 10000 && distance > 10){
precise = Precision.TWOHUNDREDFEET;
}
}
} }

View File

@ -82,7 +82,6 @@ public class CartesianTierPlotter {
* *
* @param latitude * @param latitude
* @param longitude * @param longitude
* @return
*/ */
public double getTierBoxId (double latitude, double longitude) { public double getTierBoxId (double latitude, double longitude) {
@ -106,7 +105,6 @@ public class CartesianTierPlotter {
/** /**
* get the string name representing current tier * get the string name representing current tier
* _localTier&lt;tiedId&gt; * _localTier&lt;tiedId&gt;
* @return
*/ */
public String getTierFieldName (){ public String getTierFieldName (){
@ -117,7 +115,6 @@ public class CartesianTierPlotter {
* get the string name representing tierId * get the string name representing tierId
* _localTier&lt;tierId&gt; * _localTier&lt;tierId&gt;
* @param tierId * @param tierId
* @return
*/ */
public String getTierFieldName (int tierId){ public String getTierFieldName (int tierId){
@ -133,12 +130,8 @@ public class CartesianTierPlotter {
* *
* Distances less than a mile return 15, finer granularity is * Distances less than a mile return 15, finer granularity is
* in accurate * in accurate
*
* @param latitude
* @param longitude
* @return
*/ */
public int bestFit(int miles){ public int bestFit(double miles){
//28,892 a rough circumference of the earth //28,892 a rough circumference of the earth
int circ = 28892; int circ = 28892;
@ -146,7 +139,6 @@ public class CartesianTierPlotter {
double r = miles / 2.0; double r = miles / 2.0;
double corner = r - Math.sqrt(Math.pow(r, 2) / 2.0d); double corner = r - Math.sqrt(Math.pow(r, 2) / 2.0d);
System.out.println("corner "+ corner);
double times = circ / corner; double times = circ / corner;
int bestFit = (int)Math.ceil(log2(times)) + 1; int bestFit = (int)Math.ceil(log2(times)) + 1;
@ -162,7 +154,6 @@ public class CartesianTierPlotter {
* a log to the base 2 formula * a log to the base 2 formula
* <code>Math.log(value) / Math.log(2)</code> * <code>Math.log(value) / Math.log(2)</code>
* @param value * @param value
* @return
*/ */
public double log2(double value) { public double log2(double value) {

View File

@ -146,7 +146,12 @@ public class TestCartesian extends TestCase{
public void testRange() throws IOException, InvalidGeoException { public void testRange() throws IOException, InvalidGeoException {
searcher = new IndexSearcher(directory); searcher = new IndexSearcher(directory);
final double miles = 6.0; final double[] milesToTest = new double[] {6.0, 0.5, 0.001, 0.0};
final int[] expected = new int[] {7, 1, 0, 0};
for(int x=0;x<expected.length;x++) {
final double miles = milesToTest[x];
// create a distance query // create a distance query
final DistanceQueryBuilder dq = new DistanceQueryBuilder(lat, lng, miles, final DistanceQueryBuilder dq = new DistanceQueryBuilder(lat, lng, miles,
@ -205,8 +210,8 @@ public class TestCartesian extends TestCase{
System.out.println("Distances should be 7 "+ distances.size()); System.out.println("Distances should be 7 "+ distances.size());
System.out.println("Results should be 7 "+ results); System.out.println("Results should be 7 "+ results);
assertEquals(7, distances.size()); // fixed a store of only needed distances assertEquals(expected[x], distances.size()); // fixed a store of only needed distances
assertEquals(7, results); assertEquals(expected[x], results);
double lastDistance = 0; double lastDistance = 0;
for(int i =0 ; i < results; i++){ for(int i =0 ; i < results; i++){
Document d = hits.doc(i); Document d = hits.doc(i);
@ -225,13 +230,18 @@ public class TestCartesian extends TestCase{
lastDistance = geo_distance; lastDistance = geo_distance;
} }
} }
}
public void testGeoHashRange() throws IOException, InvalidGeoException { public void testGeoHashRange() throws IOException, InvalidGeoException {
searcher = new IndexSearcher(directory); searcher = new IndexSearcher(directory);
final double miles = 6.0; final double[] milesToTest = new double[] {6.0, 0.5, 0.001, 0.0};
final int[] expected = new int[] {7, 1, 0, 0};
for(int x=0;x<expected.length;x++) {
final double miles = milesToTest[x];
// create a distance query // create a distance query
final DistanceQueryBuilder dq = new DistanceQueryBuilder(lat, lng, miles, final DistanceQueryBuilder dq = new DistanceQueryBuilder(lat, lng, miles,
@ -289,8 +299,8 @@ public class TestCartesian extends TestCase{
System.out.println("Distances should be 14 "+ distances.size()); System.out.println("Distances should be 14 "+ distances.size());
System.out.println("Results should be 7 "+ results); System.out.println("Results should be 7 "+ results);
assertEquals(14, distances.size()); assertEquals(expected[x], distances.size());
assertEquals(7, results); assertEquals(expected[x], results);
for(int i =0 ; i < results; i++){ for(int i =0 ; i < results; i++){
Document d = hits.doc(i); Document d = hits.doc(i);
@ -308,5 +318,5 @@ public class TestCartesian extends TestCase{
} }
} }
}
} }

View File

@ -17,7 +17,6 @@
package org.apache.lucene.spatial.tier; package org.apache.lucene.spatial.tier;
import java.io.IOException; import java.io.IOException;
import java.util.BitSet;
import junit.framework.TestCase; import junit.framework.TestCase;
@ -27,10 +26,9 @@ import org.apache.lucene.document.Field;
import org.apache.lucene.index.IndexWriter; import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.Term; import org.apache.lucene.index.Term;
import org.apache.lucene.index.IndexReader; import org.apache.lucene.index.IndexReader;
import org.apache.lucene.search.Filter; import org.apache.lucene.search.QueryWrapperFilter;
import org.apache.lucene.search.IndexSearcher; import org.apache.lucene.search.MatchAllDocsQuery;
import org.apache.lucene.util.NumericUtils; import org.apache.lucene.util.NumericUtils;
import org.apache.lucene.spatial.tier.LatLongDistanceFilter;
import org.apache.lucene.store.RAMDirectory; import org.apache.lucene.store.RAMDirectory;
@ -41,7 +39,6 @@ public class TestDistance extends TestCase{
private RAMDirectory directory; private RAMDirectory directory;
private IndexSearcher searcher;
// reston va // reston va
private double lat = 38.969398; private double lat = 38.969398;
private double lng= -77.386398; private double lng= -77.386398;
@ -103,13 +100,13 @@ public class TestDistance extends TestCase{
public void testLatLongFilterOnDeletedDocs() throws Exception { public void testLatLongFilterOnDeletedDocs() throws Exception {
writer.deleteDocuments(new Term("name", "Potomac")); writer.deleteDocuments(new Term("name", "Potomac"));
IndexReader r = writer.getReader(); IndexReader r = writer.getReader();
LatLongDistanceFilter f = new LatLongDistanceFilter(lat, lng, 1.0, latField, lngField); LatLongDistanceFilter f = new LatLongDistanceFilter(new QueryWrapperFilter(new MatchAllDocsQuery()),
f.bits(r); lat, lng, 1.0, latField, lngField);
BitSet allSet = new BitSet(r.maxDoc()); IndexReader[] readers = r.getSequentialSubReaders();
allSet.set(0, r.maxDoc()); for(int i=0;i<readers.length;i++) {
f.bits(r, allSet); f.getDocIdSet(readers[i]);
r.close(); }
} }

View File

@ -54,7 +54,7 @@ public abstract class FilteredDocIdSet extends DocIdSet {
* @param docid docid to be tested * @param docid docid to be tested
* @return true if input docid should be in the result set, false otherwise. * @return true if input docid should be in the result set, false otherwise.
*/ */
protected abstract boolean match(int docid); protected abstract boolean match(int docid) throws IOException;
/** /**
* Implementation of the contract to build a DocIdSetIterator. * Implementation of the contract to build a DocIdSetIterator.
@ -64,7 +64,7 @@ public abstract class FilteredDocIdSet extends DocIdSet {
// @Override // @Override
public DocIdSetIterator iterator() throws IOException { public DocIdSetIterator iterator() throws IOException {
return new FilteredDocIdSetIterator(_innerSet.iterator()) { return new FilteredDocIdSetIterator(_innerSet.iterator()) {
protected boolean match(int docid) { protected boolean match(int docid) throws IOException {
return FilteredDocIdSet.this.match(docid); return FilteredDocIdSet.this.match(docid);
} }
}; };

View File

@ -47,7 +47,7 @@ public abstract class FilteredDocIdSetIterator extends DocIdSetIterator {
* @return true if input docid should be in the result set, false otherwise. * @return true if input docid should be in the result set, false otherwise.
* @see #FilteredDocIdSetIterator(DocIdSetIterator). * @see #FilteredDocIdSetIterator(DocIdSetIterator).
*/ */
abstract protected boolean match(int doc); abstract protected boolean match(int doc) throws IOException;
/** @deprecated use {@link #docID()} instead. */ /** @deprecated use {@link #docID()} instead. */
public final int doc() { public final int doc() {