From 4f3930420cd87f1fce0be7d247206cdb247e3352 Mon Sep 17 00:00:00 2001 From: rfecher Date: Thu, 2 May 2019 09:46:39 -0400 Subject: [PATCH] Query constraints using ordered dimensions (#1549) --- .../store/query/ExplicitSpatialQuery.java | 17 +- .../query/ExplicitSpatialTemporalQuery.java | 31 +- .../store/query/ExplicitTemporalQuery.java | 12 +- .../store/query/IndexOnlySpatialQuery.java | 2 +- .../geotime/store/query/OptimalCQLQuery.java | 16 +- .../query/filter/SpatialQueryFilter.java | 4 +- .../statistics/BoundingBoxDataStatistics.java | 4 +- .../core/geotime/util/GeometryUtils.java | 24 +- .../geotime/store/query/BasicQueryTest.java | 14 +- .../core/geotime/util/GeometryUtilsTest.java | 9 +- .../core/index/IndexPersistableRegistry.java | 4 +- .../core/index/sfc/BasicSFCIndexStrategy.java | 243 ++++++++ .../core/index/sfc/binned/BinnedSFCUtils.java | 17 +- .../sfc/tiered/TieredSFCIndexStrategy.java | 10 +- .../core/store/StorePersistableRegistry.java | 12 +- .../store/api/QueryConstraintsFactory.java | 2 +- .../BasicOrderedConstraintQuery.java | 170 ++++++ .../store/query/constraints/BasicQuery.java | 517 +--------------- .../query/constraints/BasicQueryByClass.java | 567 ++++++++++++++++++ .../store/query/constraints/Constraints.java | 24 + .../QueryConstraintsFactoryImpl.java | 17 +- .../query/constraints/SimpleNumericQuery.java | 17 +- .../store/TestStorePersistableRegistry.java | 4 +- ...ryTest.java => BasicQueryByClassTest.java} | 32 +- .../filter/DistributedQueryFilterTest.java | 6 +- .../examples/ingest/SimpleIngestTest.java | 4 +- .../FeatureAttributeDimensionField.java | 26 +- .../ChooseBestMatchIndexQueryStrategy.java | 4 +- ...hooseHeuristicMatchIndexQueryStrategy.java | 4 +- ...ChooseLocalityPreservingQueryStrategy.java | 4 +- .../vector/index/IndexQueryStrategySPI.java | 4 +- .../plugin/GeoWaveDataStoreComponents.java | 4 +- .../vector/plugin/GeoWaveFeatureReader.java | 32 +- .../adapter/vector/plugin/QueryIssuer.java | 4 +- .../adapter/vector/util/QueryIndexHelper.java | 35 +- ...ChooseBestMatchIndexQueryStrategyTest.java | 16 +- ...ChooseHeuristicMatchQueryStrategyTest.java | 35 +- ...seLocalityPreservingQueryStrategyTest.java | 35 +- .../vector/util/QueryIndexHelperTest.java | 22 +- .../test/query/SpatialTemporalQueryIT.java | 4 +- 40 files changed, 1287 insertions(+), 721 deletions(-) create mode 100644 core/index/src/main/java/org/locationtech/geowave/core/index/sfc/BasicSFCIndexStrategy.java create mode 100644 core/store/src/main/java/org/locationtech/geowave/core/store/query/constraints/BasicOrderedConstraintQuery.java create mode 100644 core/store/src/main/java/org/locationtech/geowave/core/store/query/constraints/BasicQueryByClass.java create mode 100644 core/store/src/main/java/org/locationtech/geowave/core/store/query/constraints/Constraints.java rename core/store/src/test/java/org/locationtech/geowave/core/store/query/{BasicQueryTest.java => BasicQueryByClassTest.java} (93%) diff --git a/core/geotime/src/main/java/org/locationtech/geowave/core/geotime/store/query/ExplicitSpatialQuery.java b/core/geotime/src/main/java/org/locationtech/geowave/core/geotime/store/query/ExplicitSpatialQuery.java index f97f0bc9d74..54f133b8d6b 100644 --- a/core/geotime/src/main/java/org/locationtech/geowave/core/geotime/store/query/ExplicitSpatialQuery.java +++ b/core/geotime/src/main/java/org/locationtech/geowave/core/geotime/store/query/ExplicitSpatialQuery.java @@ -26,7 +26,7 @@ import org.locationtech.geowave.core.store.api.Index; import org.locationtech.geowave.core.store.dimension.NumericDimensionField; import org.locationtech.geowave.core.store.index.CommonIndexModel; -import org.locationtech.geowave.core.store.query.constraints.BasicQuery; +import org.locationtech.geowave.core.store.query.constraints.BasicQueryByClass; import org.locationtech.geowave.core.store.query.filter.BasicQueryFilter.BasicQueryCompareOperation; import org.locationtech.geowave.core.store.query.filter.QueryFilter; import org.locationtech.jts.geom.Geometry; @@ -43,7 +43,7 @@ * The Spatial Query class represents a query in two dimensions. The constraint that is applied * represents an intersection operation on the query geometry. */ -public class ExplicitSpatialQuery extends BasicQuery { +public class ExplicitSpatialQuery extends BasicQueryByClass { private static final Logger LOGGER = LoggerFactory.getLogger(ExplicitSpatialQuery.class); private static class CrsCache { @@ -75,12 +75,12 @@ public ExplicitSpatialQuery(final Geometry queryGeometry) { this(GeometryUtils.basicConstraintsFromGeometry(queryGeometry), queryGeometry); } - public ExplicitSpatialQuery(final Constraints constraints, final Geometry queryGeometry) { + public ExplicitSpatialQuery(final ConstraintsByClass constraints, final Geometry queryGeometry) { this(constraints, queryGeometry, (String) null); } public ExplicitSpatialQuery( - final Constraints constraints, + final ConstraintsByClass constraints, final Geometry queryGeometry, final String crsCode) { this( @@ -121,7 +121,7 @@ public ExplicitSpatialQuery(final Geometry queryGeometry, final CompareOperation * @param compareOp predicate associated query geometry */ public ExplicitSpatialQuery( - final Constraints constraints, + final ConstraintsByClass constraints, final Geometry queryGeometry, final CompareOperation compareOp) { this(constraints, queryGeometry, compareOp, BasicQueryCompareOperation.INTERSECTS); @@ -149,7 +149,7 @@ public ExplicitSpatialQuery( * @param nonSpatialCompareOp predicate associated non-spatial fields (i.e Time) */ public ExplicitSpatialQuery( - final Constraints constraints, + final ConstraintsByClass constraints, final Geometry queryGeometry, final CompareOperation compareOp, final BasicQueryCompareOperation nonSpatialCompareOp) { @@ -162,7 +162,7 @@ public ExplicitSpatialQuery( } public ExplicitSpatialQuery( - final Constraints constraints, + final ConstraintsByClass constraints, final Geometry queryGeometry, final String crsCode, final CompareOperation compareOp, @@ -281,8 +281,7 @@ private CrsCache transformToIndex(final String indexCrsStr, final Index index) { private static List indexConstraintsFromGeometry( final Geometry geom, final Index index) { - return GeometryUtils.basicConstraintsFromGeometry(geom).getIndexConstraints( - index.getIndexStrategy()); + return GeometryUtils.basicConstraintsFromGeometry(geom).getIndexConstraints(index); } private static String getCrs(final CommonIndexModel indexModel) { diff --git a/core/geotime/src/main/java/org/locationtech/geowave/core/geotime/store/query/ExplicitSpatialTemporalQuery.java b/core/geotime/src/main/java/org/locationtech/geowave/core/geotime/store/query/ExplicitSpatialTemporalQuery.java index 83ee6f03439..dc896bad9ca 100644 --- a/core/geotime/src/main/java/org/locationtech/geowave/core/geotime/store/query/ExplicitSpatialTemporalQuery.java +++ b/core/geotime/src/main/java/org/locationtech/geowave/core/geotime/store/query/ExplicitSpatialTemporalQuery.java @@ -123,7 +123,7 @@ public static ConstraintSet createConstraints( isDefault)); } - public static Constraints createConstraints( + public static ConstraintsByClass createConstraints( final TemporalConstraints temporalConstraints, final boolean isDefault) { final List constraints = new ArrayList<>(); @@ -135,10 +135,12 @@ public static Constraints createConstraints( new NumericRange(range.getStartTime().getTime(), range.getEndTime().getTime()), isDefault))); } - return new Constraints(constraints); + return new ConstraintsByClass(constraints); } - public static Constraints createConstraints(final Interval[] intervals, final boolean isDefault) { + public static ConstraintsByClass createConstraints( + final Interval[] intervals, + final boolean isDefault) { final List constraints = new ArrayList<>(); for (final Interval range : intervals) { constraints.add( @@ -152,7 +154,7 @@ public static Constraints createConstraints(final Interval[] intervals, final bo Math.max(range.getEnd().toEpochMilli() - 1, range.getStart().toEpochMilli())), isDefault))); } - return new Constraints(constraints); + return new ConstraintsByClass(constraints); } /** @@ -164,11 +166,12 @@ public static Constraints createConstraints(final Interval[] intervals, final bo * @param queryGeometry * @return */ - private static Constraints createSpatialTemporalConstraints( + private static ConstraintsByClass createSpatialTemporalConstraints( final TemporalConstraints temporalConstraints, final Geometry queryGeometry) { - final Constraints geoConstraints = GeometryUtils.basicConstraintsFromGeometry(queryGeometry); - final Constraints timeConstraints = createConstraints(temporalConstraints, false); + final ConstraintsByClass geoConstraints = + GeometryUtils.basicConstraintsFromGeometry(queryGeometry); + final ConstraintsByClass timeConstraints = createConstraints(temporalConstraints, false); return geoConstraints.merge(timeConstraints); } @@ -181,11 +184,12 @@ private static Constraints createSpatialTemporalConstraints( * @param queryGeometry * @return */ - private static Constraints createSpatialTemporalConstraints( + private static ConstraintsByClass createSpatialTemporalConstraints( final Interval[] intervals, final Geometry queryGeometry) { - final Constraints geoConstraints = GeometryUtils.basicConstraintsFromGeometry(queryGeometry); - final Constraints timeConstraints = createConstraints(intervals, false); + final ConstraintsByClass geoConstraints = + GeometryUtils.basicConstraintsFromGeometry(queryGeometry); + final ConstraintsByClass timeConstraints = createConstraints(intervals, false); return geoConstraints.merge(timeConstraints); } @@ -197,13 +201,14 @@ private static Constraints createSpatialTemporalConstraints( * @param queryGeometry * @return */ - private static Constraints createSpatialTemporalConstraints( + private static ConstraintsByClass createSpatialTemporalConstraints( final Date startTime, final Date endTime, final Geometry queryGeometry) { - final Constraints geoConstraints = GeometryUtils.basicConstraintsFromGeometry(queryGeometry); + final ConstraintsByClass geoConstraints = + GeometryUtils.basicConstraintsFromGeometry(queryGeometry); return geoConstraints.merge( - new Constraints( + new ConstraintsByClass( new ConstraintSet( TimeDefinition.class, new ConstraintData( diff --git a/core/geotime/src/main/java/org/locationtech/geowave/core/geotime/store/query/ExplicitTemporalQuery.java b/core/geotime/src/main/java/org/locationtech/geowave/core/geotime/store/query/ExplicitTemporalQuery.java index 89368944a99..2ac92501637 100644 --- a/core/geotime/src/main/java/org/locationtech/geowave/core/geotime/store/query/ExplicitTemporalQuery.java +++ b/core/geotime/src/main/java/org/locationtech/geowave/core/geotime/store/query/ExplicitTemporalQuery.java @@ -12,7 +12,7 @@ import java.util.List; import org.locationtech.geowave.core.geotime.index.dimension.TimeDefinition; import org.locationtech.geowave.core.index.sfc.data.NumericRange; -import org.locationtech.geowave.core.store.query.constraints.BasicQuery; +import org.locationtech.geowave.core.store.query.constraints.BasicQueryByClass; import org.threeten.extra.Interval; /** @@ -20,7 +20,7 @@ * applied represents an intersection operation on the query geometry AND a date range intersection * based on startTime and endTime. */ -public class ExplicitTemporalQuery extends BasicQuery { +public class ExplicitTemporalQuery extends BasicQueryByClass { public ExplicitTemporalQuery(final Interval[] intervals) { super(createTemporalConstraints(intervals)); } @@ -33,7 +33,7 @@ public ExplicitTemporalQuery() { super(); } - private static Constraints createTemporalConstraints( + private static ConstraintsByClass createTemporalConstraints( final TemporalConstraints temporalConstraints) { final List constraints = new ArrayList<>(); for (final TemporalRange range : temporalConstraints.getRanges()) { @@ -44,10 +44,10 @@ private static Constraints createTemporalConstraints( new NumericRange(range.getStartTime().getTime(), range.getEndTime().getTime()), false))); } - return new Constraints(constraints); + return new ConstraintsByClass(constraints); } - private static Constraints createTemporalConstraints(final Interval[] intervals) { + private static ConstraintsByClass createTemporalConstraints(final Interval[] intervals) { final List constraints = new ArrayList<>(); for (final Interval range : intervals) { constraints.add( @@ -61,6 +61,6 @@ private static Constraints createTemporalConstraints(final Interval[] intervals) Math.max(range.getEnd().toEpochMilli() - 1, range.getStart().toEpochMilli())), false))); } - return new Constraints(constraints); + return new ConstraintsByClass(constraints); } } diff --git a/core/geotime/src/main/java/org/locationtech/geowave/core/geotime/store/query/IndexOnlySpatialQuery.java b/core/geotime/src/main/java/org/locationtech/geowave/core/geotime/store/query/IndexOnlySpatialQuery.java index c06eb49d59c..5abc2309bf4 100644 --- a/core/geotime/src/main/java/org/locationtech/geowave/core/geotime/store/query/IndexOnlySpatialQuery.java +++ b/core/geotime/src/main/java/org/locationtech/geowave/core/geotime/store/query/IndexOnlySpatialQuery.java @@ -19,7 +19,7 @@ public IndexOnlySpatialQuery() { super(); } - public IndexOnlySpatialQuery(final Constraints constraints, final Geometry queryGeometry) { + public IndexOnlySpatialQuery(final ConstraintsByClass constraints, final Geometry queryGeometry) { super(constraints, queryGeometry); } diff --git a/core/geotime/src/main/java/org/locationtech/geowave/core/geotime/store/query/OptimalCQLQuery.java b/core/geotime/src/main/java/org/locationtech/geowave/core/geotime/store/query/OptimalCQLQuery.java index c04a28bafd1..ae842603831 100644 --- a/core/geotime/src/main/java/org/locationtech/geowave/core/geotime/store/query/OptimalCQLQuery.java +++ b/core/geotime/src/main/java/org/locationtech/geowave/core/geotime/store/query/OptimalCQLQuery.java @@ -29,8 +29,8 @@ import org.locationtech.geowave.core.store.api.DataTypeAdapter; import org.locationtech.geowave.core.store.api.Index; import org.locationtech.geowave.core.store.query.constraints.AdapterAndIndexBasedQueryConstraints; -import org.locationtech.geowave.core.store.query.constraints.BasicQuery; -import org.locationtech.geowave.core.store.query.constraints.BasicQuery.Constraints; +import org.locationtech.geowave.core.store.query.constraints.BasicQueryByClass; +import org.locationtech.geowave.core.store.query.constraints.BasicQueryByClass.ConstraintsByClass; import org.locationtech.geowave.core.store.query.constraints.QueryConstraints; import org.locationtech.geowave.core.store.query.filter.BasicQueryFilter.BasicQueryCompareOperation; import org.locationtech.jts.geom.Geometry; @@ -53,7 +53,7 @@ public static QueryConstraints createOptimalQuery( final String cql, final GeotoolsFeatureDataAdapter adapter, final Index index, - final BasicQuery baseQuery) throws CQLException { + final BasicQueryByClass baseQuery) throws CQLException { return createOptimalQuery(cql, adapter, CompareOperation.INTERSECTS, index, baseQuery); } @@ -62,7 +62,7 @@ public static QueryConstraints createOptimalQuery( final GeotoolsFeatureDataAdapter adapter, final CompareOperation geoCompareOp, final Index index, - final BasicQuery baseQuery) throws CQLException { + final BasicQueryByClass baseQuery) throws CQLException { final Filter cqlFilter = CQL.toFilter(cql); return createOptimalQuery(cqlFilter, adapter, geoCompareOp, index, baseQuery); } @@ -78,7 +78,7 @@ public static QueryConstraints createOptimalQuery( final Filter cqlFilter, final GeotoolsFeatureDataAdapter adapter, final Index index, - final BasicQuery baseQuery) { + final BasicQueryByClass baseQuery) { return createOptimalQuery(cqlFilter, adapter, CompareOperation.INTERSECTS, index, baseQuery); } @@ -87,7 +87,7 @@ public static QueryConstraints createOptimalQuery( final GeotoolsFeatureDataAdapter adapter, final CompareOperation geoCompareOp, final Index index, - BasicQuery baseQuery) { + BasicQueryByClass baseQuery) { final ExtractAttributesFilter attributesVisitor = new ExtractAttributesFilter(); final Object obj = cqlFilter.accept(attributesVisitor, null); @@ -138,7 +138,7 @@ public static QueryConstraints createOptimalQuery( final GeoConstraintsWrapper geoConstraints = GeometryUtils.basicGeoConstraintsWrapperFromGeometry(geometry); - Constraints constraints = geoConstraints.getConstraints(); + ConstraintsByClass constraints = geoConstraints.getConstraints(); final CompareOperation extractedCompareOp = geometryAndCompareOp.getCompareOp(); if ((timeConstraintSet != null) && !timeConstraintSet.isEmpty()) { // determine which time constraints are associated with an @@ -149,7 +149,7 @@ public static QueryConstraints createOptimalQuery( adapter.getTimeDescriptors(), timeConstraintSet); // convert to constraints - final Constraints timeConstraints = + final ConstraintsByClass timeConstraints = ExplicitSpatialTemporalQuery.createConstraints(temporalConstraints, false); constraints = geoConstraints.getConstraints().merge(timeConstraints); } diff --git a/core/geotime/src/main/java/org/locationtech/geowave/core/geotime/store/query/filter/SpatialQueryFilter.java b/core/geotime/src/main/java/org/locationtech/geowave/core/geotime/store/query/filter/SpatialQueryFilter.java index 10d8359dabc..9bc65045389 100644 --- a/core/geotime/src/main/java/org/locationtech/geowave/core/geotime/store/query/filter/SpatialQueryFilter.java +++ b/core/geotime/src/main/java/org/locationtech/geowave/core/geotime/store/query/filter/SpatialQueryFilter.java @@ -363,8 +363,8 @@ public void fromBinary(final byte[] bytes) { */ public static class GeometryImage { - byte[] geometryBinary; - PreparedGeometry preparedGeometry = null; + public byte[] geometryBinary; + public PreparedGeometry preparedGeometry = null; public GeometryImage(final PreparedGeometry preparedGeometry) { super(); diff --git a/core/geotime/src/main/java/org/locationtech/geowave/core/geotime/store/statistics/BoundingBoxDataStatistics.java b/core/geotime/src/main/java/org/locationtech/geowave/core/geotime/store/statistics/BoundingBoxDataStatistics.java index 27d3efe8fde..7fc30cba403 100644 --- a/core/geotime/src/main/java/org/locationtech/geowave/core/geotime/store/statistics/BoundingBoxDataStatistics.java +++ b/core/geotime/src/main/java/org/locationtech/geowave/core/geotime/store/statistics/BoundingBoxDataStatistics.java @@ -20,8 +20,8 @@ import org.locationtech.geowave.core.store.adapter.statistics.StatisticsType; import org.locationtech.geowave.core.store.api.StatisticsQueryBuilder; import org.locationtech.geowave.core.store.entities.GeoWaveRow; -import org.locationtech.geowave.core.store.query.constraints.BasicQuery.ConstraintData; -import org.locationtech.geowave.core.store.query.constraints.BasicQuery.ConstraintSet; +import org.locationtech.geowave.core.store.query.constraints.BasicQueryByClass.ConstraintData; +import org.locationtech.geowave.core.store.query.constraints.BasicQueryByClass.ConstraintSet; import org.locationtech.jts.geom.Envelope; public abstract class BoundingBoxDataStatistics> diff --git a/core/geotime/src/main/java/org/locationtech/geowave/core/geotime/util/GeometryUtils.java b/core/geotime/src/main/java/org/locationtech/geowave/core/geotime/util/GeometryUtils.java index 28051ca6d25..37a05aafe4b 100644 --- a/core/geotime/src/main/java/org/locationtech/geowave/core/geotime/util/GeometryUtils.java +++ b/core/geotime/src/main/java/org/locationtech/geowave/core/geotime/util/GeometryUtils.java @@ -46,9 +46,10 @@ import org.locationtech.geowave.core.index.sfc.data.NumericValue; import org.locationtech.geowave.core.store.api.Index; import org.locationtech.geowave.core.store.data.field.FieldUtils; -import org.locationtech.geowave.core.store.query.constraints.BasicQuery.ConstraintData; -import org.locationtech.geowave.core.store.query.constraints.BasicQuery.ConstraintSet; -import org.locationtech.geowave.core.store.query.constraints.BasicQuery.Constraints; +import org.locationtech.geowave.core.store.query.constraints.BasicQueryByClass.ConstraintData; +import org.locationtech.geowave.core.store.query.constraints.BasicQueryByClass.ConstraintSet; +import org.locationtech.geowave.core.store.query.constraints.BasicQueryByClass.ConstraintsByClass; +import org.locationtech.geowave.core.store.query.constraints.Constraints; import org.locationtech.geowave.core.store.util.ClasspathUtils; import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.Envelope; @@ -147,12 +148,12 @@ public static void initClassLoader() throws MalformedURLException { } } - public static Constraints basicConstraintsFromGeometry(final Geometry geometry) { + public static ConstraintsByClass basicConstraintsFromGeometry(final Geometry geometry) { final List set = new LinkedList<>(); constructListOfConstraintSetsFromGeometry(geometry, set, false); - return new Constraints(set); + return new ConstraintsByClass(set); } /** @@ -169,7 +170,10 @@ public static GeoConstraintsWrapper basicGeoConstraintsWrapperFromGeometry( final boolean geometryConstraintsExactMatch = constructListOfConstraintSetsFromGeometry(geometry, set, true); - return new GeoConstraintsWrapper(new Constraints(set), geometryConstraintsExactMatch, geometry); + return new GeoConstraintsWrapper( + new ConstraintsByClass(set), + geometryConstraintsExactMatch, + geometry); } /** @@ -243,7 +247,7 @@ public static ConstraintSet basicConstraintSetFromEnvelope(final Envelope env) { */ public static Constraints basicConstraintsFromEnvelope(final Envelope env) { - return new Constraints(basicConstraintSetFromEnvelope(env)); + return new ConstraintsByClass(basicConstraintSetFromEnvelope(env)); } /** @@ -385,12 +389,12 @@ public static Geometry infinity() { } public static class GeoConstraintsWrapper { - private final Constraints constraints; + private final ConstraintsByClass constraints; private final boolean constraintsMatchGeometry; private final Geometry jtsBounds; public GeoConstraintsWrapper( - final Constraints constraints, + final ConstraintsByClass constraints, final boolean constraintsMatchGeometry, final Geometry jtsBounds) { this.constraints = constraints; @@ -398,7 +402,7 @@ public GeoConstraintsWrapper( this.jtsBounds = jtsBounds; } - public Constraints getConstraints() { + public ConstraintsByClass getConstraints() { return constraints; } diff --git a/core/geotime/src/test/java/org/locationtech/geowave/core/geotime/store/query/BasicQueryTest.java b/core/geotime/src/test/java/org/locationtech/geowave/core/geotime/store/query/BasicQueryTest.java index 78f42d12b80..0e8e5ae1873 100644 --- a/core/geotime/src/test/java/org/locationtech/geowave/core/geotime/store/query/BasicQueryTest.java +++ b/core/geotime/src/test/java/org/locationtech/geowave/core/geotime/store/query/BasicQueryTest.java @@ -27,10 +27,10 @@ import org.locationtech.geowave.core.store.data.MultiFieldPersistentDataset; import org.locationtech.geowave.core.store.data.PersistentDataset; import org.locationtech.geowave.core.store.index.CommonIndexValue; -import org.locationtech.geowave.core.store.query.constraints.BasicQuery; -import org.locationtech.geowave.core.store.query.constraints.BasicQuery.ConstraintData; -import org.locationtech.geowave.core.store.query.constraints.BasicQuery.ConstraintSet; -import org.locationtech.geowave.core.store.query.constraints.BasicQuery.Constraints; +import org.locationtech.geowave.core.store.query.constraints.BasicQueryByClass; +import org.locationtech.geowave.core.store.query.constraints.BasicQueryByClass.ConstraintData; +import org.locationtech.geowave.core.store.query.constraints.BasicQueryByClass.ConstraintSet; +import org.locationtech.geowave.core.store.query.constraints.BasicQueryByClass.ConstraintsByClass; import org.locationtech.geowave.core.store.query.filter.BasicQueryFilter.BasicQueryCompareOperation; import org.locationtech.geowave.core.store.query.filter.QueryFilter; @@ -64,9 +64,9 @@ public void performOp(final BasicQueryCompareOperation op, final boolean[] expec df.parse("2017-02-22T12:00:00GMT-00:00").getTime(), df.parse("2017-02-22T13:00:00GMT-00:00").getTime()), true); - Constraints constaints = - new Constraints(new ConstraintSet(TimeDefinition.class, constrainData)); - final BasicQuery query = new BasicQuery(constaints, op); + ConstraintsByClass constaints = + new ConstraintsByClass(new ConstraintSet(TimeDefinition.class, constrainData)); + final BasicQueryByClass query = new BasicQueryByClass(constaints, op); final CommonIndexedPersistenceEncoding[] data = new CommonIndexedPersistenceEncoding[] { diff --git a/core/geotime/src/test/java/org/locationtech/geowave/core/geotime/util/GeometryUtilsTest.java b/core/geotime/src/test/java/org/locationtech/geowave/core/geotime/util/GeometryUtilsTest.java index 1c8c874151d..a7e5381cde6 100644 --- a/core/geotime/src/test/java/org/locationtech/geowave/core/geotime/util/GeometryUtilsTest.java +++ b/core/geotime/src/test/java/org/locationtech/geowave/core/geotime/util/GeometryUtilsTest.java @@ -26,7 +26,8 @@ import org.locationtech.geowave.core.index.QueryRanges; import org.locationtech.geowave.core.index.dimension.NumericDimensionDefinition; import org.locationtech.geowave.core.index.sfc.data.MultiDimensionalNumericData; -import org.locationtech.geowave.core.store.query.constraints.BasicQuery.Constraints; +import org.locationtech.geowave.core.store.index.IndexImpl; +import org.locationtech.geowave.core.store.query.constraints.Constraints; import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.Geometry; import org.locationtech.jts.geom.GeometryFactory; @@ -34,7 +35,7 @@ import org.locationtech.jts.geom.Polygon; public class GeometryUtilsTest { - private float DELTA = 0; + private final float DELTA = 0; private Point point3D; private Point point2D; @@ -115,7 +116,7 @@ public void testConstraintGeneration() { new Coordinate(-9, -2)})}); final Constraints constraints = GeometryUtils.basicConstraintsFromGeometry(multiPolygon); final List results = - constraints.getIndexConstraints(new ExampleNumericIndexStrategy()); + constraints.getIndexConstraints(new IndexImpl(new ExampleNumericIndexStrategy(), null)); assertEquals(2, results.size()); assertTrue(Arrays.equals(new double[] {10, 30}, results.get(0).getMinValuesPerDimension())); assertTrue(Arrays.equals(new double[] {20, 40}, results.get(0).getMaxValuesPerDimension())); @@ -249,7 +250,7 @@ public MultiDimensionalCoordinates getCoordinatesPerDimension( } @Override - public byte[][] getInsertionPartitionKeys(MultiDimensionalNumericData insertionData) { + public byte[][] getInsertionPartitionKeys(final MultiDimensionalNumericData insertionData) { // TODO Auto-generated method stub return null; } diff --git a/core/index/src/main/java/org/locationtech/geowave/core/index/IndexPersistableRegistry.java b/core/index/src/main/java/org/locationtech/geowave/core/index/IndexPersistableRegistry.java index 8d1998cbe16..20c48117fb5 100644 --- a/core/index/src/main/java/org/locationtech/geowave/core/index/IndexPersistableRegistry.java +++ b/core/index/src/main/java/org/locationtech/geowave/core/index/IndexPersistableRegistry.java @@ -13,6 +13,7 @@ import org.locationtech.geowave.core.index.dimension.BasicDimensionDefinition; import org.locationtech.geowave.core.index.dimension.UnboundedDimensionDefinition; import org.locationtech.geowave.core.index.persist.PersistableRegistrySpi; +import org.locationtech.geowave.core.index.sfc.BasicSFCIndexStrategy; import org.locationtech.geowave.core.index.sfc.SFCDimensionDefinition; import org.locationtech.geowave.core.index.sfc.data.BasicNumericDataset; import org.locationtech.geowave.core.index.sfc.data.BinnedNumericDataset; @@ -73,6 +74,7 @@ public PersistableIdAndConstructor[] getSupportedPersistables() { new PersistableIdAndConstructor((short) 130, SinglePartitionInsertionIds::new), new PersistableIdAndConstructor((short) 131, SimpleFloatIndexStrategy::new), new PersistableIdAndConstructor((short) 132, SimpleDoubleIndexStrategy::new), - new PersistableIdAndConstructor((short) 133, SimpleByteIndexStrategy::new),}; + new PersistableIdAndConstructor((short) 133, SimpleByteIndexStrategy::new), + new PersistableIdAndConstructor((short) 134, BasicSFCIndexStrategy::new),}; } } diff --git a/core/index/src/main/java/org/locationtech/geowave/core/index/sfc/BasicSFCIndexStrategy.java b/core/index/src/main/java/org/locationtech/geowave/core/index/sfc/BasicSFCIndexStrategy.java new file mode 100644 index 00000000000..a345bbf61fc --- /dev/null +++ b/core/index/src/main/java/org/locationtech/geowave/core/index/sfc/BasicSFCIndexStrategy.java @@ -0,0 +1,243 @@ +package org.locationtech.geowave.core.index.sfc; + +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import org.apache.log4j.Logger; +import org.locationtech.geowave.core.index.ByteArrayUtils; +import org.locationtech.geowave.core.index.IndexMetaData; +import org.locationtech.geowave.core.index.IndexUtils; +import org.locationtech.geowave.core.index.InsertionIds; +import org.locationtech.geowave.core.index.MultiDimensionalCoordinateRanges; +import org.locationtech.geowave.core.index.MultiDimensionalCoordinates; +import org.locationtech.geowave.core.index.NumericIndexStrategy; +import org.locationtech.geowave.core.index.QueryRanges; +import org.locationtech.geowave.core.index.SinglePartitionInsertionIds; +import org.locationtech.geowave.core.index.StringUtils; +import org.locationtech.geowave.core.index.VarintUtils; +import org.locationtech.geowave.core.index.dimension.NumericDimensionDefinition; +import org.locationtech.geowave.core.index.dimension.bin.BinRange; +import org.locationtech.geowave.core.index.persist.PersistenceUtils; +import org.locationtech.geowave.core.index.sfc.binned.BinnedSFCUtils; +import org.locationtech.geowave.core.index.sfc.data.BinnedNumericDataset; +import org.locationtech.geowave.core.index.sfc.data.MultiDimensionalNumericData; +import org.locationtech.geowave.core.index.sfc.tiered.SingleTierSubStrategy; +import org.locationtech.geowave.core.index.sfc.tiered.TieredSFCIndexStrategy; + +public class BasicSFCIndexStrategy implements NumericIndexStrategy { + private static final Logger LOGGER = Logger.getLogger(SingleTierSubStrategy.class); + private SpaceFillingCurve sfc; + private NumericDimensionDefinition[] baseDefinitions; + + public BasicSFCIndexStrategy() {} + + public BasicSFCIndexStrategy( + final SpaceFillingCurve sfc, + final NumericDimensionDefinition[] baseDefinitions) { + this.sfc = sfc; + this.baseDefinitions = baseDefinitions; + } + + @Override + public QueryRanges getQueryRanges( + final MultiDimensionalNumericData indexedRange, + final IndexMetaData... hints) { + return getQueryRanges(indexedRange, -1); + } + + @Override + public QueryRanges getQueryRanges( + final MultiDimensionalNumericData indexedRange, + final int maxRangeDecomposition, + final IndexMetaData... hints) { + final List binnedQueries = + BinnedNumericDataset.applyBins(indexedRange, baseDefinitions); + return new QueryRanges( + BinnedSFCUtils.getQueryRanges(binnedQueries, sfc, maxRangeDecomposition, null)); + } + + @Override + public MultiDimensionalNumericData getRangeForId( + final byte[] partitionKey, + final byte[] sortKey) { + final List insertionIds = + new SinglePartitionInsertionIds(partitionKey, sortKey).getCompositeInsertionIds(); + if (insertionIds.isEmpty()) { + LOGGER.warn("Unexpected empty insertion ID in getRangeForId()"); + return null; + } + final byte[] rowId = insertionIds.get(0); + return BinnedSFCUtils.getRangeForId(rowId, baseDefinitions, sfc); + } + + @Override + public MultiDimensionalCoordinates getCoordinatesPerDimension( + final byte[] partitionKey, + final byte[] sortKey) { + final byte[] rowId = + ByteArrayUtils.combineArrays( + partitionKey == null ? null : partitionKey, + sortKey == null ? null : sortKey); + return new MultiDimensionalCoordinates( + new byte[0], + BinnedSFCUtils.getCoordinatesForId(rowId, baseDefinitions, sfc)); + } + + @Override + public InsertionIds getInsertionIds(final MultiDimensionalNumericData indexedData) { + return getInsertionIds(indexedData, 1); + } + + @Override + public InsertionIds getInsertionIds( + final MultiDimensionalNumericData indexedData, + final int maxDuplicateInsertionIds) { + // we need to duplicate per bin so we can't adhere to max duplication + // anyways + final List ranges = + BinnedNumericDataset.applyBins(indexedData, baseDefinitions); + final Set retVal = new HashSet<>(ranges.size()); + for (final BinnedNumericDataset range : ranges) { + final SinglePartitionInsertionIds binRowIds = + TieredSFCIndexStrategy.getRowIdsAtTier(range, null, sfc, null, 0); + if (binRowIds != null) { + retVal.add(binRowIds); + } + } + return new InsertionIds(retVal); + } + + @Override + public NumericDimensionDefinition[] getOrderedDimensionDefinitions() { + return baseDefinitions; + } + + @Override + public String getId() { + return StringUtils.intToString(hashCode()); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = (prime * result) + Arrays.hashCode(baseDefinitions); + result = (prime * result) + ((sfc == null) ? 0 : sfc.hashCode()); + return result; + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final BasicSFCIndexStrategy other = (BasicSFCIndexStrategy) obj; + if (!Arrays.equals(baseDefinitions, other.baseDefinitions)) { + return false; + } + if (sfc == null) { + if (other.sfc != null) { + return false; + } + } else if (!sfc.equals(other.sfc)) { + return false; + } + return true; + } + + @Override + public byte[] toBinary() { + int byteBufferLength = VarintUtils.unsignedIntByteLength(baseDefinitions.length); + final List dimensionBinaries = new ArrayList<>(baseDefinitions.length); + final byte[] sfcBinary = PersistenceUtils.toBinary(sfc); + byteBufferLength += (VarintUtils.unsignedIntByteLength(sfcBinary.length) + sfcBinary.length); + for (final NumericDimensionDefinition dimension : baseDefinitions) { + final byte[] dimensionBinary = PersistenceUtils.toBinary(dimension); + byteBufferLength += + (VarintUtils.unsignedIntByteLength(dimensionBinary.length) + dimensionBinary.length); + dimensionBinaries.add(dimensionBinary); + } + final ByteBuffer buf = ByteBuffer.allocate(byteBufferLength); + VarintUtils.writeUnsignedInt(baseDefinitions.length, buf); + VarintUtils.writeUnsignedInt(sfcBinary.length, buf); + buf.put(sfcBinary); + for (final byte[] dimensionBinary : dimensionBinaries) { + VarintUtils.writeUnsignedInt(dimensionBinary.length, buf); + buf.put(dimensionBinary); + } + return buf.array(); + } + + @Override + public void fromBinary(final byte[] bytes) { + final ByteBuffer buf = ByteBuffer.wrap(bytes); + final int numDimensions = VarintUtils.readUnsignedInt(buf); + baseDefinitions = new NumericDimensionDefinition[numDimensions]; + final byte[] sfcBinary = new byte[VarintUtils.readUnsignedInt(buf)]; + buf.get(sfcBinary); + sfc = (SpaceFillingCurve) PersistenceUtils.fromBinary(sfcBinary); + for (int i = 0; i < numDimensions; i++) { + final byte[] dim = new byte[VarintUtils.readUnsignedInt(buf)]; + buf.get(dim); + baseDefinitions[i] = (NumericDimensionDefinition) PersistenceUtils.fromBinary(dim); + } + } + + @Override + public double[] getHighestPrecisionIdRangePerDimension() { + return sfc.getInsertionIdRangePerDimension(); + } + + @Override + public int getPartitionKeyLength() { + int rowIdOffset = 1; + for (int dimensionIdx = 0; dimensionIdx < baseDefinitions.length; dimensionIdx++) { + final int binSize = baseDefinitions[dimensionIdx].getFixedBinIdSize(); + if (binSize > 0) { + rowIdOffset += binSize; + } + } + return rowIdOffset; + } + + @Override + public List createMetaData() { + return Collections.emptyList(); + } + + @Override + public MultiDimensionalCoordinateRanges[] getCoordinateRangesPerDimension( + final MultiDimensionalNumericData dataRange, + final IndexMetaData... hints) { + final BinRange[][] binRangesPerDimension = + BinnedNumericDataset.getBinnedRangesPerDimension(dataRange, baseDefinitions); + return new MultiDimensionalCoordinateRanges[] { + BinnedSFCUtils.getCoordinateRanges( + binRangesPerDimension, + sfc, + baseDefinitions.length, + null)}; + } + + @Override + public byte[][] getInsertionPartitionKeys(final MultiDimensionalNumericData insertionData) { + return IndexUtils.getInsertionPartitionKeys(this, insertionData); + } + + @Override + public byte[][] getQueryPartitionKeys( + final MultiDimensionalNumericData queryData, + final IndexMetaData... hints) { + return IndexUtils.getQueryPartitionKeys(this, queryData, hints); + } +} diff --git a/core/index/src/main/java/org/locationtech/geowave/core/index/sfc/binned/BinnedSFCUtils.java b/core/index/src/main/java/org/locationtech/geowave/core/index/sfc/binned/BinnedSFCUtils.java index 3415bfef920..6b8c4e83e4f 100644 --- a/core/index/src/main/java/org/locationtech/geowave/core/index/sfc/binned/BinnedSFCUtils.java +++ b/core/index/src/main/java/org/locationtech/geowave/core/index/sfc/binned/BinnedSFCUtils.java @@ -37,7 +37,7 @@ public static List getQueryRanges( final List binnedQueries, final SpaceFillingCurve sfc, final int maxRanges, - final byte tier) { + final Byte tier) { final List queryRanges = new ArrayList<>(); int maxRangeDecompositionPerBin = maxRanges; @@ -48,10 +48,10 @@ public static List getQueryRanges( for (final BinnedNumericDataset binnedQuery : binnedQueries) { final RangeDecomposition rangeDecomp = sfc.decomposeRange(binnedQuery, true, maxRangeDecompositionPerBin); - final byte[] tierAndBinId = ByteArrayUtils.combineArrays(new byte[] {tier + final byte[] tierAndBinId = tier != null ? ByteArrayUtils.combineArrays(new byte[] {tier // we're assuming tiers only go to 127 (the max byte // value) - }, binnedQuery.getBinId()); + }, binnedQuery.getBinId()) : binnedQuery.getBinId(); queryRanges.add( new SinglePartitionQueryRanges(tierAndBinId, Arrays.asList(rangeDecomp.getRanges()))); @@ -63,7 +63,7 @@ public static MultiDimensionalCoordinateRanges getCoordinateRanges( final BinRange[][] binRangesPerDimension, final SpaceFillingCurve sfc, final int numDimensions, - final byte tier) { + final Byte tier) { final CoordinateRange[][] coordinateRangesPerDimension = new CoordinateRange[numDimensions][]; for (int d = 0; d < coordinateRangesPerDimension.length; d++) { coordinateRangesPerDimension[d] = new CoordinateRange[binRangesPerDimension[d].length]; @@ -77,17 +77,22 @@ public static MultiDimensionalCoordinateRanges getCoordinateRanges( new CoordinateRange(range[0], range[1], binRangesPerDimension[d][i].getBinId()); } } + if (tier == null) { + return new MultiDimensionalCoordinateRanges(new byte[0], coordinateRangesPerDimension); + } return new MultiDimensionalCoordinateRanges(new byte[] {tier}, coordinateRangesPerDimension); } public static SinglePartitionInsertionIds getSingleBinnedInsertionId( final BigInteger rowCount, - final byte multiDimensionalId, + final Byte multiDimensionalId, final BinnedNumericDataset index, final SpaceFillingCurve sfc) { if (rowCount.equals(BigInteger.ONE)) { final byte[] tierAndBinId = - ByteArrayUtils.combineArrays(new byte[] {multiDimensionalId}, index.getBinId()); + multiDimensionalId != null + ? ByteArrayUtils.combineArrays(new byte[] {multiDimensionalId}, index.getBinId()) + : index.getBinId(); final double[] minValues = index.getMinValuesPerDimension(); final double[] maxValues = index.getMaxValuesPerDimension(); byte[] singleId = null; diff --git a/core/index/src/main/java/org/locationtech/geowave/core/index/sfc/tiered/TieredSFCIndexStrategy.java b/core/index/src/main/java/org/locationtech/geowave/core/index/sfc/tiered/TieredSFCIndexStrategy.java index 864abe94fab..4560c286a3b 100644 --- a/core/index/src/main/java/org/locationtech/geowave/core/index/sfc/tiered/TieredSFCIndexStrategy.java +++ b/core/index/src/main/java/org/locationtech/geowave/core/index/sfc/tiered/TieredSFCIndexStrategy.java @@ -355,9 +355,9 @@ private synchronized SinglePartitionInsertionIds getRowIds( return new SinglePartitionInsertionIds(null, new ArrayList()); } - protected static SinglePartitionInsertionIds getRowIdsAtTier( + public static SinglePartitionInsertionIds getRowIdsAtTier( final BinnedNumericDataset index, - final byte tierId, + final Byte tierId, final SpaceFillingCurve sfc, final BigInteger maxEstimatedDuplicateIds, final int sfcIndex) { @@ -380,10 +380,12 @@ protected static SinglePartitionInsertionIds getRowIdsAtTier( protected static SinglePartitionInsertionIds decomposeRangesForEntry( final BinnedNumericDataset index, - final byte tierId, + final Byte tierId, final SpaceFillingCurve sfc) { final List retVal = new ArrayList<>(); - final byte[] tierAndBinId = ByteArrayUtils.combineArrays(new byte[] {tierId}, index.getBinId()); + final byte[] tierAndBinId = + tierId != null ? ByteArrayUtils.combineArrays(new byte[] {tierId}, index.getBinId()) + : index.getBinId(); final RangeDecomposition rangeDecomp = sfc.decomposeRange(index, false, DEFAULT_MAX_RANGES); // this range does not fit into a single row ID at the lowest // tier, decompose it diff --git a/core/store/src/main/java/org/locationtech/geowave/core/store/StorePersistableRegistry.java b/core/store/src/main/java/org/locationtech/geowave/core/store/StorePersistableRegistry.java index b9c9a307b3b..3de5410ea1f 100644 --- a/core/store/src/main/java/org/locationtech/geowave/core/store/StorePersistableRegistry.java +++ b/core/store/src/main/java/org/locationtech/geowave/core/store/StorePersistableRegistry.java @@ -38,7 +38,11 @@ import org.locationtech.geowave.core.store.query.aggregate.CountAggregation; import org.locationtech.geowave.core.store.query.aggregate.DataStatisticsAggregation; import org.locationtech.geowave.core.store.query.aggregate.MergingAggregation; +import org.locationtech.geowave.core.store.query.constraints.BasicOrderedConstraintQuery; +import org.locationtech.geowave.core.store.query.constraints.BasicOrderedConstraintQuery.OrderedConstraints; import org.locationtech.geowave.core.store.query.constraints.BasicQuery; +import org.locationtech.geowave.core.store.query.constraints.BasicQueryByClass; +import org.locationtech.geowave.core.store.query.constraints.BasicQueryByClass.ConstraintsByClass; import org.locationtech.geowave.core.store.query.constraints.CoordinateRangeQuery; import org.locationtech.geowave.core.store.query.constraints.DataIdQuery; import org.locationtech.geowave.core.store.query.constraints.DataIdRangeQuery; @@ -92,7 +96,7 @@ public PersistableIdAndConstructor[] getSupportedPersistables() { new PersistableIdAndConstructor((short) 225, TemporalIndexStrategy::new), new PersistableIdAndConstructor((short) 226, TextExactMatchFilter::new), new PersistableIdAndConstructor((short) 227, TextIndexStrategy::new), - new PersistableIdAndConstructor((short) 228, BasicQuery::new), + new PersistableIdAndConstructor((short) 228, BasicQueryByClass::new), new PersistableIdAndConstructor((short) 229, CoordinateRangeQuery::new), new PersistableIdAndConstructor((short) 230, CoordinateRangeQueryFilter::new), new PersistableIdAndConstructor((short) 231, CommonQueryOptions::new), @@ -120,6 +124,10 @@ public PersistableIdAndConstructor[] getSupportedPersistables() { new PersistableIdAndConstructor((short) 253, EverythingQuery::new), new PersistableIdAndConstructor((short) 254, SimpleRowTransform::new), new PersistableIdAndConstructor((short) 255, MergingAggregation::new), - new PersistableIdAndConstructor((short) 256, SimpleNumericQuery::new),}; + new PersistableIdAndConstructor((short) 256, SimpleNumericQuery::new), + new PersistableIdAndConstructor((short) 257, ConstraintsByClass::new), + new PersistableIdAndConstructor((short) 258, OrderedConstraints::new), + new PersistableIdAndConstructor((short) 259, BasicOrderedConstraintQuery::new), + new PersistableIdAndConstructor((short) 260, BasicQuery::new)}; } } diff --git a/core/store/src/main/java/org/locationtech/geowave/core/store/api/QueryConstraintsFactory.java b/core/store/src/main/java/org/locationtech/geowave/core/store/api/QueryConstraintsFactory.java index 12a712e2b61..7b12951220f 100644 --- a/core/store/src/main/java/org/locationtech/geowave/core/store/api/QueryConstraintsFactory.java +++ b/core/store/src/main/java/org/locationtech/geowave/core/store/api/QueryConstraintsFactory.java @@ -10,7 +10,7 @@ import org.locationtech.geowave.core.index.MultiDimensionalCoordinateRangesArray; import org.locationtech.geowave.core.index.NumericIndexStrategy; -import org.locationtech.geowave.core.store.query.constraints.BasicQuery.Constraints; +import org.locationtech.geowave.core.store.query.constraints.Constraints; import org.locationtech.geowave.core.store.query.constraints.QueryConstraints; import org.locationtech.geowave.core.store.query.filter.BasicQueryFilter.BasicQueryCompareOperation; diff --git a/core/store/src/main/java/org/locationtech/geowave/core/store/query/constraints/BasicOrderedConstraintQuery.java b/core/store/src/main/java/org/locationtech/geowave/core/store/query/constraints/BasicOrderedConstraintQuery.java new file mode 100644 index 00000000000..015c7eca063 --- /dev/null +++ b/core/store/src/main/java/org/locationtech/geowave/core/store/query/constraints/BasicOrderedConstraintQuery.java @@ -0,0 +1,170 @@ +package org.locationtech.geowave.core.store.query.constraints; + +import java.nio.ByteBuffer; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import org.apache.commons.lang3.Range; +import org.locationtech.geowave.core.index.StringUtils; +import org.locationtech.geowave.core.index.VarintUtils; +import org.locationtech.geowave.core.index.sfc.data.BasicNumericDataset; +import org.locationtech.geowave.core.index.sfc.data.MultiDimensionalNumericData; +import org.locationtech.geowave.core.index.sfc.data.NumericData; +import org.locationtech.geowave.core.index.sfc.data.NumericRange; +import org.locationtech.geowave.core.store.api.Index; +import org.locationtech.geowave.core.store.dimension.NumericDimensionField; +import org.locationtech.geowave.core.store.query.filter.BasicQueryFilter.BasicQueryCompareOperation; +import org.locationtech.geowave.core.store.query.filter.QueryFilter; + +public class BasicOrderedConstraintQuery extends BasicQuery { + + /** A list of Constraint Sets. Each Constraint Set is an individual hyper-cube query. */ + public static class OrderedConstraints implements Constraints { + private Range[] rangesPerDimension; + private String indexName; + + public OrderedConstraints() {} + + public OrderedConstraints(final Range rangePerDimension) { + this(new Range[] {rangePerDimension}, null); + } + + public OrderedConstraints(final Range[] rangesPerDimension) { + this(rangesPerDimension, null); + } + + public OrderedConstraints(final Range[] rangesPerDimension, final String indexName) { + this.rangesPerDimension = rangesPerDimension; + this.indexName = indexName; + } + + @Override + public byte[] toBinary() { + final byte[] indexNameBinary; + if (indexName != null) { + indexNameBinary = StringUtils.stringToBinary(indexName); + } else { + indexNameBinary = new byte[0]; + } + final ByteBuffer buf = + ByteBuffer.allocate( + VarintUtils.unsignedIntByteLength(rangesPerDimension.length) + + VarintUtils.unsignedIntByteLength(indexNameBinary.length) + + (16 * rangesPerDimension.length) + + indexNameBinary.length); + VarintUtils.writeUnsignedInt(rangesPerDimension.length, buf); + VarintUtils.writeUnsignedInt(indexNameBinary.length, buf); + for (int i = 0; i < rangesPerDimension.length; i++) { + buf.putDouble(rangesPerDimension[i].getMinimum()); + buf.putDouble(rangesPerDimension[i].getMaximum()); + } + buf.put(indexNameBinary); + return buf.array(); + } + + @Override + public void fromBinary(final byte[] bytes) { + final ByteBuffer buf = ByteBuffer.wrap(bytes); + rangesPerDimension = new Range[VarintUtils.readUnsignedInt(buf)]; + final byte[] indexNameBinary = new byte[VarintUtils.readUnsignedInt(buf)]; + for (int i = 0; i < rangesPerDimension.length; i++) { + rangesPerDimension[i] = Range.between(buf.getDouble(), buf.getDouble()); + } + if (indexNameBinary.length > 0) { + buf.get(indexNameBinary); + indexName = StringUtils.stringFromBinary(indexNameBinary); + } else { + indexName = null; + } + } + + @Override + public List getIndexConstraints(final Index index) { + if (((indexName == null) || indexName.equals(index.getName())) + && (index.getIndexStrategy().getOrderedDimensionDefinitions().length == rangesPerDimension.length)) { + return Collections.singletonList(getIndexConstraints()); + } + return Collections.emptyList(); + } + + protected MultiDimensionalNumericData getIndexConstraints() { + return new BasicNumericDataset( + Arrays.stream(rangesPerDimension).map( + r -> new NumericRange(r.getMinimum(), r.getMaximum())).toArray( + i -> new NumericData[i])); + } + + @Override + public List createFilters(final Index index, final BasicQuery parentQuery) { + final QueryFilter filter = + parentQuery.createQueryFilter( + getIndexConstraints(), + index.getIndexModel().getDimensions(), + new NumericDimensionField[0], + index); + if (filter != null) { + return Collections.singletonList(filter); + } + return Collections.emptyList(); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = (prime * result) + ((indexName == null) ? 0 : indexName.hashCode()); + result = (prime * result) + Arrays.hashCode(rangesPerDimension); + return result; + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final OrderedConstraints other = (OrderedConstraints) obj; + if (indexName == null) { + if (other.indexName != null) { + return false; + } + } else if (!indexName.equals(other.indexName)) { + return false; + } + if (!Arrays.equals(rangesPerDimension, other.rangesPerDimension)) { + return false; + } + return true; + } + + } + + public BasicOrderedConstraintQuery() {} + + public BasicOrderedConstraintQuery(final OrderedConstraints constraints) { + super(constraints); + } + + + public BasicOrderedConstraintQuery( + final OrderedConstraints constraints, + final BasicQueryCompareOperation compareOp) { + super(constraints, compareOp); + } + + @Override + public byte[] toBinary() { + return constraints.toBinary(); + } + + @Override + public void fromBinary(final byte[] bytes) { + constraints = new OrderedConstraints(); + constraints.fromBinary(bytes); + } +} diff --git a/core/store/src/main/java/org/locationtech/geowave/core/store/query/constraints/BasicQuery.java b/core/store/src/main/java/org/locationtech/geowave/core/store/query/constraints/BasicQuery.java index 3dfe6e1d758..606cf25e6f8 100644 --- a/core/store/src/main/java/org/locationtech/geowave/core/store/query/constraints/BasicQuery.java +++ b/core/store/src/main/java/org/locationtech/geowave/core/store/query/constraints/BasicQuery.java @@ -8,473 +8,20 @@ */ package org.locationtech.geowave.core.store.query.constraints; -import java.nio.ByteBuffer; -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.LinkedList; import java.util.List; -import java.util.Map; -import java.util.Map.Entry; -import java.util.Set; -import org.locationtech.geowave.core.index.NumericIndexStrategy; -import org.locationtech.geowave.core.index.StringUtils; -import org.locationtech.geowave.core.index.VarintUtils; -import org.locationtech.geowave.core.index.dimension.NumericDimensionDefinition; -import org.locationtech.geowave.core.index.sfc.data.BasicNumericDataset; +import org.locationtech.geowave.core.index.persist.PersistenceUtils; import org.locationtech.geowave.core.index.sfc.data.MultiDimensionalNumericData; -import org.locationtech.geowave.core.index.sfc.data.NumericData; -import org.locationtech.geowave.core.index.sfc.data.NumericRange; import org.locationtech.geowave.core.store.api.Index; import org.locationtech.geowave.core.store.dimension.NumericDimensionField; -import org.locationtech.geowave.core.store.index.CommonIndexModel; import org.locationtech.geowave.core.store.query.filter.BasicQueryFilter; import org.locationtech.geowave.core.store.query.filter.BasicQueryFilter.BasicQueryCompareOperation; -import org.locationtech.geowave.core.store.query.filter.FilterList; import org.locationtech.geowave.core.store.query.filter.QueryFilter; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import com.google.common.math.DoubleMath; -/** - * The Basic Query class represent a hyper-cube(s) query across all dimensions that match the - * Constraints passed into the constructor - * - *

NOTE: query to an index that requires a constraint and the constraint is missing within the - * query equates to an unconstrained index scan. The query filter is still applied. - */ public class BasicQuery implements QueryConstraints { - private static final double DOUBLE_TOLERANCE = 1E-12d; - private static final Logger LOGGER = LoggerFactory.getLogger(BasicQuery.class); - protected boolean exact = true; - - /** A set of constraints, one range per dimension */ - public static class ConstraintSet { - protected Map, ConstraintData> constraintsPerTypeOfDimensionDefinition; - - public ConstraintSet() { - constraintsPerTypeOfDimensionDefinition = new HashMap<>(); - } - - public ConstraintSet( - final Map, ConstraintData> constraintsPerTypeOfDimensionDefinition) { - this.constraintsPerTypeOfDimensionDefinition = constraintsPerTypeOfDimensionDefinition; - } - - public ConstraintSet( - final Class dimDefinition, - final ConstraintData constraintData) { - this(); - addConstraint(dimDefinition, constraintData); - } - - public void addConstraint( - final Class dimDefinition, - final ConstraintData constraintData) { - final ConstraintData myCd = constraintsPerTypeOfDimensionDefinition.get(dimDefinition); - if (myCd != null) { - constraintsPerTypeOfDimensionDefinition.put(dimDefinition, myCd.merge(constraintData)); - } else { - constraintsPerTypeOfDimensionDefinition.put(dimDefinition, constraintData); - } - } - - public ConstraintSet merge(final ConstraintSet constraintSet) { - final Map, ConstraintData> newSet = - new HashMap<>(); - - for (final Map.Entry, ConstraintData> entry : constraintSet.constraintsPerTypeOfDimensionDefinition.entrySet()) { - final ConstraintData data = constraintsPerTypeOfDimensionDefinition.get(entry.getKey()); - - if (data == null) { - newSet.put(entry.getKey(), entry.getValue()); - } else { - newSet.put(entry.getKey(), data.merge(entry.getValue())); - } - } - for (final Map.Entry, ConstraintData> entry : constraintsPerTypeOfDimensionDefinition.entrySet()) { - final ConstraintData data = - constraintSet.constraintsPerTypeOfDimensionDefinition.get(entry.getKey()); - - if (data == null) { - newSet.put(entry.getKey(), entry.getValue()); - } - } - return new ConstraintSet(newSet); - } - - public boolean isEmpty() { - return constraintsPerTypeOfDimensionDefinition.isEmpty(); - } - - public boolean matches(final ConstraintSet constraints) { - if (constraints.isEmpty() != isEmpty()) { - return false; - } - for (final Map.Entry, ConstraintData> entry : constraintsPerTypeOfDimensionDefinition.entrySet()) { - final ConstraintData data = - constraints.constraintsPerTypeOfDimensionDefinition.get(entry.getKey()); - if ((data == null) || !data.matches(entry.getValue())) { - return false; - } - } - return true; - } - - /** - * @param constraints - * @return true if all dimensions intersect - */ - public boolean intersects(final ConstraintSet constraints) { - if (constraints.isEmpty() != isEmpty()) { - return true; - } - boolean intersects = true; - for (final Map.Entry, ConstraintData> entry : constraintsPerTypeOfDimensionDefinition.entrySet()) { - final ConstraintData data = - constraints.constraintsPerTypeOfDimensionDefinition.get(entry.getKey()); - intersects &= ((data != null) && data.intersects(entry.getValue())); - } - return intersects; - } - - /* - * Makes the decision to provide a empty data set if an one dimension is left unconstrained. - */ - public MultiDimensionalNumericData getIndexConstraints( - final NumericIndexStrategy indexStrategy) { - if (constraintsPerTypeOfDimensionDefinition.isEmpty()) { - return new BasicNumericDataset(); - } - - final NumericDimensionDefinition[] dimensionDefinitions = - indexStrategy.getOrderedDimensionDefinitions(); - final NumericData[] dataPerDimension = new NumericData[dimensionDefinitions.length]; - // all or nothing...for now - for (int d = 0; d < dimensionDefinitions.length; d++) { - final ConstraintData dimConstraint = - constraintsPerTypeOfDimensionDefinition.get(dimensionDefinitions[d].getClass()); - if (dimConstraint == null) { - return new BasicNumericDataset(); - } - dataPerDimension[d] = dimConstraint.range; - } - return new BasicNumericDataset(dataPerDimension); - } - - protected QueryFilter createFilter(final Index index, final BasicQuery basicQuery) { - final CommonIndexModel indexModel = index.getIndexModel(); - final NumericDimensionField[] dimensionFields = indexModel.getDimensions(); - NumericDimensionField[] orderedConstrainedDimensionFields = dimensionFields; - NumericDimensionField[] unconstrainedDimensionFields; - NumericData[] orderedConstraintsPerDimension = new NumericData[dimensionFields.length]; - // trim dimension fields to be only what is contained in the - // constraints - final Set fieldsToTrim = new HashSet<>(); - for (int d = 0; d < dimensionFields.length; d++) { - final ConstraintData nd = - constraintsPerTypeOfDimensionDefinition.get( - dimensionFields[d].getBaseDefinition().getClass()); - if (nd == null) { - fieldsToTrim.add(d); - } else { - orderedConstraintsPerDimension[d] = - constraintsPerTypeOfDimensionDefinition.get( - dimensionFields[d].getBaseDefinition().getClass()).range; - } - } - if (!fieldsToTrim.isEmpty()) { - final NumericDimensionField[] newDimensionFields = - new NumericDimensionField[dimensionFields.length - fieldsToTrim.size()]; - - unconstrainedDimensionFields = new NumericDimensionField[fieldsToTrim.size()]; - final NumericData[] newOrderedConstraintsPerDimension = - new NumericData[newDimensionFields.length]; - int newDimensionCtr = 0; - int constrainedCtr = 0; - for (int i = 0; i < dimensionFields.length; i++) { - if (!fieldsToTrim.contains(i)) { - newDimensionFields[newDimensionCtr] = dimensionFields[i]; - newOrderedConstraintsPerDimension[newDimensionCtr++] = - orderedConstraintsPerDimension[i]; - } else { - unconstrainedDimensionFields[constrainedCtr++] = dimensionFields[i]; - } - } - orderedConstrainedDimensionFields = newDimensionFields; - orderedConstraintsPerDimension = newOrderedConstraintsPerDimension; - } else { - unconstrainedDimensionFields = new NumericDimensionField[] {}; - } - return basicQuery.createQueryFilter( - new BasicNumericDataset(orderedConstraintsPerDimension), - orderedConstrainedDimensionFields, - unconstrainedDimensionFields, - index); - } - - public byte[] toBinary() { - final List bytes = new ArrayList<>(constraintsPerTypeOfDimensionDefinition.size()); - int totalBytes = VarintUtils.unsignedIntByteLength(bytes.size()); - for (final Entry, ConstraintData> c : constraintsPerTypeOfDimensionDefinition.entrySet()) { - final byte[] className = StringUtils.stringToBinary(c.getKey().getName()); - final double min = c.getValue().range.getMin(); - final double max = c.getValue().range.getMax(); - final int entryLength = - className.length + 17 + VarintUtils.unsignedIntByteLength(className.length); - final byte isDefault = (byte) (c.getValue().isDefault ? 1 : 0); - final ByteBuffer entryBuf = ByteBuffer.allocate(entryLength); - VarintUtils.writeUnsignedInt(className.length, entryBuf); - entryBuf.put(className); - entryBuf.putDouble(min); - entryBuf.putDouble(max); - entryBuf.put(isDefault); - bytes.add(entryBuf.array()); - totalBytes += entryLength; - } - - final ByteBuffer buf = ByteBuffer.allocate(totalBytes); - VarintUtils.writeUnsignedInt(bytes.size(), buf); - for (final byte[] entryBytes : bytes) { - buf.put(entryBytes); - } - return buf.array(); - } - - public void fromBinary(final byte[] bytes) { - final ByteBuffer buf = ByteBuffer.wrap(bytes); - final int numEntries = VarintUtils.readUnsignedInt(buf); - final Map, ConstraintData> constraintsPerTypeOfDimensionDefinition = - new HashMap<>(numEntries); - for (int i = 0; i < numEntries; i++) { - final int classNameLength = VarintUtils.readUnsignedInt(buf); - final byte[] className = new byte[classNameLength]; - buf.get(className); - final double min = buf.getDouble(); - final double max = buf.getDouble(); - final boolean isDefault = buf.get() > 0; - final String classNameStr = StringUtils.stringFromBinary(className); - try { - final Class cls = - (Class) Class.forName(classNameStr); - constraintsPerTypeOfDimensionDefinition.put( - cls, - new ConstraintData(new NumericRange(min, max), isDefault)); - } catch (final ClassNotFoundException e) { - // HP Fortify "Improper Output Neutralization" false - // positive - // What Fortify considers "user input" comes only - // from users with OS-level access anyway - LOGGER.warn("Cannot find dimension definition class: " + classNameStr, e); - } - } - this.constraintsPerTypeOfDimensionDefinition = constraintsPerTypeOfDimensionDefinition; - } - } - - public static class ConstraintData { - protected NumericData range; - protected boolean isDefault; - - public ConstraintData(final NumericData range, final boolean isDefault) { - super(); - this.range = range; - this.isDefault = isDefault; - } - - public boolean intersects(final ConstraintData cd) { - final double i1 = cd.range.getMin(); - final double i2 = cd.range.getMax(); - final double j1 = range.getMin(); - final double j2 = range.getMax(); - return ((i1 < j2) || DoubleMath.fuzzyEquals(i1, j2, DOUBLE_TOLERANCE)) - && ((i2 > j1) || DoubleMath.fuzzyEquals(i2, j1, DOUBLE_TOLERANCE)); - } - - public ConstraintData merge(final ConstraintData cd) { - if (range.equals(cd.range)) { - return new ConstraintData(range, isDefault); - } - return new ConstraintData( - new NumericRange( - Math.min(cd.range.getMin(), range.getMin()), - Math.max(cd.range.getMax(), range.getMax())), - false); // TODO: ideally, this would be set - // based on some - // logic - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = (prime * result) + (isDefault ? 1231 : 1237); - result = (prime * result) + ((range == null) ? 0 : range.hashCode()); - return result; - } - - @Override - public boolean equals(final Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - final ConstraintData other = (ConstraintData) obj; - if (isDefault != other.isDefault) { - return false; - } - if (range == null) { - if (other.range != null) { - return false; - } - } else if (!range.equals(other.range)) { - return false; - } - return true; - } - - /** - * Ignores 'default' indicator - * - * @param other - * @return - */ - public boolean matches(final ConstraintData other) { - if (this == other) { - return true; - } - - if (range == null) { - if (other.range != null) { - return false; - } - } else if (!DoubleMath.fuzzyEquals(range.getMin(), other.range.getMin(), DOUBLE_TOLERANCE) - || !DoubleMath.fuzzyEquals(range.getMax(), other.range.getMax(), DOUBLE_TOLERANCE)) { - return false; - } - return true; - } - } - - /** A list of Constraint Sets. Each Constraint Set is an individual hyper-cube query. */ - public static class Constraints { - // these basic queries are tied to NumericDimensionDefinition types, not - // ideal, but third-parties can and will nned to implement their own - // queries if they implement their own dimension definitions - private final List constraintsSets = new LinkedList<>(); - - public Constraints() {} - - public Constraints(final ConstraintSet constraintSet) { - constraintsSets.add(constraintSet); - } - - public Constraints(final List constraintSets) { - constraintsSets.addAll(constraintSets); - } - - public Constraints merge(final Constraints constraints) { - return merge(constraints.constraintsSets); - } - - public Constraints merge(final List otherConstraintSets) { - - if (otherConstraintSets.isEmpty()) { - return this; - } else if (isEmpty()) { - return new Constraints(otherConstraintSets); - } - final List newSets = new LinkedList<>(); - - for (final ConstraintSet newSet : otherConstraintSets) { - add(newSets, constraintsSets, newSet); - } - return new Constraints(newSets); - } - - private static void add( - final List newSets, - final List currentSets, - final ConstraintSet newSet) { - for (final ConstraintSet cs : currentSets) { - newSets.add(cs.merge(newSet)); - } - } - - public boolean isEmpty() { - return constraintsSets.isEmpty(); - } - - public boolean matches(final Constraints constraints) { - if (constraints.isEmpty() != isEmpty()) { - return false; - } - for (final ConstraintSet set : constraintsSets) { - boolean foundMatch = false; - for (final ConstraintSet otherSet : constraints.constraintsSets) { - foundMatch |= set.matches(otherSet); - } - if (!foundMatch) { - return false; - } - } - return true; - } - - @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = (prime * result) + ((constraintsSets == null) ? 0 : constraintsSets.hashCode()); - return result; - } - - @Override - public boolean equals(final Object obj) { - if (this == obj) { - return true; - } - if (obj == null) { - return false; - } - if (getClass() != obj.getClass()) { - return false; - } - final Constraints other = (Constraints) obj; - if (constraintsSets == null) { - if (other.constraintsSets != null) { - return false; - } - } else if (!constraintsSets.equals(other.constraintsSets)) { - return false; - } - return true; - } - - public List getIndexConstraints( - final NumericIndexStrategy indexStrategy) { - if (constraintsSets.isEmpty()) { - return Collections.emptyList(); - } - final List setRanges = new ArrayList<>(constraintsSets.size()); - for (final ConstraintSet set : constraintsSets) { - final MultiDimensionalNumericData mdSet = set.getIndexConstraints(indexStrategy); - if (!mdSet.isEmpty()) { - setRanges.add(mdSet); - } - } - return setRanges; - } - } - - private Constraints constraints; - BasicQueryCompareOperation compareOp = BasicQueryCompareOperation.INTERSECTS; + protected Constraints constraints; + // compare OP doesn't need to be serialized because its only used clientside to generate the query + // filter + protected transient BasicQueryCompareOperation compareOp = BasicQueryCompareOperation.INTERSECTS; public BasicQuery() {} @@ -491,18 +38,7 @@ public BasicQuery(final Constraints constraints, final BasicQueryCompareOperatio @Override public List createFilters(final Index index) { - final List filters = new ArrayList<>(); - for (final ConstraintSet constraint : constraints.constraintsSets) { - final QueryFilter filter = constraint.createFilter(index, this); - if (filter != null) { - filters.add(filter); - } - } - if (!filters.isEmpty()) { - return Collections.singletonList( - filters.size() == 1 ? filters.get(0) : new FilterList(false, filters)); - } - return Collections.emptyList(); + return constraints.createFilters(index, this); } protected QueryFilter createQueryFilter( @@ -514,50 +50,17 @@ protected QueryFilter createQueryFilter( } @Override - public List getIndexConstraints(final Index primaryIndex) { - return constraints.getIndexConstraints(primaryIndex.getIndexStrategy()); + public List getIndexConstraints(final Index index) { + return constraints.getIndexConstraints(index); } @Override public byte[] toBinary() { - final List bytes = new ArrayList<>(constraints.constraintsSets.size()); - int totalBytes = 0; - for (final ConstraintSet c : constraints.constraintsSets) { - bytes.add(c.toBinary()); - final int length = bytes.get(bytes.size() - 1).length; - totalBytes += (length + VarintUtils.unsignedIntByteLength(length)); - } - - final ByteBuffer buf = - ByteBuffer.allocate(totalBytes + VarintUtils.unsignedIntByteLength(bytes.size())); - VarintUtils.writeUnsignedInt(bytes.size(), buf); - for (final byte[] entryBytes : bytes) { - VarintUtils.writeUnsignedInt(entryBytes.length, buf); - buf.put(entryBytes); - } - return buf.array(); + return PersistenceUtils.toBinary(constraints); } @Override public void fromBinary(final byte[] bytes) { - final ByteBuffer buf = ByteBuffer.wrap(bytes); - final int numEntries = VarintUtils.readUnsignedInt(buf); - final List sets = new LinkedList<>(); - for (int i = 0; i < numEntries; i++) { - final byte[] d = new byte[VarintUtils.readUnsignedInt(buf)]; - buf.get(d); - final ConstraintSet cs = new ConstraintSet(); - cs.fromBinary(d); - sets.add(cs); - } - constraints = new Constraints(sets); - } - - public boolean isExact() { - return exact; - } - - public void setExact(final boolean exact) { - this.exact = exact; + constraints = (Constraints) PersistenceUtils.fromBinary(bytes); } } diff --git a/core/store/src/main/java/org/locationtech/geowave/core/store/query/constraints/BasicQueryByClass.java b/core/store/src/main/java/org/locationtech/geowave/core/store/query/constraints/BasicQueryByClass.java new file mode 100644 index 00000000000..fd92db0d0e6 --- /dev/null +++ b/core/store/src/main/java/org/locationtech/geowave/core/store/query/constraints/BasicQueryByClass.java @@ -0,0 +1,567 @@ +/** + * Copyright (c) 2013-2019 Contributors to the Eclipse Foundation + * + *

See the NOTICE file distributed with this work for additional information regarding copyright + * ownership. All rights reserved. This program and the accompanying materials are made available + * under the terms of the Apache License, Version 2.0 which accompanies this distribution and is + * available at http://www.apache.org/licenses/LICENSE-2.0.txt + */ +package org.locationtech.geowave.core.store.query.constraints; + +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import org.locationtech.geowave.core.index.NumericIndexStrategy; +import org.locationtech.geowave.core.index.StringUtils; +import org.locationtech.geowave.core.index.VarintUtils; +import org.locationtech.geowave.core.index.dimension.NumericDimensionDefinition; +import org.locationtech.geowave.core.index.sfc.data.BasicNumericDataset; +import org.locationtech.geowave.core.index.sfc.data.MultiDimensionalNumericData; +import org.locationtech.geowave.core.index.sfc.data.NumericData; +import org.locationtech.geowave.core.index.sfc.data.NumericRange; +import org.locationtech.geowave.core.store.api.Index; +import org.locationtech.geowave.core.store.dimension.NumericDimensionField; +import org.locationtech.geowave.core.store.index.CommonIndexModel; +import org.locationtech.geowave.core.store.query.filter.BasicQueryFilter.BasicQueryCompareOperation; +import org.locationtech.geowave.core.store.query.filter.FilterList; +import org.locationtech.geowave.core.store.query.filter.QueryFilter; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import com.google.common.math.DoubleMath; + +/** + * The Basic Query class represent a hyper-cube(s) query across all dimensions that match the + * Constraints passed into the constructor + * + *

NOTE: query to an index that requires a constraint and the constraint is missing within the + * query equates to an unconstrained index scan. The query filter is still applied. + */ +public class BasicQueryByClass extends BasicQuery { + private static final double DOUBLE_TOLERANCE = 1E-12d; + private static final Logger LOGGER = LoggerFactory.getLogger(BasicQueryByClass.class); + + /** A set of constraints, one range per dimension */ + public static class ConstraintSet { + protected Map, ConstraintData> constraintsPerTypeOfDimensionDefinition; + + public ConstraintSet() { + constraintsPerTypeOfDimensionDefinition = new HashMap<>(); + } + + public ConstraintSet( + final Map, ConstraintData> constraintsPerTypeOfDimensionDefinition) { + this.constraintsPerTypeOfDimensionDefinition = constraintsPerTypeOfDimensionDefinition; + } + + public ConstraintSet( + final Class dimDefinition, + final ConstraintData constraintData) { + this(); + addConstraint(dimDefinition, constraintData); + } + + public void addConstraint( + final Class dimDefinition, + final ConstraintData constraintData) { + final ConstraintData myCd = constraintsPerTypeOfDimensionDefinition.get(dimDefinition); + if (myCd != null) { + constraintsPerTypeOfDimensionDefinition.put(dimDefinition, myCd.merge(constraintData)); + } else { + constraintsPerTypeOfDimensionDefinition.put(dimDefinition, constraintData); + } + } + + public ConstraintSet merge(final ConstraintSet constraintSet) { + final Map, ConstraintData> newSet = + new HashMap<>(); + + for (final Map.Entry, ConstraintData> entry : constraintSet.constraintsPerTypeOfDimensionDefinition.entrySet()) { + final ConstraintData data = constraintsPerTypeOfDimensionDefinition.get(entry.getKey()); + + if (data == null) { + newSet.put(entry.getKey(), entry.getValue()); + } else { + newSet.put(entry.getKey(), data.merge(entry.getValue())); + } + } + for (final Map.Entry, ConstraintData> entry : constraintsPerTypeOfDimensionDefinition.entrySet()) { + final ConstraintData data = + constraintSet.constraintsPerTypeOfDimensionDefinition.get(entry.getKey()); + + if (data == null) { + newSet.put(entry.getKey(), entry.getValue()); + } + } + return new ConstraintSet(newSet); + } + + public boolean isEmpty() { + return constraintsPerTypeOfDimensionDefinition.isEmpty(); + } + + public boolean matches(final ConstraintSet constraints) { + if (constraints.isEmpty() != isEmpty()) { + return false; + } + for (final Map.Entry, ConstraintData> entry : constraintsPerTypeOfDimensionDefinition.entrySet()) { + final ConstraintData data = + constraints.constraintsPerTypeOfDimensionDefinition.get(entry.getKey()); + if ((data == null) || !data.matches(entry.getValue())) { + return false; + } + } + return true; + } + + /** + * @param constraints + * @return true if all dimensions intersect + */ + public boolean intersects(final ConstraintSet constraints) { + if (constraints.isEmpty() != isEmpty()) { + return true; + } + boolean intersects = true; + for (final Map.Entry, ConstraintData> entry : constraintsPerTypeOfDimensionDefinition.entrySet()) { + final ConstraintData data = + constraints.constraintsPerTypeOfDimensionDefinition.get(entry.getKey()); + intersects &= ((data != null) && data.intersects(entry.getValue())); + } + return intersects; + } + + /* + * Makes the decision to provide a empty data set if an one dimension is left unconstrained. + */ + public MultiDimensionalNumericData getIndexConstraints( + final NumericIndexStrategy indexStrategy) { + if (constraintsPerTypeOfDimensionDefinition.isEmpty()) { + return new BasicNumericDataset(); + } + + final NumericDimensionDefinition[] dimensionDefinitions = + indexStrategy.getOrderedDimensionDefinitions(); + final NumericData[] dataPerDimension = new NumericData[dimensionDefinitions.length]; + // all or nothing...for now + for (int d = 0; d < dimensionDefinitions.length; d++) { + final ConstraintData dimConstraint = + constraintsPerTypeOfDimensionDefinition.get(dimensionDefinitions[d].getClass()); + if (dimConstraint == null) { + return new BasicNumericDataset(); + } + dataPerDimension[d] = dimConstraint.range; + } + return new BasicNumericDataset(dataPerDimension); + } + + protected QueryFilter createFilter(final Index index, final BasicQuery basicQuery) { + final CommonIndexModel indexModel = index.getIndexModel(); + final NumericDimensionField[] dimensionFields = indexModel.getDimensions(); + NumericDimensionField[] orderedConstrainedDimensionFields = dimensionFields; + NumericDimensionField[] unconstrainedDimensionFields; + NumericData[] orderedConstraintsPerDimension = new NumericData[dimensionFields.length]; + // trim dimension fields to be only what is contained in the + // constraints + final Set fieldsToTrim = new HashSet<>(); + for (int d = 0; d < dimensionFields.length; d++) { + final ConstraintData nd = + constraintsPerTypeOfDimensionDefinition.get( + dimensionFields[d].getBaseDefinition().getClass()); + if (nd == null) { + fieldsToTrim.add(d); + } else { + orderedConstraintsPerDimension[d] = + constraintsPerTypeOfDimensionDefinition.get( + dimensionFields[d].getBaseDefinition().getClass()).range; + } + } + if (!fieldsToTrim.isEmpty()) { + final NumericDimensionField[] newDimensionFields = + new NumericDimensionField[dimensionFields.length - fieldsToTrim.size()]; + + unconstrainedDimensionFields = new NumericDimensionField[fieldsToTrim.size()]; + final NumericData[] newOrderedConstraintsPerDimension = + new NumericData[newDimensionFields.length]; + int newDimensionCtr = 0; + int constrainedCtr = 0; + for (int i = 0; i < dimensionFields.length; i++) { + if (!fieldsToTrim.contains(i)) { + newDimensionFields[newDimensionCtr] = dimensionFields[i]; + newOrderedConstraintsPerDimension[newDimensionCtr++] = + orderedConstraintsPerDimension[i]; + } else { + unconstrainedDimensionFields[constrainedCtr++] = dimensionFields[i]; + } + } + orderedConstrainedDimensionFields = newDimensionFields; + orderedConstraintsPerDimension = newOrderedConstraintsPerDimension; + } else { + unconstrainedDimensionFields = new NumericDimensionField[] {}; + } + return basicQuery.createQueryFilter( + new BasicNumericDataset(orderedConstraintsPerDimension), + orderedConstrainedDimensionFields, + unconstrainedDimensionFields, + index); + } + + public byte[] toBinary() { + final List bytes = new ArrayList<>(constraintsPerTypeOfDimensionDefinition.size()); + int totalBytes = VarintUtils.unsignedIntByteLength(bytes.size()); + for (final Entry, ConstraintData> c : constraintsPerTypeOfDimensionDefinition.entrySet()) { + final byte[] className = StringUtils.stringToBinary(c.getKey().getName()); + final double min = c.getValue().range.getMin(); + final double max = c.getValue().range.getMax(); + final int entryLength = + className.length + 17 + VarintUtils.unsignedIntByteLength(className.length); + final byte isDefault = (byte) (c.getValue().isDefault ? 1 : 0); + final ByteBuffer entryBuf = ByteBuffer.allocate(entryLength); + VarintUtils.writeUnsignedInt(className.length, entryBuf); + entryBuf.put(className); + entryBuf.putDouble(min); + entryBuf.putDouble(max); + entryBuf.put(isDefault); + bytes.add(entryBuf.array()); + totalBytes += entryLength; + } + + final ByteBuffer buf = ByteBuffer.allocate(totalBytes); + VarintUtils.writeUnsignedInt(bytes.size(), buf); + for (final byte[] entryBytes : bytes) { + buf.put(entryBytes); + } + return buf.array(); + } + + public void fromBinary(final byte[] bytes) { + final ByteBuffer buf = ByteBuffer.wrap(bytes); + final int numEntries = VarintUtils.readUnsignedInt(buf); + final Map, ConstraintData> constraintsPerTypeOfDimensionDefinition = + new HashMap<>(numEntries); + for (int i = 0; i < numEntries; i++) { + final int classNameLength = VarintUtils.readUnsignedInt(buf); + final byte[] className = new byte[classNameLength]; + buf.get(className); + final double min = buf.getDouble(); + final double max = buf.getDouble(); + final boolean isDefault = buf.get() > 0; + final String classNameStr = StringUtils.stringFromBinary(className); + try { + final Class cls = + (Class) Class.forName(classNameStr); + constraintsPerTypeOfDimensionDefinition.put( + cls, + new ConstraintData(new NumericRange(min, max), isDefault)); + } catch (final ClassNotFoundException e) { + // HP Fortify "Improper Output Neutralization" false + // positive + // What Fortify considers "user input" comes only + // from users with OS-level access anyway + LOGGER.warn("Cannot find dimension definition class: " + classNameStr, e); + } + } + this.constraintsPerTypeOfDimensionDefinition = constraintsPerTypeOfDimensionDefinition; + } + } + + public static class ConstraintData { + protected NumericData range; + protected boolean isDefault; + + public ConstraintData(final NumericData range, final boolean isDefault) { + super(); + this.range = range; + this.isDefault = isDefault; + } + + public boolean intersects(final ConstraintData cd) { + final double i1 = cd.range.getMin(); + final double i2 = cd.range.getMax(); + final double j1 = range.getMin(); + final double j2 = range.getMax(); + return ((i1 < j2) || DoubleMath.fuzzyEquals(i1, j2, DOUBLE_TOLERANCE)) + && ((i2 > j1) || DoubleMath.fuzzyEquals(i2, j1, DOUBLE_TOLERANCE)); + } + + public ConstraintData merge(final ConstraintData cd) { + if (range.equals(cd.range)) { + return new ConstraintData(range, isDefault); + } + return new ConstraintData( + new NumericRange( + Math.min(cd.range.getMin(), range.getMin()), + Math.max(cd.range.getMax(), range.getMax())), + false); // TODO: ideally, this would be set + // based on some + // logic + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = (prime * result) + (isDefault ? 1231 : 1237); + result = (prime * result) + ((range == null) ? 0 : range.hashCode()); + return result; + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final ConstraintData other = (ConstraintData) obj; + if (isDefault != other.isDefault) { + return false; + } + if (range == null) { + if (other.range != null) { + return false; + } + } else if (!range.equals(other.range)) { + return false; + } + return true; + } + + /** + * Ignores 'default' indicator + * + * @param other + * @return + */ + public boolean matches(final ConstraintData other) { + if (this == other) { + return true; + } + + if (range == null) { + if (other.range != null) { + return false; + } + } else if (!DoubleMath.fuzzyEquals(range.getMin(), other.range.getMin(), DOUBLE_TOLERANCE) + || !DoubleMath.fuzzyEquals(range.getMax(), other.range.getMax(), DOUBLE_TOLERANCE)) { + return false; + } + return true; + } + } + + /** A list of Constraint Sets. Each Constraint Set is an individual hyper-cube query. */ + public static class ConstraintsByClass implements Constraints { + // these basic queries are tied to NumericDimensionDefinition types, not + // ideal, but third-parties can and will nned to implement their own + // queries if they implement their own dimension definitions + private List constraintsSets = new LinkedList<>(); + + public ConstraintsByClass() {} + + public ConstraintsByClass(final ConstraintSet constraintSet) { + constraintsSets.add(constraintSet); + } + + public ConstraintsByClass(final List constraintSets) { + constraintsSets.addAll(constraintSets); + } + + public ConstraintsByClass merge(final ConstraintsByClass constraints) { + return merge(constraints.constraintsSets); + } + + public ConstraintsByClass merge(final List otherConstraintSets) { + + if (otherConstraintSets.isEmpty()) { + return this; + } else if (isEmpty()) { + return new ConstraintsByClass(otherConstraintSets); + } + final List newSets = new LinkedList<>(); + + for (final ConstraintSet newSet : otherConstraintSets) { + add(newSets, constraintsSets, newSet); + } + return new ConstraintsByClass(newSets); + } + + private static void add( + final List newSets, + final List currentSets, + final ConstraintSet newSet) { + for (final ConstraintSet cs : currentSets) { + newSets.add(cs.merge(newSet)); + } + } + + public boolean isEmpty() { + return constraintsSets.isEmpty(); + } + + public boolean matches(final ConstraintsByClass constraints) { + if (constraints.isEmpty() != isEmpty()) { + return false; + } + for (final ConstraintSet set : constraintsSets) { + boolean foundMatch = false; + for (final ConstraintSet otherSet : constraints.constraintsSets) { + foundMatch |= set.matches(otherSet); + } + if (!foundMatch) { + return false; + } + } + return true; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = (prime * result) + ((constraintsSets == null) ? 0 : constraintsSets.hashCode()); + return result; + } + + @Override + public boolean equals(final Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (getClass() != obj.getClass()) { + return false; + } + final ConstraintsByClass other = (ConstraintsByClass) obj; + if (constraintsSets == null) { + if (other.constraintsSets != null) { + return false; + } + } else if (!constraintsSets.equals(other.constraintsSets)) { + return false; + } + return true; + } + + /* + * (non-Javadoc) + * + * @see + * org.locationtech.geowave.core.store.query.constraints.Constraints#getIndexConstraints(org. + * locationtech.geowave.core.index.NumericIndexStrategy) + */ + @Override + public List getIndexConstraints(final Index index) { + final NumericIndexStrategy indexStrategy = index.getIndexStrategy(); + if (constraintsSets.isEmpty()) { + return Collections.emptyList(); + } + final List setRanges = new ArrayList<>(constraintsSets.size()); + for (final ConstraintSet set : constraintsSets) { + final MultiDimensionalNumericData mdSet = set.getIndexConstraints(indexStrategy); + if (!mdSet.isEmpty()) { + setRanges.add(mdSet); + } + } + return setRanges; + } + + @Override + public byte[] toBinary() { + final List bytes = new ArrayList<>(constraintsSets.size()); + int totalBytes = 0; + for (final ConstraintSet c : constraintsSets) { + bytes.add(c.toBinary()); + final int length = bytes.get(bytes.size() - 1).length; + totalBytes += (length + VarintUtils.unsignedIntByteLength(length)); + } + + final ByteBuffer buf = + ByteBuffer.allocate(totalBytes + VarintUtils.unsignedIntByteLength(bytes.size())); + VarintUtils.writeUnsignedInt(bytes.size(), buf); + for (final byte[] entryBytes : bytes) { + VarintUtils.writeUnsignedInt(entryBytes.length, buf); + buf.put(entryBytes); + } + return buf.array(); + } + + @Override + public void fromBinary(final byte[] bytes) { + final ByteBuffer buf = ByteBuffer.wrap(bytes); + final int numEntries = VarintUtils.readUnsignedInt(buf); + final List sets = new LinkedList<>(); + for (int i = 0; i < numEntries; i++) { + final byte[] d = new byte[VarintUtils.readUnsignedInt(buf)]; + buf.get(d); + final ConstraintSet cs = new ConstraintSet(); + cs.fromBinary(d); + sets.add(cs); + } + this.constraintsSets = sets; + } + + @Override + public List createFilters(final Index index, final BasicQuery parentQuery) { + final List filters = new ArrayList<>(); + for (final ConstraintSet constraint : constraintsSets) { + final QueryFilter filter = constraint.createFilter(index, parentQuery); + if (filter != null) { + filters.add(filter); + } + } + if (!filters.isEmpty()) { + return Collections.singletonList( + filters.size() == 1 ? filters.get(0) : new FilterList(false, filters)); + } + return Collections.emptyList(); + } + } + + // this is a clientside flag that is unnecessary to persist + protected transient boolean exact = true; + + public BasicQueryByClass() {} + + public BasicQueryByClass(final ConstraintsByClass constraints) { + super(constraints); + } + + + public BasicQueryByClass( + final ConstraintsByClass constraints, + final BasicQueryCompareOperation compareOp) { + super(constraints, compareOp); + } + + @Override + public byte[] toBinary() { + return constraints.toBinary(); + } + + @Override + public void fromBinary(final byte[] bytes) { + constraints = new ConstraintsByClass(); + constraints.fromBinary(bytes); + } + + public boolean isExact() { + return exact; + } + + public void setExact(final boolean exact) { + this.exact = exact; + } +} diff --git a/core/store/src/main/java/org/locationtech/geowave/core/store/query/constraints/Constraints.java b/core/store/src/main/java/org/locationtech/geowave/core/store/query/constraints/Constraints.java new file mode 100644 index 00000000000..5407e4fdc09 --- /dev/null +++ b/core/store/src/main/java/org/locationtech/geowave/core/store/query/constraints/Constraints.java @@ -0,0 +1,24 @@ +/** + * Copyright (c) 2013-2019 Contributors to the Eclipse Foundation + * + *

See the NOTICE file distributed with this work for additional information regarding copyright + * ownership. All rights reserved. This program and the accompanying materials are made available + * under the terms of the Apache License, Version 2.0 which accompanies this distribution and is + * available at http://www.apache.org/licenses/LICENSE-2.0.txt + */ +package org.locationtech.geowave.core.store.query.constraints; + +import java.util.List; +import org.locationtech.geowave.core.index.persist.Persistable; +import org.locationtech.geowave.core.index.sfc.data.MultiDimensionalNumericData; +import org.locationtech.geowave.core.store.api.Index; +import org.locationtech.geowave.core.store.query.filter.QueryFilter; + + +public interface Constraints extends Persistable { + + List getIndexConstraints(Index index); + + List createFilters(Index index, BasicQuery parentQuery); + +} diff --git a/core/store/src/main/java/org/locationtech/geowave/core/store/query/constraints/QueryConstraintsFactoryImpl.java b/core/store/src/main/java/org/locationtech/geowave/core/store/query/constraints/QueryConstraintsFactoryImpl.java index 9e6758258e2..b567b0bff8e 100644 --- a/core/store/src/main/java/org/locationtech/geowave/core/store/query/constraints/QueryConstraintsFactoryImpl.java +++ b/core/store/src/main/java/org/locationtech/geowave/core/store/query/constraints/QueryConstraintsFactoryImpl.java @@ -11,7 +11,8 @@ import org.locationtech.geowave.core.index.MultiDimensionalCoordinateRangesArray; import org.locationtech.geowave.core.index.NumericIndexStrategy; import org.locationtech.geowave.core.store.api.QueryConstraintsFactory; -import org.locationtech.geowave.core.store.query.constraints.BasicQuery.Constraints; +import org.locationtech.geowave.core.store.query.constraints.BasicOrderedConstraintQuery.OrderedConstraints; +import org.locationtech.geowave.core.store.query.constraints.BasicQueryByClass.ConstraintsByClass; import org.locationtech.geowave.core.store.query.filter.BasicQueryFilter.BasicQueryCompareOperation; public class QueryConstraintsFactoryImpl implements QueryConstraintsFactory { @@ -37,6 +38,13 @@ public QueryConstraints coordinateRanges( @Override public QueryConstraints constraints(final Constraints constraints) { + if (constraints instanceof ConstraintsByClass) { + // slightly optimized wrapper for ConstraintsByClass + return new BasicQueryByClass((ConstraintsByClass) constraints); + } else if (constraints instanceof OrderedConstraints) { + // slightly optimized wrapper for OrderedConstraints + return new BasicOrderedConstraintQuery((OrderedConstraints) constraints); + } return new BasicQuery(constraints); } @@ -44,6 +52,13 @@ public QueryConstraints constraints(final Constraints constraints) { public QueryConstraints constraints( final Constraints constraints, final BasicQueryCompareOperation compareOp) { + if (constraints instanceof ConstraintsByClass) { + // slightly optimized wrapper for ConstraintsByClass + return new BasicQueryByClass((ConstraintsByClass) constraints, compareOp); + } else if (constraints instanceof OrderedConstraints) { + // slightly optimized wrapper for OrderedConstraints + return new BasicOrderedConstraintQuery((OrderedConstraints) constraints, compareOp); + } return new BasicQuery(constraints, compareOp); } diff --git a/core/store/src/main/java/org/locationtech/geowave/core/store/query/constraints/SimpleNumericQuery.java b/core/store/src/main/java/org/locationtech/geowave/core/store/query/constraints/SimpleNumericQuery.java index fad08b50ff4..597fa51f461 100644 --- a/core/store/src/main/java/org/locationtech/geowave/core/store/query/constraints/SimpleNumericQuery.java +++ b/core/store/src/main/java/org/locationtech/geowave/core/store/query/constraints/SimpleNumericQuery.java @@ -1,18 +1,14 @@ package org.locationtech.geowave.core.store.query.constraints; -import java.util.ArrayList; -import java.util.List; import org.apache.commons.lang3.Range; -import org.locationtech.geowave.core.index.dimension.BasicDimensionDefinition; import org.locationtech.geowave.core.index.sfc.data.MultiDimensionalNumericData; -import org.locationtech.geowave.core.index.sfc.data.NumericRange; import org.locationtech.geowave.core.store.api.Index; import org.locationtech.geowave.core.store.dimension.NumericDimensionField; import org.locationtech.geowave.core.store.query.filter.QueryFilter; -public class SimpleNumericQuery extends BasicQuery { +public class SimpleNumericQuery extends BasicOrderedConstraintQuery { public SimpleNumericQuery(final Range range) { - super(createNumericConstraints(range)); + super(new OrderedConstraints(range)); } public SimpleNumericQuery() { @@ -29,13 +25,4 @@ protected QueryFilter createQueryFilter( // index, we don't need fine-grained filtering for simple numeric queries return null; } - - private static Constraints createNumericConstraints(final Range range) { - final List constraints = new ArrayList<>(); - constraints.add( - new ConstraintSet( - BasicDimensionDefinition.class, - new ConstraintData(new NumericRange(range.getMinimum(), range.getMaximum()), false))); - return new Constraints(constraints); - } } diff --git a/core/store/src/test/java/org/locationtech/geowave/core/store/TestStorePersistableRegistry.java b/core/store/src/test/java/org/locationtech/geowave/core/store/TestStorePersistableRegistry.java index 6bc241c19cb..e48007846ab 100644 --- a/core/store/src/test/java/org/locationtech/geowave/core/store/TestStorePersistableRegistry.java +++ b/core/store/src/test/java/org/locationtech/geowave/core/store/TestStorePersistableRegistry.java @@ -16,8 +16,8 @@ import org.locationtech.geowave.core.store.adapter.MockComponents.TestDimensionField; import org.locationtech.geowave.core.store.adapter.MockComponents.TestIndexModel; import org.locationtech.geowave.core.store.adapter.MockComponents.TestPersistentIndexFieldHandler; -import org.locationtech.geowave.core.store.query.BasicQueryTest.ExampleDimensionOne; -import org.locationtech.geowave.core.store.query.BasicQueryTest.ExampleNumericIndexStrategy; +import org.locationtech.geowave.core.store.query.BasicQueryByClassTest.ExampleDimensionOne; +import org.locationtech.geowave.core.store.query.BasicQueryByClassTest.ExampleNumericIndexStrategy; public class TestStorePersistableRegistry implements PersistableRegistrySpi { diff --git a/core/store/src/test/java/org/locationtech/geowave/core/store/query/BasicQueryTest.java b/core/store/src/test/java/org/locationtech/geowave/core/store/query/BasicQueryByClassTest.java similarity index 93% rename from core/store/src/test/java/org/locationtech/geowave/core/store/query/BasicQueryTest.java rename to core/store/src/test/java/org/locationtech/geowave/core/store/query/BasicQueryByClassTest.java index cf0fffb1026..0f7b7c73174 100644 --- a/core/store/src/test/java/org/locationtech/geowave/core/store/query/BasicQueryTest.java +++ b/core/store/src/test/java/org/locationtech/geowave/core/store/query/BasicQueryByClassTest.java @@ -19,7 +19,6 @@ import java.util.List; import java.util.Map; import org.junit.Test; -import org.locationtech.geowave.core.index.ByteArray; import org.locationtech.geowave.core.index.IndexMetaData; import org.locationtech.geowave.core.index.InsertionIds; import org.locationtech.geowave.core.index.MultiDimensionalCoordinateRanges; @@ -43,13 +42,14 @@ import org.locationtech.geowave.core.store.index.CommonIndexModel; import org.locationtech.geowave.core.store.index.CommonIndexValue; import org.locationtech.geowave.core.store.index.CustomNameIndex; -import org.locationtech.geowave.core.store.query.constraints.BasicQuery; -import org.locationtech.geowave.core.store.query.constraints.BasicQuery.ConstraintData; -import org.locationtech.geowave.core.store.query.constraints.BasicQuery.ConstraintSet; -import org.locationtech.geowave.core.store.query.constraints.BasicQuery.Constraints; +import org.locationtech.geowave.core.store.index.IndexImpl; +import org.locationtech.geowave.core.store.query.constraints.BasicQueryByClass; +import org.locationtech.geowave.core.store.query.constraints.BasicQueryByClass.ConstraintData; +import org.locationtech.geowave.core.store.query.constraints.BasicQueryByClass.ConstraintSet; +import org.locationtech.geowave.core.store.query.constraints.BasicQueryByClass.ConstraintsByClass; import org.locationtech.geowave.core.store.query.filter.QueryFilter; -public class BasicQueryTest { +public class BasicQueryByClassTest { final SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ssz"); @@ -82,15 +82,15 @@ public void testIntersectCasesWithPersistence() { ExampleDimensionTwo.class, new ConstraintData(new ConstrainedIndexValue(0.1, 0.2), true)); - final Constraints constraints = - new Constraints(Arrays.asList(cs2a)).merge(Collections.singletonList(cs1)); + final ConstraintsByClass constraints = + new ConstraintsByClass(Arrays.asList(cs2a)).merge(Collections.singletonList(cs1)); assertEquals( expectedResults, - constraints.getIndexConstraints(new ExampleNumericIndexStrategy())); + constraints.getIndexConstraints(new IndexImpl(new ExampleNumericIndexStrategy(), null))); - final byte[] image = new BasicQuery(constraints).toBinary(); - final BasicQuery query = new BasicQuery(); + final byte[] image = new BasicQueryByClass(constraints).toBinary(); + final BasicQueryByClass query = new BasicQueryByClass(); query.fromBinary(image); assertEquals(expectedResults, query.getIndexConstraints(index)); @@ -134,15 +134,15 @@ public void testDisjointCasesWithPersistence() { ExampleDimensionTwo.class, new ConstraintData(new ConstrainedIndexValue(3.4, 3.7), true)); - final Constraints constraints = - new Constraints(Arrays.asList(cs2a, cs2b)).merge(Collections.singletonList(cs1)); + final ConstraintsByClass constraints = + new ConstraintsByClass(Arrays.asList(cs2a, cs2b)).merge(Collections.singletonList(cs1)); assertEquals( expectedResults, - constraints.getIndexConstraints(new ExampleNumericIndexStrategy())); + constraints.getIndexConstraints(new IndexImpl(new ExampleNumericIndexStrategy(), null))); - final byte[] image = new BasicQuery(constraints).toBinary(); - final BasicQuery query = new BasicQuery(); + final byte[] image = new BasicQueryByClass(constraints).toBinary(); + final BasicQueryByClass query = new BasicQueryByClass(); query.fromBinary(image); final Index index = new CustomNameIndex( diff --git a/core/store/src/test/java/org/locationtech/geowave/core/store/query/filter/DistributedQueryFilterTest.java b/core/store/src/test/java/org/locationtech/geowave/core/store/query/filter/DistributedQueryFilterTest.java index f509e62550a..96fc2e4d52b 100644 --- a/core/store/src/test/java/org/locationtech/geowave/core/store/query/filter/DistributedQueryFilterTest.java +++ b/core/store/src/test/java/org/locationtech/geowave/core/store/query/filter/DistributedQueryFilterTest.java @@ -19,7 +19,7 @@ import org.locationtech.geowave.core.index.sfc.data.NumericRange; import org.locationtech.geowave.core.index.sfc.data.NumericValue; import org.locationtech.geowave.core.store.dimension.NumericDimensionField; -import org.locationtech.geowave.core.store.query.BasicQueryTest; +import org.locationtech.geowave.core.store.query.BasicQueryByClassTest; import org.locationtech.geowave.core.store.query.filter.BasicQueryFilter.BasicQueryCompareOperation; public class DistributedQueryFilterTest { @@ -30,7 +30,7 @@ public void test() { filters.add( new BasicQueryFilter( new BasicNumericDataset(new NumericData[] {new NumericValue(0.4)}), - new NumericDimensionField[] {new BasicQueryTest.ExampleDimensionOne()}, + new NumericDimensionField[] {new BasicQueryByClassTest.ExampleDimensionOne()}, BasicQueryCompareOperation.CONTAINS)); filters.add(new DedupeFilter()); FilterList list = new FilterList(false, filters); @@ -47,7 +47,7 @@ public void test() { filters.add( new BasicQueryFilter( new BasicNumericDataset(new NumericData[] {new NumericValue(0.5)}), - new NumericDimensionField[] {new BasicQueryTest.ExampleDimensionOne()}, + new NumericDimensionField[] {new BasicQueryByClassTest.ExampleDimensionOne()}, BasicQueryCompareOperation.INTERSECTS)); filters.add(new DedupeFilter()); list = new FilterList(true, filters); diff --git a/examples/java-api/src/test/java/org/locationtech/geowave/examples/ingest/SimpleIngestTest.java b/examples/java-api/src/test/java/org/locationtech/geowave/examples/ingest/SimpleIngestTest.java index 756bd8924e5..7d0a99d35bd 100644 --- a/examples/java-api/src/test/java/org/locationtech/geowave/examples/ingest/SimpleIngestTest.java +++ b/examples/java-api/src/test/java/org/locationtech/geowave/examples/ingest/SimpleIngestTest.java @@ -18,7 +18,7 @@ import org.locationtech.geowave.core.store.api.DataStore; import org.locationtech.geowave.core.store.api.QueryBuilder; import org.locationtech.geowave.core.store.index.IndexStore; -import org.locationtech.geowave.core.store.query.constraints.BasicQuery; +import org.locationtech.geowave.core.store.query.constraints.BasicQueryByClass; import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.GeometryFactory; import org.locationtech.jts.geom.Point; @@ -50,7 +50,7 @@ protected static Set getStoredPointSet(final DataStore ds) { final CloseableIterator itr = ds.query( QueryBuilder.newBuilder().constraints( - new BasicQuery(new BasicQuery.Constraints())).build()); + new BasicQueryByClass(new BasicQueryByClass.ConstraintsByClass())).build()); final Set readPoints = new TreeSet<>(); while (itr.hasNext()) { final Object n = itr.next(); diff --git a/extensions/adapters/vector/src/main/java/org/locationtech/geowave/adapter/vector/FeatureAttributeDimensionField.java b/extensions/adapters/vector/src/main/java/org/locationtech/geowave/adapter/vector/FeatureAttributeDimensionField.java index 221921024d5..c41e95a6370 100644 --- a/extensions/adapters/vector/src/main/java/org/locationtech/geowave/adapter/vector/FeatureAttributeDimensionField.java +++ b/extensions/adapters/vector/src/main/java/org/locationtech/geowave/adapter/vector/FeatureAttributeDimensionField.java @@ -23,10 +23,17 @@ public FeatureAttributeDimensionField() { super(); } + public FeatureAttributeDimensionField(final AttributeDescriptor attributeDescriptor) { + this(attributeDescriptor, null); + attributeName = attributeDescriptor.getLocalName(); + } + public FeatureAttributeDimensionField( final AttributeDescriptor attributeDescriptor, final Range range) { - super(new BasicDimensionDefinition(range.getMinimum(), range.getMaximum())); + super( + range == null ? null + : new BasicDimensionDefinition(range.getMinimum(), range.getMaximum())); writer = new FeatureAttributeWriterWrapper( (FieldWriter) FieldUtils.getDefaultWriterForClass( @@ -60,7 +67,12 @@ public FieldReader getReader() { @Override public byte[] toBinary() { - final byte[] bytes = baseDefinition.toBinary(); + final byte[] bytes; + if (baseDefinition != null) { + bytes = baseDefinition.toBinary(); + } else { + bytes = new byte[0]; + } final byte[] strBytes = StringUtils.stringToBinary(attributeName); final ByteBuffer buf = ByteBuffer.allocate( @@ -80,9 +92,13 @@ public void fromBinary(final byte[] bytes) { attributeName = StringUtils.stringFromBinary(strBytes); final byte[] rest = new byte[bytes.length - VarintUtils.unsignedIntByteLength(attrNameLength) - attrNameLength]; - buf.get(rest); - baseDefinition = new BasicDimensionDefinition(); - baseDefinition.fromBinary(rest); + if (rest.length > 0) { + buf.get(rest); + baseDefinition = new BasicDimensionDefinition(); + baseDefinition.fromBinary(rest); + } else { + baseDefinition = null; + } } private static class FeatureAttributeReaderWrapper implements diff --git a/extensions/adapters/vector/src/main/java/org/locationtech/geowave/adapter/vector/index/ChooseBestMatchIndexQueryStrategy.java b/extensions/adapters/vector/src/main/java/org/locationtech/geowave/adapter/vector/index/ChooseBestMatchIndexQueryStrategy.java index 45ee230ff72..7a17f46104f 100644 --- a/extensions/adapters/vector/src/main/java/org/locationtech/geowave/adapter/vector/index/ChooseBestMatchIndexQueryStrategy.java +++ b/extensions/adapters/vector/src/main/java/org/locationtech/geowave/adapter/vector/index/ChooseBestMatchIndexQueryStrategy.java @@ -21,7 +21,7 @@ import org.locationtech.geowave.core.store.adapter.statistics.histogram.NumericHistogram; import org.locationtech.geowave.core.store.api.Index; import org.locationtech.geowave.core.store.api.StatisticsQuery; -import org.locationtech.geowave.core.store.query.constraints.BasicQuery; +import org.locationtech.geowave.core.store.query.constraints.BasicQueryByClass; import org.locationtech.geowave.core.store.util.DataStoreUtils; import org.opengis.feature.simple.SimpleFeature; import org.slf4j.Logger; @@ -40,7 +40,7 @@ public String toString() { @Override public CloseableIterator getIndices( final Map> stats, - final BasicQuery query, + final BasicQueryByClass query, final Index[] indices, final Map hints) { return new CloseableIterator() { diff --git a/extensions/adapters/vector/src/main/java/org/locationtech/geowave/adapter/vector/index/ChooseHeuristicMatchIndexQueryStrategy.java b/extensions/adapters/vector/src/main/java/org/locationtech/geowave/adapter/vector/index/ChooseHeuristicMatchIndexQueryStrategy.java index 390087ad410..7afcecd1c53 100644 --- a/extensions/adapters/vector/src/main/java/org/locationtech/geowave/adapter/vector/index/ChooseHeuristicMatchIndexQueryStrategy.java +++ b/extensions/adapters/vector/src/main/java/org/locationtech/geowave/adapter/vector/index/ChooseHeuristicMatchIndexQueryStrategy.java @@ -17,7 +17,7 @@ import org.locationtech.geowave.core.store.adapter.statistics.InternalDataStatistics; import org.locationtech.geowave.core.store.adapter.statistics.StatisticsId; import org.locationtech.geowave.core.store.api.Index; -import org.locationtech.geowave.core.store.query.constraints.BasicQuery; +import org.locationtech.geowave.core.store.query.constraints.BasicQueryByClass; import org.opengis.feature.simple.SimpleFeature; /** @@ -37,7 +37,7 @@ public String toString() { @Override public CloseableIterator getIndices( final Map> stats, - final BasicQuery query, + final BasicQueryByClass query, final Index[] indices, final Map hints) { return new CloseableIterator() { diff --git a/extensions/adapters/vector/src/main/java/org/locationtech/geowave/adapter/vector/index/ChooseLocalityPreservingQueryStrategy.java b/extensions/adapters/vector/src/main/java/org/locationtech/geowave/adapter/vector/index/ChooseLocalityPreservingQueryStrategy.java index c921d0977e8..e2c5a2d203d 100644 --- a/extensions/adapters/vector/src/main/java/org/locationtech/geowave/adapter/vector/index/ChooseLocalityPreservingQueryStrategy.java +++ b/extensions/adapters/vector/src/main/java/org/locationtech/geowave/adapter/vector/index/ChooseLocalityPreservingQueryStrategy.java @@ -17,7 +17,7 @@ import org.locationtech.geowave.core.store.adapter.statistics.InternalDataStatistics; import org.locationtech.geowave.core.store.adapter.statistics.StatisticsId; import org.locationtech.geowave.core.store.api.Index; -import org.locationtech.geowave.core.store.query.constraints.BasicQuery; +import org.locationtech.geowave.core.store.query.constraints.BasicQueryByClass; import org.opengis.feature.simple.SimpleFeature; /** @@ -37,7 +37,7 @@ public String toString() { @Override public CloseableIterator getIndices( final Map> stats, - final BasicQuery query, + final BasicQueryByClass query, final Index[] indices, final Map hints) { return new CloseableIterator() { diff --git a/extensions/adapters/vector/src/main/java/org/locationtech/geowave/adapter/vector/index/IndexQueryStrategySPI.java b/extensions/adapters/vector/src/main/java/org/locationtech/geowave/adapter/vector/index/IndexQueryStrategySPI.java index db36ed8a78a..0b268df463a 100644 --- a/extensions/adapters/vector/src/main/java/org/locationtech/geowave/adapter/vector/index/IndexQueryStrategySPI.java +++ b/extensions/adapters/vector/src/main/java/org/locationtech/geowave/adapter/vector/index/IndexQueryStrategySPI.java @@ -13,7 +13,7 @@ import org.locationtech.geowave.core.store.adapter.statistics.InternalDataStatistics; import org.locationtech.geowave.core.store.adapter.statistics.StatisticsId; import org.locationtech.geowave.core.store.api.Index; -import org.locationtech.geowave.core.store.query.constraints.BasicQuery; +import org.locationtech.geowave.core.store.query.constraints.BasicQueryByClass; import org.opengis.feature.simple.SimpleFeature; public interface IndexQueryStrategySPI { @@ -23,7 +23,7 @@ public enum QueryHint { public CloseableIterator getIndices( Map> stats, - BasicQuery query, + BasicQueryByClass query, Index[] indices, Map hints); } diff --git a/extensions/adapters/vector/src/main/java/org/locationtech/geowave/adapter/vector/plugin/GeoWaveDataStoreComponents.java b/extensions/adapters/vector/src/main/java/org/locationtech/geowave/adapter/vector/plugin/GeoWaveDataStoreComponents.java index a0c219ada8b..5b6a02eeea3 100644 --- a/extensions/adapters/vector/src/main/java/org/locationtech/geowave/adapter/vector/plugin/GeoWaveDataStoreComponents.java +++ b/extensions/adapters/vector/src/main/java/org/locationtech/geowave/adapter/vector/plugin/GeoWaveDataStoreComponents.java @@ -32,7 +32,7 @@ import org.locationtech.geowave.core.store.data.visibility.GlobalVisibilityHandler; import org.locationtech.geowave.core.store.data.visibility.UniformVisibilityWriter; import org.locationtech.geowave.core.store.index.IndexStore; -import org.locationtech.geowave.core.store.query.constraints.BasicQuery; +import org.locationtech.geowave.core.store.query.constraints.BasicQueryByClass; import org.opengis.feature.simple.SimpleFeature; import org.spark_project.guava.collect.Maps; @@ -102,7 +102,7 @@ public DataStatisticsStore getStatsStore() { public CloseableIterator getIndices( final Map> stats, - final BasicQuery query, + final BasicQueryByClass query, final boolean spatialOnly) { final GeoWaveGTDataStore gtStore = getGTstore(); final Map queryHints = Maps.newHashMap(); diff --git a/extensions/adapters/vector/src/main/java/org/locationtech/geowave/adapter/vector/plugin/GeoWaveFeatureReader.java b/extensions/adapters/vector/src/main/java/org/locationtech/geowave/adapter/vector/plugin/GeoWaveFeatureReader.java index 40e9e908d59..4479baeb44e 100644 --- a/extensions/adapters/vector/src/main/java/org/locationtech/geowave/adapter/vector/plugin/GeoWaveFeatureReader.java +++ b/extensions/adapters/vector/src/main/java/org/locationtech/geowave/adapter/vector/plugin/GeoWaveFeatureReader.java @@ -58,8 +58,8 @@ import org.locationtech.geowave.core.store.adapter.statistics.InternalDataStatistics; import org.locationtech.geowave.core.store.adapter.statistics.StatisticsId; import org.locationtech.geowave.core.store.api.Index; -import org.locationtech.geowave.core.store.query.constraints.BasicQuery; -import org.locationtech.geowave.core.store.query.constraints.BasicQuery.Constraints; +import org.locationtech.geowave.core.store.query.constraints.BasicQueryByClass; +import org.locationtech.geowave.core.store.query.constraints.BasicQueryByClass.ConstraintsByClass; import org.locationtech.geowave.core.store.util.DataStoreUtils; import org.locationtech.jts.geom.Envelope; import org.locationtech.jts.geom.Geometry; @@ -161,11 +161,11 @@ protected long getCountInternal( return countIssuer.count; } - private BasicQuery getQuery( + private BasicQueryByClass getQuery( final Map> statsMap, final Geometry jtsBounds, final TemporalConstraintsSet timeBounds) { - final Constraints timeConstraints = + final ConstraintsByClass timeConstraints = QueryIndexHelper.composeTimeBoundedConstraints( components.getAdapter().getFeatureType(), components.getAdapter().getTimeDescriptors(), @@ -179,7 +179,7 @@ private BasicQuery getQuery( * NOTE: query to an index that requires a constraint and the constraint is missing equates to a * full table scan. @see BasicQuery */ - final BasicQuery query = composeQuery(geoConstraints, timeConstraints); + final BasicQueryByClass query = composeQuery(geoConstraints, timeConstraints); query.setExact(timeBounds.isExact()); return query; } @@ -193,7 +193,7 @@ public CloseableIterator issueQuery( final Map> statsMap = transaction.getDataStatistics(); - final BasicQuery query = getQuery(statsMap, jtsBounds, timeBounds); + final BasicQueryByClass query = getQuery(statsMap, jtsBounds, timeBounds); boolean spatialOnly = false; if (this.query.getHints().containsKey(SubsampleProcess.SUBSAMPLE_ENABLED) @@ -273,7 +273,9 @@ public BaseIssuer(final Filter filter, final Integer limit) { } @Override - public CloseableIterator query(final Index index, final BasicQuery query) { + public CloseableIterator query( + final Index index, + final BasicQueryByClass query) { VectorQueryBuilder bldr = VectorQueryBuilder.newBuilder().addTypeName( components.getAdapter().getTypeName()).indexName(index.getName()).setAuthorizations( @@ -311,7 +313,9 @@ public CountQueryIssuer(final Filter filter, final Integer limit) { } @Override - public CloseableIterator query(final Index index, final BasicQuery query) { + public CloseableIterator query( + final Index index, + final BasicQueryByClass query) { VectorAggregationQueryBuilder bldr = (VectorAggregationQueryBuilder) VectorAggregationQueryBuilder.newBuilder().count( components.getAdapter().getTypeName()).indexName(index.getName()).setAuthorizations( @@ -353,7 +357,9 @@ public EnvelopeQueryIssuer( } @Override - public CloseableIterator query(final Index index, final BasicQuery query) { + public CloseableIterator query( + final Index index, + final BasicQueryByClass query) { VectorQueryBuilder bldr = VectorQueryBuilder.newBuilder().addTypeName( @@ -414,7 +420,9 @@ public RenderQueryIssuer( } @Override - public CloseableIterator query(final Index index, final BasicQuery query) { + public CloseableIterator query( + final Index index, + final BasicQueryByClass query) { final VectorAggregationQueryBuilder bldr = (VectorAggregationQueryBuilder) VectorAggregationQueryBuilder.newBuilder().indexName( index.getName()).setAuthorizations(transaction.composeAuthorizations()); @@ -535,9 +543,9 @@ protected Geometry clipIndexedBBOXConstraints(final Geometry bbox) { transaction.getDataStatistics()); } - private BasicQuery composeQuery( + private BasicQueryByClass composeQuery( final GeoConstraintsWrapper geoConstraints, - final Constraints temporalConstraints) { + final ConstraintsByClass temporalConstraints) { // TODO: this actually doesn't boost performance much, if at // all, and one key is missing - the query geometry has to be diff --git a/extensions/adapters/vector/src/main/java/org/locationtech/geowave/adapter/vector/plugin/QueryIssuer.java b/extensions/adapters/vector/src/main/java/org/locationtech/geowave/adapter/vector/plugin/QueryIssuer.java index 6f911bcc2bc..814295372b6 100644 --- a/extensions/adapters/vector/src/main/java/org/locationtech/geowave/adapter/vector/plugin/QueryIssuer.java +++ b/extensions/adapters/vector/src/main/java/org/locationtech/geowave/adapter/vector/plugin/QueryIssuer.java @@ -10,12 +10,12 @@ import org.locationtech.geowave.core.store.CloseableIterator; import org.locationtech.geowave.core.store.api.Index; -import org.locationtech.geowave.core.store.query.constraints.BasicQuery; +import org.locationtech.geowave.core.store.query.constraints.BasicQueryByClass; import org.opengis.feature.simple.SimpleFeature; import org.opengis.filter.Filter; public interface QueryIssuer { - CloseableIterator query(Index index, BasicQuery constraints); + CloseableIterator query(Index index, BasicQueryByClass constraints); Filter getFilter(); diff --git a/extensions/adapters/vector/src/main/java/org/locationtech/geowave/adapter/vector/util/QueryIndexHelper.java b/extensions/adapters/vector/src/main/java/org/locationtech/geowave/adapter/vector/util/QueryIndexHelper.java index 984f060bf90..83342c1eee6 100644 --- a/extensions/adapters/vector/src/main/java/org/locationtech/geowave/adapter/vector/util/QueryIndexHelper.java +++ b/extensions/adapters/vector/src/main/java/org/locationtech/geowave/adapter/vector/util/QueryIndexHelper.java @@ -23,8 +23,8 @@ import org.locationtech.geowave.core.geotime.util.TimeUtils; import org.locationtech.geowave.core.store.adapter.statistics.InternalDataStatistics; import org.locationtech.geowave.core.store.adapter.statistics.StatisticsId; -import org.locationtech.geowave.core.store.query.constraints.BasicQuery.ConstraintSet; -import org.locationtech.geowave.core.store.query.constraints.BasicQuery.Constraints; +import org.locationtech.geowave.core.store.query.constraints.BasicQueryByClass.ConstraintSet; +import org.locationtech.geowave.core.store.query.constraints.BasicQueryByClass.ConstraintsByClass; import org.locationtech.jts.geom.Geometry; import org.opengis.feature.simple.SimpleFeature; import org.opengis.feature.simple.SimpleFeatureType; @@ -196,7 +196,7 @@ public static ConstraintSet getBBOXIndexConstraintsFromIndex( * @param timeBoundsSet * @return */ - public static Constraints composeTimeConstraints( + public static ConstraintsByClass composeTimeConstraints( final SimpleFeatureType featureType, final TimeDescriptors timeDescriptors, final Map> statsMap, @@ -206,7 +206,7 @@ public static Constraints composeTimeConstraints( TimeUtils.getTemporalConstraintsForDescriptors(timeDescriptors, timeBoundsSet); return (timeBounds != null) && !timeBounds.isEmpty() ? ExplicitSpatialTemporalQuery.createConstraints(timeBounds, false) - : new Constraints(getTimeConstraintsFromIndex(timeDescriptors, statsMap)); + : new ConstraintsByClass(getTimeConstraintsFromIndex(timeDescriptors, statsMap)); } /** @@ -219,29 +219,30 @@ public static Constraints composeTimeConstraints( * @param timeBoundsSet * @return */ - public static Constraints composeTimeBoundedConstraints( + public static ConstraintsByClass composeTimeBoundedConstraints( final SimpleFeatureType featureType, final TimeDescriptors timeDescriptors, final Map> statsMap, final TemporalConstraintsSet timeBoundsSet) { if ((timeBoundsSet == null) || timeBoundsSet.isEmpty() || !timeDescriptors.hasTime()) { - return new Constraints(); + return new ConstraintsByClass(); } final TemporalConstraints boundsTemporalConstraints = TimeUtils.getTemporalConstraintsForDescriptors(timeDescriptors, timeBoundsSet); if (boundsTemporalConstraints.isEmpty()) { - return new Constraints(); + return new ConstraintsByClass(); } - final Constraints indexTimeConstraints = - new Constraints(QueryIndexHelper.getTimeConstraintsFromIndex(timeDescriptors, statsMap)); + final ConstraintsByClass indexTimeConstraints = + new ConstraintsByClass( + QueryIndexHelper.getTimeConstraintsFromIndex(timeDescriptors, statsMap)); - final Constraints boundsTimeConstraints = + final ConstraintsByClass boundsTimeConstraints = ExplicitSpatialTemporalQuery.createConstraints(boundsTemporalConstraints, false); - return (boundsTimeConstraints.matches(indexTimeConstraints)) ? new Constraints() + return (boundsTimeConstraints.matches(indexTimeConstraints)) ? new ConstraintsByClass() : boundsTimeConstraints; } @@ -260,15 +261,15 @@ public static GeoConstraintsWrapper composeGeometricConstraints( final Map> statsMap, final Geometry jtsBounds) { if (jtsBounds == null) { - return new GeoConstraintsWrapper(new Constraints(), true, null); + return new GeoConstraintsWrapper(new ConstraintsByClass(), true, null); } final GeoConstraintsWrapper geoConstraints = GeometryUtils.basicGeoConstraintsWrapperFromGeometry(jtsBounds); - final Constraints statsConstraints = - new Constraints(getBBOXIndexConstraintsFromIndex(featureType, statsMap)); + final ConstraintsByClass statsConstraints = + new ConstraintsByClass(getBBOXIndexConstraintsFromIndex(featureType, statsMap)); if (geoConstraints.getConstraints().matches(statsConstraints)) { return new GeoConstraintsWrapper( - new Constraints(), + new ConstraintsByClass(), geoConstraints.isConstraintsMatchGeometry(), jtsBounds); } @@ -286,14 +287,14 @@ public static GeoConstraintsWrapper composeGeometricConstraints( * @param timeBoundsSet * @return */ - public static Constraints composeConstraints( + public static ConstraintsByClass composeConstraints( final SimpleFeatureType featureType, final TimeDescriptors timeDescriptors, final Map> statsMap, final Geometry jtsBounds, final TemporalConstraintsSet timeBoundsSet) { - final Constraints timeConstraints = + final ConstraintsByClass timeConstraints = composeTimeConstraints(featureType, timeDescriptors, statsMap, timeBoundsSet); final GeoConstraintsWrapper geoConstraints = composeGeometricConstraints(featureType, statsMap, jtsBounds); diff --git a/extensions/adapters/vector/src/test/java/org/locationtech/geowave/adapter/vector/index/ChooseBestMatchIndexQueryStrategyTest.java b/extensions/adapters/vector/src/test/java/org/locationtech/geowave/adapter/vector/index/ChooseBestMatchIndexQueryStrategyTest.java index 37e24ba3985..91b0477dc47 100644 --- a/extensions/adapters/vector/src/test/java/org/locationtech/geowave/adapter/vector/index/ChooseBestMatchIndexQueryStrategyTest.java +++ b/extensions/adapters/vector/src/test/java/org/locationtech/geowave/adapter/vector/index/ChooseBestMatchIndexQueryStrategyTest.java @@ -41,10 +41,10 @@ import org.locationtech.geowave.core.store.entities.GeoWaveValue; import org.locationtech.geowave.core.store.index.CommonIndexValue; import org.locationtech.geowave.core.store.index.NullIndex; -import org.locationtech.geowave.core.store.query.constraints.BasicQuery; -import org.locationtech.geowave.core.store.query.constraints.BasicQuery.ConstraintData; -import org.locationtech.geowave.core.store.query.constraints.BasicQuery.ConstraintSet; -import org.locationtech.geowave.core.store.query.constraints.BasicQuery.Constraints; +import org.locationtech.geowave.core.store.query.constraints.BasicQueryByClass; +import org.locationtech.geowave.core.store.query.constraints.BasicQueryByClass.ConstraintData; +import org.locationtech.geowave.core.store.query.constraints.BasicQueryByClass.ConstraintSet; +import org.locationtech.geowave.core.store.query.constraints.BasicQueryByClass.ConstraintsByClass; import org.opengis.feature.simple.SimpleFeature; import com.beust.jcommander.internal.Maps; @@ -91,10 +91,10 @@ public void testChooseSpatialTemporalWithStats() { TimeDefinition.class, new ConstraintData(new ConstrainedIndexValue(0.1, 0.2), true)); - final Constraints constraints = - new Constraints(Arrays.asList(cs2a)).merge(Collections.singletonList(cs1)); + final ConstraintsByClass constraints = + new ConstraintsByClass(Arrays.asList(cs2a)).merge(Collections.singletonList(cs1)); - final BasicQuery query = new BasicQuery(constraints); + final BasicQueryByClass query = new BasicQueryByClass(constraints); final NumericIndexStrategy temporalIndexStrategy = new SpatialTemporalIndexBuilder().createIndex().getIndexStrategy(); @@ -159,7 +159,7 @@ public void testChooseSpatialTemporalWithStats() { public Iterator getIndices( final Map> stats, - final BasicQuery query, + final BasicQueryByClass query, final ChooseBestMatchIndexQueryStrategy strategy) { return strategy.getIndices( stats, diff --git a/extensions/adapters/vector/src/test/java/org/locationtech/geowave/adapter/vector/index/ChooseHeuristicMatchQueryStrategyTest.java b/extensions/adapters/vector/src/test/java/org/locationtech/geowave/adapter/vector/index/ChooseHeuristicMatchQueryStrategyTest.java index 912a48e8f0c..e07d454f1ea 100644 --- a/extensions/adapters/vector/src/test/java/org/locationtech/geowave/adapter/vector/index/ChooseHeuristicMatchQueryStrategyTest.java +++ b/extensions/adapters/vector/src/test/java/org/locationtech/geowave/adapter/vector/index/ChooseHeuristicMatchQueryStrategyTest.java @@ -34,10 +34,10 @@ import org.locationtech.geowave.core.store.dimension.NumericDimensionField; import org.locationtech.geowave.core.store.index.CommonIndexValue; import org.locationtech.geowave.core.store.index.NullIndex; -import org.locationtech.geowave.core.store.query.constraints.BasicQuery; -import org.locationtech.geowave.core.store.query.constraints.BasicQuery.ConstraintData; -import org.locationtech.geowave.core.store.query.constraints.BasicQuery.ConstraintSet; -import org.locationtech.geowave.core.store.query.constraints.BasicQuery.Constraints; +import org.locationtech.geowave.core.store.query.constraints.BasicQueryByClass; +import org.locationtech.geowave.core.store.query.constraints.BasicQueryByClass.ConstraintData; +import org.locationtech.geowave.core.store.query.constraints.BasicQueryByClass.ConstraintSet; +import org.locationtech.geowave.core.store.query.constraints.BasicQueryByClass.ConstraintsByClass; import org.opengis.feature.simple.SimpleFeature; public class ChooseHeuristicMatchQueryStrategyTest { @@ -70,7 +70,7 @@ public void testChooseTemporalWithoutStatsHouseHour() { final Iterator it = getIndices( new HashMap>(), - new BasicQuery(createConstraints(HOUSE, HOUSE, HOUR)), + new BasicQueryByClass(createConstraints(HOUSE, HOUSE, HOUR)), strategy); assertTrue(it.hasNext()); assertEquals(indices.get(1).getName(), it.next().getName()); @@ -85,7 +85,7 @@ public void testChooseSpatialWithoutStatsHouseDay() { final Iterator it = getIndices( new HashMap>(), - new BasicQuery(createConstraints(HOUSE, HOUSE, DAY)), + new BasicQueryByClass(createConstraints(HOUSE, HOUSE, DAY)), strategy); assertTrue(it.hasNext()); assertEquals(indices.get(1).getName(), it.next().getName()); @@ -100,7 +100,7 @@ public void testChooseSpatialWithoutStatsHouseWeek() { final Iterator it = getIndices( new HashMap>(), - new BasicQuery(createConstraints(HOUSE, HOUSE, WEEK)), + new BasicQueryByClass(createConstraints(HOUSE, HOUSE, WEEK)), strategy); assertTrue(it.hasNext()); assertEquals(indices.get(1).getName(), it.next().getName()); @@ -115,7 +115,7 @@ public void testChooseTemporalWithoutStatsBlockHour() { final Iterator it = getIndices( new HashMap>(), - new BasicQuery(createConstraints(BLOCK, BLOCK, HOUR)), + new BasicQueryByClass(createConstraints(BLOCK, BLOCK, HOUR)), strategy); assertTrue(it.hasNext()); assertEquals(indices.get(1).getName(), it.next().getName()); @@ -130,7 +130,7 @@ public void testChooseSpatialWithoutStatsBlockDay() { final Iterator it = getIndices( new HashMap>(), - new BasicQuery(createConstraints(BLOCK, BLOCK, DAY)), + new BasicQueryByClass(createConstraints(BLOCK, BLOCK, DAY)), strategy); assertTrue(it.hasNext()); assertEquals(indices.get(1).getName(), it.next().getName()); @@ -145,7 +145,7 @@ public void testChooseSpatialWithoutStatsBlockWeek() { final Iterator it = getIndices( new HashMap>(), - new BasicQuery(createConstraints(BLOCK, BLOCK, WEEK)), + new BasicQueryByClass(createConstraints(BLOCK, BLOCK, WEEK)), strategy); assertTrue(it.hasNext()); assertEquals(indices.get(1).getName(), it.next().getName()); @@ -160,7 +160,7 @@ public void testChooseTemporalWithoutStatsCityHour() { final Iterator it = getIndices( new HashMap>(), - new BasicQuery(createConstraints(CITY, CITY, HOUR)), + new BasicQueryByClass(createConstraints(CITY, CITY, HOUR)), strategy); assertTrue(it.hasNext()); assertEquals(indices.get(1).getName(), it.next().getName()); @@ -175,7 +175,7 @@ public void testChooseTemporalWithoutStatsCityDay() { final Iterator it = getIndices( new HashMap>(), - new BasicQuery(createConstraints(CITY, CITY, DAY)), + new BasicQueryByClass(createConstraints(CITY, CITY, DAY)), strategy); assertTrue(it.hasNext()); assertEquals(indices.get(1).getName(), it.next().getName()); @@ -190,7 +190,7 @@ public void testChooseSpatialWithoutStatsCityWeek() { final Iterator it = getIndices( new HashMap>(), - new BasicQuery(createConstraints(CITY, CITY, WEEK)), + new BasicQueryByClass(createConstraints(CITY, CITY, WEEK)), strategy); assertTrue(it.hasNext()); assertEquals(indices.get(1).getName(), it.next().getName()); @@ -199,7 +199,7 @@ public void testChooseSpatialWithoutStatsCityWeek() { public Iterator getIndices( final Map> stats, - final BasicQuery query, + final BasicQueryByClass query, final ChooseHeuristicMatchIndexQueryStrategy strategy) { return strategy.getIndices( stats, @@ -232,7 +232,10 @@ public boolean overlaps(final NumericDimensionField[] field, final NumericData[] } } - private Constraints createConstraints(final double lat, final double lon, final double time) { + private ConstraintsByClass createConstraints( + final double lat, + final double lon, + final double time) { final ConstraintSet cs1 = new ConstraintSet(); cs1.addConstraint( LatitudeDefinition.class, @@ -247,6 +250,6 @@ private Constraints createConstraints(final double lat, final double lon, final TimeDefinition.class, new ConstraintData(new ConstrainedIndexValue(0, time), true)); - return new Constraints(Arrays.asList(cs2a)).merge(Collections.singletonList(cs1)); + return new ConstraintsByClass(Arrays.asList(cs2a)).merge(Collections.singletonList(cs1)); } } diff --git a/extensions/adapters/vector/src/test/java/org/locationtech/geowave/adapter/vector/index/ChooseLocalityPreservingQueryStrategyTest.java b/extensions/adapters/vector/src/test/java/org/locationtech/geowave/adapter/vector/index/ChooseLocalityPreservingQueryStrategyTest.java index 7dcd32aaf4b..36b6527521c 100644 --- a/extensions/adapters/vector/src/test/java/org/locationtech/geowave/adapter/vector/index/ChooseLocalityPreservingQueryStrategyTest.java +++ b/extensions/adapters/vector/src/test/java/org/locationtech/geowave/adapter/vector/index/ChooseLocalityPreservingQueryStrategyTest.java @@ -34,10 +34,10 @@ import org.locationtech.geowave.core.store.dimension.NumericDimensionField; import org.locationtech.geowave.core.store.index.CommonIndexValue; import org.locationtech.geowave.core.store.index.NullIndex; -import org.locationtech.geowave.core.store.query.constraints.BasicQuery; -import org.locationtech.geowave.core.store.query.constraints.BasicQuery.ConstraintData; -import org.locationtech.geowave.core.store.query.constraints.BasicQuery.ConstraintSet; -import org.locationtech.geowave.core.store.query.constraints.BasicQuery.Constraints; +import org.locationtech.geowave.core.store.query.constraints.BasicQueryByClass; +import org.locationtech.geowave.core.store.query.constraints.BasicQueryByClass.ConstraintData; +import org.locationtech.geowave.core.store.query.constraints.BasicQueryByClass.ConstraintSet; +import org.locationtech.geowave.core.store.query.constraints.BasicQueryByClass.ConstraintsByClass; import org.opengis.feature.simple.SimpleFeature; public class ChooseLocalityPreservingQueryStrategyTest { @@ -70,7 +70,7 @@ public void testChooseTemporalWithoutStatsHouseHour() { final Iterator it = getIndices( new HashMap>(), - new BasicQuery(createConstraints(HOUSE, HOUSE, HOUR)), + new BasicQueryByClass(createConstraints(HOUSE, HOUSE, HOUR)), strategy); assertTrue(it.hasNext()); assertEquals(indices.get(1).getName(), it.next().getName()); @@ -85,7 +85,7 @@ public void testChooseSpatialWithoutStatsHouseDay() { final Iterator it = getIndices( new HashMap>(), - new BasicQuery(createConstraints(HOUSE, HOUSE, DAY)), + new BasicQueryByClass(createConstraints(HOUSE, HOUSE, DAY)), strategy); assertTrue(it.hasNext()); assertEquals(indices.get(3).getName(), it.next().getName()); @@ -100,7 +100,7 @@ public void testChooseSpatialWithoutStatsHouseWeek() { final Iterator it = getIndices( new HashMap>(), - new BasicQuery(createConstraints(HOUSE, HOUSE, WEEK)), + new BasicQueryByClass(createConstraints(HOUSE, HOUSE, WEEK)), strategy); assertTrue(it.hasNext()); assertEquals(indices.get(3).getName(), it.next().getName()); @@ -115,7 +115,7 @@ public void testChooseTemporalWithoutStatsBlockHour() { final Iterator it = getIndices( new HashMap>(), - new BasicQuery(createConstraints(BLOCK, BLOCK, HOUR)), + new BasicQueryByClass(createConstraints(BLOCK, BLOCK, HOUR)), strategy); assertTrue(it.hasNext()); assertEquals(indices.get(1).getName(), it.next().getName()); @@ -130,7 +130,7 @@ public void testChooseSpatialWithoutStatsBlockDay() { final Iterator it = getIndices( new HashMap>(), - new BasicQuery(createConstraints(BLOCK, BLOCK, DAY)), + new BasicQueryByClass(createConstraints(BLOCK, BLOCK, DAY)), strategy); assertTrue(it.hasNext()); assertEquals(indices.get(3).getName(), it.next().getName()); @@ -145,7 +145,7 @@ public void testChooseSpatialWithoutStatsBlockWeek() { final Iterator it = getIndices( new HashMap>(), - new BasicQuery(createConstraints(BLOCK, BLOCK, WEEK)), + new BasicQueryByClass(createConstraints(BLOCK, BLOCK, WEEK)), strategy); assertTrue(it.hasNext()); assertEquals(indices.get(3).getName(), it.next().getName()); @@ -160,7 +160,7 @@ public void testChooseTemporalWithoutStatsCityHour() { final Iterator it = getIndices( new HashMap>(), - new BasicQuery(createConstraints(CITY, CITY, HOUR)), + new BasicQueryByClass(createConstraints(CITY, CITY, HOUR)), strategy); assertTrue(it.hasNext()); assertEquals(indices.get(1).getName(), it.next().getName()); @@ -175,7 +175,7 @@ public void testChooseTemporalWithoutStatsCityDay() { final Iterator it = getIndices( new HashMap>(), - new BasicQuery(createConstraints(CITY, CITY, DAY)), + new BasicQueryByClass(createConstraints(CITY, CITY, DAY)), strategy); assertTrue(it.hasNext()); assertEquals(indices.get(1).getName(), it.next().getName()); @@ -190,7 +190,7 @@ public void testChooseSpatialWithoutStatsCityWeek() { final Iterator it = getIndices( new HashMap>(), - new BasicQuery(createConstraints(CITY, CITY, WEEK)), + new BasicQueryByClass(createConstraints(CITY, CITY, WEEK)), strategy); assertTrue(it.hasNext()); assertEquals(indices.get(3).getName(), it.next().getName()); @@ -199,7 +199,7 @@ public void testChooseSpatialWithoutStatsCityWeek() { public Iterator getIndices( final Map> stats, - final BasicQuery query, + final BasicQueryByClass query, final ChooseLocalityPreservingQueryStrategy strategy) { return strategy.getIndices( stats, @@ -232,7 +232,10 @@ public boolean overlaps(final NumericDimensionField[] field, final NumericData[] } } - private Constraints createConstraints(final double lat, final double lon, final double time) { + private ConstraintsByClass createConstraints( + final double lat, + final double lon, + final double time) { final ConstraintSet cs1 = new ConstraintSet(); cs1.addConstraint( LatitudeDefinition.class, @@ -247,6 +250,6 @@ private Constraints createConstraints(final double lat, final double lon, final TimeDefinition.class, new ConstraintData(new ConstrainedIndexValue(0, time), true)); - return new Constraints(Arrays.asList(cs2a)).merge(Collections.singletonList(cs1)); + return new ConstraintsByClass(Arrays.asList(cs2a)).merge(Collections.singletonList(cs1)); } } diff --git a/extensions/adapters/vector/src/test/java/org/locationtech/geowave/adapter/vector/util/QueryIndexHelperTest.java b/extensions/adapters/vector/src/test/java/org/locationtech/geowave/adapter/vector/util/QueryIndexHelperTest.java index 0e84642e5db..b2318a51f75 100644 --- a/extensions/adapters/vector/src/test/java/org/locationtech/geowave/adapter/vector/util/QueryIndexHelperTest.java +++ b/extensions/adapters/vector/src/test/java/org/locationtech/geowave/adapter/vector/util/QueryIndexHelperTest.java @@ -40,8 +40,8 @@ import org.locationtech.geowave.core.store.adapter.statistics.InternalDataStatistics; import org.locationtech.geowave.core.store.adapter.statistics.StatisticsId; import org.locationtech.geowave.core.store.api.Index; -import org.locationtech.geowave.core.store.query.constraints.BasicQuery; -import org.locationtech.geowave.core.store.query.constraints.BasicQuery.Constraints; +import org.locationtech.geowave.core.store.query.constraints.BasicQueryByClass; +import org.locationtech.geowave.core.store.query.constraints.Constraints; import org.locationtech.jts.geom.Coordinate; import org.locationtech.jts.geom.Envelope; import org.locationtech.jts.geom.Geometry; @@ -285,8 +285,8 @@ public void testComposeQueryWithTimeRange() throws ParseException { final TemporalConstraintsSet constraintsSet = new TemporalConstraintsSet(); constraintsSet.getConstraintsForRange("start", "end").add(new TemporalRange(stime, etime)); - final BasicQuery query = - new BasicQuery( + final BasicQueryByClass query = + new BasicQueryByClass( QueryIndexHelper.composeConstraints( rangeType, rangeTimeDescriptors, @@ -299,8 +299,8 @@ public void testComposeQueryWithTimeRange() throws ParseException { assertEquals(stime.getTime(), (long) nd.get(0).getDataPerDimension()[2].getMin()); assertEquals(etime.getTime(), (long) nd.get(0).getDataPerDimension()[2].getMax()); - final BasicQuery query1 = - new BasicQuery( + final BasicQueryByClass query1 = + new BasicQueryByClass( QueryIndexHelper.composeConstraints( rangeType, rangeTimeDescriptors, @@ -336,8 +336,8 @@ public void testComposeQueryWithOutTimeRange() { final Envelope bounds = new Envelope(21.23, 26.23, 41.75, 43.1); - final BasicQuery query = - new BasicQuery( + final BasicQueryByClass query = + new BasicQueryByClass( QueryIndexHelper.composeConstraints( geoType, geoTimeDescriptors, @@ -486,7 +486,7 @@ public void testComposeSubsetConstraints() throws ParseException { statsMap, constraintsSet); final List nd = - constraints.getIndexConstraints(SPATIAL_TEMPORAL_INDEX.getIndexStrategy()); + constraints.getIndexConstraints(SPATIAL_TEMPORAL_INDEX); assertTrue(nd.isEmpty()); final FeatureBoundingBoxStatistics geoStats = new FeatureBoundingBoxStatistics("geometry"); @@ -512,7 +512,7 @@ public void testComposeSubsetConstraints() throws ParseException { null, constraintsSet); final List nd1 = - constraints1.getIndexConstraints(SPATIAL_TEMPORAL_INDEX.getIndexStrategy()); + constraints1.getIndexConstraints(SPATIAL_TEMPORAL_INDEX); assertTrue(nd1.isEmpty()); /* * assertEquals( stime.getTime(), (long) nd1.get( 0).getDataPerDimension()[2].getMin()); @@ -529,7 +529,7 @@ public void testComposeSubsetConstraints() throws ParseException { statsMap, constraintsSet2); final List nd2 = - constraints2.getIndexConstraints(SPATIAL_TEMPORAL_INDEX.getIndexStrategy()); + constraints2.getIndexConstraints(SPATIAL_TEMPORAL_INDEX); assertTrue(nd2.isEmpty()); } diff --git a/test/src/test/java/org/locationtech/geowave/test/query/SpatialTemporalQueryIT.java b/test/src/test/java/org/locationtech/geowave/test/query/SpatialTemporalQueryIT.java index 4916ea29a5a..88b822f0db6 100644 --- a/test/src/test/java/org/locationtech/geowave/test/query/SpatialTemporalQueryIT.java +++ b/test/src/test/java/org/locationtech/geowave/test/query/SpatialTemporalQueryIT.java @@ -53,7 +53,7 @@ import org.locationtech.geowave.core.store.cli.remote.options.DataStorePluginOptions; import org.locationtech.geowave.core.store.cli.remote.options.IndexPluginOptions.PartitionStrategy; import org.locationtech.geowave.core.store.entities.GeoWaveRow; -import org.locationtech.geowave.core.store.query.constraints.BasicQuery; +import org.locationtech.geowave.core.store.query.constraints.BasicQueryByClass; import org.locationtech.geowave.test.GeoWaveITRunner; import org.locationtech.geowave.test.TestUtils; import org.locationtech.geowave.test.annotation.GeoWaveTestStore; @@ -280,7 +280,7 @@ public IndexQueryStrategySPI getIndexQueryStrategy() { @Override public CloseableIterator getIndices( final Map> stats, - final BasicQuery query, + final BasicQueryByClass query, final Index[] indices, final Map hints) { return new CloseableIteratorWrapper<>(new Closeable() {