use of org.exist.dom.persistent.NodeSet in project exist by eXist-db.
the class GeneralComparison method quickNodeSetCompare.
/**
* Optimized implementation: first checks if a range index is defined on the nodes in the left argument.
* Otherwise, fall back to {@link #nodeSetCompare(NodeSet, Sequence)}.
*
* @param contextSequence DOCUMENT ME!
*
* @return DOCUMENT ME!
*
* @throws XPathException DOCUMENT ME!
*/
protected Sequence quickNodeSetCompare(Sequence contextSequence) throws XPathException {
if (context.getProfiler().isEnabled()) {
context.getProfiler().message(this, Profiler.OPTIMIZATION_FLAGS, "OPTIMIZATION CHOICE", "quickNodeSetCompare");
}
final long start = System.currentTimeMillis();
// get the NodeSet on the left
final Sequence leftSeq = getLeft().eval(contextSequence);
if (!leftSeq.isPersistentSet()) {
return (genericCompare(leftSeq, contextSequence, null));
}
final NodeSet nodes = leftSeq.isEmpty() ? NodeSet.EMPTY_SET : (NodeSet) leftSeq;
// nothing on the left, so nothing to do
if (!(nodes instanceof VirtualNodeSet) && nodes.isEmpty()) {
// Well, we might discuss this one ;-)
hasUsedIndex = true;
return (Sequence.EMPTY_SEQUENCE);
}
// get the Sequence on the right
final Sequence rightSeq = getRight().eval(contextSequence);
// nothing on the right, so nothing to do
if (rightSeq.isEmpty()) {
// Well, we might discuss this one ;-)
hasUsedIndex = true;
return (Sequence.EMPTY_SEQUENCE);
}
// get the type of a possible index
final int indexType = nodes.getIndexType();
// remember that Type.ITEM means... no index ;-)
if (indexType != Type.ITEM) {
if (LOG.isTraceEnabled()) {
LOG.trace("found an index of type: {}", Type.getTypeName(indexType));
}
boolean indexScan = false;
boolean indexMixed = false;
QName myContextQName = contextQName;
if (contextSequence != null) {
final IndexFlags iflags = checkForQNameIndex(idxflags, context, contextSequence, myContextQName);
boolean indexFound = false;
if (!iflags.indexOnQName) {
// if myContextQName != null and no index is defined on
// myContextQName, we don't need to scan other QName indexes
// and can just use the generic range index
indexFound = myContextQName != null;
if (iflags.partialIndexOnQName) {
indexMixed = true;
} else {
// set myContextQName to null so the index lookup below is not
// restricted to that QName
myContextQName = null;
}
}
if (!indexFound && (myContextQName == null)) {
// we need to check them all
if (iflags.hasIndexOnQNames) {
indexScan = true;
}
// else use range index defined on path by default
}
} else {
return (nodeSetCompare(nodes, contextSequence));
}
// Get the documents from the node set
final DocumentSet docs = nodes.getDocumentSet();
// Holds the result
NodeSet result = null;
// Iterate through the right hand sequence
for (final SequenceIterator itRightSeq = Atomize.atomize(rightSeq).iterate(); itRightSeq.hasNext(); ) {
// Get the index key
Item key = itRightSeq.nextItem();
// if key has truncation, convert it to string
if (truncation != StringTruncationOperator.NONE) {
if (!Type.subTypeOf(key.getType(), Type.STRING)) {
LOG.info("Truncated key. Converted from {} to xs:string", Type.getTypeName(key.getType()));
// truncation is only possible on strings
key = key.convertTo(Type.STRING);
}
} else // TODO : use Type.isSubType() ??? -pb
if (key.getType() != indexType) {
// try to convert the key to the index type
try {
key = key.convertTo(indexType);
} catch (final XPathException xpe) {
// Could not convert the key to a suitable type for the index, fallback to nodeSetCompare()
if (context.getProfiler().isEnabled()) {
context.getProfiler().message(this, Profiler.OPTIMIZATION_FLAGS, "OPTIMIZATION FALLBACK", "Falling back to nodeSetCompare (" + xpe.getMessage() + ")");
}
if (LOG.isTraceEnabled()) {
LOG.trace("Cannot convert key: {} to required index type: {}", Type.getTypeName(key.getType()), Type.getTypeName(indexType));
}
return (nodeSetCompare(nodes, contextSequence));
}
}
// If key implements org.exist.storage.Indexable, we can use the index
if (key instanceof Indexable) {
if (LOG.isTraceEnabled()) {
LOG.trace("Checking if range index can be used for key: {}", key.getStringValue());
}
final Collator collator = ((collationArg != null) ? getCollator(contextSequence) : null);
if (Type.subTypeOf(key.getType(), indexType)) {
if (truncation == StringTruncationOperator.NONE) {
if (LOG.isTraceEnabled()) {
LOG.trace("Using range index for key: {}", key.getStringValue());
}
// key without truncation, find key
context.getProfiler().message(this, Profiler.OPTIMIZATIONS, "OPTIMIZATION", "Using value index '" + context.getBroker().getValueIndex().toString() + "' to find key '" + Type.getTypeName(key.getType()) + "(" + key.getStringValue() + ")'");
NodeSet ns;
if (indexScan) {
ns = context.getBroker().getValueIndex().findAll(context.getWatchDog(), relation, docs, nodes, NodeSet.ANCESTOR, (Indexable) key);
} else {
ns = context.getBroker().getValueIndex().find(context.getWatchDog(), relation, docs, nodes, NodeSet.ANCESTOR, myContextQName, (Indexable) key, indexMixed);
}
hasUsedIndex = true;
if (result == null) {
result = ns;
} else {
result = result.union(ns);
}
} else {
// key with truncation, match key
if (LOG.isTraceEnabled()) {
context.getProfiler().message(this, Profiler.OPTIMIZATIONS, "OPTIMIZATION", "Using value index '" + context.getBroker().getValueIndex().toString() + "' to match key '" + Type.getTypeName(key.getType()) + "(" + key.getStringValue() + ")'");
}
if (LOG.isTraceEnabled()) {
LOG.trace("Using range index for key: {}", key.getStringValue());
}
try {
NodeSet ns;
final String matchString = key.getStringValue();
final int matchType = getMatchType(truncation);
if (indexScan) {
ns = context.getBroker().getValueIndex().matchAll(context.getWatchDog(), docs, nodes, NodeSet.ANCESTOR, matchString, matchType, 0, true, collator, truncation);
} else {
ns = context.getBroker().getValueIndex().match(context.getWatchDog(), docs, nodes, NodeSet.ANCESTOR, matchString, myContextQName, matchType, collator, truncation);
}
hasUsedIndex = true;
if (result == null) {
result = ns;
} else {
result = result.union(ns);
}
} catch (final EXistException e) {
throw (new XPathException(this, e));
}
}
} else {
// our key does is not of the correct type
if (context.getProfiler().isEnabled()) {
context.getProfiler().message(this, Profiler.OPTIMIZATION_FLAGS, "OPTIMIZATION FALLBACK", "Falling back to nodeSetCompare (key is of type: " + Type.getTypeName(key.getType()) + ") whereas index is of type '" + Type.getTypeName(indexType) + "'");
}
if (LOG.isTraceEnabled()) {
LOG.trace("Cannot use range index: key is of type: {}) whereas index is of type '{}", Type.getTypeName(key.getType()), Type.getTypeName(indexType));
}
return (nodeSetCompare(nodes, contextSequence));
}
} else {
// our key does not implement org.exist.storage.Indexable
if (context.getProfiler().isEnabled()) {
context.getProfiler().message(this, Profiler.OPTIMIZATION_FLAGS, "OPTIMIZATION FALLBACK", "Falling back to nodeSetCompare (key is not an indexable type: " + key.getClass().getName());
}
if (LOG.isTraceEnabled()) {
LOG.trace("Cannot use key which is of type '{}", key.getClass().getName());
}
return (nodeSetCompare(nodes, contextSequence));
}
}
if (context.getProfiler().traceFunctions()) {
context.getProfiler().traceIndexUsage(context, PerformanceStats.RANGE_IDX_TYPE, this, PerformanceStats.BASIC_INDEX, System.currentTimeMillis() - start);
}
return (result);
} else {
if (LOG.isTraceEnabled()) {
LOG.trace("No suitable index found for key: {}", rightSeq.getStringValue());
}
// no range index defined on the nodes in this sequence, so fallback to nodeSetCompare
if (context.getProfiler().isEnabled()) {
context.getProfiler().message(this, Profiler.OPTIMIZATION_FLAGS, "OPTIMIZATION FALLBACK", "falling back to nodeSetCompare (no index available)");
}
return (nodeSetCompare(nodes, contextSequence));
}
}
use of org.exist.dom.persistent.NodeSet in project exist by eXist-db.
the class OpOr method eval.
public Sequence eval(Sequence contextSequence, Item contextItem) throws XPathException {
if (context.getProfiler().isEnabled()) {
context.getProfiler().start(this);
context.getProfiler().message(this, Profiler.DEPENDENCIES, "DEPENDENCIES", Dependency.getDependenciesName(this.getDependencies()));
if (contextSequence != null) {
context.getProfiler().message(this, Profiler.START_SEQUENCES, "CONTEXT SEQUENCE", contextSequence);
}
if (contextItem != null) {
context.getProfiler().message(this, Profiler.START_SEQUENCES, "CONTEXT ITEM", contextItem.toSequence());
}
}
if (getLength() == 0) {
return Sequence.EMPTY_SEQUENCE;
}
if (contextItem != null) {
contextSequence = contextItem.toSequence();
}
boolean doOptimize = optimize;
if (contextSequence != null && !contextSequence.isPersistentSet()) {
doOptimize = false;
}
Sequence result;
final Expression left = getLeft();
final Expression right = getRight();
final Sequence ls = left.eval(contextSequence, null);
// first check if left operand is a persistent set
doOptimize = doOptimize && (ls.isPersistentSet() || ls.isEmpty());
if (doOptimize) {
final Sequence rs = right.eval(contextSequence, null);
if (inPredicate) {
NodeSet lr = ls.toNodeSet();
lr = lr.getContextNodes(getContextId());
NodeSet rr = rs.toNodeSet();
rr = rr.getContextNodes(contextId);
result = lr.union(rr);
} else {
// fall back
final boolean rl = ls.effectiveBooleanValue();
if (rl) {
result = BooleanValue.TRUE;
} else {
final boolean rr = rs.effectiveBooleanValue();
result = rl || rr ? BooleanValue.TRUE : BooleanValue.FALSE;
}
}
} else {
// no: default evaluation based on boolean value
final boolean rl = ls.effectiveBooleanValue();
if (rl) {
result = BooleanValue.TRUE;
} else {
final Sequence rs = right.eval(contextSequence, null);
final boolean rr = rs.effectiveBooleanValue();
result = rl || rr ? BooleanValue.TRUE : BooleanValue.FALSE;
}
}
if (context.getProfiler().isEnabled()) {
context.getProfiler().end(this, "", result);
}
return result;
}
use of org.exist.dom.persistent.NodeSet in project exist by eXist-db.
the class Optimize method eval.
public Sequence eval(Sequence contextSequence, Item contextItem) throws XPathException {
if (contextItem != null) {
contextSequence = contextItem.toSequence();
}
boolean useCached = false;
boolean optimize = false;
NodeSet originalContext = null;
if (contextSequence == null || contextSequence.isPersistentSet()) {
// don't try to optimize in-memory node sets!
// contextSequence will be overwritten
originalContext = contextSequence == null ? null : contextSequence.toNodeSet();
if (cachedContext != null && cachedContext == originalContext) {
useCached = !originalContext.hasChanged(cachedTimestamp);
}
if (contextVar != null) {
contextSequence = contextVar.eval(contextSequence);
}
// in the current context
if (useCached) {
optimize = cachedOptimize;
} else {
if (optimizables != null && optimizables.length > 0) {
for (Optimizable optimizable : optimizables) {
if (optimizable.canOptimize(contextSequence)) {
optimize = true;
} else {
optimize = false;
break;
}
}
}
}
}
if (optimize) {
cachedContext = originalContext;
cachedTimestamp = originalContext == null ? 0 : originalContext.getState();
cachedOptimize = true;
NodeSet ancestors;
NodeSet result = null;
for (int current = 0; current < optimizables.length; current++) {
NodeSet selection = optimizables[current].preSelect(contextSequence, current > 0);
if (LOG.isTraceEnabled()) {
LOG.trace("exist:optimize: pre-selection: {}", selection.getLength());
}
// be re-evaluated to filter out wrong matches
if (selection.isEmpty()) {
ancestors = selection;
} else if (contextStep == null || current > 0) {
ancestors = selection.selectAncestorDescendant(contextSequence.toNodeSet(), NodeSet.ANCESTOR, true, contextId, true);
} else {
// NodeSelector selector;
final long start = System.currentTimeMillis();
// selector = new AncestorSelector(selection, contextId, true, false);
final StructuralIndex index = context.getBroker().getStructuralIndex();
final QName ancestorQN = contextStep.getTest().getName();
if (optimizables[current].optimizeOnSelf()) {
ancestors = index.findAncestorsByTagName(ancestorQN.getNameType(), ancestorQN, Constants.SELF_AXIS, selection.getDocumentSet(), selection, contextId);
} else {
ancestors = index.findAncestorsByTagName(ancestorQN.getNameType(), ancestorQN, optimizables[current].optimizeOnChild() ? Constants.PARENT_AXIS : Constants.ANCESTOR_SELF_AXIS, selection.getDocumentSet(), selection, contextId);
}
if (LOG.isTraceEnabled()) {
LOG.trace("Ancestor selection took {}", System.currentTimeMillis() - start);
LOG.trace("Found: {}", ancestors.getLength());
}
}
result = ancestors;
contextSequence = result;
}
if (contextStep == null) {
return innerExpr.eval(result);
} else {
contextStep.setPreloadedData(result.getDocumentSet(), result);
if (LOG.isTraceEnabled()) {
LOG.trace("exist:optimize: context after optimize: {}", result.getLength());
}
final long start = System.currentTimeMillis();
if (originalContext != null) {
contextSequence = originalContext.filterDocuments(result);
} else {
contextSequence = null;
}
final Sequence seq = innerExpr.eval(contextSequence);
if (LOG.isTraceEnabled()) {
LOG.trace("exist:optimize: inner expr took {}; found: {}", System.currentTimeMillis() - start, seq.getItemCount());
}
return seq;
}
} else {
if (LOG.isTraceEnabled()) {
LOG.trace("exist:optimize: Cannot optimize expression.");
}
if (originalContext != null) {
contextSequence = originalContext;
}
return innerExpr.eval(contextSequence, contextItem);
}
}
use of org.exist.dom.persistent.NodeSet in project exist by eXist-db.
the class FindLastModified method eval.
@Override
public Sequence eval(Sequence[] args, Sequence contextSequence) throws XPathException {
final NodeSet nodes = args[0].toNodeSet();
if (nodes.isEmpty()) {
return Sequence.EMPTY_SEQUENCE;
}
final NodeSet result = new NewArrayNodeSet();
final DateTimeValue dtv = (DateTimeValue) args[1].itemAt(0);
final long lastModified = dtv.getDate().getTime();
for (final NodeProxy proxy : nodes) {
final DocumentImpl doc = proxy.getOwnerDocument();
final long modified = doc.getLastModified();
boolean matches;
if (this.isCalledAs("find-last-modified-since")) {
matches = modified > lastModified;
} else {
matches = modified <= lastModified;
}
if (matches) {
result.add(proxy);
}
}
return result;
}
Aggregations