package mil.nga.giat.geowave.adapter.vector.index; import java.io.IOException; import java.util.List; import java.util.Map; import java.util.NoSuchElementException; import org.opengis.feature.simple.SimpleFeature; import mil.nga.giat.geowave.core.index.ByteArrayId; import mil.nga.giat.geowave.core.index.IndexUtils; import mil.nga.giat.geowave.core.index.sfc.data.MultiDimensionalNumericData; import mil.nga.giat.geowave.core.store.CloseableIterator; import mil.nga.giat.geowave.core.store.adapter.statistics.DataStatistics; import mil.nga.giat.geowave.core.store.index.Index; import mil.nga.giat.geowave.core.store.index.PrimaryIndex; import mil.nga.giat.geowave.core.store.query.BasicQuery; /** * This Query Strategy chooses the index that satisfies the most dimensions of * the underlying query first and then if multiple are found it will choose the * one that most closely preserves locality. It won't be optimized for a single * prefix query but it will choose the index with the most dimensions defined, * enabling more fine-grained contraints given a larger set of indexable ranges. * * */ public class ChooseHeuristicMatchIndexQueryStrategy implements IndexQueryStrategySPI { public static final String NAME = "Heuristic Match"; @Override public String toString() { return NAME; } @Override public CloseableIterator<Index<?, ?>> getIndices( final Map<ByteArrayId, DataStatistics<SimpleFeature>> stats, final BasicQuery query, final PrimaryIndex[] indices ) { return new CloseableIterator<Index<?, ?>>() { PrimaryIndex nextIdx = null; boolean done = false; int i = 0; @Override public boolean hasNext() { double bestIndexBitsUsed = -1; int bestIndexDimensionCount = -1; PrimaryIndex bestIdx = null; while (!done && (i < indices.length)) { nextIdx = indices[i++]; if (nextIdx.getIndexStrategy().getOrderedDimensionDefinitions().length == 0) { continue; } final List<MultiDimensionalNumericData> queryRanges = query.getIndexConstraints(nextIdx .getIndexStrategy()); if (IndexUtils.isFullTableScan(queryRanges)) { // keep this is as a default in case all indices // result in a full table scan if (bestIdx == null) { bestIdx = nextIdx; } } else { double currentBitsUsed = 0; int currentDimensionCount = nextIdx.getIndexStrategy().getOrderedDimensionDefinitions().length; if (currentDimensionCount >= bestIndexDimensionCount) { for (final MultiDimensionalNumericData qr : queryRanges) { final double[] dataRangePerDimension = new double[qr.getDimensionCount()]; for (int d = 0; d < dataRangePerDimension.length; d++) { dataRangePerDimension[d] = qr.getMaxValuesPerDimension()[d] - qr.getMinValuesPerDimension()[d]; } currentBitsUsed += IndexUtils.getDimensionalBitsUsed( nextIdx.getIndexStrategy(), dataRangePerDimension); } if (currentDimensionCount > bestIndexDimensionCount || currentBitsUsed > bestIndexBitsUsed) { bestIndexBitsUsed = currentBitsUsed; bestIndexDimensionCount = currentDimensionCount; bestIdx = nextIdx; } } } } nextIdx = bestIdx; done = true; return nextIdx != null; } @Override public Index<?, ?> next() throws NoSuchElementException { if (nextIdx == null) { throw new NoSuchElementException(); } final Index<?, ?> returnVal = nextIdx; nextIdx = null; return returnVal; } @Override public void remove() {} @Override public void close() throws IOException {} }; } }