diff --git a/lucene/CHANGES.txt b/lucene/CHANGES.txt index 4f2928dc7c5..1335f8f4681 100644 --- a/lucene/CHANGES.txt +++ b/lucene/CHANGES.txt @@ -253,6 +253,9 @@ Bug Fixes * LUCENE-9581: Japanese tokenizer should discard the compound token instead of disabling the decomposition of long tokens when discardCompoundToken is activated. (Jim Ferenczi) +* LUCENE-9595: Make Component2D#withinPoint implementations consistent with ShapeQuery logic. + (Ignacio Vera) + Other --------------------- diff --git a/lucene/core/src/java/org/apache/lucene/document/LatLonShapeBoundingBoxQuery.java b/lucene/core/src/java/org/apache/lucene/document/LatLonShapeBoundingBoxQuery.java index d3ad3e253bc..aca93492b22 100644 --- a/lucene/core/src/java/org/apache/lucene/document/LatLonShapeBoundingBoxQuery.java +++ b/lucene/core/src/java/org/apache/lucene/document/LatLonShapeBoundingBoxQuery.java @@ -125,7 +125,8 @@ final class LatLonShapeBoundingBoxQuery extends ShapeQuery { switch (scratchTriangle.type) { case POINT: { - return Component2D.WithinRelation.DISJOINT; + return encodedRectangle.contains(scratchTriangle.aX, scratchTriangle.aY) + ? Component2D.WithinRelation.NOTWITHIN : Component2D.WithinRelation.DISJOINT; } case LINE: { return encodedRectangle.withinLine(scratchTriangle.aX, scratchTriangle.aY, scratchTriangle.ab, diff --git a/lucene/core/src/java/org/apache/lucene/geo/Circle2D.java b/lucene/core/src/java/org/apache/lucene/geo/Circle2D.java index 17178463eac..4cc64ed8b98 100644 --- a/lucene/core/src/java/org/apache/lucene/geo/Circle2D.java +++ b/lucene/core/src/java/org/apache/lucene/geo/Circle2D.java @@ -110,7 +110,7 @@ class Circle2D implements Component2D { @Override public WithinRelation withinPoint(double x, double y) { - return WithinRelation.DISJOINT; + return contains(x, y) ? WithinRelation.NOTWITHIN : WithinRelation.DISJOINT; } @Override diff --git a/lucene/core/src/java/org/apache/lucene/geo/Line2D.java b/lucene/core/src/java/org/apache/lucene/geo/Line2D.java index 313728e239f..97b2a494c10 100644 --- a/lucene/core/src/java/org/apache/lucene/geo/Line2D.java +++ b/lucene/core/src/java/org/apache/lucene/geo/Line2D.java @@ -129,13 +129,15 @@ final class Line2D implements Component2D { @Override public WithinRelation withinPoint(double x, double y) { - return WithinRelation.DISJOINT; + return contains(x, y) ? WithinRelation.NOTWITHIN : WithinRelation.DISJOINT; } @Override public WithinRelation withinLine(double minX, double maxX, double minY, double maxY, double aX, double aY, boolean ab, double bX, double bY) { - // can be improved? + if (ab && intersectsLine(minX, maxX, minY, maxY, aX, aY, bX, bY)) { + return WithinRelation.NOTWITHIN; + } return WithinRelation.DISJOINT; } diff --git a/lucene/core/src/java/org/apache/lucene/geo/Polygon2D.java b/lucene/core/src/java/org/apache/lucene/geo/Polygon2D.java index 438756da3e2..a601f468184 100644 --- a/lucene/core/src/java/org/apache/lucene/geo/Polygon2D.java +++ b/lucene/core/src/java/org/apache/lucene/geo/Polygon2D.java @@ -181,7 +181,7 @@ final class Polygon2D implements Component2D { @Override public WithinRelation withinPoint(double x, double y) { - return WithinRelation.DISJOINT; + return contains(x, y) ? WithinRelation.NOTWITHIN : WithinRelation.DISJOINT; } @Override diff --git a/lucene/core/src/java/org/apache/lucene/geo/Rectangle2D.java b/lucene/core/src/java/org/apache/lucene/geo/Rectangle2D.java index 37b5f032991..e0f18588e17 100644 --- a/lucene/core/src/java/org/apache/lucene/geo/Rectangle2D.java +++ b/lucene/core/src/java/org/apache/lucene/geo/Rectangle2D.java @@ -118,13 +118,13 @@ final class Rectangle2D implements Component2D { @Override public WithinRelation withinPoint(double x, double y) { - return WithinRelation.DISJOINT; + return contains(x, y) ? WithinRelation.NOTWITHIN : WithinRelation.DISJOINT; } @Override public WithinRelation withinLine(double minX, double maxX, double minY, double maxY, double aX, double aY, boolean ab, double bX, double bY) { - if (ab == true && Component2D.disjoint(this.minX, this.maxX, this.minY, this.maxY, minX, maxX, minY, maxY) ==false && + if (ab == true && Component2D.disjoint(this.minX, this.maxX, this.minY, this.maxY, minX, maxX, minY, maxY) == false && edgesIntersect(aX, aY, bX, bY)) { return WithinRelation.NOTWITHIN; } diff --git a/lucene/core/src/test/org/apache/lucene/document/TestLatLonShape.java b/lucene/core/src/test/org/apache/lucene/document/TestLatLonShape.java index 24d7784ab79..a8bee73fc8b 100644 --- a/lucene/core/src/test/org/apache/lucene/document/TestLatLonShape.java +++ b/lucene/core/src/test/org/apache/lucene/document/TestLatLonShape.java @@ -26,6 +26,7 @@ import org.apache.lucene.geo.GeoUtils; import org.apache.lucene.geo.LatLonGeometry; import org.apache.lucene.geo.Line; import org.apache.lucene.geo.Polygon; +import org.apache.lucene.geo.Rectangle; import org.apache.lucene.geo.Tessellator; import org.apache.lucene.index.DirectoryReader; import org.apache.lucene.index.IndexReader; @@ -827,4 +828,49 @@ public class TestLatLonShape extends LuceneTestCase { IOUtils.close(r, dir); } + + public void testContainsIndexedGeometryCollection() throws Exception { + Directory dir = newDirectory(); + RandomIndexWriter w = new RandomIndexWriter(random(), dir); + Polygon polygon = new Polygon(new double[] {-64, -64, 64, 64, -64}, new double[] {-132, 132, 132, -132, -132}); + Field[] polygonFields = LatLonShape.createIndexableFields(FIELDNAME, polygon); + // POINT(5, 5) inside the indexed polygon + Field[] pointFields = LatLonShape.createIndexableFields(FIELDNAME, 5, 5); + int numDocs = random().nextInt(1000); + // index the same multi geometry many times + for (int i = 0; i < numDocs; i++) { + Document doc = new Document(); + for (Field f : polygonFields) { + doc.add(f); + } + for(int j = 0; j < 10; j++) { + for (Field f : pointFields) { + doc.add(f); + } + } + w.addDocument(doc); + } + w.forceMerge(1); + + ///// search ////// + IndexReader reader = w.getReader(); + w.close(); + IndexSearcher searcher = newSearcher(reader); + // Contains is only true if the query geometry is inside a geometry and does not intersect with any other geometry + // belonging to the same document. In this case the query geometry contains the indexed polygon but the point is + // inside the query as well, hence the result is 0. + Polygon polygonQuery = new Polygon(new double[] {4, 4, 6, 6, 4}, new double[] {4, 6, 6, 4, 4}); + Query query = LatLonShape.newGeometryQuery(FIELDNAME, QueryRelation.CONTAINS, polygonQuery); + assertEquals(0, searcher.count(query)); + + Rectangle rectangle = new Rectangle(4.0, 6.0, 4.0, 6.0); + query = LatLonShape.newGeometryQuery(FIELDNAME, QueryRelation.CONTAINS, rectangle); + assertEquals(0, searcher.count(query)); + + Circle circle = new Circle(5, 5, 10000); + query = LatLonShape.newGeometryQuery(FIELDNAME, QueryRelation.CONTAINS, circle); + assertEquals(0, searcher.count(query)); + + IOUtils.close(w, reader, dir); + } } diff --git a/lucene/core/src/test/org/apache/lucene/document/TestXYShape.java b/lucene/core/src/test/org/apache/lucene/document/TestXYShape.java index 5cfdf072cc9..3a909772a38 100644 --- a/lucene/core/src/test/org/apache/lucene/document/TestXYShape.java +++ b/lucene/core/src/test/org/apache/lucene/document/TestXYShape.java @@ -202,6 +202,51 @@ public class TestXYShape extends LuceneTestCase { IOUtils.close(r, dir); } + public void testContainsIndexedGeometryCollection() throws Exception { + Directory dir = newDirectory(); + RandomIndexWriter w = new RandomIndexWriter(random(), dir); + XYPolygon polygon = new XYPolygon(new float[] {-132, 132, 132, -132, -132}, new float[] {-64, -64, 64, 64, -64}); + Field[] polygonFields = XYShape.createIndexableFields(FIELDNAME, polygon); + // POINT(5, 5) inside the indexed polygon + Field[] pointFields = XYShape.createIndexableFields(FIELDNAME, 5, 5); + int numDocs = random().nextInt(1000); + // index the same multi geometry many times + for (int i = 0; i < numDocs; i++) { + Document doc = new Document(); + for (Field f : polygonFields) { + doc.add(f); + } + for(int j = 0; j < 10; j++) { + for (Field f : pointFields) { + doc.add(f); + } + } + w.addDocument(doc); + } + w.forceMerge(1); + + ///// search ////// + IndexReader reader = w.getReader(); + w.close(); + IndexSearcher searcher = newSearcher(reader); + // Contains is only true if the query geometry is inside a geometry and does not intersect with any other geometry + // belonging to the same document. In this case the query geometry contains the indexed polygon but the point is + // inside the query as well, hence the result is 0. + XYPolygon polygonQuery = new XYPolygon(new float[] {4, 6, 6, 4, 4}, new float[] {4, 4, 6, 6, 4}); + Query query = XYShape.newGeometryQuery(FIELDNAME, QueryRelation.CONTAINS, polygonQuery); + assertEquals(0, searcher.count(query)); + + XYRectangle rectangle = new XYRectangle(4, 6, 4, 6); + query = XYShape.newGeometryQuery(FIELDNAME, QueryRelation.CONTAINS, rectangle); + assertEquals(0, searcher.count(query)); + + XYCircle circle = new XYCircle(5, 5, 1); + query = XYShape.newGeometryQuery(FIELDNAME, QueryRelation.CONTAINS, circle); + assertEquals(0, searcher.count(query)); + + IOUtils.close(w, reader, dir); + } + private static boolean areBoxDisjoint(XYRectangle r1, XYRectangle r2) { return ( r1.minX <= r2.minX && r1.minY <= r2.minY && r1.maxX >= r2.maxX && r1.maxY >= r2.maxY); }