package mil.nga.giat.geowave.adapter.vector.index; import java.util.ArrayList; import java.util.List; import java.util.Map; import mil.nga.giat.geowave.adapter.vector.stats.FeatureHyperLogLogStatistics; import mil.nga.giat.geowave.adapter.vector.stats.FeatureNumericHistogramStatistics; import mil.nga.giat.geowave.adapter.vector.stats.StatsManager; import mil.nga.giat.geowave.core.index.ByteArrayId; import mil.nga.giat.geowave.core.index.Persistable; import mil.nga.giat.geowave.core.index.PersistenceUtils; import mil.nga.giat.geowave.core.store.adapter.DataAdapter; import mil.nga.giat.geowave.core.store.adapter.statistics.DataStatistics; import mil.nga.giat.geowave.core.store.adapter.statistics.FieldIdStatisticVisibility; import mil.nga.giat.geowave.core.store.index.SecondaryIndex; import mil.nga.giat.geowave.core.store.index.SecondaryIndexType; import mil.nga.giat.geowave.core.store.index.numeric.NumericIndexStrategy; import mil.nga.giat.geowave.core.store.index.temporal.TemporalIndexStrategy; import mil.nga.giat.geowave.core.store.index.text.TextIndexStrategy; import org.opengis.feature.simple.SimpleFeature; import org.opengis.feature.simple.SimpleFeatureType; import org.opengis.feature.type.AttributeDescriptor; import com.google.common.base.Splitter; /** * Class to manage secondary indexes for a Simple Feature Type. It keeps a list * of supported secondary indices associated with all the attributes attached to * the SimpleFeatureType provided upon instantiation. */ public class SecondaryIndexManager implements Persistable { private final List<SecondaryIndex<SimpleFeature>> supportedSecondaryIndices = new ArrayList<>(); private transient DataAdapter<SimpleFeature> dataAdapter; private transient SimpleFeatureType sft; private transient StatsManager statsManager; @Deprecated protected SecondaryIndexManager() {} /** * Create a SecondaryIndexManager for the given DataAdapter and * SimpleFeatureType, while providing * * @param dataAdapter * @param sft * @param statsManager */ public SecondaryIndexManager( final DataAdapter<SimpleFeature> dataAdapter, final SimpleFeatureType sft, final StatsManager statsManager ) { this.dataAdapter = dataAdapter; this.statsManager = statsManager; this.sft = sft; initializeIndices(); } /** * For every attribute of the SFT to be managed by this index, determine * type and if found, create a secondaryIndex of strategy type Temporal, * Text or Numeric for that attribute. */ private void initializeIndices() { for (final AttributeDescriptor desc : sft.getAttributeDescriptors()) { final Map<Object, Object> userData = desc.getUserData(); final String attributeName = desc.getLocalName(); final ByteArrayId fieldId = new ByteArrayId( attributeName); String secondaryIndex = null; SecondaryIndexType secondaryIndexType = null; final List<ByteArrayId> fieldsForPartial = new ArrayList<>(); if (userData.containsKey(NumericSecondaryIndexConfiguration.INDEX_KEY)) { secondaryIndex = NumericSecondaryIndexConfiguration.INDEX_KEY; secondaryIndexType = SecondaryIndexType.valueOf((String) userData .get(NumericSecondaryIndexConfiguration.INDEX_KEY)); } else if (userData.containsKey(TextSecondaryIndexConfiguration.INDEX_KEY)) { secondaryIndex = TextSecondaryIndexConfiguration.INDEX_KEY; secondaryIndexType = SecondaryIndexType.valueOf((String) userData .get(TextSecondaryIndexConfiguration.INDEX_KEY)); } else if (userData.containsKey(TemporalSecondaryIndexConfiguration.INDEX_KEY)) { secondaryIndex = TemporalSecondaryIndexConfiguration.INDEX_KEY; secondaryIndexType = SecondaryIndexType.valueOf((String) userData .get(TemporalSecondaryIndexConfiguration.INDEX_KEY)); } // If a valid secondary index type is provided, and the type is // PARTIAL, then // go through list of fields to be joined at add to tracked list of // fieldsForPartial if (secondaryIndexType != null) { if (secondaryIndexType.equals(SecondaryIndexType.PARTIAL)) { final String joined = (String) userData.get(SecondaryIndexType.PARTIAL.getValue()); final Iterable<String> split = Splitter.on( ",").split( joined); for (final String field : split) { fieldsForPartial.add(new ByteArrayId( field)); } } addIndex( secondaryIndex, fieldId, secondaryIndexType, fieldsForPartial); } } } public List<SecondaryIndex<SimpleFeature>> getSupportedSecondaryIndices() { return supportedSecondaryIndices; } /** * Add an index-based secondary index key * * @param secondaryIndexKey * @param fieldId * @param secondaryIndexType * @param fieldsForPartial */ private void addIndex( final String secondaryIndexKey, final ByteArrayId fieldId, final SecondaryIndexType secondaryIndexType, final List<ByteArrayId> fieldsForPartial ) { final List<DataStatistics<SimpleFeature>> statistics = new ArrayList<>(); DataStatistics<SimpleFeature> stat = null; switch (secondaryIndexKey) { case NumericSecondaryIndexConfiguration.INDEX_KEY: stat = new FeatureNumericHistogramStatistics( dataAdapter.getAdapterId(), fieldId.getString()); statistics.add(stat); supportedSecondaryIndices.add(new SecondaryIndex<SimpleFeature>( new NumericIndexStrategy(), fieldId, statistics, secondaryIndexType, fieldsForPartial)); break; case TextSecondaryIndexConfiguration.INDEX_KEY: stat = new FeatureHyperLogLogStatistics( dataAdapter.getAdapterId(), fieldId.getString(), 16); statistics.add(stat); supportedSecondaryIndices.add(new SecondaryIndex<SimpleFeature>( new TextIndexStrategy(), fieldId, statistics, secondaryIndexType, fieldsForPartial)); break; case TemporalSecondaryIndexConfiguration.INDEX_KEY: stat = new FeatureNumericHistogramStatistics( dataAdapter.getAdapterId(), fieldId.getString()); statistics.add(stat); supportedSecondaryIndices.add(new SecondaryIndex<SimpleFeature>( new TemporalIndexStrategy(), fieldId, statistics, secondaryIndexType, fieldsForPartial)); break; default: break; } for (final DataStatistics<SimpleFeature> statistic : statistics) { statsManager.addStats( statistic, new FieldIdStatisticVisibility<SimpleFeature>( statistic.getStatisticsId())); } } /** * {@inheritDoc} * * This consists of converting supported secondary indices. */ @Override public byte[] toBinary() { final List<Persistable> persistables = new ArrayList<Persistable>(); for (final SecondaryIndex<SimpleFeature> secondaryIndex : supportedSecondaryIndices) { persistables.add(secondaryIndex); } return PersistenceUtils.toBinary(persistables); } /** * {@inheritDoc} * * This extracts the supported secondary indices from the binary stream and * adds them in this object. */ @SuppressWarnings("unchecked") @Override public void fromBinary( byte[] bytes ) { final List<Persistable> persistables = PersistenceUtils.fromBinary(bytes); for (final Persistable persistable : persistables) { supportedSecondaryIndices.add((SecondaryIndex<SimpleFeature>) persistable); } } /** * * @return the StatsManager object being used by this SecondaryIndex. */ public StatsManager getStatsManager() { return statsManager; } }