org.sirix.index.redblacktree.RBTreeReader Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of sirix-core Show documentation
Show all versions of sirix-core Show documentation
SirixDB is a hybrid on-disk and in-memory document oriented, versioned database system. It has a lightweight buffer manager, stores everything in a huge persistent and durable tree and allows efficient reconstruction of every revision. Furthermore, SirixDB implements change tracking, diffing and supports time travel queries.
package org.sirix.index.redblacktree;
import com.google.common.collect.AbstractIterator;
import org.sirix.api.Move;
import org.sirix.api.NodeCursor;
import org.sirix.api.PageReadOnlyTrx;
import org.sirix.api.PageTrx;
import org.sirix.axis.DescendantAxis;
import org.sirix.axis.IncludeSelf;
import org.sirix.cache.Cache;
import org.sirix.cache.RBIndexKey;
import org.sirix.exception.SirixIOException;
import org.sirix.index.IndexType;
import org.sirix.index.SearchMode;
import org.sirix.index.redblacktree.interfaces.References;
import org.sirix.node.NodeKind;
import org.sirix.node.NullNode;
import org.sirix.node.interfaces.DataRecord;
import org.sirix.node.interfaces.Node;
import org.sirix.node.interfaces.StructNode;
import org.sirix.node.interfaces.immutable.ImmutableNode;
import org.sirix.settings.Constants;
import org.sirix.settings.Fixed;
import org.checkerframework.checker.index.qual.NonNegative;
import org.checkerframework.checker.nullness.qual.NonNull;
import java.io.PrintStream;
import java.util.ArrayDeque;
import java.util.Comparator;
import java.util.Deque;
import java.util.Optional;
import static com.google.common.base.Preconditions.checkArgument;
import static com.google.common.base.Preconditions.checkNotNull;
/**
* Simple Red-black tree reader (balanced binary search-tree -- based on BaseX(.org) version).
*
* @param the key to search for or insert
* @param the value
* @author Johannes Lichtenberger, University of Konstanz
*/
@SuppressWarnings("ConstantConditions")
public final class RBTreeReader, V extends References> implements NodeCursor {
/**
* Cache.
*/
private final Cache> cache;
/**
* The index type.
*/
final IndexType indexType;
/**
* The index number.
*/
private final int indexNumber;
/**
* The revision number.
*/
private final int revisionNumber;
/**
* Determines if tree is closed or not.
*/
private boolean isClosed;
/**
* Strong reference to currently selected node.
*/
private Node currentNode;
/**
* {@link PageReadOnlyTrx} for persistent storage.
*/
final PageReadOnlyTrx pageReadOnlyTrx;
/**
* Index number.
*/
final int index;
/**
* Determines movement of the internal cursor.
*/
public enum MoveCursor {
/**
* Cursor should be moved document root.
*/
TO_DOCUMENT_ROOT,
/**
* Cursor should not be moved.
*/
NO_MOVE
}
/**
* Get a new instance.
*
* @param key instance which extends comparable
* @param value
* @param cache a cache shared between all read-only tree readers
* @param pageReadTrx {@link PageReadOnlyTrx} for persistent storage
* @param type type of index
* @param index index
* @return new tree instance
*/
public static , V extends References> RBTreeReader getInstance(
final Cache> cache, final PageReadOnlyTrx pageReadTrx, final IndexType type,
@NonNegative final int index) {
return new RBTreeReader<>(cache, pageReadTrx, type, index);
}
/**
* Private constructor.
*
* @param cache a cache shared between all read-only tree readers
* @param pageReadOnlyTrx {@link PageReadOnlyTrx} for persistent storage
* @param indexType kind of indexType
* @param indexNumber the indexNumber number
*/
private RBTreeReader(final Cache> cache, final PageReadOnlyTrx pageReadOnlyTrx,
final IndexType indexType, final int indexNumber) {
this.cache = checkNotNull(cache);
this.pageReadOnlyTrx = checkNotNull(pageReadOnlyTrx);
this.indexType = checkNotNull(indexType);
this.indexNumber = indexNumber;
revisionNumber = pageReadOnlyTrx.getRevisionNumber();
isClosed = false;
this.index = indexNumber;
final Optional extends DataRecord> node =
this.pageReadOnlyTrx.getRecord(Fixed.DOCUMENT_NODE_KEY.getStandardProperty(), IndexType.PATH_SUMMARY, 0);
currentNode = (StructNode) node.orElseThrow(() -> new IllegalStateException(
"Node couldn't be fetched from persistent storage!"));
for (final long nodeKey : new DescendantAxis(this, IncludeSelf.YES)) {
if (nodeKey == 0) {
continue;
}
if (pageReadOnlyTrx instanceof PageTrx) {
continue;
}
this.cache.put(new RBIndexKey(nodeKey, revisionNumber, indexType, indexNumber), getCurrentNode());
}
}
/**
* Dump the AVLTree in preorder.
*
* @param out {@link PrintStream} to print to
*/
public void dump(final PrintStream out) {
assertNotClosed();
moveToDocumentRoot();
if (((StructNode) getNode()).hasFirstChild()) {
moveToFirstChild();
} else {
return;
}
internalDump(out);
}
// Internal function to dump data to a PrintStream instance.
private void internalDump(final PrintStream out) {
out.println(getCurrentNode());
@SuppressWarnings("ConstantConditions")
final long nodeKey = getCurrentNode().getNodeKey();
if (getCurrentNode().hasLeftChild()) {
moveToFirstChild();
internalDump(out);
}
moveTo(nodeKey);
if (getCurrentNode().hasRightChild()) {
moveToLastChild();
internalDump(out);
}
}
/**
* Get the {@link RBNode}.
*
* @return {@link RBNode} instance
*/
RBNode getCurrentNode() {
assertNotClosed();
if (currentNode.getKind() != NodeKind.XML_DOCUMENT && currentNode.getKind() != NodeKind.JSON_DOCUMENT) {
@SuppressWarnings("unchecked")
final RBNode node = (RBNode) currentNode;
return node;
}
return null;
}
/**
* Set the {@link RBNode}.
*
* @param node the node to set
* @return {@link RBNode} instance
*/
RBNode setCurrentAVLNode(final RBNode node) {
assertNotClosed();
currentNode = node;
return node;
}
/**
* Finds the specified key in the index and returns its value.
*
* @param startNodeKey the key of the node to start from
* @param key key to be found
* @param mode the search mode
* @return {@link Optional} reference (with the found value, or a reference which indicates that the
* value hasn't been found)
*/
public Optional get(final long startNodeKey, final K key, final SearchMode mode) {
assertNotClosed();
final boolean movedToStartNode = moveTo(startNodeKey).hasMoved();
if (!movedToStartNode) {
return Optional.empty();
}
moveToFirstChild();
RBNode node = getCurrentNode();
return getNode(key, mode, node);
}
@NonNull
private Optional getNode(K key, SearchMode mode, RBNode node) {
while (true) {
final int c = mode.compare(key, node.getKey());
if (c == 0) {
return Optional.ofNullable(node.getValue());
}
boolean moved;
if (c < 0) {
if (node.getLeftChild() != null) {
node = node.getLeftChild();
currentNode = node;
moved = true;
} else if (node.hasLeftChild()) {
//noinspection unchecked
node = pageReadOnlyTrx instanceof PageTrx
? null
: (RBNode) cache.get(new RBIndexKey(node.getLeftChildKey(),
revisionNumber,
indexType,
indexNumber));
if (node == null) {
moved = moveToFirstChild().hasMoved();
if (moved) {
node = getCurrentNode();
}
} else {
currentNode = node;
moved = true;
}
} else {
moved = false;
}
} else {
if (node.getRightChild() != null) {
node = node.getRightChild();
currentNode = node;
moved = true;
} else if (node.hasRightChild()) {
//noinspection unchecked
node = pageReadOnlyTrx instanceof PageTrx
? null
: (RBNode) cache.get(new RBIndexKey(node.getRightChildKey(),
revisionNumber,
indexType,
indexNumber));
if (node == null) {
moved = moveToLastChild().hasMoved();
if (moved) {
node = getCurrentNode();
}
} else {
currentNode = node;
moved = true;
}
} else {
moved = false;
}
}
if (!moved) {
break;
}
}
return Optional.empty();
}
/**
* Finds the specified key in the index and returns its value.
*
* @param key key to be found
* @param mode the search mode
* @return {@link Optional} reference (with the found value, or a reference which indicates that the
* value hasn't been found)
*/
public Optional get(final K key, final SearchMode mode) {
assertNotClosed();
moveToDocumentRoot();
if (!((StructNode) getNode()).hasFirstChild()) {
return Optional.empty();
}
moveToFirstChild();
RBNode node = getCurrentNode();
return getNode(key, mode, node);
}
/**
* Finds the specified key in the index starting from the specified node key and returns its
* AVLNode.
*
* @param startNodeKey specified node
* @param key key to be found
* @param mode the search mode
* @return Optional {@link RBNode} reference
*/
public Optional> getCurrentNode(final long startNodeKey, final K key, final SearchMode mode) {
assertNotClosed();
final boolean movedToStartNode = moveTo(startNodeKey).hasMoved();
if (!movedToStartNode) {
return Optional.empty();
}
RBNode node = getCurrentNode();
return getTheSearchedNode(key, mode, node);
}
@NonNull
private Optional> getTheSearchedNode(K key, SearchMode mode, RBNode node) {
while (true) {
final int c = key.compareTo(node.getKey());
if (mode != SearchMode.EQUAL && mode.compare(key, node.getKey()) == 0) {
return Optional.ofNullable(node);
}
if (c == 0) {
if (mode == SearchMode.EQUAL) {
return Optional.ofNullable(node);
}
}
final boolean moved = c < 0 ? moveToFirstChild().hasMoved() : moveToLastChild().hasMoved();
if (moved) {
node = getCurrentNode();
} else {
break;
}
}
return Optional.empty();
}
/**
* Finds the specified key in the index and returns its AVLNode.
*
* @param key key to be found
* @param mode the search mode
* @return Optional {@link RBNode} reference
*/
public Optional> getCurrentNode(final K key, final SearchMode mode) {
assertNotClosed();
moveToDocumentRoot();
if (!((StructNode) getNode()).hasFirstChild()) {
return Optional.empty();
}
moveToFirstChild();
RBNode node = getCurrentNode();
return getTheSearchedNode(key, mode, node);
}
/**
* Finds the specified key in the index and returns its AVLNode.
*
* @param key key to be found
* @param mode the search mode
* @param comp comparator to be used to compare keys
* @return Optional {@link RBNode} reference
*/
public Optional> getCurrentNode(final K key, final SearchMode mode,
final Comparator super K> comp) {
assertNotClosed();
moveToDocumentRoot();
if (!((StructNode) getNode()).hasFirstChild()) {
return Optional.empty();
}
moveToFirstChild();
RBNode node = getCurrentNode();
while (true) {
final int c = key.compareTo(node.getKey());
if (mode.compare(key, node.getKey(), comp) == 0) {
return Optional.ofNullable(node);
}
final boolean moved = c < 0 ? moveToFirstChild().hasMoved() : moveToLastChild().hasMoved();
if (moved) {
node = getCurrentNode();
} else {
break;
}
}
return Optional.empty();
}
/**
* Returns the number of index entries.
*
* @return number of index entries
*/
public long size() {
return ((StructNode) moveToDocumentRoot().trx().getNode()).getDescendantCount();
}
@Override
public void close() {
isClosed = true;
}
/**
* Make sure that the path summary is not yet closed when calling this method.
*/
final void assertNotClosed() {
if (isClosed) {
throw new IllegalStateException("AVL tree reader is already closed.");
}
}
/**
* Get the structural node of the current node.
*
* @return structural node
*/
private StructNode getStructuralNode() {
assertNotClosed();
if (currentNode instanceof StructNode) {
return (StructNode) currentNode;
} else {
return new NullNode(currentNode);
}
}
@Override
public boolean hasNode(final long key) {
assertNotClosed();
final long currKey = currentNode.getNodeKey();
final boolean moved = moveTo(key).hasMoved();
final Move> movedCursor = moveTo(currKey);
assert movedCursor.hasMoved() : "Must be movable back!";
return moved;
}
@Override
public boolean hasParent() {
assertNotClosed();
return currentNode.hasParent();
}
@Override
public boolean hasFirstChild() {
assertNotClosed();
return getStructuralNode().hasFirstChild();
}
@Override
public boolean hasLastChild() {
assertNotClosed();
if (currentNode instanceof RBNode) {
return getCurrentNode().hasRightChild();
}
return false;
}
@Override
public boolean hasLeftSibling() {
assertNotClosed();
return getStructuralNode().hasLeftSibling();
}
@Override
public boolean hasRightSibling() {
assertNotClosed();
return getStructuralNode().hasRightSibling();
}
@Override
public Move> moveTo(final long nodeKey) {
assertNotClosed();
if (nodeKey == Fixed.NULL_NODE_KEY.getStandardProperty()) {
return Move.notMoved();
}
// Remember old node and fetch new one.
final Node oldNode = currentNode;
Optional extends Node> newNode;
try {
// Immediately return node from item list if node key negative.
newNode = pageReadOnlyTrx.getRecord(nodeKey, indexType, index);
} catch (final SirixIOException e) {
newNode = Optional.empty();
}
if (newNode.isPresent()) {
currentNode = newNode.get();
return Move.moved(this);
} else {
currentNode = oldNode;
return Move.notMoved();
}
}
@Override
public Move> moveToDocumentRoot() {
assertNotClosed();
return moveTo(Fixed.DOCUMENT_NODE_KEY.getStandardProperty());
}
@Override
public Move> moveToParent() {
assertNotClosed();
return moveTo(currentNode.getParentKey());
}
@Override
public Move> moveToFirstChild() {
assertNotClosed();
if (currentNode instanceof RBNode) {
final RBNode node = getCurrentNode();
if (!node.hasLeftChild()) {
return Move.notMoved();
}
final var move = moveTo(node.getLeftChildKey());
@SuppressWarnings("unchecked")
final var currentNode = (RBNode) this.currentNode;
node.setLeftChild(currentNode);
currentNode.setParent(node);
return move;
}
return moveTo(((StructNode) currentNode).getFirstChildKey());
}
@Override
public Move> moveToLastChild() {
assertNotClosed();
if (currentNode instanceof RBNode) {
final RBNode node = getCurrentNode();
if (!node.hasRightChild()) {
return Move.notMoved();
}
final var move = moveTo(node.getRightChildKey());
@SuppressWarnings("unchecked")
final var currentNode = (RBNode) this.currentNode;
node.setRightChild(currentNode);
currentNode.setParent(node);
return move;
}
return Move.notMoved();
}
@Override
public Move extends NodeCursor> moveToPrevious() {
assertNotClosed();
return moveToParent();
}
@Override
public Move extends NodeCursor> moveToNext() {
assertNotClosed();
if (currentNode instanceof RBNode) {
@SuppressWarnings("unchecked")
final RBNode node = (RBNode) currentNode;
if (node.hasLeftChild()) {
moveToFirstChild();
} else if (node.hasRightChild()) {
moveToLastChild();
} else {
do {
moveToParent();
} while (getNode() instanceof RBNode && !hasLastChild());
if (getNode() instanceof RBNode) {
return Move.moved(moveToLastChild().trx());
} else {
return Move.notMoved();
}
}
}
if (moveToFirstChild().hasMoved()) {
return Move.moved(this);
} else {
return Move.notMoved();
}
}
@Override
public Move> moveToLeftSibling() {
assertNotClosed();
return Move.notMoved();
}
@Override
public Move> moveToRightSibling() {
assertNotClosed();
return Move.notMoved();
}
@Override
public long getNodeKey() {
assertNotClosed();
return currentNode.getNodeKey();
}
@Override
public NodeKind getRightSiblingKind() {
assertNotClosed();
return NodeKind.UNKNOWN;
}
@Override
public NodeKind getLeftSiblingKind() {
assertNotClosed();
return NodeKind.UNKNOWN;
}
@Override
public NodeKind getFirstChildKind() {
assertNotClosed();
final NodeKind firstChildKind;
if (moveToFirstChild().hasMoved()) {
firstChildKind = getNode().getKind();
moveToParent();
} else {
firstChildKind = NodeKind.UNKNOWN;
}
return firstChildKind;
}
@Override
public NodeKind getLastChildKind() {
assertNotClosed();
final NodeKind lastChildKind;
if (moveToLastChild().hasMoved()) {
lastChildKind = getNode().getKind();
moveToParent();
} else {
lastChildKind = NodeKind.UNKNOWN;
}
return lastChildKind;
}
@Override
public NodeKind getParentKind() {
assertNotClosed();
if (hasParent()) {
final long nodeKey = currentNode.getNodeKey();
final NodeKind parentKind = moveToParent().trx().getKind();
moveTo(nodeKey);
return parentKind;
}
return NodeKind.UNKNOWN;
}
@Override
public NodeKind getKind() {
assertNotClosed();
return currentNode.getKind();
}
@Override
public ImmutableNode getNode() {
assertNotClosed();
return currentNode;
}
@Override
public Move extends NodeCursor> moveToNextFollowing() {
throw new UnsupportedOperationException();
}
@Override
public long getLeftSiblingKey() {
assertNotClosed();
return Constants.NULL_ID_LONG;
}
@Override
public long getRightSiblingKey() {
assertNotClosed();
return Constants.NULL_ID_LONG;
}
@Override
public long getFirstChildKey() {
assertNotClosed();
final long firstChildKey;
if (moveToFirstChild().hasMoved()) {
firstChildKey = getNodeKey();
moveToParent();
} else {
firstChildKey = Fixed.NULL_NODE_KEY.getStandardProperty();
}
return firstChildKey;
}
@Override
public long getLastChildKey() {
assertNotClosed();
final long lastChildKey;
if (moveToLastChild().hasMoved()) {
lastChildKey = getNodeKey();
moveToParent();
} else {
lastChildKey = Fixed.NULL_NODE_KEY.getStandardProperty();
}
return lastChildKey;
}
@Override
public long getParentKey() {
return getNode().getParentKey();
}
/**
* Iterator supporting different search modes.
*
* @author Johannes Lichtenberger
*/
public final class RBNodeIterator extends AbstractIterator> {
/**
* Determines if it's the first call.
*/
private boolean first;
/**
* All AVLNode keys which are part of the result sequence.
*/
private final Deque keys;
/**
* Start node key.
*/
private final long key;
/**
* Constructor.
*
* @param nodeKey node key to start from, root node of AVLTree is selected if
* {@code Fixed.DOCUMENT_NODE_KEY.getStandardProperty} is specified.
*/
public RBNodeIterator(final long nodeKey) {
first = true;
keys = new ArrayDeque<>();
checkArgument(nodeKey >= 0, "nodeKey must be >= 0!");
key = nodeKey;
}
@Override
protected RBNode computeNext() {
if (!first) {
if (!keys.isEmpty()) {
// Subsequent results.
final RBNode node = moveTo(keys.pop()).trx().getCurrentNode();
stackOperation(node);
return node;
}
return endOfData();
}
// First search.
first = false;
boolean moved = moveTo(key).hasMoved();
if (key == Fixed.DOCUMENT_NODE_KEY.getStandardProperty()) {
moved = moveToFirstChild().hasMoved();
}
if (moved) {
final RBNode node = getCurrentNode();
stackOperation(node);
return node;
}
return endOfData();
}
private void stackOperation(final RBNode node) {
if (node.hasRightChild()) {
final RBNode right = moveToLastChild().trx().getCurrentNode();
keys.push(right.getNodeKey());
}
moveTo(node.getNodeKey());
if (node.hasLeftChild()) {
final RBNode left = moveToFirstChild().trx().getCurrentNode();
keys.push(left.getNodeKey());
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy