/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
package com.github.geophile.erdo.map.forestmap;
import com.github.geophile.erdo.AbstractKey;
import com.github.geophile.erdo.bloomfilter.BloomFilter;
import com.github.geophile.erdo.forest.ForestSnapshot;
import com.github.geophile.erdo.map.LazyRecord;
import com.github.geophile.erdo.map.MapCursor;
import com.github.geophile.erdo.map.SealedMap;
import com.github.geophile.erdo.map.mergescan.MergeCursor;
import java.io.IOException;
import java.util.List;
import java.util.logging.Level;
class ForestMapMatchCursor extends ForestMapCursor
{
// MapCursor interface
@Override
public LazyRecord next() throws IOException, InterruptedException
{
return neighbor(true);
}
@Override
public LazyRecord previous() throws IOException, InterruptedException
{
return neighbor(false);
}
// ForestMapMatchCursor interface
ForestMapMatchCursor(ForestSnapshot forestSnapshot, AbstractKey key)
throws IOException, InterruptedException
{
super(forestSnapshot, key, true);
}
// For use by this class
private LazyRecord updateRecord(AbstractKey key) throws IOException, InterruptedException
{
SealedMap map = forestSnapshot.mapContainingTransaction(key.transactionTimestamp());
if (LOG.isLoggable(Level.FINE)) {
LOG.log(Level.FINE, "Getting record of {0} from {1}", new Object[]{key, map});
}
assert map != null : key;
MapCursor cursor = map.cursor(key, true);
LazyRecord updateRecord = cursor.next();
cursor.close();
assert updateRecord != null : key;
return updateRecord;
}
private LazyRecord neighbor(boolean forward) throws IOException, InterruptedException
{
MergeCursor smallTreeRecordScan = null;
MergeCursor bigTreeRecordScan = null;
LazyRecord neighbor = null;
List<SealedMap> smallTrees = null;
try {
if (state != State.DONE) {
// cursor vs. keyScan:
// - cursor merges after getting records, but uses bloom filter to avoid getting
// record unnecessarily.
// - keyScan uses KeyArrays, probably slower than bloom filter, but has the
// advantage of finding only the most recent.
// - Big trees don't have KeyArrays.
smallTreeRecordScan = new MergeCursor(startKey, forward);
smallTrees = forestSnapshot.smallTrees();
if (LOG.isLoggable(Level.FINE)) {
LOG.log(Level.FINE, "Scanning small trees: {0}", smallTrees);
}
for (SealedMap smallTree : smallTrees) {
smallTreeRecordScan.addInput(BloomFilter.USE_BLOOM_FILTER
? smallTree.cursor(startKey, true)
: smallTree.keyScan(startKey, true));
}
smallTreeRecordScan.start();
// If smallTreeKeyScan.next()/previous() returns a key, it is the one and only key that
// this cursor will yield. Otherwise, if the key is present, it must come from a
// big tree.
neighbor = forward ? smallTreeRecordScan.next() : smallTreeRecordScan.previous();
if (neighbor != null) {
if (!BloomFilter.USE_BLOOM_FILTER) {
AbstractKey key = neighbor.key();
neighbor.destroyRecordReference();
neighbor = updateRecord(key);
}
} else {
bigTreeRecordScan = new MergeCursor(startKey, forward);
for (SealedMap bigTree : forestSnapshot.bigTrees()) {
bigTreeRecordScan.addInput(bigTree.cursor(startKey, true));
}
bigTreeRecordScan.start();
neighbor = forward ? bigTreeRecordScan.next() : bigTreeRecordScan.previous();
}
// Because this is an exact-match cursor, at most one record will be returned.
// So whether we found one or not, we're done.
close();
}
} finally {
if (smallTreeRecordScan != null) {
smallTreeRecordScan.close();
}
if (LOG.isLoggable(Level.FINE)) {
LOG.log(Level.FINE, "Scan of small trees done: {0}", smallTrees);
}
if (bigTreeRecordScan != null) {
bigTreeRecordScan.close();
}
}
return neighbor;
}
}