Search in sources :

Example 6 with VirtualNodeSet

use of org.exist.dom.persistent.VirtualNodeSet in project exist by eXist-db.

the class Predicate method recomputeExecutionMode.

private Tuple2<ExecutionMode, Sequence> recomputeExecutionMode(final Sequence contextSequence, final Expression inner) throws XPathException {
    ExecutionMode recomputedExecutionMode = executionMode;
    Sequence innerSeq = null;
    // Atomic context sequences :
    if (Type.subTypeOf(contextSequence.getItemType(), Type.ATOMIC)) {
        // the inner sequence
        if (executionMode == NODE && !(contextSequence instanceof VirtualNodeSet)) {
            // (1,2,2,4)[.]
            if (Type.subTypeOfUnion(contextSequence.getItemType(), Type.NUMBER)) {
                recomputedExecutionMode = POSITIONAL;
            } else {
                recomputedExecutionMode = BOOLEAN;
            }
        }
        // If there is no dependency on the context item, try a positional promotion
        if (executionMode == BOOLEAN && !Dependency.dependsOn(inner, Dependency.CONTEXT_ITEM) && // computation should now be better
        !((inner instanceof GeneralComparison) && ((GeneralComparison) inner).invalidNodeEvaluation)) {
            innerSeq = inner.eval(contextSequence);
            // Only if we have an actual *singleton* of numeric items
            if (innerSeq.hasOne() && Type.subTypeOfUnion(innerSeq.getItemType(), Type.NUMBER)) {
                recomputedExecutionMode = POSITIONAL;
            }
        }
    } else if (executionMode == NODE && !contextSequence.isPersistentSet()) {
        recomputedExecutionMode = BOOLEAN;
    } else {
        if (executionMode == BOOLEAN && !Dependency.dependsOn(inner, Dependency.CONTEXT_ITEM)) {
            /*
                 *
                 * WARNING : this sequence will be evaluated with
                 * preloadable nodesets !
                 */
            innerSeq = inner.eval(contextSequence);
            // We are now sure of the inner sequence return type
            if (Type.subTypeOf(innerSeq.getItemType(), Type.NODE) && innerSeq.isPersistentSet()) {
                recomputedExecutionMode = NODE;
            // Try to promote a boolean evaluation to a positional one
            // Only if we have an actual *singleton* of numeric items
            } else if (innerSeq.hasOne() && Type.subTypeOfUnion(innerSeq.getItemType(), Type.NUMBER)) {
                recomputedExecutionMode = POSITIONAL;
            }
        }
    }
    return Tuple(recomputedExecutionMode, innerSeq);
}
Also used : ExecutionMode(org.exist.xquery.Predicate.ExecutionMode) ValueSequence(org.exist.xquery.value.ValueSequence) Sequence(org.exist.xquery.value.Sequence) VirtualNodeSet(org.exist.dom.persistent.VirtualNodeSet)

Example 7 with VirtualNodeSet

use of org.exist.dom.persistent.VirtualNodeSet 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));
    }
}
Also used : NodeSet(org.exist.dom.persistent.NodeSet) VirtualNodeSet(org.exist.dom.persistent.VirtualNodeSet) NewArrayNodeSet(org.exist.dom.persistent.NewArrayNodeSet) QName(org.exist.dom.QName) Sequence(org.exist.xquery.value.Sequence) EXistException(org.exist.EXistException) VirtualNodeSet(org.exist.dom.persistent.VirtualNodeSet) Collator(com.ibm.icu.text.Collator) Item(org.exist.xquery.value.Item) ContextItem(org.exist.dom.persistent.ContextItem) SequenceIterator(org.exist.xquery.value.SequenceIterator) Indexable(org.exist.storage.Indexable) DocumentSet(org.exist.dom.persistent.DocumentSet)

Aggregations

VirtualNodeSet (org.exist.dom.persistent.VirtualNodeSet)7 NodeSet (org.exist.dom.persistent.NodeSet)5 DocumentSet (org.exist.dom.persistent.DocumentSet)4 QName (org.exist.dom.QName)3 ContextItem (org.exist.dom.persistent.ContextItem)3 NewArrayNodeSet (org.exist.dom.persistent.NewArrayNodeSet)3 Sequence (org.exist.xquery.value.Sequence)3 IOException (java.io.IOException)2 NodeProxy (org.exist.dom.persistent.NodeProxy)2 SequenceIterator (org.exist.xquery.value.SequenceIterator)2 ValueSequence (org.exist.xquery.value.ValueSequence)2 Collator (com.ibm.icu.text.Collator)1 TreeSet (java.util.TreeSet)1 EXistException (org.exist.EXistException)1 DocumentImpl (org.exist.dom.persistent.DocumentImpl)1 LuceneIndexWorker (org.exist.indexing.lucene.LuceneIndexWorker)1 RangeIndex (org.exist.indexing.range.RangeIndex)1 RangeIndexWorker (org.exist.indexing.range.RangeIndexWorker)1 Indexable (org.exist.storage.Indexable)1 ExecutionMode (org.exist.xquery.Predicate.ExecutionMode)1