/**
* This file is hereby placed into the Public Domain. This means anyone is
* free to do whatever they wish with this file.
*/
package mil.nga.giat.data.elasticsearch;
import java.io.IOException;
import java.util.HashSet;
import java.util.List;
import java.util.NoSuchElementException;
import java.util.Set;
import java.util.logging.Logger;
import org.geotools.data.FeatureReader;
import org.geotools.data.store.ContentState;
import org.geotools.util.logging.Logging;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.feature.simple.SimpleFeatureType;
public class ElasticFeatureReaderScroll implements FeatureReader<SimpleFeatureType, SimpleFeature> {
private final static Logger LOGGER = Logging.getLogger(ElasticFeatureReaderScroll.class);
private final ContentState contentState;
private final int maxFeatures;
private String nextScrollId;
private ElasticFeatureReader delegate;
private int numFeatures;
private boolean lastScroll;
private Set<String> scrollIds;
public ElasticFeatureReaderScroll(ContentState contentState, ElasticResponse searchResponse, int maxFeatures) {
this.contentState = contentState;
this.maxFeatures = maxFeatures;
this.numFeatures = 0;
this.scrollIds = new HashSet<>();
processResponse(searchResponse);
}
private void advanceScroll() throws IOException {
final ElasticDataStore dataStore;
dataStore = (ElasticDataStore) contentState.getEntry().getDataStore();
processResponse(dataStore.getClient().scroll(nextScrollId, dataStore.getScrollTime()));
}
private void processResponse(ElasticResponse searchResponse) {
final int numHits = searchResponse.getNumHits();
final List<ElasticHit> hits;
if (numFeatures+numHits <= maxFeatures) {
hits = searchResponse.getResults().getHits();
} else {
final int n = maxFeatures-numFeatures;
hits = searchResponse.getResults().getHits().subList(0,n);
}
delegate = new ElasticFeatureReader(contentState, hits, searchResponse.getAggregations(), 0);
nextScrollId = searchResponse.getScrollId();
lastScroll = numHits == 0 || numFeatures+hits.size()>=maxFeatures;
LOGGER.fine("Scoll numHits=" + hits.size() + " (total=" + numFeatures+hits.size());
scrollIds.add(nextScrollId);
}
@Override
public SimpleFeatureType getFeatureType() {
return delegate.getFeatureType();
}
@Override
public SimpleFeature next() throws IOException {
final SimpleFeature feature;
if (hasNext()) {
numFeatures++;
feature = delegate.next();
} else {
throw new NoSuchElementException();
}
return feature;
}
@Override
public boolean hasNext() throws IOException {
if (!delegate.hasNext() && !lastScroll) {
advanceScroll();
}
return (delegate.hasNext() || !lastScroll) && numFeatures<maxFeatures;
}
@Override
public void close() throws IOException {
if (!scrollIds.isEmpty()) {
final ElasticDataStore dataStore;
dataStore = (ElasticDataStore) contentState.getEntry().getDataStore();
dataStore.getClient().clearScroll(scrollIds);
}
delegate.close();
}
}