package mil.nga.giat.geowave.datastore.accumulo.query; import java.util.Iterator; import java.util.List; import java.util.Map.Entry; import org.apache.accumulo.core.client.IteratorSetting; import org.apache.accumulo.core.client.ScannerBase; import org.apache.accumulo.core.data.Key; import org.apache.accumulo.core.data.Value; import org.apache.accumulo.core.iterators.user.WholeRowIterator; import org.apache.commons.lang3.tuple.Pair; import com.google.common.collect.Iterators; import mil.nga.giat.geowave.core.index.ByteArrayId; import mil.nga.giat.geowave.core.index.ByteArrayRange; import mil.nga.giat.geowave.core.index.ByteArrayUtils; import mil.nga.giat.geowave.core.index.IndexMetaData; import mil.nga.giat.geowave.core.index.Mergeable; import mil.nga.giat.geowave.core.index.MultiDimensionalCoordinateRangesArray; import mil.nga.giat.geowave.core.index.MultiDimensionalCoordinateRangesArray.ArrayOfArrays; import mil.nga.giat.geowave.core.index.PersistenceUtils; import mil.nga.giat.geowave.core.index.sfc.data.MultiDimensionalNumericData; import mil.nga.giat.geowave.core.store.adapter.AdapterStore; import mil.nga.giat.geowave.core.store.adapter.DataAdapter; import mil.nga.giat.geowave.core.store.adapter.statistics.DuplicateEntryCount; import mil.nga.giat.geowave.core.store.callback.ScanCallback; import mil.nga.giat.geowave.core.store.data.visibility.DifferingFieldVisibilityEntryCount; import mil.nga.giat.geowave.core.store.filter.DedupeFilter; import mil.nga.giat.geowave.core.store.filter.DistributableFilterList; import mil.nga.giat.geowave.core.store.filter.DistributableQueryFilter; import mil.nga.giat.geowave.core.store.filter.QueryFilter; import mil.nga.giat.geowave.core.store.index.PrimaryIndex; import mil.nga.giat.geowave.core.store.query.ConstraintsQuery; import mil.nga.giat.geowave.core.store.query.Query; import mil.nga.giat.geowave.core.store.query.aggregate.Aggregation; import mil.nga.giat.geowave.core.store.query.aggregate.CommonIndexAggregation; /** * This class represents basic numeric contraints applied to an Accumulo Query * */ public class AccumuloConstraintsQuery extends AccumuloFilteredIndexQuery { protected final ConstraintsQuery base; private boolean queryFiltersEnabled; public AccumuloConstraintsQuery( final List<ByteArrayId> adapterIds, final PrimaryIndex index, final Query query, final DedupeFilter clientDedupeFilter, final ScanCallback<?> scanCallback, final Pair<DataAdapter<?>, Aggregation<?, ?, ?>> aggregation, final Pair<List<String>, DataAdapter<?>> fieldIdsAdapterPair, final IndexMetaData[] indexMetaData, final DuplicateEntryCount duplicateCounts, final DifferingFieldVisibilityEntryCount visibilityCounts, final String[] authorizations ) { this( adapterIds, index, query != null ? query.getIndexConstraints(index.getIndexStrategy()) : null, query != null ? query.createFilters(index.getIndexModel()) : null, clientDedupeFilter, scanCallback, aggregation, fieldIdsAdapterPair, indexMetaData, duplicateCounts, visibilityCounts, authorizations); } public AccumuloConstraintsQuery( final List<ByteArrayId> adapterIds, final PrimaryIndex index, final List<MultiDimensionalNumericData> constraints, final List<QueryFilter> queryFilters, final DedupeFilter clientDedupeFilter, final ScanCallback<?> scanCallback, final Pair<DataAdapter<?>, Aggregation<?, ?, ?>> aggregation, final Pair<List<String>, DataAdapter<?>> fieldIdsAdapterPair, final IndexMetaData[] indexMetaData, final DuplicateEntryCount duplicateCounts, final DifferingFieldVisibilityEntryCount visibilityCounts, final String[] authorizations ) { super( adapterIds, index, scanCallback, fieldIdsAdapterPair, visibilityCounts, authorizations); base = new ConstraintsQuery( constraints, aggregation, indexMetaData, index, queryFilters, clientDedupeFilter, duplicateCounts, this); queryFiltersEnabled = true; } @Override protected boolean isAggregation() { return base.isAggregation(); } @Override protected void addScanIteratorSettings( final ScannerBase scanner ) { addFieldSubsettingToIterator(scanner); IteratorSetting iteratorSettings = null; if (isAggregation()) { if (useWholeRowIterator()) { iteratorSettings = new IteratorSetting( QueryFilterIterator.QUERY_ITERATOR_PRIORITY, QueryFilterIterator.QUERY_ITERATOR_NAME, WholeRowAggregationIterator.class); } else { iteratorSettings = new IteratorSetting( QueryFilterIterator.QUERY_ITERATOR_PRIORITY, QueryFilterIterator.QUERY_ITERATOR_NAME, AggregationIterator.class); } if (!(base.aggregation.getRight() instanceof CommonIndexAggregation) && base.aggregation.getLeft() != null) { iteratorSettings.addOption( AggregationIterator.ADAPTER_OPTION_NAME, ByteArrayUtils.byteArrayToString(PersistenceUtils.toBinary(base.aggregation.getLeft()))); } final Aggregation aggr = base.aggregation.getRight(); iteratorSettings.addOption( AggregationIterator.AGGREGATION_OPTION_NAME, aggr.getClass().getName()); if (aggr.getParameters() != null) { // sets the parameters iteratorSettings.addOption( AggregationIterator.PARAMETER_OPTION_NAME, ByteArrayUtils.byteArrayToString((PersistenceUtils.toBinary(aggr.getParameters())))); } iteratorSettings.addOption( AggregationIterator.CONSTRAINTS_OPTION_NAME, ByteArrayUtils.byteArrayToString((PersistenceUtils.toBinary(base.constraints)))); iteratorSettings.addOption( AggregationIterator.INDEX_STRATEGY_OPTION_NAME, ByteArrayUtils.byteArrayToString(PersistenceUtils.toBinary(index.getIndexStrategy()))); // the index model must be provided for the aggregation iterator to // deserialize each entry iteratorSettings.addOption( QueryFilterIterator.MODEL, ByteArrayUtils.byteArrayToString(PersistenceUtils.toBinary(index.getIndexModel()))); // don't bother setting max decomposition because it is just the // default anyways } boolean usingDistributableFilter = false; if ((base.distributableFilters != null) && !base.distributableFilters.isEmpty() && queryFiltersEnabled) { usingDistributableFilter = true; if (iteratorSettings == null) { if (useWholeRowIterator()) { iteratorSettings = new IteratorSetting( QueryFilterIterator.QUERY_ITERATOR_PRIORITY, QueryFilterIterator.QUERY_ITERATOR_NAME, WholeRowQueryFilterIterator.class); } else { iteratorSettings = new IteratorSetting( QueryFilterIterator.QUERY_ITERATOR_PRIORITY, QueryFilterIterator.QUERY_ITERATOR_NAME, QueryFilterIterator.class); } } DistributableQueryFilter filter; if (base.distributableFilters.size() > 1) { filter = new DistributableFilterList( base.distributableFilters); } else { filter = base.distributableFilters.get(0); } iteratorSettings.addOption( QueryFilterIterator.FILTER, ByteArrayUtils.byteArrayToString(PersistenceUtils.toBinary(filter))); if (!iteratorSettings.getOptions().containsKey( QueryFilterIterator.MODEL)) { // it may already be added as an option if its an aggregation iteratorSettings.addOption( QueryFilterIterator.MODEL, ByteArrayUtils.byteArrayToString(PersistenceUtils.toBinary(index.getIndexModel()))); } } else if ((iteratorSettings == null) && useWholeRowIterator()) { // we have to at least use a whole row iterator iteratorSettings = new IteratorSetting( QueryFilterIterator.QUERY_ITERATOR_PRIORITY, QueryFilterIterator.QUERY_ITERATOR_NAME, WholeRowIterator.class); } if (!usingDistributableFilter) { // it ends up being duplicative and slower to add both a // distributable query and the index constraints, but one of the two // is important to limit client-side filtering addIndexFilterToIterator(scanner); } if (iteratorSettings != null) { scanner.addScanIterator(iteratorSettings); } } protected void addIndexFilterToIterator( final ScannerBase scanner ) { final List<MultiDimensionalCoordinateRangesArray> coords = base.getCoordinateRanges(); if (!coords.isEmpty()) { final IteratorSetting iteratorSetting = new IteratorSetting( NumericIndexStrategyFilterIterator.IDX_FILTER_ITERATOR_PRIORITY, NumericIndexStrategyFilterIterator.IDX_FILTER_ITERATOR_NAME, NumericIndexStrategyFilterIterator.class); iteratorSetting.addOption( NumericIndexStrategyFilterIterator.INDEX_STRATEGY_KEY, ByteArrayUtils.byteArrayToString(PersistenceUtils.toBinary(index.getIndexStrategy()))); iteratorSetting.addOption( NumericIndexStrategyFilterIterator.COORDINATE_RANGE_KEY, ByteArrayUtils.byteArrayToString(new ArrayOfArrays( coords.toArray(new MultiDimensionalCoordinateRangesArray[] {})).toBinary())); scanner.addScanIterator(iteratorSetting); } } @Override protected List<ByteArrayRange> getRanges() { return base.getRanges(); } public boolean isQueryFiltersEnabled() { return queryFiltersEnabled; } public void setQueryFiltersEnabled( final boolean queryFiltersEnabled ) { this.queryFiltersEnabled = queryFiltersEnabled; } @Override protected Iterator initIterator( final AdapterStore adapterStore, final ScannerBase scanner ) { if (isAggregation()) { // aggregate the stats to a single value here try { final Iterator<Entry<Key, Value>> it = scanner.iterator(); Mergeable mergedAggregationResult = null; if (!it.hasNext()) { return Iterators.emptyIterator(); } else { while (it.hasNext()) { final Entry<Key, Value> input = it.next(); if (input.getValue() != null) { if (mergedAggregationResult == null) { mergedAggregationResult = PersistenceUtils.fromBinary( input.getValue().get(), Mergeable.class); } else { mergedAggregationResult.merge(PersistenceUtils.fromBinary( input.getValue().get(), Mergeable.class)); } } } } return Iterators.singletonIterator(mergedAggregationResult); } finally { scanner.close(); } } else { return super.initIterator( adapterStore, scanner); } } }