Package org.apache.jackrabbit.core.query.lucene

Source Code of org.apache.jackrabbit.core.query.lucene.LazyQueryResultImpl$LazyScoreNodeIterator

/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements.  See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License.  You may obtain a copy of the License at
*
*      http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.jackrabbit.core.query.lucene;

import org.apache.jackrabbit.core.ItemManager;
import org.apache.jackrabbit.core.NodeId;
import org.apache.jackrabbit.core.NodeImpl;
import org.apache.jackrabbit.core.security.AccessManager;
import org.apache.jackrabbit.name.NamespaceResolver;
import org.apache.jackrabbit.name.NoPrefixDeclaredException;
import org.apache.jackrabbit.name.QName;
import org.apache.jackrabbit.name.NameFormat;
import org.apache.lucene.search.Query;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.jcr.NodeIterator;
import javax.jcr.RepositoryException;
import javax.jcr.ItemNotFoundException;
import javax.jcr.Node;
import javax.jcr.query.QueryResult;
import javax.jcr.query.RowIterator;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
import java.util.NoSuchElementException;

/**
* Implements the <code>javax.jcr.query.QueryResult</code> interface.
*/
public class LazyQueryResultImpl implements QueryResult {

    /**
     * The logger instance for this class
     */
    private static final Logger log = LoggerFactory.getLogger(QueryResultImpl.class);

    /**
     * The search index to execute the query.
     */
    private final SearchIndex index;

    /**
     * The item manager of the session executing the query
     */
    private final ItemManager itemMgr;

    /**
     * The namespace resolver of the session executing the query
     */
    protected final NamespaceResolver resolver;

    /**
     * The access manager of the session that executes the query.
     */
    private final AccessManager accessMgr;

    /**
     * The query instance which created this query result.
     */
    protected final QueryImpl queryImpl;

    /**
     * The lucene query to execute.
     */
    protected final Query query;

    /**
     * The select properties
     */
    protected final QName[] selectProps;

    /**
     * The names of properties to use for ordering the result set.
     */
    protected final QName[] orderProps;

    /**
     * The order specifier for each of the order properties.
     */
    protected final boolean[] orderSpecs;

    /**
     * The result nodes including their score. This list is populated on a lazy
     * basis while a client iterates through the results.
     */
    private final List resultNodes = new ArrayList();

    /**
     * This is the raw number of results that matched the query. This number
     * also includes matches which will not be returned due to access
     * restrictions. This value is set when the query is executed the first
     * time.
     */
    private int numResults = -1;

    /**
     * The number of results that are invalid, either because a node does not
     * exist anymore or because the session does not have access to the node.
     */
    private int invalid = 0;

    /**
     * If <code>true</code> nodes are returned in document order.
     */
    private final boolean docOrder;

    /**
     * Creates a new query result.
     *
     * @param index         the search index where the query is executed.
     * @param itemMgr       the item manager of the session executing the
     *                      query.
     * @param resolver      the namespace resolver of the session executing the
     *                      query.
     * @param accessMgr     the access manager of the session executiong the
     *                      query.
     * @param queryImpl     the query instance which created this query result.
     * @param query         the lucene query to execute on the index.
     * @param selectProps   the select properties of the query.
     * @param orderProps    the names of the order properties.
     * @param orderSpecs    the order specs, one for each order property name.
     * @param documentOrder if <code>true</code> the result is returned in
     *                      document order.
     */
    public LazyQueryResultImpl(SearchIndex index,
                           ItemManager itemMgr,
                           NamespaceResolver resolver,
                           AccessManager accessMgr,
                           QueryImpl queryImpl,
                           Query query,
                           QName[] selectProps,
                           QName[] orderProps,
                           boolean[] orderSpecs,
                           boolean documentOrder) throws RepositoryException {
        this.index = index;
        this.itemMgr = itemMgr;
        this.resolver = resolver;
        this.accessMgr = accessMgr;
        this.queryImpl = queryImpl;
        this.query = query;
        this.selectProps = selectProps;
        this.orderProps = orderProps;
        this.orderSpecs = orderSpecs;
        this.docOrder = orderProps.length == 0 && documentOrder;
        // if document order is requested get all results right away
        getResults(docOrder ? Integer.MAX_VALUE : index.getResultFetchSize());
    }

    /**
     * {@inheritDoc}
     */
    public String[] getColumnNames() throws RepositoryException {
        try {
            String[] propNames = new String[selectProps.length];
            for (int i = 0; i < selectProps.length; i++) {
                propNames[i] = NameFormat.format(selectProps[i], resolver);
            }
            return propNames;
        } catch (NoPrefixDeclaredException npde) {
            String msg = "encountered invalid property name";
            log.debug(msg);
            throw new RepositoryException(msg, npde);

        }
    }

    /**
     * {@inheritDoc}
     */
    public NodeIterator getNodes() throws RepositoryException {
        return getNodeIterator();
    }

    /**
     * {@inheritDoc}
     */
    public RowIterator getRows() throws RepositoryException {
        return new RowIteratorImpl(getNodeIterator(), selectProps, resolver);
    }

    /**
     * Executes the query for this result and returns hits. The caller must
     * close the query hits when he is done using it.
     *
     * @return hits for this query result.
     * @throws IOException if an error occurs while executing the query.
     */
    protected QueryHits executeQuery() throws IOException {
        return index.executeQuery(queryImpl, query, orderProps, orderSpecs);
    }

    //--------------------------------< internal >------------------------------

    /**
     * Creates a node iterator over the result nodes.
     *
     * @return a node iterator over the result nodes.
     */
    private ScoreNodeIterator getNodeIterator() {
        if (docOrder) {
            return new DocOrderNodeIteratorImpl(itemMgr, resultNodes);
        } else {
            return new LazyScoreNodeIterator();
        }
    }

    /**
     * Attempts to get <code>size</code> results and puts them into {@link
     * #resultNodes}. If the size of {@link #resultNodes} is less than
     * <code>size</code> then there are no more than <code>resultNodes.size()</code>
     * results for this query.
     *
     * @param size the number of results to fetch for the query.
     * @throws RepositoryException if an error occurs while executing the
     *                             query.
     */
    private void getResults(int size) throws RepositoryException {
        if (log.isDebugEnabled()) {
            log.debug("getResults(" + size + ")");
        }
        if (resultNodes.size() >= size) {
            // we already have them all
            return;
        }

        // execute it
        QueryHits result = null;
        try {
            result = executeQuery();

            // set num results with the first query execution
            if (numResults == -1) {
                numResults = result.length();
            }

            int start = resultNodes.size() + invalid;
            int max = Math.min(result.length(), numResults);
            for (int i = start; i < max && resultNodes.size() < size; i++) {
                NodeId id = NodeId.valueOf(result.doc(i).get(FieldNames.UUID));
                // check access
                try {
                    if (accessMgr.isGranted(id, AccessManager.READ)) {
                        resultNodes.add(new ScoreNode(id, result.score(i)));
                    }
                } catch (ItemNotFoundException e) {
                    // has been deleted meanwhile
                    invalid++;
                }
            }
        } catch (IOException e) {
            log.error("Exception while executing query: ", e);
            // todo throw?
        } finally {
            if (result != null) {
                try {
                    result.close();
                } catch (IOException e) {
                    log.warn("Unable to close query result: " + e);
                }
            }
        }
    }

    private final class LazyScoreNodeIterator implements ScoreNodeIterator {

        private int position = -1;

        private boolean initialized = false;

        private NodeImpl next;

        /**
         * {@inheritDoc}
         */
        public float getScore() {
            initialize();
            if (!hasNext()) {
                throw new NoSuchElementException();
            }
            return ((ScoreNode) resultNodes.get(position)).getScore();
        }

        /**
         * {@inheritDoc}
         */
        public NodeImpl nextNodeImpl() {
            initialize();
            if (next == null) {
                throw new NoSuchElementException();
            }
            NodeImpl n = next;
            fetchNext();
            return n;
        }

        /**
         * {@inheritDoc}
         */
        public Node nextNode() {
            return nextNodeImpl();
        }

        /**
         * {@inheritDoc}
         */
        public void skip(long skipNum) {
            initialize();
            if (skipNum < 0) {
                throw new IllegalArgumentException("skipNum must not be negative");
            }
            if ((position + invalid + skipNum) > numResults) {
                throw new NoSuchElementException();
            }
            if (skipNum == 0) {
                // do nothing
            } else {
                // attempt to get enough results
                try {
                    getResults(position + invalid + (int) skipNum);
                    if (resultNodes.size() >= position + skipNum) {
                        // skip within already fetched results
                        position += skipNum - 1;
                        fetchNext();
                    } else {
                        // not enough results after getResults()
                        throw new NoSuchElementException();
                    }
                } catch (RepositoryException e) {
                    throw new NoSuchElementException(e.getMessage());
                }
            }
        }

        /**
         * {@inheritDoc}
         * <p/>
         * This value may shrink when the query result encounters non-existing
         * nodes or the session does not have access to a node.
         */
        public long getSize() {
            return numResults - invalid;
        }

        /**
         * {@inheritDoc}
         */
        public long getPosition() {
            initialize();
            return position;
        }

        /**
         * @throws UnsupportedOperationException always.
         */
        public void remove() {
            throw new UnsupportedOperationException("remove");
        }

        /**
         * {@inheritDoc}
         */
        public boolean hasNext() {
            initialize();
            return next != null;
        }

        /**
         * {@inheritDoc}
         */
        public Object next() {
            return nextNodeImpl();
        }

        /**
         * Initializes this iterator but only if it is not yet initialized.
         */
        private void initialize() {
            if (!initialized) {
                fetchNext();
                initialized = true;
            }
        }

        /**
         * Fetches the next node to return by this iterator. If this method
         * returns and {@link #next} is <code>null</code> then there is no next
         * node.
         */
        private void fetchNext() {
            next = null;
            int nextPos = position + 1;
            while (next == null && (nextPos + invalid) < numResults) {
                if (nextPos >= resultNodes.size()) {
                    // fetch more results
                    try {
                        int num;
                        if (resultNodes.size() == 0) {
                            num = index.getResultFetchSize();
                        } else {
                            num = resultNodes.size() * 2;
                        }
                        getResults(num);
                    } catch (RepositoryException e) {
                        log.warn("Exception getting more results: " + e);
                    }
                    // check again
                    if (nextPos >= resultNodes.size()) {
                        // no more valid results
                        return;
                    }
                }
                ScoreNode sn = (ScoreNode) resultNodes.get(nextPos);
                try {
                    next = (NodeImpl) itemMgr.getItem(sn.getNodeId());
                } catch (RepositoryException e) {
                    log.warn("Exception retrieving Node with UUID: "
                            + sn.getNodeId() + ": " + e.toString());
                    // remove score node and try next
                    resultNodes.remove(nextPos);
                    invalid++;
                }
            }
            position++;
        }
    }
}
TOP

Related Classes of org.apache.jackrabbit.core.query.lucene.LazyQueryResultImpl$LazyScoreNodeIterator

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.