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 mil.nga.giat.geowave.core.index.ByteArrayId;
import mil.nga.giat.geowave.core.index.ByteArrayRange;
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.adapter.statistics.RowRangeHistogramStatistics;
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;
import mil.nga.giat.geowave.core.store.query.ConstraintsQuery;
import mil.nga.giat.geowave.core.store.util.DataStoreUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.opengis.feature.simple.SimpleFeature;
public class ChooseBestMatchIndexQueryStrategy implements
IndexQueryStrategySPI
{
public static final String NAME = "Best Match";
private final static Logger LOGGER = LoggerFactory.getLogger(ChooseBestMatchIndexQueryStrategy.class);
@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() {
long min = Long.MAX_VALUE;
PrimaryIndex bestIdx = null;
while (!done && i < indices.length) {
nextIdx = (PrimaryIndex) indices[i++];
if (nextIdx.getIndexStrategy().getOrderedDimensionDefinitions().length == 0) continue;
final List<MultiDimensionalNumericData> constraints = query.getIndexConstraints(nextIdx
.getIndexStrategy());
if (!stats.containsKey(RowRangeHistogramStatistics.composeId(nextIdx.getId()))) {
LOGGER
.warn("Best Match Heuristic requires statistic RowRangeHistogramStatistics for each index to properly choose an index.");
}
if (IndexUtils.isFullTableScan(constraints)) {
// keep this is as a default in case all indices
// result in a full table scan
if (bestIdx == null) {
bestIdx = nextIdx;
}
}
else {
final List<ByteArrayRange> ranges = DataStoreUtils.constraintsToByteArrayRanges(
constraints,
nextIdx.getIndexStrategy(),
ConstraintsQuery.MAX_RANGE_DECOMPOSITION);
final long temp = DataStoreUtils.cardinality(
nextIdx,
stats,
ranges);
if (temp < min) {
bestIdx = nextIdx;
min = temp;
}
}
}
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 {}
};
}
}