package dovetaildb.bagindex;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import dovetaildb.bytes.ArrayBytes;
import dovetaildb.bytes.Bytes;
import dovetaildb.querynode.FilteredRangeQueryNode;
import dovetaildb.querynode.QueryNode;
import dovetaildb.querynode.RangeQueryNode;
import dovetaildb.scan.AbstractScanner;
import dovetaildb.scan.IntegerScanner;
import dovetaildb.scan.LiteralScanner;
import dovetaildb.scan.Scanner;
import dovetaildb.util.Pair;
import dovetaildb.util.Util;
/**
*
* Simplest functional in-memory implementation of a BagIndex I can think of.
* Good for testing and not much else.
*
*/
public class TrivialBagIndex extends BagIndex {
ArrayList<ArrayList<EditRec>> revs = new ArrayList<ArrayList<EditRec>>();
public TrivialBagIndex() {
revs.add(new ArrayList<EditRec>());
}
protected ArrayList<EditRec> all(long revNum) { return revs.get((int)revNum); }
// ArrayList<Long> deletions = new ArrayList<Long>(); // maps doc id to deleted in rev
// HashMap<Long,InstanciatedBagIndexDoc> docsById = new HashMap<Long,InstanciatedBagIndexDoc>();
// long maxDocId = 1;
@Override
public void close() {
}
@Override
public long commitNewRev(Collection<EditRec> edits) {
ArrayList<EditRec> all = revs.get(revs.size()-1);
ArrayList<EditRec> newAll = new ArrayList<EditRec>(all);
newAll.addAll(edits);
EditRec.sortById(newAll);
revs.add(newAll);
return revs.size()-1;
}
/*
int numInserts = inserts.size();
long revNum = maxDocId + numInserts;
if (numInserts == 0) {
// commits with only deletions occupy a docId space, so pre-delete it
inserts.add(new Pair<byte[][],byte[][]>(new byte[0][], new byte[0][]));
revNum++;
}
while(this.deletions.size() < revNum) {
this.deletions.add(null);
}
for(long delDocId : deletions) {
this.deletions.set((int) delDocId, revNum);
}
for(Pair<byte[][],byte[][]> docPair : inserts) {
byte[][] indexTerms = docPair.getLeft();
byte[][] storeTerms = docPair.getRight();
InstanciatedBagIndexDoc doc = new InstanciatedBagIndexDoc(indexTerms, storeTerms);
docsById.put(maxDocId, doc);
maxDocId++;
}
return revNum;
}
@Override
public Scanner fetchDeletions(long revNum) {
ArrayList<Long> ids = new ArrayList<Long>();
for(long i=1; i<maxDocId; i++) {
Long delInRev = deletions.get((int) i);
if (delInRev != null && delInRev.longValue() <= revNum ) {
ids.add(i);
}
}
return new LiteralScanner(ids);
}
@Override
public BagIndexDoc fetchDoc(long docId) {
return docsById.get(docId);
}
*/
// @Override
// public Scanner fetchRange(byte[] term1, byte[] term2, boolean isExclusive1, boolean isExclusive2, long revNum) {
// ArrayList<Long> ids = new ArrayList<Long>();
// for(long i=1; i<revNum; i++) {
// Long delInRev = deletions.get((int) i);
// if (delInRev != null && delInRev.longValue() <= revNum ) continue;
// BagIndexDoc doc = docsById.get(i);
// for(byte[] term : doc.getAllTerms()) {
// int cmp;
// cmp = Util.compareBytes(term1, term);
// if (cmp > 0 || (cmp == 0 && isExclusive1)) continue;
// cmp = Util.compareBytes(term2, term);
// if (cmp < 0 || (cmp == 0 && isExclusive2)) continue;
// ids.add(i);
// break;
// }
// }
// return new LiteralScanner(ids);
// }
// @Override
// public Scanner fetchTd(byte[] term, long revNum) {
// return fetchRange(term, term, false, false, revNum);
// }
@Override
public long getCurrentRevNum() {
return revs.size()-1;
}
String homeDir;
@Override
public String getHomedir() {
return homeDir;
}
// @Override
// public BagIndexDoc refetchDoc(BagIndexDoc doc, long docId) {
// return this.fetchDoc(docId);
// }
@Override
public void setHomedir(String homeDir) {
this.homeDir = homeDir;
}
// @Override
// public Scanner fetchAll(long revNum) {
// return new IntegerScanner(1,revNum);
// }
// utility for specialized use cases (like in a local transaction buffer)
// public ArrayList<Pair<byte[][],byte[][]>> dumpRecords() {
// ArrayList<Pair<byte[][],byte[][]>> recs = new ArrayList<Pair<byte[][],byte[][]>>();
// for(InstanciatedBagIndexDoc doc : docsById.values()) {
// Pair<byte[][],byte[][]> pair = new Pair<byte[][],byte[][]>(doc.getIndexedTerms(), doc.getStoredTerms());
// recs.add(pair);
// }
// return recs;
// }
@Override
public RangeQueryNode getRange(final Bytes prefix, final Bytes term1, final Bytes term2,
final boolean isExclusive1, final boolean isExclusive2, long revNum) {
final ArrayList<EditRec> all = revs.get((int)revNum);
if (all.size() == 0) return null;
RangeQueryNode world = new RangeQueryNode() {
int i=0;
public void adjustPrefix(Bytes prefix) { throw new RuntimeException(); }
public void adjustSuffixMax(Bytes newMax, boolean isExclusive) { throw new RuntimeException(); }
public void adjustSuffixMin(Bytes newMin, boolean isExclusive) { throw new RuntimeException(); }
public Bytes getPrefix() { return prefix; }
public Bytes getSuffixMax() { return term2; }
public Bytes getSuffixMin() { return term1; }
public boolean isSuffixMaxExclusive() { return isExclusive2; }
public boolean isSuffixMinExclusive() { return isExclusive1; }
public long doc() {
return all.get(i).docId;
}
public boolean next() {
long orig = all.get(i).docId;
while(++i < all.size()) {
long cur = all.get(i).docId;
if (cur != orig) break;
}
return (i<all.size());
}
public NextStatus nextTerm() {
long orig = all.get(i).docId;
i++;
if (i >= all.size()) return NextStatus.AT_END;
long cur = all.get(i).docId;
return (cur != orig) ? NextStatus.NEXT_DOC : NextStatus.NEXT_TERM;
}
public void resetTerms() { throw new RuntimeException(); }
public boolean skipTo(long target) {
while(doc() < target) {
i++;
if (i >= all.size()) return false;
}
return true;
}
public Bytes term() { return all.get(i).term; }
};
return FilteredRangeQueryNode.make(world, prefix, term1, term2, isExclusive1, isExclusive2);
}
@Override
public QueryNode getTerm(Bytes term, long revNum) {
return getRange(term, ArrayBytes.EMPTY_BYTES, ArrayBytes.EMPTY_BYTES, false, false, revNum);
}
}