-
Notifications
You must be signed in to change notification settings - Fork 25.3k
Implement IndexOrDocValuesQuery for geo_shape field #64688
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
10 commits
Select commit
Hold shift + click to select a range
8df0f78
Implement IndexOrDocValuesQuery for geo_shape field
iverase dfa97c0
Merge branch 'master' into IndexOrDocValues
iverase 0dfc256
Merge branch 'master' into IndexOrDocValues
iverase 16216c5
Address review comments and fix some bugs on the way
iverase 0d28e8a
Merge branch 'master' into IndexOrDocValues
iverase d6f4d26
clean up test
iverase d76fe59
unused imports
iverase 7cbbfcf
Merge branch 'master' into IndexOrDocValues
iverase c946c99
compile error
iverase 9a3a932
checkStyle
iverase File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
388 changes: 388 additions & 0 deletions
388
...ial/src/main/java/org/elasticsearch/xpack/spatial/index/fielddata/Component2DVisitor.java
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,388 @@ | ||
/* | ||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one | ||
* or more contributor license agreements. Licensed under the Elastic License; | ||
* you may not use this file except in compliance with the Elastic License. | ||
*/ | ||
|
||
package org.elasticsearch.xpack.spatial.index.fielddata; | ||
|
||
import org.apache.lucene.document.ShapeField; | ||
import org.apache.lucene.geo.Component2D; | ||
import org.apache.lucene.index.PointValues; | ||
|
||
/** | ||
* A {@link TriangleTreeReader.Visitor} implementation for {@link Component2D} geometries. | ||
* It can solve spatial relationships against a serialize triangle tree. | ||
*/ | ||
public abstract class Component2DVisitor implements TriangleTreeReader.Visitor { | ||
|
||
protected final Component2D component2D; | ||
private final CoordinateEncoder encoder; | ||
|
||
private Component2DVisitor(Component2D component2D, CoordinateEncoder encoder) { | ||
this.component2D = component2D; | ||
this.encoder = encoder; | ||
} | ||
|
||
/** If the relationship has been honour. */ | ||
public abstract boolean matches(); | ||
|
||
/** Reset the visitor to the initial state. */ | ||
public abstract void reset(); | ||
|
||
@Override | ||
public void visitPoint(int x, int y) { | ||
doVisitPoint(encoder.decodeX(x), encoder.decodeY(y)); | ||
} | ||
|
||
abstract void doVisitPoint(double x, double y); | ||
|
||
@Override | ||
public void visitLine(int aX, int aY, int bX, int bY, byte metadata) { | ||
doVisitLine(encoder.decodeX(aX), encoder.decodeY(aY), encoder.decodeX(bX), encoder.decodeY(bY), metadata); | ||
} | ||
|
||
abstract void doVisitLine(double aX, double aY, double bX, double bY, byte metadata); | ||
|
||
@Override | ||
public void visitTriangle(int aX, int aY, int bX, int bY, int cX, int cY, byte metadata) { | ||
doVisitTriangle( | ||
encoder.decodeX(aX), | ||
encoder.decodeY(aY), | ||
encoder.decodeX(bX), | ||
encoder.decodeY(bY), | ||
encoder.decodeX(cX), | ||
encoder.decodeY(cY), | ||
metadata | ||
); | ||
} | ||
|
||
abstract void doVisitTriangle(double aX, double aY, double bX, double bY, double cX, double cY, byte metadata); | ||
|
||
@Override | ||
public boolean pushX(int minX) { | ||
return component2D.getMaxX() >= encoder.decodeX(minX); | ||
} | ||
|
||
@Override | ||
public boolean pushY(int minY) { | ||
return component2D.getMaxY() >= encoder.decodeY(minY); | ||
} | ||
|
||
@Override | ||
public boolean push(int maxX, int maxY) { | ||
return component2D.getMinX() <= encoder.decodeX(maxX) && | ||
component2D.getMinY() <= encoder.decodeY(maxY); | ||
|
||
} | ||
|
||
@Override | ||
public boolean push(int minX, int minY, int maxX, int maxY) { | ||
final PointValues.Relation relation = component2D.relate( | ||
encoder.decodeX(minX), | ||
encoder.decodeX(maxX), | ||
encoder.decodeY(minY), | ||
encoder.decodeY(maxY) | ||
); | ||
return doPush(relation); | ||
} | ||
|
||
/** Relation between the query shape and the doc value bounding box. Depending on the query relationship, | ||
* decide if we should traverse the tree. | ||
* | ||
* @return if true, the visitor keeps traversing the tree, else it stops. | ||
* */ | ||
abstract boolean doPush(PointValues.Relation relation); | ||
|
||
/** | ||
* Creates a visitor from the provided Component2D and spatial relationship. Visitors are re-usable by | ||
* calling the {@link #reset()} method. | ||
*/ | ||
public static Component2DVisitor getVisitor( | ||
Component2D component2D, | ||
ShapeField.QueryRelation relation, | ||
CoordinateEncoder encoder | ||
) { | ||
switch (relation) { | ||
case CONTAINS: | ||
return new ContainsVisitor(component2D, encoder); | ||
case INTERSECTS: | ||
return new IntersectsVisitor(component2D, encoder); | ||
case DISJOINT: | ||
return new DisjointVisitor(component2D, encoder); | ||
case WITHIN: | ||
return new WithinVisitor(component2D, encoder); | ||
default: | ||
throw new IllegalArgumentException("Invalid query relation:[" + relation + "]"); | ||
} | ||
} | ||
|
||
/** | ||
* Intersects visitor stops as soon as there is one triangle intersecting the component | ||
*/ | ||
private static class IntersectsVisitor extends Component2DVisitor { | ||
|
||
boolean intersects; | ||
|
||
private IntersectsVisitor(Component2D component2D, CoordinateEncoder encoder) { | ||
super(component2D, encoder); | ||
} | ||
|
||
@Override | ||
public boolean matches() { | ||
return intersects; | ||
} | ||
|
||
@Override | ||
public void reset() { | ||
// Start assuming that shapes are disjoint. As soon an intersecting component is found, | ||
// stop traversing the tree. | ||
intersects = false; | ||
} | ||
|
||
@Override | ||
void doVisitPoint(double x, double y) { | ||
intersects = component2D.contains(x, y); | ||
} | ||
|
||
@Override | ||
void doVisitLine(double aX, double aY, double bX, double bY, byte metadata) { | ||
intersects = component2D.intersectsLine(aX, aY, bX, bY); | ||
} | ||
|
||
@Override | ||
void doVisitTriangle(double aX, double aY, double bX, double bY, double cX, double cY, byte metadata) { | ||
intersects = component2D.intersectsTriangle(aX, aY, bX, bY, cX, cY); | ||
} | ||
|
||
@Override | ||
public boolean push() { | ||
// as far as shapes don't intersect, keep traversing the tree | ||
return intersects == false; | ||
} | ||
|
||
@Override | ||
boolean doPush(PointValues.Relation relation) { | ||
if (relation == PointValues.Relation.CELL_OUTSIDE_QUERY) { | ||
// shapes are disjoint, stop traversing the tree. | ||
return false; | ||
} else if (relation == PointValues.Relation.CELL_INSIDE_QUERY) { | ||
// shapes intersects, stop traversing the tree. | ||
intersects = true; | ||
return false; | ||
} else { | ||
// traverse the tree. | ||
return true; | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* Disjoint visitor stops as soon as there is one triangle intersecting the component | ||
*/ | ||
private static class DisjointVisitor extends Component2DVisitor { | ||
|
||
boolean disjoint; | ||
|
||
private DisjointVisitor(Component2D component2D, CoordinateEncoder encoder) { | ||
super(component2D, encoder); | ||
disjoint = true; | ||
} | ||
|
||
@Override | ||
public boolean matches() { | ||
return disjoint; | ||
} | ||
|
||
@Override | ||
public void reset() { | ||
// Start assuming that shapes are disjoint. As soon an intersecting component is found, | ||
// stop traversing the tree. | ||
disjoint = true; | ||
} | ||
|
||
@Override | ||
void doVisitPoint(double x, double y) { | ||
disjoint = component2D.contains(x, y) == false; | ||
} | ||
|
||
@Override | ||
void doVisitLine(double aX, double aY, double bX, double bY, byte metadata) { | ||
disjoint = component2D.intersectsLine(aX, aY, bX, bY) == false; | ||
} | ||
|
||
@Override | ||
void doVisitTriangle(double aX, double aY, double bX, double bY, double cX, double cY, byte metadata) { | ||
disjoint = component2D.intersectsTriangle(aX, aY, bX, bY, cX, cY) == false; | ||
} | ||
|
||
@Override | ||
public boolean push() { | ||
// as far as the shapes are disjoint, keep traversing the tree | ||
return disjoint; | ||
} | ||
|
||
@Override | ||
boolean doPush(PointValues.Relation relation) { | ||
if (relation == PointValues.Relation.CELL_OUTSIDE_QUERY) { | ||
// shapes are disjoint, stop traversing the tree. | ||
return false; | ||
} else if (relation == PointValues.Relation.CELL_INSIDE_QUERY) { | ||
// shapes intersects, stop traversing the tree. | ||
disjoint = false; | ||
return false; | ||
} else { | ||
// trasverse the tree | ||
return true; | ||
} | ||
} | ||
} | ||
|
||
/** | ||
* within visitor stops as soon as there is one triangle that is not within the component | ||
*/ | ||
private static class WithinVisitor extends Component2DVisitor { | ||
|
||
boolean within; | ||
|
||
private WithinVisitor(Component2D component2D, CoordinateEncoder encoder) { | ||
super(component2D, encoder); | ||
within = true; | ||
} | ||
|
||
@Override | ||
public boolean matches() { | ||
return within; | ||
} | ||
|
||
@Override | ||
public void reset() { | ||
// Start assuming that the doc value is within the query shape. As soon | ||
// as a component is not within the query, stop traversing the tree. | ||
within = true; | ||
} | ||
|
||
@Override | ||
void doVisitPoint(double x, double y) { | ||
within = component2D.contains(x, y); | ||
} | ||
|
||
@Override | ||
void doVisitLine(double aX, double aY, double bX, double bY, byte metadata) { | ||
within = component2D.containsLine(aX, aY, bX, bY); | ||
} | ||
|
||
@Override | ||
void doVisitTriangle(double aX, double aY, double bX, double bY, double cX, double cY, byte metadata) { | ||
within = component2D.containsTriangle(aX, aY, bX, bY, cX, cY); | ||
} | ||
|
||
@Override | ||
public boolean push() { | ||
// as far as the doc value is within the query shape, keep traversing the tree | ||
return within; | ||
} | ||
|
||
@Override | ||
public boolean pushX(int minX) { | ||
// if any part of the tree is skipped, then the doc value is not within the shape, | ||
// stop traversing the tree | ||
within = super.pushX(minX); | ||
return within; | ||
} | ||
|
||
@Override | ||
public boolean pushY(int minY) { | ||
// if any part of the tree is skipped, then the doc value is not within the shape, | ||
// stop traversing the tree | ||
within = super.pushY(minY); | ||
return within; | ||
} | ||
|
||
@Override | ||
public boolean push(int maxX, int maxY) { | ||
// if any part of the tree is skipped, then the doc value is not within the shape, | ||
// stop traversing the tree | ||
within = super.push(maxX, maxY); | ||
return within; | ||
} | ||
|
||
@Override | ||
boolean doPush(PointValues.Relation relation) { | ||
if (relation == PointValues.Relation.CELL_OUTSIDE_QUERY) { | ||
// shapes are disjoint, stop traversing the tree. | ||
within = false; | ||
} | ||
return within; | ||
} | ||
} | ||
|
||
/** | ||
* contains visitor stops as soon as there is one triangle that intersects the component | ||
* with an edge belonging to the original polygon. | ||
*/ | ||
private static class ContainsVisitor extends Component2DVisitor { | ||
|
||
Component2D.WithinRelation answer; | ||
|
||
private ContainsVisitor(Component2D component2D, CoordinateEncoder encoder) { | ||
super(component2D, encoder); | ||
answer = Component2D.WithinRelation.DISJOINT; | ||
} | ||
|
||
@Override | ||
public boolean matches() { | ||
return answer == Component2D.WithinRelation.CANDIDATE; | ||
} | ||
|
||
@Override | ||
public void reset() { | ||
// Start assuming that shapes are disjoint. As soon | ||
// as a component has a NOTWITHIN relationship, stop traversing the tree. | ||
answer = Component2D.WithinRelation.DISJOINT; | ||
} | ||
|
||
@Override | ||
void doVisitPoint(double x, double y) { | ||
final Component2D.WithinRelation rel = component2D.withinPoint(x, y); | ||
if (rel != Component2D.WithinRelation.DISJOINT) { | ||
// Only override relationship if different to DISJOINT | ||
answer = rel; | ||
} | ||
} | ||
|
||
@Override | ||
void doVisitLine(double aX, double aY, double bX, double bY, byte metadata) { | ||
final boolean ab = (metadata & 1 << 4) == 1 << 4; | ||
final Component2D.WithinRelation rel = component2D.withinLine(aX, aY, ab, bX, bY); | ||
if (rel != Component2D.WithinRelation.DISJOINT) { | ||
// Only override relationship if different to DISJOINT | ||
answer = rel; | ||
} | ||
} | ||
|
||
@Override | ||
void doVisitTriangle(double aX, double aY, double bX, double bY, double cX, double cY, byte metadata) { | ||
final boolean ab = (metadata & 1 << 4) == 1 << 4; | ||
final boolean bc = (metadata & 1 << 5) == 1 << 5; | ||
final boolean ca = (metadata & 1 << 6) == 1 << 6; | ||
final Component2D.WithinRelation rel = component2D.withinTriangle(aX, aY, ab, bX, bY, bc, cX, cY, ca); | ||
if (rel != Component2D.WithinRelation.DISJOINT) { | ||
// Only override relationship if different to DISJOINT | ||
answer = rel; | ||
} | ||
} | ||
|
||
@Override | ||
public boolean push() { | ||
// If the relationship is NOTWITHIN, stop traversing the tree | ||
return answer != Component2D.WithinRelation.NOTWITHIN; | ||
} | ||
|
||
@Override | ||
boolean doPush(PointValues.Relation relation) { | ||
// Only traverse the tree if the shapes intersects. | ||
return relation == PointValues.Relation.CELL_CROSSES_QUERY; | ||
} | ||
} | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Although this is an extension to the
push
that is documented inTriangleTreeReader.Visitor
, I think it would be convenient to re-write in the documentation what the return value of this function signals