package dovetaildb.dbservice;
import java.io.UnsupportedEncodingException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import dovetaildb.apiservice.ApiBuffer;
import dovetaildb.bagindex.BagIndex;
import dovetaildb.bagindex.BagIndexUtil;
import dovetaildb.bagindex.EditRec;
import dovetaildb.bytes.ArrayBytes;
import dovetaildb.bytes.Bytes;
import dovetaildb.bytes.CompoundBytes;
import dovetaildb.iter.EmptyIter;
import dovetaildb.iter.Iter;
import dovetaildb.iter.IterUtil;
import dovetaildb.iter.ScannerIter;
import dovetaildb.querynode.OrderedOrQueryNode;
import dovetaildb.querynode.QueryNode;
import dovetaildb.querynode.QueryNodeTemplate;
import dovetaildb.querynode.QueryNode.NextStatus;
import dovetaildb.scan.Scanner;
import dovetaildb.util.Pair;
import dovetaildb.util.Util;
public class BagIndexBridge implements DbService {
ConcurrentHashMap<String,BagEntry> bagIndexes =
new ConcurrentHashMap<String,BagEntry>();
TransactionMapper txnMapper = null;
public TransactionMapper getTxnMapper() { return txnMapper; }
public void setTxnMapper(TransactionMapper txnMapper) { this.txnMapper = txnMapper; }
private BagEntryFactory bagEntryFactory;
public BagEntryFactory getBagEntryFactory() {
return bagEntryFactory;
}
public void setBagIndexFactory(BagEntryFactory bagEntryFactory) {
this.bagEntryFactory = bagEntryFactory;
}
public BagEntry getBagEntry(String bag) {
return bagIndexes.get(bag);
}
public void setBagEntry(String bag, BagEntry bagEntry) {
bagIndexes.put(bag, bagEntry);
}
public long capacity() {
return 500;
}
// protected byte[][] encodeEntry(Map<String,Object> entry, TermEncoder encoder) {
// byte[][] terms = new byte[entry.size()][];
// int i = 0;
// for(Map.Entry<String, Object> e : entry.entrySet()) {
// terms[i++] = TermEncoderUtil.encodeWholeTerm(encoder, e.getKey(), e.getValue());
// }
// return terms;
// }
/*
protected long commitBag(BufferedData buffer, BagIndex bagIndex, TermEncoder encoder, long revNum) {
ArrayList<Pair<byte[][],byte[][]>> inserts = buffer.entries.dumpRecords();
// ArrayList<Pair<byte[][],byte[][]>> inserts = new ArrayList<Pair<byte[][],byte[][]>>();
// for(Map<String,Object> entry : buffer.getEntries().values()) {
// inserts.add(new Pair<byte[][],byte[][]>(EMPTY_BYTES_ARRAY, encodeEntry(entry, encoder)));
// }
//
HashSet<String> deletions = buffer.getDeletions();
HashSet<String> deletionStrings = new HashSet<String>();
byte[][] idTerms = new byte[deletionStrings.size()][];
int i=0;
for(String idString : deletionStrings) {
idTerms[i++] = TermEncoderUtil.encodeWholeTerm(encoder, "id", idString);
}
long[] deletions = BagIndexUtil.getDocidsForIdTerms(idTerms, bagIndex, revNum);
long commitRevNum = bagIndex.commitNewRev(deletions, inserts);
return commitRevNum;
}
*/
static final ArrayBytes ID_BYTES;
static final int NUM_ID_BYTES;
static {
try {
ID_BYTES = new ArrayBytes("{id:".getBytes("utf-8"));
NUM_ID_BYTES = ID_BYTES.getLength();
} catch (UnsupportedEncodingException e) {
throw new RuntimeException(e);
}
}
private ArrayList<EditRec> encodeBuffer(String bagName, long revNum, ApiBuffer value) {
ArrayList<EditRec> edits = new ArrayList<EditRec>();
// fashion directly and make a nodeiterator !!!
BagEntry bagRec = bagIndexes.get(bagName);
BagIndex index = bagRec.bagIndex;
if (revNum == -1) revNum = index.getCurrentRevNum();
// TODO: more efficient; BagIndex needs a "get multi"
QueryNode data = index.getRange(ArrayBytes.EMPTY_BYTES, null, null, false, false, revNum);
ArrayList<QueryNode> clauses = new ArrayList<QueryNode>(value.getDeletions().size()+value.getEntries().size());
for(String key : value.getDeletions()) {
QueryNode q = index.getTerm(new CompoundBytes(ID_BYTES, new ArrayBytes(Util.decodeString(key))), revNum);
if (q == null) throw new RuntimeException("Object not found; id=\""+key+"\"");
clauses.add(q);
}
if (clauses.size() > 0) {
QueryNode idQuery = new OrderedOrQueryNode(clauses, null, null, null, false, false);
do {
long docId = idQuery.doc();
data.skipTo(docId);
do {
edits.add(new EditRec(docId, data.term(), true));
} while(data.nextTerm() == NextStatus.NEXT_TERM);
} while(idQuery.next());
}
long insId = -1;
for(Map.Entry<String,Object> entry : value.getEntries().entrySet()) {
String key = entry.getKey();
QueryNode q = index.getTerm(new CompoundBytes(ID_BYTES, new ArrayBytes(Util.decodeString(key))), revNum);
if (q != null) {
clauses.add(q);
} else {
long docId = insId--;
DbServiceUtil.sencodeMulti(ArrayBytes.EMPTY_BYTES, entry.getValue(), edits, docId, false);
}
}
if (clauses.size() > 0) {
QueryNode idQuery = new OrderedOrQueryNode(clauses, null, null, null, false, false);
Map<String, Object> entries = value.getEntries();
do {
long docId = idQuery.doc();
Bytes idTerm = idQuery.term();
byte[] idBytes = idTerm.subBytes(NUM_ID_BYTES, idTerm.getLength()-NUM_ID_BYTES).getBytes();
Object val = entries.get(Util.encodeBytes(idBytes));
DbServiceUtil.sencodeMulti(ArrayBytes.EMPTY_BYTES, val, edits, docId, false);
} while(idQuery.next());
}
return edits;
}
public void commit(long fromTxnId, long commitTxnId, Map<String, ApiBuffer> batch) throws CommitFailedException {
HashMap<String,Long> bagRevs = new HashMap<String,Long>();
for(Map.Entry<String,ApiBuffer> entry : batch.entrySet()) {
String bag = entry.getKey();
BagEntry rec = bagIndexes.get(bag);
if (rec == null) {
rec = bagEntryFactory.makeBagEntry(bag);
rec.initialTxn = commitTxnId;
bagIndexes.put(bag, rec);
}
try {
long revNum = txnMapper.getRevForTxn(bag, fromTxnId);
ArrayList<EditRec> bufferedData = encodeBuffer(bag, revNum, entry.getValue());
long commitRevNum = rec.bagIndex.commitNewRev(bufferedData);
bagRevs.put(bag, commitRevNum);
} catch (TxnNotFoundException e) {
throw new RuntimeException(e);
}
}
txnMapper.addRevsForTxn(commitTxnId, bagRevs);
}
public void drop() {
for(String bag : bagIndexes.keySet()) {
dropBag(bag);
}
}
public void dropBag(String bagName) {
BagEntry entry = bagIndexes.get(bagName);
entry.bagIndex.close();
bagIndexes.put(bagName, null);
}
public Collection<String> getBags(long txnId) {
ArrayList<String> bags = new ArrayList<String>();
for(String bag : bagIndexes.keySet()) {
try {
long rev = txnMapper.getRevForTxn(bag, txnId);
if (rev >= 1) bags.add(bag);
} catch (TxnNotFoundException e) {
return null;
}
}
return bags;
}
public void initialize() {
}
@Override
public Iter query(String bag, long txnId, Object query, Map<String, Object> options) {
BagEntry rec = bagIndexes.get(bag);
if (rec == null) return EmptyIter.EMPTY_ITER;
BagIndex index = rec.bagIndex;
try {
long revNum = txnMapper.getRevForTxn(bag, txnId);
if (revNum == -1) return EmptyIter.EMPTY_ITER;
QueryNodeTemplate templ = DbServiceUtil.applyPatternToBagIndex(query, index, revNum);
return QueryNodeIter.make(templ);
} catch (TxnNotFoundException e) {
throw new RuntimeException(e);
}
}
public void rollback(long commitId) {
}
public ApiBuffer createBufferedData(String bagName) {
return new ApiBuffer();
}
}