package me.prettyprint.cassandra.service;
import java.util.Iterator;
import me.prettyprint.cassandra.serializers.StringSerializer;
import me.prettyprint.hector.api.Keyspace;
import me.prettyprint.hector.api.Serializer;
import me.prettyprint.hector.api.beans.OrderedRows;
import me.prettyprint.hector.api.beans.Row;
import me.prettyprint.hector.api.factory.HFactory;
import me.prettyprint.hector.api.query.QueryResult;
import me.prettyprint.hector.api.query.RangeSlicesQuery;
/**
* This class returns each key in the specified Column Family as an Iterator. You
* can use this class in a for loop without the overhead of first storing each
* key in a large array. See StringKeyIterator for a convenience class if the key
* is a String.
* @author Tim Koop
* @param <K> the type of the row key
* @see StringKeyIterator
*/
public class KeyIterator<K> implements Iterable<K> {
private static StringSerializer stringSerializer = new StringSerializer();
private static int MAX_ROW_COUNT_DEFAULT = 500;
private int maxColumnCount = 2; // we only need this to tell if there are any columns in the row (to test for tombstones)
private Iterator<Row<K, String, String>> rowsIterator = null;
private RangeSlicesQuery<K, String, String> query = null;
private K nextValue = null;
private K lastReadValue = null;
private K endKey;
private boolean firstRun = true;
private int rowCount = MAX_ROW_COUNT_DEFAULT;
private Iterator<K> keyIterator = new Iterator<K>() {
@Override
public boolean hasNext() {
return nextValue != null;
}
@Override
public K next() {
K next = nextValue;
findNext(false);
return next;
}
@Override
public void remove() {
throw new UnsupportedOperationException();
}
};
private void findNext(boolean fromRunQuery) {
nextValue = null;
if (rowsIterator == null) {
return;
}
while (rowsIterator.hasNext() && nextValue == null) {
Row<K, String, String> row = rowsIterator.next();
lastReadValue = row.getKey();
if (!row.getColumnSlice().getColumns().isEmpty()) {
nextValue = lastReadValue;
}
}
if (!rowsIterator.hasNext() && nextValue == null) {
runQuery(lastReadValue, endKey);
}
}
@Deprecated
public KeyIterator(Keyspace keyspace, String columnFamily, Serializer<K> serializer) {
this(keyspace, columnFamily, serializer, null, null, MAX_ROW_COUNT_DEFAULT);
}
@Deprecated
public KeyIterator(Keyspace keyspace, String columnFamily, Serializer<K> serializer, int maxRowCount) {
this(keyspace, columnFamily, serializer, null, null, maxRowCount);
}
@Deprecated
public KeyIterator(Keyspace keyspace, String columnFamily, Serializer<K> serializer, K start, K end) {
this(keyspace, columnFamily, serializer, start, end, MAX_ROW_COUNT_DEFAULT);
}
@Deprecated
/*
* When pulling deprecated methods out, do not remove this but change it to private constructor
*/
public KeyIterator(Keyspace keyspace, String columnFamily, Serializer<K> serializer, K start, K end, int maxRowCount) {
query = HFactory
.createRangeSlicesQuery(keyspace, serializer, stringSerializer, stringSerializer)
.setColumnFamily(columnFamily)
.setRange(null, null, false, maxColumnCount)
.setRowCount(maxRowCount);
endKey = end;
if(maxRowCount < Integer.MAX_VALUE) {
rowCount = maxRowCount+1; //to compensate the first entry skip (except in first run)
}
runQuery(start, end);
}
private void runQuery(K start, K end) {
query.setKeys(start, end);
if(!firstRun) {
query.setRowCount(rowCount);
}
rowsIterator = null;
QueryResult<OrderedRows<K, String, String>> result = query.execute();
OrderedRows<K, String, String> rows = (result != null) ? result.get() : null;
rowsIterator = (rows != null) ? rows.iterator() : null;
// we'll skip this first one, since it is the same as the last one from previous time we executed
if (!firstRun && rowsIterator != null)
rowsIterator.next();
firstRun = false;
if (rowsIterator != null && !rowsIterator.hasNext()) {
nextValue = null; // all done. our iterator's hasNext() will now return false;
} else {
findNext(true);
}
}
@Override
public Iterator<K> iterator() {
return keyIterator;
}
public static class Builder<K> {
//required
private Keyspace keyspace;
private String columnFamily;
private Serializer<K> serializer;
//optional
private K start;
private K end;
private Integer maxRowCount;
public Builder(Keyspace keyspace, String columnFamily, Serializer<K> serializer) {
this.keyspace = keyspace;
this.columnFamily = columnFamily;
this.serializer = serializer;
}
public Builder<K> start(K start) {
this.start = start;
return this;
}
public Builder<K> end(K end) {
this.end = end;
return this;
}
public Builder<K> maxRowCount(int maxRowCount) {
this.maxRowCount = maxRowCount;
return this;
}
public KeyIterator<K> build() {
return new KeyIterator<K>(this);
}
}
protected KeyIterator(Builder<K> builder) {
this(builder.keyspace, builder.columnFamily, builder.serializer, builder.start, builder.end,
builder.maxRowCount == null? MAX_ROW_COUNT_DEFAULT : builder.maxRowCount);
}
}