Skip to content

Commit

Permalink
[optimise] Speed up path expressions on the preceding-sibling and fol…
Browse files Browse the repository at this point in the history
…lowing-sibling axes
  • Loading branch information
adamretter committed Oct 28, 2024
1 parent b38ce03 commit 19fece0
Show file tree
Hide file tree
Showing 14 changed files with 403 additions and 7 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -458,6 +458,16 @@ private void setHasChanged() {
state = (state == Integer.MAX_VALUE ? 0 : state + 1);
}

@Override
public boolean containsPrecedingSiblingOf(final DocumentImpl doc, final NodeId nodeId) {
throw new UnsupportedOperationException("TODO(AR) do we need to implement this?");
}

@Override
public boolean containsFollowingSiblingOf(final DocumentImpl doc, final NodeId nodeId) {
throw new UnsupportedOperationException("TODO(AR) do we need to implement this?");
}

private static class InorderTraversal implements NodeSetIterator, SequenceIterator {
@Nullable private final Node root;
private final Deque<Node> nodes = new ArrayDeque<>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,16 @@ public NodeProxy parentWithChild(final DocumentImpl doc, final NodeId nodeId,
return null;
}

@Override
public boolean containsPrecedingSiblingOf(final DocumentImpl doc, final NodeId nodeId) {
return false;
}

@Override
public boolean containsFollowingSiblingOf(final DocumentImpl doc, final NodeId nodeId) {
return false;
}

@Override
public NodeProxy get(final NodeProxy proxy) {
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -332,6 +332,20 @@ public NodeProxy parentWithChild(final DocumentImpl doc, final NodeId nodeId,
directParent, includeSelf);
}

@Override
public boolean containsPrecedingSiblingOf(final DocumentImpl doc, final NodeId nodeId) {
sort();
lastPart = getPart(doc, false, initialSize);
return lastPart == null ? null : lastPart.containsPrecedingSiblingOf(doc, nodeId);
}

@Override
public boolean containsFollowingSiblingOf(final DocumentImpl doc, final NodeId nodeId) {
sort();
lastPart = getPart(doc, false, initialSize);
return lastPart == null ? null : lastPart.containsFollowingSiblingOf(doc, nodeId);
}

/**
* The method <code>debugParts</code>
*
Expand Down Expand Up @@ -807,6 +821,69 @@ NodeProxy parentWithChild(final DocumentImpl doc, final NodeId nodeId, final boo
return null;
}

/**
* Tests this nodeset contains a preceding sibling of the provided node (e.g. a following sibling).
*
* @param doc the document containing the node to test.
* @param nodeId the nodeId of the node to test.
*
* @return true if the node is a following sibling of a node in this nodeset.
*/
boolean containsPrecedingSiblingOf(final DocumentImpl doc, final NodeId nodeId) {
sort();
int low = 0;
int high = length - 1;
int mid;
int cmp;
NodeProxy p;
while (low <= high) {
mid = (low + high) / 2;
p = array[mid];
cmp = p.getNodeId().compareTo(nodeId);
if (cmp < 0) {
if (nodeId.isPrecedingSiblingOf(p.getNodeId())) {
return true;
}
low = mid + 1;
} else {
high = mid - 1;
}
}
return false;
}

/**
* Tests this nodeset contains a following sibling of the provided node (e.g. a preceding sibling).
*
* @param doc the document containing the node to test.
* @param nodeId the nodeId of the node to test.
*
* @return true if the node is a preceding sibling of a node in this nodeset.
*/
boolean containsFollowingSiblingOf(final DocumentImpl doc, final NodeId nodeId) {
// NOTE(AR) this is almost a copy of the code in NewArrayNodeSet#hasPrecedingSibling, a bug in one might indicate a bug in the other
sort();
int low = 0;
int high = length - 1;
int mid;
int cmp;
NodeProxy p;
while (low <= high) {
mid = (low + high) / 2;
p = array[mid];
cmp = p.getNodeId().compareTo(nodeId);
if (cmp > 0) {
if (nodeId.isPrecedingSiblingOf(p.getNodeId())) {
return true;
}
high = mid - 1;
} else {
low = mid + 1;
}
}
return false;
}

NodeProxy hasDescendantsInSet(final NodeId ancestorId, final int contextId, final boolean includeSelf) {
// do a binary search to pick some node in the range
// of valid child ids
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -913,6 +913,74 @@ private NodeProxy parentWithChild(final int docIdx, final NodeId nodeId, final b
return null;
}

@Override
public boolean containsPrecedingSiblingOf(final DocumentImpl doc, final NodeId nodeId) {
sort();
final int docIdx = findDoc(doc);
if (docIdx < 0) {
return false;
}
return containsPrecedingSiblingOf(docIdx, nodeId);
}

private boolean containsPrecedingSiblingOf(final int docIdx, final NodeId nodeId) {
sort();
// NOTE(AR) this is almost a copy of the code in ExtArrayNodeSet#containsPrecedingSiblingOf, a bug in one might indicate a bug in the other
int low = documentNodesOffset[docIdx];
int high = low + (documentNodesCount[docIdx] - 1);
int mid;
int cmp;
NodeProxy p;
while (low <= high) {
mid = (low + high) / 2;
p = nodes[mid];
cmp = p.getNodeId().compareTo(nodeId);
if (cmp < 0) {
if (nodeId.isPrecedingSiblingOf(p.getNodeId())) {
return true;
}
low = mid + 1;
} else {
high = mid - 1;
}
}
return false;
}

@Override
public boolean containsFollowingSiblingOf(final DocumentImpl doc, final NodeId nodeId) {
sort();
final int docIdx = findDoc(doc);
if (docIdx < 0) {
return false;
}
return containsFollowingSiblingOf(docIdx, nodeId);
}

private boolean containsFollowingSiblingOf(final int docIdx, final NodeId nodeId) {
sort();
// NOTE(AR) this is almost a copy of the code in ExtArrayNodeSet.Part#containsFollowingSiblingOf, a bug in one might indicate a bug in the other
int low = documentNodesOffset[docIdx];
int high = low + (documentNodesCount[docIdx] - 1);
int mid;
int cmp;
NodeProxy p;
while (low <= high) {
mid = (low + high) / 2;
p = nodes[mid];
cmp = p.getNodeId().compareTo(nodeId);
if (cmp > 0) {
if (nodeId.isPrecedingSiblingOf(p.getNodeId())) {
return true;
}
high = mid - 1;
} else {
low = mid + 1;
}
}
return false;
}

@Override
public NodeSet except(final NodeSet other) {
final NewArrayNodeSet result = new NewArrayNodeSet();
Expand Down
16 changes: 16 additions & 0 deletions exist-core/src/main/java/org/exist/dom/persistent/NodeProxy.java
Original file line number Diff line number Diff line change
Expand Up @@ -1008,6 +1008,22 @@ public NodeProxy parentWithChild(final DocumentImpl otherDoc, final NodeId other
}
}

@Override
public boolean containsPrecedingSiblingOf(final DocumentImpl otherDoc, final NodeId otherId) {
if (otherDoc.getDocId() != doc.getDocId()) {
return false;
}
return otherId.isFollowingSiblingOf(nodeId);
}

@Override
public boolean containsFollowingSiblingOf(final DocumentImpl otherDoc, final NodeId otherId) {
if (otherDoc.getDocId() != doc.getDocId()) {
return false;
}
return otherId.isPrecedingSiblingOf(nodeId);
}

@Override
public NodeSet getContextNodes(final int contextId) {
final NewArrayNodeSet result = new NewArrayNodeSet();
Expand Down
19 changes: 19 additions & 0 deletions exist-core/src/main/java/org/exist/dom/persistent/NodeSet.java
Original file line number Diff line number Diff line change
Expand Up @@ -386,4 +386,23 @@ boolean matchAncestorDescendant(NodeSet al, int mode, boolean includeSelf,

void setTrackMatches(boolean track);

/**
* Tests this nodeset contains a preceding sibling of the provided node (e.g. a following sibling).
*
* @param doc the document containing the node to test.
* @param nodeId the nodeId of the node to test.
*
* @return true if the node is a following sibling of a node in this nodeset.
*/
boolean containsPrecedingSiblingOf(DocumentImpl doc, NodeId nodeId);

/**
* Tests this nodeset contains a following sibling of the provided node (e.g. a preceding sibling).
*
* @param doc the document containing the node to test.
* @param nodeId the nodeId of the node to test.
*
* @return true if the node is a preceding sibling of a node in this nodeset.
*/
boolean containsFollowingSiblingOf(DocumentImpl doc, NodeId nodeId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,16 @@ public SequenceIterator unorderedIterator() {
return new SortedNodeSetIterator(list.iterator());
}

@Override
public boolean containsPrecedingSiblingOf(final DocumentImpl doc, final NodeId nodeId) {
throw new UnsupportedOperationException("TODO(AR) do we need to implement this?");
}

@Override
public boolean containsFollowingSiblingOf(final DocumentImpl doc, final NodeId nodeId) {
throw new UnsupportedOperationException("TODO(AR) do we need to implement this?");
}

private static final class SortedNodeSetIterator implements NodeSetIterator, SequenceIterator {

private final Iterator<IteratorItem> ii;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -298,6 +298,24 @@ public NodeProxy parentWithChild(final DocumentImpl doc, final NodeId nodeId,
}
}

@Override
public boolean containsPrecedingSiblingOf(final DocumentImpl doc, final NodeId nodeId) {
if (realSet != null && realSetIsComplete) {
return realSet.containsPrecedingSiblingOf(doc, nodeId);
} else {
throw new UnsupportedOperationException("TODO(AR) do we need to implement this");
}
}

@Override
public boolean containsFollowingSiblingOf(final DocumentImpl doc, final NodeId nodeId) {
if (realSet != null && realSetIsComplete) {
return realSet.containsFollowingSiblingOf(doc, nodeId);
} else {
throw new UnsupportedOperationException("TODO(AR) do we need to implement this");
}
}

/**
* Realize the node set by recursively scanning the
* DOM.
Expand Down
12 changes: 12 additions & 0 deletions exist-core/src/main/java/org/exist/numbering/DLN.java
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,18 @@ public boolean isSiblingOf(final NodeId sibling) {
return sibling.isChildOf(parent);
}

@Override
public boolean isFollowingSiblingOf(final NodeId otherNodeId) {
// TODO(AR) ideally use some bit arithmetic in a new function of the DLN implementation method to avoid conversion from bits to ints
return getParentId().equals(otherNodeId.getParentId()) && compareTo(otherNodeId) > 0;
}

@Override
public boolean isPrecedingSiblingOf(final NodeId otherNodeId) {
// TODO(AR) ideally use some bit arithmetic in a new function of the DLN implementation method to avoid conversion from bits to ints
return getParentId().equals(otherNodeId.getParentId()) && compareTo(otherNodeId) < 0;
}

/**
* Returns the level within the document tree at which
* this node occurs.
Expand Down
20 changes: 19 additions & 1 deletion exist-core/src/main/java/org/exist/numbering/NodeId.java
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ public interface NodeId extends Comparable<NodeId> {
*
* @param other the node id to compare with
* @param isFollowing if true, return false for descendants of the current node
* @return true true if the current node comes after the other node in document order
* @return true if the current node comes after the other node in document order
*/
boolean after(NodeId other, boolean isFollowing);

Expand Down Expand Up @@ -167,6 +167,24 @@ public interface NodeId extends Comparable<NodeId> {

boolean isSiblingOf(NodeId sibling);

/**
* Determines if this NodeId is a preceding sibling of the provided NodeId.
*
* @param nodeId the NodeId to test against.
*
* @return true if this NodeId is a preceding sibling of the provided NodeId
*/
boolean isPrecedingSiblingOf(NodeId nodeId);

/**
* Determines if this NodeId is a following sibling of the provided NodeId.
*
* @param nodeId the NodeId to test against.
*
* @return true if this NodeId is a following sibling of the provided NodeId
*/
boolean isFollowingSiblingOf(NodeId nodeId);

/**
* Returns the level within the document tree at which
* this node occurs.
Expand Down
Loading

0 comments on commit 19fece0

Please sign in to comment.