package com.alvazan.orm.layer9z.spi.db.inmemory;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import com.alvazan.orm.api.z8spi.BatchListener;
import com.alvazan.orm.api.z8spi.Cache;
import com.alvazan.orm.api.z8spi.KeyValue;
import com.alvazan.orm.api.z8spi.Row;
import com.alvazan.orm.api.z8spi.RowHolder;
import com.alvazan.orm.api.z8spi.conv.ByteArray;
import com.alvazan.orm.api.z8spi.iter.AbstractCursor;
import com.alvazan.orm.api.z8spi.iter.StringLocal;
import com.alvazan.orm.api.z8spi.meta.DboTableMeta;
public class CursorKeysToRows extends AbstractCursor<KeyValue<Row>> {
private Iterable<byte[]> rowKeys;
private Iterator<byte[]> theKeys;
private BatchListener list;
private Iterator<KeyValue<Row>> cachedRows;
private Cache cache;
private DboTableMeta colFamily;
private NoSqlDatabase database;
public CursorKeysToRows(DboTableMeta colFamily, Iterable<byte[]> rowKeys, BatchListener list, NoSqlDatabase database, Cache cache2) {
this.colFamily = colFamily;
this.cache = cache2;
this.database = database;
this.rowKeys = rowKeys;
this.list = list;
beforeFirst();
}
@Override
public void beforeFirst() {
theKeys = rowKeys.iterator();
cachedRows = null;
}
@Override
public com.alvazan.orm.api.z8spi.iter.AbstractCursor.Holder<KeyValue<Row>> nextImpl() {
loadCache();
if(cachedRows == null || !cachedRows.hasNext())
return null;
return new Holder<KeyValue<Row>>(cachedRows.next());
}
private void loadCache() {
if(cachedRows != null && cachedRows.hasNext())
return; //There are more rows so return and the code will return the next result from cache
else if(!theKeys.hasNext())
return;
List<RowHolder<Row>> results = new ArrayList<RowHolder<Row>>();
List<byte[]> keysToLookup = new ArrayList<byte[]>();
while(theKeys.hasNext()) {
byte[] key = theKeys.next();
RowHolder<Row> result = cache.fromCache(colFamily, key);
if(result == null)
keysToLookup.add(key);
results.add(result);
}
List<KeyValue<Row>> rows = new ArrayList<KeyValue<Row>>();
if(keysToLookup.size() > 0) {
if(list != null)
list.beforeFetchingNextBatch();
rows = fetchRows();
if(list != null)
list.afterFetchingNextBatch(rows.size());
}
Iterator<KeyValue<Row>> resultingRows = rows.iterator();
Map<ByteArray, KeyValue<Row>> map = new HashMap<ByteArray, KeyValue<Row>>();
while(resultingRows.hasNext()) {
KeyValue<Row> kv = resultingRows.next();
byte[] key = (byte[]) kv.getKey();
ByteArray b = new ByteArray(key);
map.put(b, kv);
cache.cacheRow(colFamily, key, kv.getValue());
}
//UNFORTUNATELY, astyanax's result is NOT ORDERED by the keys we provided so, we need to iterate over the whole thing here
//into our own List :( :( .....okay, well, we are now in memory and would have been ordered but oh well....rework this later
List<KeyValue<Row>> finalRes = new ArrayList<KeyValue<Row>>();
Iterator<byte[]> keyIter = keysToLookup.iterator();
for(RowHolder<Row> r : results) {
if(r == null) {
byte[] key = keyIter.next();
ByteArray b = new ByteArray(key);
KeyValue<Row> kv = map.get(b);
finalRes.add(kv);
} else {
Row row = r.getValue();
KeyValue<Row> kv = new KeyValue<Row>();
kv.setKey(r.getKey());
kv.setValue(row);
finalRes.add(kv);
}
}
cachedRows = finalRes.iterator();
}
public List<KeyValue<Row>> fetchRows() {
List<KeyValue<Row>> rows = new ArrayList<KeyValue<Row>>();
Table table = database.findTable(colFamily.getColumnFamily());
for(byte[] key : rowKeys) {
Row row = findRow(table, key);
Row newRow = null;
if(row != null)
newRow = row.deepCopy();
KeyValue<Row> kv = new KeyValue<Row>();
kv.setKey(key);
kv.setValue(newRow);
//This add null if there is no row to the list on purpose
rows.add(kv);
}
return rows;
}
private Row findRow(Table table, byte[] key) {
if(table == null)
return null;
return table.getRow(key);
}
@Override
public String toString() {
String tabs = StringLocal.getAndAdd();
String keys = ""+rowKeys;
if(rowKeys instanceof List)
keys = "List"+keys;
String retVal = "CursorKeysToRows(inmemoryRowLookup)["+tabs+keys+tabs+"]";
StringLocal.set(tabs.length());
return retVal;
}
}