You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@lucene.apache.org by mi...@apache.org on 2016/03/09 16:06:41 UTC

lucene-solr git commit: LUCENE-7085: PointRangeQuery.equals sometimes returns false even if queries were in fact equal

Repository: lucene-solr
Updated Branches:
  refs/heads/master 004e83bb6 -> 770e508fd


LUCENE-7085: PointRangeQuery.equals sometimes returns false even if queries were in fact equal


Project: http://git-wip-us.apache.org/repos/asf/lucene-solr/repo
Commit: http://git-wip-us.apache.org/repos/asf/lucene-solr/commit/770e508f
Tree: http://git-wip-us.apache.org/repos/asf/lucene-solr/tree/770e508f
Diff: http://git-wip-us.apache.org/repos/asf/lucene-solr/diff/770e508f

Branch: refs/heads/master
Commit: 770e508fd3d908e9bf7997361299af96aa437b75
Parents: 004e83b
Author: Mike McCandless <mi...@apache.org>
Authored: Wed Mar 9 10:07:15 2016 -0500
Committer: Mike McCandless <mi...@apache.org>
Committed: Wed Mar 9 10:07:15 2016 -0500

----------------------------------------------------------------------
 .../apache/lucene/search/PointInSetQuery.java   | 10 +--
 .../apache/lucene/search/PointRangeQuery.java   | 38 +++++++---
 .../apache/lucene/search/TestPointQueries.java  | 80 ++++++++++++++++++++
 .../lucene/document/InetAddressPoint.java       | 23 +++++-
 .../lucene/document/TestBigIntegerPoint.java    | 15 ++++
 .../lucene/document/TestInetAddressPoint.java   | 21 +++++
 .../apache/lucene/document/TestLatLonPoint.java | 20 +++++
 .../spatial3d/PointInGeo3DShapeQuery.java       |  4 +-
 .../apache/lucene/spatial3d/TestGeo3DPoint.java | 14 ++++
 9 files changed, 202 insertions(+), 23 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/770e508f/lucene/core/src/java/org/apache/lucene/search/PointInSetQuery.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/search/PointInSetQuery.java b/lucene/core/src/java/org/apache/lucene/search/PointInSetQuery.java
index 944fadf..bee864f 100644
--- a/lucene/core/src/java/org/apache/lucene/search/PointInSetQuery.java
+++ b/lucene/core/src/java/org/apache/lucene/search/PointInSetQuery.java
@@ -103,7 +103,7 @@ public abstract class PointInSetQuery extends Query {
   }
 
   @Override
-  public Weight createWeight(IndexSearcher searcher, boolean needsScores) throws IOException {
+  public final Weight createWeight(IndexSearcher searcher, boolean needsScores) throws IOException {
 
     // We don't use RandomAccessWeight here: it's no good to approximate with "match all docs".
     // This is an inverted structure and should be used in the first pass:
@@ -161,14 +161,12 @@ public abstract class PointInSetQuery extends Query {
     private final DocIdSetBuilder result;
     private TermIterator iterator;
     private BytesRef nextQueryPoint;
-    private final byte[] lastMaxPackedValue;
     private final BytesRef scratch = new BytesRef();
     private final PrefixCodedTerms sortedPackedPoints;
 
     public MergePointVisitor(PrefixCodedTerms sortedPackedPoints, DocIdSetBuilder result) throws IOException {
       this.result = result;
       this.sortedPackedPoints = sortedPackedPoints;
-      lastMaxPackedValue = new byte[bytesPerDim];
       scratch.length = bytesPerDim;
       this.iterator = sortedPackedPoints.iterator();
       nextQueryPoint = iterator.next();
@@ -304,7 +302,7 @@ public abstract class PointInSetQuery extends Query {
   }
 
   @Override
-  public int hashCode() {
+  public final int hashCode() {
     int hash = super.hashCode();
     hash = 31 * hash + sortedPackedPointsHashCode;
     hash = 31 * hash + numDims;
@@ -313,7 +311,7 @@ public abstract class PointInSetQuery extends Query {
   }
 
   @Override
-  public boolean equals(Object other) {
+  public final boolean equals(Object other) {
     if (super.equals(other)) {
       final PointInSetQuery q = (PointInSetQuery) other;
       return q.numDims == numDims &&
@@ -326,7 +324,7 @@ public abstract class PointInSetQuery extends Query {
   }
 
   @Override
-  public String toString(String field) {
+  public final String toString(String field) {
     final StringBuilder sb = new StringBuilder();
     if (this.field.equals(field) == false) {
       sb.append(this.field);

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/770e508f/lucene/core/src/java/org/apache/lucene/search/PointRangeQuery.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/java/org/apache/lucene/search/PointRangeQuery.java b/lucene/core/src/java/org/apache/lucene/search/PointRangeQuery.java
index ebbe7e2..9384d23 100644
--- a/lucene/core/src/java/org/apache/lucene/search/PointRangeQuery.java
+++ b/lucene/core/src/java/org/apache/lucene/search/PointRangeQuery.java
@@ -109,7 +109,7 @@ public abstract class PointRangeQuery extends Query {
   }
 
   @Override
-  public Weight createWeight(IndexSearcher searcher, boolean needsScores) throws IOException {
+  public final Weight createWeight(IndexSearcher searcher, boolean needsScores) throws IOException {
 
     // We don't use RandomAccessWeight here: it's no good to approximate with "match all docs".
     // This is an inverted structure and should be used in the first pass:
@@ -239,7 +239,7 @@ public abstract class PointRangeQuery extends Query {
   }
 
   @Override
-  public int hashCode() {
+  public final int hashCode() {
     int hash = super.hashCode();
     hash = 31 * hash + Arrays.hashCode(lowerPoint);
     hash = 31 * hash + Arrays.hashCode(upperPoint);
@@ -249,20 +249,36 @@ public abstract class PointRangeQuery extends Query {
   }
 
   @Override
-  public boolean equals(Object other) {
-    if (super.equals(other)) {
-      final PointRangeQuery q = (PointRangeQuery) other;
-      return q.numDims == numDims &&
-        q.bytesPerDim == bytesPerDim &&
-        Arrays.equals(lowerPoint, q.lowerPoint) &&
-        Arrays.equals(upperPoint, q.upperPoint);
+  public final boolean equals(Object other) {
+    if (super.equals(other) == false) {
+      return false;
     }
 
-    return false;
+    final PointRangeQuery q = (PointRangeQuery) other;
+    if (q.numDims != numDims) {
+      return false;
+    }
+
+    if (q.bytesPerDim != bytesPerDim) {
+      return false;
+    }
+
+    // Cannot use Arrays.equals here, because it in turn uses byte[].equals
+    // to compare each value, which only uses "=="
+    for(int dim=0;dim<numDims;dim++) {
+      if (Arrays.equals(lowerPoint[dim], q.lowerPoint[dim]) == false) {
+        return false;
+      }
+      if (Arrays.equals(upperPoint[dim], q.upperPoint[dim]) == false) {
+        return false;
+      }
+    }
+
+    return true;
   }
 
   @Override
-  public String toString(String field) {
+  public final String toString(String field) {
     final StringBuilder sb = new StringBuilder();
     if (this.field.equals(field) == false) {
       sb.append(this.field);

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/770e508f/lucene/core/src/test/org/apache/lucene/search/TestPointQueries.java
----------------------------------------------------------------------
diff --git a/lucene/core/src/test/org/apache/lucene/search/TestPointQueries.java b/lucene/core/src/test/org/apache/lucene/search/TestPointQueries.java
index 500bb8f..4d9aa59 100644
--- a/lucene/core/src/test/org/apache/lucene/search/TestPointQueries.java
+++ b/lucene/core/src/test/org/apache/lucene/search/TestPointQueries.java
@@ -1886,4 +1886,84 @@ public class TestPointQueries extends LuceneTestCase {
     w.close();
     dir.close();
   }
+
+  public void testPointRangeEquals() {
+    Query q = IntPoint.newRangeQuery("a", 0, 1000);
+    assertEquals(q, IntPoint.newRangeQuery("a", 0, 1000));
+    assertFalse(q.equals(IntPoint.newRangeQuery("a", 1, 1000)));
+
+    q = LongPoint.newRangeQuery("a", 0, 1000);
+    assertEquals(q, LongPoint.newRangeQuery("a", 0, 1000));
+    assertFalse(q.equals(LongPoint.newRangeQuery("a", 1, 1000)));
+
+    q = FloatPoint.newRangeQuery("a", 0, 1000);
+    assertEquals(q, FloatPoint.newRangeQuery("a", 0, 1000));
+    assertFalse(q.equals(FloatPoint.newRangeQuery("a", 1, 1000)));
+
+    q = DoublePoint.newRangeQuery("a", 0, 1000);
+    assertEquals(q, DoublePoint.newRangeQuery("a", 0, 1000));
+    assertFalse(q.equals(DoublePoint.newRangeQuery("a", 1, 1000)));
+
+    byte[] zeros = new byte[5];
+    byte[] ones = new byte[5];
+    Arrays.fill(ones, (byte) 0xff);
+    q = BinaryPoint.newRangeQuery("a", new byte[][] {zeros}, new byte[][] {ones});
+    assertEquals(q, BinaryPoint.newRangeQuery("a", new byte[][] {zeros}, new byte[][] {ones}));
+    byte[] other = ones.clone();
+    other[2] = (byte) 5;
+    assertFalse(q.equals(BinaryPoint.newRangeQuery("a", new byte[][] {zeros}, new byte[][] {other})));
+  }
+
+  public void testPointExactEquals() {
+    Query q = IntPoint.newExactQuery("a", 1000);
+    assertEquals(q, IntPoint.newExactQuery("a", 1000));
+    assertFalse(q.equals(IntPoint.newExactQuery("a", 1)));
+
+    q = LongPoint.newExactQuery("a", 1000);
+    assertEquals(q, LongPoint.newExactQuery("a", 1000));
+    assertFalse(q.equals(LongPoint.newExactQuery("a", 1)));
+
+    q = FloatPoint.newExactQuery("a", 1000);
+    assertEquals(q, FloatPoint.newExactQuery("a", 1000));
+    assertFalse(q.equals(FloatPoint.newExactQuery("a", 1)));
+
+    q = DoublePoint.newExactQuery("a", 1000);
+    assertEquals(q, DoublePoint.newExactQuery("a", 1000));
+    assertFalse(q.equals(DoublePoint.newExactQuery("a", 1)));
+
+    byte[] ones = new byte[5];
+    Arrays.fill(ones, (byte) 0xff);
+    q = BinaryPoint.newExactQuery("a", ones);
+    assertEquals(q, BinaryPoint.newExactQuery("a", ones));
+    byte[] other = ones.clone();
+    other[2] = (byte) 5;
+    assertFalse(q.equals(BinaryPoint.newExactQuery("a", other)));
+  }
+
+  public void testPointInSetEquals() {
+    Query q = IntPoint.newSetQuery("a", 0, 1000, 17);
+    assertEquals(q, IntPoint.newSetQuery("a", 17, 0, 1000));
+    assertFalse(q.equals(IntPoint.newSetQuery("a", 1, 17, 1000)));
+
+    q = LongPoint.newSetQuery("a", 0, 1000, 17);
+    assertEquals(q, LongPoint.newSetQuery("a", 17, 0, 1000));
+    assertFalse(q.equals(LongPoint.newSetQuery("a", 1, 17, 1000)));
+
+    q = FloatPoint.newSetQuery("a", 0, 1000, 17);
+    assertEquals(q, FloatPoint.newSetQuery("a", 17, 0, 1000));
+    assertFalse(q.equals(FloatPoint.newSetQuery("a", 1, 17, 1000)));
+
+    q = DoublePoint.newSetQuery("a", 0, 1000, 17);
+    assertEquals(q, DoublePoint.newSetQuery("a", 17, 0, 1000));
+    assertFalse(q.equals(DoublePoint.newSetQuery("a", 1, 17, 1000)));
+
+    byte[] zeros = new byte[5];
+    byte[] ones = new byte[5];
+    Arrays.fill(ones, (byte) 0xff);
+    q = BinaryPoint.newSetQuery("a", new byte[][] {zeros, ones});
+    assertEquals(q, BinaryPoint.newSetQuery("a", new byte[][] {zeros, ones}));
+    byte[] other = ones.clone();
+    other[2] = (byte) 5;
+    assertFalse(q.equals(BinaryPoint.newSetQuery("a", new byte[][] {zeros, other})));
+  }
 }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/770e508f/lucene/sandbox/src/java/org/apache/lucene/document/InetAddressPoint.java
----------------------------------------------------------------------
diff --git a/lucene/sandbox/src/java/org/apache/lucene/document/InetAddressPoint.java b/lucene/sandbox/src/java/org/apache/lucene/document/InetAddressPoint.java
index f0df6ff..7abc1b6 100644
--- a/lucene/sandbox/src/java/org/apache/lucene/document/InetAddressPoint.java
+++ b/lucene/sandbox/src/java/org/apache/lucene/document/InetAddressPoint.java
@@ -19,12 +19,14 @@ package org.apache.lucene.document;
 import java.net.InetAddress;
 import java.net.UnknownHostException;
 import java.util.Arrays;
+import java.util.Comparator;
 
 import org.apache.lucene.index.PointValues;
 import org.apache.lucene.search.PointInSetQuery;
 import org.apache.lucene.search.PointRangeQuery;
 import org.apache.lucene.search.Query;
 import org.apache.lucene.util.BytesRef;
+import org.apache.lucene.util.StringHelper;
 
 /** 
  * An indexed 128-bit {@code InetAddress} field.
@@ -214,9 +216,22 @@ public class InetAddressPoint extends Field {
    */
   public static Query newSetQuery(String field, InetAddress... values) {
 
-    // Don't unexpectedly change the user's incoming values array:
-    InetAddress[] sortedValues = values.clone();
-    Arrays.sort(sortedValues);
+    // We must compare the encoded form (InetAddress doesn't implement Comparable, and even if it
+    // did, we do our own thing with ipv4 addresses):
+
+    // NOTE: we could instead convert-per-comparison and save this extra array, at cost of slower sort:
+    byte[][] sortedValues = new byte[values.length][];
+    for(int i=0;i<values.length;i++) {
+      sortedValues[i] = encode(values[i]);
+    }
+
+    Arrays.sort(sortedValues,
+                new Comparator<byte[]>() {
+                  @Override
+                  public int compare(byte[] a, byte[] b) {
+                    return StringHelper.compare(BYTES, a, 0, b, 0);
+                  }
+                });
 
     final BytesRef encoded = new BytesRef(new byte[BYTES]);
 
@@ -230,7 +245,7 @@ public class InetAddressPoint extends Field {
                                    if (upto == sortedValues.length) {
                                      return null;
                                    } else {
-                                     encoded.bytes = encode(sortedValues[upto]);
+                                     encoded.bytes = sortedValues[upto];
                                      assert encoded.bytes.length == encoded.length;
                                      upto++;
                                      return encoded;

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/770e508f/lucene/sandbox/src/test/org/apache/lucene/document/TestBigIntegerPoint.java
----------------------------------------------------------------------
diff --git a/lucene/sandbox/src/test/org/apache/lucene/document/TestBigIntegerPoint.java b/lucene/sandbox/src/test/org/apache/lucene/document/TestBigIntegerPoint.java
index 500c2a3..8f38bcd 100644
--- a/lucene/sandbox/src/test/org/apache/lucene/document/TestBigIntegerPoint.java
+++ b/lucene/sandbox/src/test/org/apache/lucene/document/TestBigIntegerPoint.java
@@ -21,6 +21,7 @@ import java.math.BigInteger;
 import org.apache.lucene.index.IndexReader;
 import org.apache.lucene.index.RandomIndexWriter;
 import org.apache.lucene.search.IndexSearcher;
+import org.apache.lucene.search.Query;
 import org.apache.lucene.store.Directory;
 import org.apache.lucene.util.LuceneTestCase;
 
@@ -93,4 +94,18 @@ public class TestBigIntegerPoint extends LuceneTestCase {
                                                                                  new BigInteger[] {BigInteger.valueOf(17), BigInteger.valueOf(42)}).toString());
     assertEquals("field:{1}", BigIntegerPoint.newSetQuery("field", BigInteger.ONE).toString());
   }
+
+  public void testQueryEquals() throws Exception {
+    Query q = BigIntegerPoint.newRangeQuery("a", BigInteger.valueOf(0), BigInteger.valueOf(1000));
+    assertEquals(q, BigIntegerPoint.newRangeQuery("a", BigInteger.valueOf(0), BigInteger.valueOf(1000)));
+    assertFalse(q.equals(BigIntegerPoint.newRangeQuery("a", BigInteger.valueOf(1), BigInteger.valueOf(1000))));
+
+    q = BigIntegerPoint.newExactQuery("a", BigInteger.valueOf(1000));
+    assertEquals(q, BigIntegerPoint.newExactQuery("a", BigInteger.valueOf(1000)));
+    assertFalse(q.equals(BigIntegerPoint.newExactQuery("a", BigInteger.valueOf(1))));
+
+    q = BigIntegerPoint.newSetQuery("a", BigInteger.valueOf(0), BigInteger.valueOf(1000), BigInteger.valueOf(17));
+    assertEquals(q, BigIntegerPoint.newSetQuery("a", BigInteger.valueOf(17), BigInteger.valueOf(0), BigInteger.valueOf(1000)));
+    assertFalse(q.equals(BigIntegerPoint.newSetQuery("a", BigInteger.valueOf(1), BigInteger.valueOf(17), BigInteger.valueOf(1000))));
+  }     
 }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/770e508f/lucene/sandbox/src/test/org/apache/lucene/document/TestInetAddressPoint.java
----------------------------------------------------------------------
diff --git a/lucene/sandbox/src/test/org/apache/lucene/document/TestInetAddressPoint.java b/lucene/sandbox/src/test/org/apache/lucene/document/TestInetAddressPoint.java
index d4ddb3a..c91b52b 100644
--- a/lucene/sandbox/src/test/org/apache/lucene/document/TestInetAddressPoint.java
+++ b/lucene/sandbox/src/test/org/apache/lucene/document/TestInetAddressPoint.java
@@ -21,6 +21,7 @@ import java.net.InetAddress;
 import org.apache.lucene.index.IndexReader;
 import org.apache.lucene.index.RandomIndexWriter;
 import org.apache.lucene.search.IndexSearcher;
+import org.apache.lucene.search.Query;
 import org.apache.lucene.store.Directory;
 import org.apache.lucene.util.LuceneTestCase;
 
@@ -45,6 +46,7 @@ public class TestInetAddressPoint extends LuceneTestCase {
     assertEquals(1, searcher.count(InetAddressPoint.newPrefixQuery("field", address, 24)));
     assertEquals(1, searcher.count(InetAddressPoint.newRangeQuery("field", InetAddress.getByName("1.2.3.3"), InetAddress.getByName("1.2.3.5"))));
     assertEquals(1, searcher.count(InetAddressPoint.newSetQuery("field", InetAddress.getByName("1.2.3.4"))));
+    assertEquals(1, searcher.count(InetAddressPoint.newSetQuery("field", InetAddress.getByName("1.2.3.4"), InetAddress.getByName("1.2.3.5"))));
     assertEquals(0, searcher.count(InetAddressPoint.newSetQuery("field", InetAddress.getByName("1.2.3.3"))));
     assertEquals(0, searcher.count(InetAddressPoint.newSetQuery("field")));
 
@@ -88,4 +90,23 @@ public class TestInetAddressPoint extends LuceneTestCase {
     assertEquals("field:[fdc8:57ed:f042:ad1:0:0:0:0 TO fdc8:57ed:f042:ad1:ffff:ffff:ffff:ffff]", InetAddressPoint.newPrefixQuery("field", InetAddress.getByName("fdc8:57ed:f042:0ad1:f66d:4ff:fe90:ce0c"), 64).toString());
     assertEquals("field:{fdc8:57ed:f042:ad1:f66d:4ff:fe90:ce0c}", InetAddressPoint.newSetQuery("field", InetAddress.getByName("fdc8:57ed:f042:0ad1:f66d:4ff:fe90:ce0c")).toString());
   }
+
+  public void testQueryEquals() throws Exception {
+    Query q = InetAddressPoint.newRangeQuery("a", InetAddress.getByName("1.2.3.3"), InetAddress.getByName("1.2.3.5"));
+    assertEquals(q, InetAddressPoint.newRangeQuery("a", InetAddress.getByName("1.2.3.3"), InetAddress.getByName("1.2.3.5")));
+    assertFalse(q.equals(InetAddressPoint.newRangeQuery("a", InetAddress.getByName("1.2.3.3"), InetAddress.getByName("1.2.3.7"))));
+
+    q = InetAddressPoint.newPrefixQuery("a", InetAddress.getByName("1.2.3.3"), 16);
+    assertEquals(q, InetAddressPoint.newPrefixQuery("a", InetAddress.getByName("1.2.3.3"), 16));
+    assertFalse(q.equals(InetAddressPoint.newPrefixQuery("a", InetAddress.getByName("1.1.3.5"), 16)));
+    assertFalse(q.equals(InetAddressPoint.newPrefixQuery("a", InetAddress.getByName("1.2.3.5"), 24)));
+
+    q = InetAddressPoint.newExactQuery("a", InetAddress.getByName("1.2.3.3"));
+    assertEquals(q, InetAddressPoint.newExactQuery("a", InetAddress.getByName("1.2.3.3")));
+    assertFalse(q.equals(InetAddressPoint.newExactQuery("a", InetAddress.getByName("1.2.3.5"))));
+
+    q = InetAddressPoint.newSetQuery("a", InetAddress.getByName("1.2.3.3"), InetAddress.getByName("1.2.3.5"));
+    assertEquals(q, InetAddressPoint.newSetQuery("a", InetAddress.getByName("1.2.3.3"), InetAddress.getByName("1.2.3.5")));
+    assertFalse(q.equals(InetAddressPoint.newSetQuery("a", InetAddress.getByName("1.2.3.3"), InetAddress.getByName("1.2.3.7"))));
+  }
 }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/770e508f/lucene/sandbox/src/test/org/apache/lucene/document/TestLatLonPoint.java
----------------------------------------------------------------------
diff --git a/lucene/sandbox/src/test/org/apache/lucene/document/TestLatLonPoint.java b/lucene/sandbox/src/test/org/apache/lucene/document/TestLatLonPoint.java
index 0ef948d..61c6754 100644
--- a/lucene/sandbox/src/test/org/apache/lucene/document/TestLatLonPoint.java
+++ b/lucene/sandbox/src/test/org/apache/lucene/document/TestLatLonPoint.java
@@ -19,6 +19,7 @@ package org.apache.lucene.document;
 import org.apache.lucene.index.IndexReader;
 import org.apache.lucene.index.RandomIndexWriter;
 import org.apache.lucene.search.IndexSearcher;
+import org.apache.lucene.search.Query;
 import org.apache.lucene.store.Directory;
 import org.apache.lucene.util.LuceneTestCase;
 
@@ -170,4 +171,23 @@ public class TestLatLonPoint extends LuceneTestCase {
       assertEquals(lonEnc, lonEnc2, 0.0);
     }
   }
+
+  public void testQueryEquals() throws Exception {
+    Query q = LatLonPoint.newBoxQuery("field", 50, 70, -40, 20);
+    assertEquals(q, LatLonPoint.newBoxQuery("field", 50, 70, -40, 20));
+    assertFalse(q.equals(LatLonPoint.newBoxQuery("field", 50, 70, -40, 10)));
+
+    q = LatLonPoint.newDistanceQuery("field", 50, 70, 10000);
+    assertEquals(q, LatLonPoint.newDistanceQuery("field", 50, 70, 10000));
+    assertFalse(q.equals(LatLonPoint.newDistanceQuery("field", 50, 70, 11000)));
+    assertFalse(q.equals(LatLonPoint.newDistanceQuery("field", 50, 60, 10000)));
+
+                
+    double[] polyLats1 = new double[] {30, 40, 40, 30, 30};
+    double[] polyLons1 = new double[] {90, 90, -40, -40, 90};
+    double[] polyLats2 = new double[] {20, 40, 40, 20, 20};
+    q = LatLonPoint.newPolygonQuery("field", polyLats1, polyLons1);
+    assertEquals(q, LatLonPoint.newPolygonQuery("field", polyLats1, polyLons1));
+    assertFalse(q.equals(LatLonPoint.newPolygonQuery("field", polyLats2, polyLons1)));
+  }     
 }

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/770e508f/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/PointInGeo3DShapeQuery.java
----------------------------------------------------------------------
diff --git a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/PointInGeo3DShapeQuery.java b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/PointInGeo3DShapeQuery.java
index 9df8752..c9b5e4e 100644
--- a/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/PointInGeo3DShapeQuery.java
+++ b/lucene/spatial3d/src/java/org/apache/lucene/spatial3d/PointInGeo3DShapeQuery.java
@@ -43,7 +43,7 @@ import org.apache.lucene.util.NumericUtils;
  *
  * @lucene.experimental */
 
-class PointInGeo3DShapeQuery extends Query {
+final class PointInGeo3DShapeQuery extends Query {
   final String field;
   final GeoShape shape;
 
@@ -192,7 +192,7 @@ class PointInGeo3DShapeQuery extends Query {
   }
 
   @Override
-  public final int hashCode() {
+  public int hashCode() {
     int result = super.hashCode();
     result = 31 * result + shape.hashCode();
     return result;

http://git-wip-us.apache.org/repos/asf/lucene-solr/blob/770e508f/lucene/spatial3d/src/test/org/apache/lucene/spatial3d/TestGeo3DPoint.java
----------------------------------------------------------------------
diff --git a/lucene/spatial3d/src/test/org/apache/lucene/spatial3d/TestGeo3DPoint.java b/lucene/spatial3d/src/test/org/apache/lucene/spatial3d/TestGeo3DPoint.java
index a4d8ed1..3061b76 100644
--- a/lucene/spatial3d/src/test/org/apache/lucene/spatial3d/TestGeo3DPoint.java
+++ b/lucene/spatial3d/src/test/org/apache/lucene/spatial3d/TestGeo3DPoint.java
@@ -807,4 +807,18 @@ public class TestGeo3DPoint extends LuceneTestCase {
   private static Directory getDirectory() {     
     return newDirectory();
   }
+
+  public void testEquals() {
+    GeoShape shape = randomShape(PlanetModel.WGS84);
+    Query q = Geo3DPoint.newShapeQuery("point", shape);
+    assertEquals(q, Geo3DPoint.newShapeQuery("point", shape));
+    
+    // make a different random shape:
+    GeoShape shape2;
+    do {
+      shape2 = randomShape(PlanetModel.WGS84);
+    } while (shape.equals(shape2));
+
+    assertFalse(q.equals(Geo3DPoint.newShapeQuery("point", shape2)));
+  }
 }