Search in sources :

Example 1 with VirtualNodeSet

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

the class Predicate method selectByPosition.

/**
 * @param outerSequence the outer sequence
 * @param contextSequence the context sequence
 * @param mode the mode
 * @param innerSeq the inner sequence
 *
 * @return The result of the positional evaluation of the predicate.
 *
 * @throws XPathException if an error occurs
 */
private Sequence selectByPosition(final Sequence outerSequence, final Sequence contextSequence, final int mode, final Sequence innerSeq) throws XPathException {
    if (outerSequence != null && !outerSequence.isEmpty() && Type.subTypeOf(contextSequence.getItemType(), Type.NODE) && contextSequence.isPersistentSet() && outerSequence.isPersistentSet()) {
        final Sequence result = new NewArrayNodeSet();
        final NodeSet contextSet = contextSequence.toNodeSet();
        switch(mode) {
            case Constants.CHILD_AXIS:
            case Constants.ATTRIBUTE_AXIS:
            case Constants.DESCENDANT_AXIS:
            case Constants.DESCENDANT_SELF_AXIS:
            case Constants.DESCENDANT_ATTRIBUTE_AXIS:
                {
                    final NodeSet outerNodeSet = outerSequence.toNodeSet();
                    // TODO: in some cases, especially with in-memory nodes,
                    // outerSequence.toNodeSet() will generate a document
                    // which will be different from the one(s) in contextSet
                    // ancestors will thus be empty :-(
                    // A special treatment of VirtualNodeSet does not seem to be
                    // required anymore
                    final Sequence ancestors = outerNodeSet.selectAncestors(contextSet, true, getExpressionId());
                    if (contextSet.getDocumentSet().intersection(outerNodeSet.getDocumentSet()).getDocumentCount() == 0) {
                        LOG.info("contextSet and outerNodeSet don't share any document");
                    }
                    final NewArrayNodeSet temp = new NewArrayNodeSet();
                    for (final SequenceIterator i = ancestors.iterate(); i.hasNext(); ) {
                        NodeProxy p = (NodeProxy) i.nextItem();
                        ContextItem contextNode = p.getContext();
                        temp.reset();
                        while (contextNode != null) {
                            if (contextNode.getContextId() == getExpressionId()) {
                                temp.add(contextNode.getNode());
                            }
                            contextNode = contextNode.getNextDirect();
                        }
                        p.clearContext(getExpressionId());
                        // TODO : understand why we sort here...
                        temp.sortInDocumentOrder();
                        for (final SequenceIterator j = innerSeq.iterate(); j.hasNext(); ) {
                            final NumericValue v = (NumericValue) j.nextItem();
                            // Non integers return... nothing, not even an error !
                            if (!v.hasFractionalPart() && !v.isZero()) {
                                // ... whereas we don't want a sorted array here
                                // TODO : rename this method as getInDocumentOrder ? -pb
                                p = temp.get(v.getInt() - 1);
                                if (p != null) {
                                    result.add(p);
                                }
                            // TODO : does null make sense here ? Well... sometimes ;-)
                            }
                        }
                    }
                    break;
                }
            default:
                for (final SequenceIterator i = outerSequence.iterate(); i.hasNext(); ) {
                    NodeProxy p = (NodeProxy) i.nextItem();
                    Sequence temp;
                    boolean reverseAxis = true;
                    switch(mode) {
                        case Constants.ANCESTOR_AXIS:
                            temp = contextSet.selectAncestors(p, false, Expression.IGNORE_CONTEXT);
                            break;
                        case Constants.ANCESTOR_SELF_AXIS:
                            temp = contextSet.selectAncestors(p, true, Expression.IGNORE_CONTEXT);
                            break;
                        case Constants.PARENT_AXIS:
                            // TODO : understand why the contextSet is not involved
                            // here
                            // NodeProxy.getParent returns a *theoretical* parent
                            // which is *not* guaranteed to be in the context set !
                            temp = p.getParents(Expression.NO_CONTEXT_ID);
                            break;
                        case Constants.PRECEDING_AXIS:
                            temp = contextSet.selectPreceding(p, Expression.IGNORE_CONTEXT);
                            break;
                        case Constants.PRECEDING_SIBLING_AXIS:
                            temp = contextSet.selectPrecedingSiblings(p, Expression.IGNORE_CONTEXT);
                            break;
                        case Constants.FOLLOWING_SIBLING_AXIS:
                            temp = contextSet.selectFollowingSiblings(p, Expression.IGNORE_CONTEXT);
                            reverseAxis = false;
                            break;
                        case Constants.FOLLOWING_AXIS:
                            temp = contextSet.selectFollowing(p, Expression.IGNORE_CONTEXT);
                            reverseAxis = false;
                            break;
                        case Constants.SELF_AXIS:
                            temp = p;
                            reverseAxis = false;
                            break;
                        default:
                            throw new IllegalArgumentException("Tried to test unknown axis");
                    }
                    if (!temp.isEmpty()) {
                        for (final SequenceIterator j = innerSeq.iterate(); j.hasNext(); ) {
                            final NumericValue v = (NumericValue) j.nextItem();
                            // Non integers return... nothing, not even an error !
                            if (!v.hasFractionalPart() && !v.isZero()) {
                                final int pos = (reverseAxis ? temp.getItemCount() - v.getInt() : v.getInt() - 1);
                                // Other positions are ignored
                                if (pos >= 0 && pos < temp.getItemCount()) {
                                    final NodeProxy t = (NodeProxy) temp.itemAt(pos);
                                    // for the current context: filter out those
                                    // context items not selected by the positional predicate
                                    ContextItem ctx = t.getContext();
                                    t.clearContext(Expression.IGNORE_CONTEXT);
                                    while (ctx != null) {
                                        if (ctx.getContextId() == outerContextId) {
                                            if (ctx.getNode().getNodeId().equals(p.getNodeId())) {
                                                t.addContextNode(outerContextId, ctx.getNode());
                                            }
                                        } else {
                                            t.addContextNode(ctx.getContextId(), ctx.getNode());
                                        }
                                        ctx = ctx.getNextDirect();
                                    }
                                    result.add(t);
                                }
                            }
                        }
                    }
                }
        }
        return result;
    } else {
        final boolean reverseAxis = Type.subTypeOf(contextSequence.getItemType(), Type.NODE) && (mode == Constants.ANCESTOR_AXIS || mode == Constants.ANCESTOR_SELF_AXIS || mode == Constants.PARENT_AXIS || mode == Constants.PRECEDING_AXIS || mode == Constants.PRECEDING_SIBLING_AXIS);
        final Set<NumericValue> set = new TreeSet<>();
        final ValueSequence result = new ValueSequence();
        for (final SequenceIterator i = innerSeq.iterate(); i.hasNext(); ) {
            final NumericValue v = (NumericValue) i.nextItem();
            // Non integers return... nothing, not even an error !
            if (!v.hasFractionalPart() && !v.isZero()) {
                final int pos = (reverseAxis ? contextSequence.getItemCount() - v.getInt() : v.getInt() - 1);
                // Other positions are ignored
                if (pos >= 0 && pos < contextSequence.getItemCount() && !set.contains(v)) {
                    result.add(contextSequence.itemAt(pos));
                    set.add(v);
                }
            }
        }
        return result;
    }
}
Also used : NodeSet(org.exist.dom.persistent.NodeSet) VirtualNodeSet(org.exist.dom.persistent.VirtualNodeSet) NewArrayNodeSet(org.exist.dom.persistent.NewArrayNodeSet) NewArrayNodeSet(org.exist.dom.persistent.NewArrayNodeSet) ContextItem(org.exist.dom.persistent.ContextItem) ValueSequence(org.exist.xquery.value.ValueSequence) Sequence(org.exist.xquery.value.Sequence) NodeProxy(org.exist.dom.persistent.NodeProxy) SequenceIterator(org.exist.xquery.value.SequenceIterator) TreeSet(java.util.TreeSet) ValueSequence(org.exist.xquery.value.ValueSequence) NumericValue(org.exist.xquery.value.NumericValue)

Example 2 with VirtualNodeSet

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

the class PathExpr method eval.

@Override
public Sequence eval(Sequence contextSequence, final 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 (contextItem != null) {
        contextSequence = contextItem.toSequence();
    }
    Sequence result = null;
    if (steps.size() == 0) {
        result = Sequence.EMPTY_SEQUENCE;
    } else {
        // we will filter out nodes from the contextSequence
        result = contextSequence;
        Sequence currentContext = contextSequence;
        DocumentSet contextDocs = null;
        Expression expr = steps.get(0);
        if (expr instanceof VariableReference) {
            final Variable var = ((VariableReference) expr).getVariable(new AnalyzeContextInfo(parent, 0));
            // TOUNDERSTAND : how null could be possible here ? -pb
            if (var != null) {
                contextDocs = var.getContextDocs();
            }
        }
        // contextDocs == null *is* significant
        setContextDocSet(contextDocs);
        // To prevent processing nodes after atomic values...
        // TODO : let the parser do it ? -pb
        boolean gotAtomicResult = false;
        Expression prev = null;
        for (Expression step : steps) {
            prev = expr;
            expr = step;
            context.getWatchDog().proceed(expr);
            // TODO : maybe this could be detected by the parser ? -pb
            if (gotAtomicResult && !Type.subTypeOf(expr.returnsType(), Type.NODE) && // Ugly workaround to allow preceding *text* nodes.
            !(expr instanceof EnclosedExpr)) {
                throw new XPathException(this, ErrorCodes.XPTY0019, "left operand of '/' must be a node. Got '" + Type.getTypeName(result.getItemType()) + " " + result.getCardinality().getHumanDescription() + "'");
            }
            // contextDocs == null *is* significant
            expr.setContextDocSet(contextDocs);
            // switch into single step mode if we are processing in-memory nodes only
            final boolean inMemProcessing = currentContext != null && Type.subTypeOf(currentContext.getItemType(), Type.NODE) && !currentContext.isPersistentSet();
            // DESIGN : first test the dependency then the result
            final int exprDeps = expr.getDependencies();
            if (inMemProcessing || ((Dependency.dependsOn(exprDeps, Dependency.CONTEXT_ITEM) || Dependency.dependsOn(exprDeps, Dependency.CONTEXT_POSITION)) && // TODO : reconsider since that may be expensive (type evaluation)
            !(this instanceof Predicate && Type.subTypeOfUnion(this.returnsType(), Type.NUMBER)) && currentContext != null && !currentContext.isEmpty())) {
                Sequence exprResult = new ValueSequence(Type.subTypeOf(expr.returnsType(), Type.NODE));
                ((ValueSequence) exprResult).keepUnOrdered(unordered);
                // Restore a position which may have been modified by inner expressions
                int p = context.getContextPosition();
                final Sequence seq = context.getContextSequence();
                for (final SequenceIterator iterInner = currentContext.iterate(); iterInner.hasNext(); p++) {
                    context.setContextSequencePosition(p, seq);
                    context.getWatchDog().proceed(expr);
                    final Item current = iterInner.nextItem();
                    // 0 or 1 item
                    if (!currentContext.hasMany()) {
                        exprResult = expr.eval(currentContext, current);
                    } else {
                        exprResult.addAll(expr.eval(currentContext, current));
                    }
                }
                result = exprResult;
            } else {
                try {
                    result = expr.eval(currentContext);
                } catch (XPathException ex) {
                    // enrich exception when information is available
                    if (ex.getLine() < 1 || ex.getColumn() < 1) {
                        ex.setLocation(expr.getLine(), expr.getColumn());
                    }
                    throw ex;
                }
            }
            // well, no so stupid I think...
            if (result != null) {
                if (steps.size() > 1 && !(result instanceof VirtualNodeSet) && !(expr instanceof EnclosedExpr) && !result.isEmpty() && !Type.subTypeOf(result.getItemType(), Type.NODE)) {
                    gotAtomicResult = true;
                }
                if (steps.size() > 1 && getLastExpression() instanceof Step) {
                    // remove duplicate nodes if this is a path
                    // expression with more than one step
                    result.removeDuplicates();
                }
            }
            if (!staticContext) {
                currentContext = result;
            }
        }
        final boolean allowMixedNodesInReturn;
        if (prev != null) {
            allowMixedNodesInReturn = prev.allowMixedNodesInReturn() | expr.allowMixedNodesInReturn();
        } else {
            allowMixedNodesInReturn = expr.allowMixedNodesInReturn();
        }
        if (gotAtomicResult && result != null && !allowMixedNodesInReturn && !Type.subTypeOf(result.getItemType(), Type.ATOMIC)) {
            throw new XPathException(this, ErrorCodes.XPTY0018, "Cannot mix nodes and atomic values in the result of a path expression.");
        }
    }
    if (context.getProfiler().isEnabled()) {
        context.getProfiler().end(this, "", result);
    }
    return result;
}
Also used : VirtualNodeSet(org.exist.dom.persistent.VirtualNodeSet) CompiledExpression(org.xmldb.api.base.CompiledExpression) DocumentSet(org.exist.dom.persistent.DocumentSet)

Example 3 with VirtualNodeSet

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

the class Query method eval.

public Sequence eval(Sequence contextSequence, Item contextItem) throws XPathException {
    if (contextItem != null)
        contextSequence = contextItem.toSequence();
    if (contextSequence != null && !contextSequence.isPersistentSet())
        // in-memory docs won't have an index
        return Sequence.EMPTY_SEQUENCE;
    NodeSet result;
    if (preselectResult == null) {
        long start = System.currentTimeMillis();
        Sequence input = getArgument(0).eval(contextSequence);
        if (!(input instanceof VirtualNodeSet) && input.isEmpty())
            result = NodeSet.EMPTY_SET;
        else {
            NodeSet inNodes = input.toNodeSet();
            DocumentSet docs = inNodes.getDocumentSet();
            LuceneIndexWorker index = (LuceneIndexWorker) context.getBroker().getIndexController().getWorkerByIndexId(LuceneIndex.ID);
            Item key = getKey(contextSequence, contextItem);
            List<QName> qnames = null;
            if (contextQName != null) {
                qnames = new ArrayList<>(1);
                qnames.add(contextQName);
            }
            QueryOptions options = parseOptions(this, contextSequence, contextItem, 3);
            try {
                if (key != null && Type.subTypeOf(key.getType(), Type.ELEMENT)) {
                    final Element queryXML = (Element) ((NodeValue) key).getNode();
                    result = index.query(getExpressionId(), docs, inNodes, qnames, queryXML, NodeSet.ANCESTOR, options);
                } else {
                    final String query = key == null ? null : key.getStringValue();
                    result = index.query(getExpressionId(), docs, inNodes, qnames, query, NodeSet.ANCESTOR, options);
                }
            } catch (IOException | org.apache.lucene.queryparser.classic.ParseException e) {
                throw new XPathException(this, e.getMessage());
            }
        }
        if (context.getProfiler().traceFunctions()) {
            context.getProfiler().traceIndexUsage(context, "lucene", this, PerformanceStats.BASIC_INDEX, System.currentTimeMillis() - start);
        }
    } else {
        // DW: contextSequence can be null
        contextStep.setPreloadedData(contextSequence.getDocumentSet(), preselectResult);
        result = getArgument(0).eval(contextSequence).toNodeSet();
    }
    return result;
}
Also used : NodeSet(org.exist.dom.persistent.NodeSet) VirtualNodeSet(org.exist.dom.persistent.VirtualNodeSet) QName(org.exist.dom.QName) Element(org.w3c.dom.Element) IOException(java.io.IOException) LuceneIndexWorker(org.exist.indexing.lucene.LuceneIndexWorker) VirtualNodeSet(org.exist.dom.persistent.VirtualNodeSet) DocumentSet(org.exist.dom.persistent.DocumentSet)

Example 4 with VirtualNodeSet

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

the class Lookup method eval.

@Override
public Sequence eval(Sequence contextSequence, Item contextItem) throws XPathException {
    if (!canOptimize && fallback != null) {
        return fallback.eval(contextSequence, contextItem);
    }
    if (contextItem != null)
        contextSequence = contextItem.toSequence();
    if (contextSequence != null && !contextSequence.isPersistentSet()) {
        // in-memory docs won't have an index
        if (fallback == null) {
            return Sequence.EMPTY_SEQUENCE;
        } else {
            return fallback.eval(contextSequence, contextItem);
        }
    }
    NodeSet result;
    if (preselectResult == null) {
        long start = System.currentTimeMillis();
        Sequence input = getArgument(0).eval(contextSequence);
        if (!(input instanceof VirtualNodeSet) && input.isEmpty())
            result = NodeSet.EMPTY_SET;
        else {
            RangeIndexWorker index = (RangeIndexWorker) context.getBroker().getIndexController().getWorkerByIndexId(RangeIndex.ID);
            AtomicValue[] keys = getKeys(contextSequence);
            if (keys.length == 0) {
                return NodeSet.EMPTY_SET;
            }
            List<QName> qnames = null;
            if (contextQName != null) {
                qnames = new ArrayList<>(1);
                qnames.add(contextQName);
            }
            final RangeIndex.Operator operator = getOperator();
            // throw an exception if substring match operation is applied to collated index
            if (usesCollation && !operator.supportsCollation()) {
                throw new XPathException(this, RangeIndexModule.EXXQDYFT0001, "Index defines a collation which cannot be " + "used with the '" + operator + "' operation.");
            }
            try {
                NodeSet inNodes = input.toNodeSet();
                DocumentSet docs = inNodes.getDocumentSet();
                result = index.query(getExpressionId(), docs, inNodes, qnames, keys, operator, NodeSet.ANCESTOR);
            } catch (IOException e) {
                throw new XPathException(this, e.getMessage());
            }
        }
        if (context.getProfiler().traceFunctions()) {
            context.getProfiler().traceIndexUsage(context, "new-range", this, PerformanceStats.BASIC_INDEX, System.currentTimeMillis() - start);
        }
    // LOG.info("eval plain took " + (System.currentTimeMillis() - start));
    } else {
        // long start = System.currentTimeMillis();
        contextStep.setPreloadedData(preselectResult.getDocumentSet(), preselectResult);
        result = getArgument(0).eval(contextSequence).toNodeSet();
    // LOG.info("eval took " + (System.currentTimeMillis() - start));
    }
    return result;
}
Also used : NodeSet(org.exist.dom.persistent.NodeSet) VirtualNodeSet(org.exist.dom.persistent.VirtualNodeSet) QName(org.exist.dom.QName) IOException(java.io.IOException) VirtualNodeSet(org.exist.dom.persistent.VirtualNodeSet) RangeIndexWorker(org.exist.indexing.range.RangeIndexWorker) DocumentSet(org.exist.dom.persistent.DocumentSet) RangeIndex(org.exist.indexing.range.RangeIndex)

Example 5 with VirtualNodeSet

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

the class Predicate method selectByNodeSet.

/**
 * @param contextSequence the context sequence
 *
 * @return The result of the node set evaluation of the predicate.
 *
 * @throws XPathException if an error occurs
 */
private Sequence selectByNodeSet(final Sequence contextSequence) throws XPathException {
    final NewArrayNodeSet result = new NewArrayNodeSet();
    final NodeSet contextSet = contextSequence.toNodeSet();
    final boolean contextIsVirtual = contextSet instanceof VirtualNodeSet;
    contextSet.setTrackMatches(false);
    final NodeSet nodes = super.eval(contextSet, null).toNodeSet();
    /*
         * if the predicate expression returns results from the cache we can
         * also return the cached result.
         */
    if (cached != null && cached.isValid(contextSequence, null) && nodes.isCached()) {
        if (context.getProfiler().isEnabled()) {
            context.getProfiler().message(this, Profiler.OPTIMIZATIONS, "Using cached results", result);
        }
        return cached.getResult();
    }
    DocumentImpl lastDoc = null;
    for (final NodeProxy currentNode : nodes) {
        int sizeHint = Constants.NO_SIZE_HINT;
        if (lastDoc == null || currentNode.getOwnerDocument() != lastDoc) {
            lastDoc = currentNode.getOwnerDocument();
            sizeHint = nodes.getSizeHint(lastDoc);
        }
        ContextItem contextItem = currentNode.getContext();
        if (contextItem == null) {
            throw new XPathException(this, "Internal evaluation error: context is missing for node " + currentNode.getNodeId() + " !");
        }
        // TODO : review to consider transverse context
        while (contextItem != null) {
            if (contextItem.getContextId() == getExpressionId()) {
                final NodeProxy next = contextItem.getNode();
                if (contextIsVirtual || contextSet.contains(next)) {
                    next.addMatches(currentNode);
                    result.add(next, sizeHint);
                }
            }
            contextItem = contextItem.getNextDirect();
        }
    }
    if (contextSequence.isCacheable()) {
        cached = new CachedResult(contextSequence, null, result);
    }
    contextSet.setTrackMatches(true);
    return result;
}
Also used : NodeSet(org.exist.dom.persistent.NodeSet) VirtualNodeSet(org.exist.dom.persistent.VirtualNodeSet) NewArrayNodeSet(org.exist.dom.persistent.NewArrayNodeSet) NewArrayNodeSet(org.exist.dom.persistent.NewArrayNodeSet) ContextItem(org.exist.dom.persistent.ContextItem) DocumentImpl(org.exist.dom.persistent.DocumentImpl) NodeProxy(org.exist.dom.persistent.NodeProxy) VirtualNodeSet(org.exist.dom.persistent.VirtualNodeSet)

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