zoukankan      html  css  js  c++  java
  • Apache Jackrabbit源码研究(五)

    上文最后提到jackrabbit的检索默认实现类QueryImpl,先熟悉一下该类的继承层次

    QueryImpl继承自抽象类AbstractQueryImpl,而抽象类实现了Query接口(JCR的接口)

    Query接口源码如下: 

    /**
     * A <code>Query</code> object.
     */
    public interface Query {
    
        /**
         * A string constant representing the XPath query language as defined in JCR
         * 1.0.
         *
         * @deprecated As of JCR 2.0, this language is deprecated.
         */
        public static final String XPATH = "xpath";
    
        /**
         * A string constant representing the SQL query language as defined in JCR
         * 1.0.
         *
         * @deprecated As of JCR 2.0, this language is deprecated.
         */
        public static final String SQL = "sql";
    
        /**
         * A string constant representing the JCR-SQL2 query language.
         *
         * @since JCR 2.0
         */
        public static final String JCR_SQL2 = "JCR-SQL2";
    
        /**
         * A string constant representing the JCR-JQOM query language.
         *
         * @since JCR 2.0
         */
        public static final String JCR_JQOM = "JCR-JQOM";
    
        /**
         * Executes this query and returns a <code>{@link QueryResult}</code>
         * object.
         * <p>
         * If this <code>Query</code> contains a variable (see {@link
         * javax.jcr.query.qom.BindVariableValue BindVariableValue}) which has not
         * been bound to a value (see {@link Query#bindValue}) then this method
         * throws an <code>InvalidQueryException</code>.
         *
         * @return a <code>QueryResult</code> object
         * @throws InvalidQueryException if the query contains an unbound variable.
         * @throws RepositoryException   if another error occurs.
         */
        public QueryResult execute() throws InvalidQueryException, RepositoryException;
    
        /**
         * Sets the maximum size of the result set to <code>limit</code>.
         *
         * @param limit a <code>long</code>
         * @since JCR 2.0
         */
        public void setLimit(long limit);
    
        /**
         * Sets the start offset of the result set to <code>offset</code>.
         *
         * @param offset a <code>long</code>
         * @since JCR 2.0
         */
        public void setOffset(long offset);
    
        /**
         * Returns the statement defined for this query.
         * <p>
         * If the language of this query is JCR-SQL2 or another string-based
         * language, this method will return the statement that was used to create
         * this query.
         * <p>
         * If the language of this query is JCR-JQOM, this method will return the
         * JCR-SQL2 equivalent of the JCR-JQOM object tree. This is the standard
         * serialization of JCR-JQOM and is also the string stored in the
         * <code>jcr:statement</code> property if the query is persisted. See {@link
         * #storeAsNode(String)}.
         *
         * @return the query statement.
         */
        public String getStatement();
    
        /**
         * Returns the language set for this query. This will be one of the query
         * language constants returned by {@link QueryManager#getSupportedQueryLanguages}.
         *
         * @return the query language.
         */
        public String getLanguage();
    
        /**
         * If this is a <code>Query</code> object that has been stored using {@link
         * Query#storeAsNode} (regardless of whether it has been <code>save</code>d
         * yet) or retrieved using {@link QueryManager#getQuery}), then this method
         * returns the path of the <code>nt:query</code> node that stores the
         * query.
         *
         * @return path of the node representing this query.
         * @throws ItemNotFoundException if this query is not a stored query.
         * @throws RepositoryException   if another error occurs.
         */
        public String getStoredQueryPath() throws ItemNotFoundException, RepositoryException;
    
        /**
         * Creates a node of type <code>nt:query</code> holding this query at
         * <code>absPath</code> and returns that node.
         * <p>
         * This is  a session-write method and therefore requires a
         * <code>Session.save()</code> to dispatch the change.
         * <p>
         * The <code>absPath</code> provided must not have an index on its final
         * element. If ordering is supported by the node type of the parent node
         * then the new node is appended to the end of the child node list.
         * <p>
         * An <code>ItemExistsException</code> will be thrown either immediately, on
         * dispatch or on persists, if an item at the specified path already exists
         * and same-name siblings are not allowed. Implementations may differ on
         * when this validation is performed.
         * <p>
         * A <code>PathNotFoundException</code> will be thrown either immediately,
         * on dispatch or on persists, if the specified path implies intermediary
         * nodes that do not exist. Implementations may differ on when this
         * validation is performed.
         * <p>
         * A <code>ConstraintViolationException</code>will be thrown either
         * immediately, on dispatch or on persists, if adding the node would violate
         * a node type or implementation-specific constraint or if an attempt is
         * made to add a node as the child of a property. Implementations may differ
         * on when this validation is performed.
         * <p>
         * A <code>VersionException</code> will be thrown either immediately, on
         * dispatch or on persists, if the node to which the new child is being
         * added is read-only due to a checked-in node. Implementations may differ
         * on when this validation is performed.
         * <p>
         * A <code>LockException</code> will be thrown either immediately, on
         * dispatch or on persists, if a lock prevents the addition of the node.
         * Implementations may differ on when this validation is performed.
         *
         * @param absPath absolute path the query should be stored at
         * @return the newly created node.
         * @throws ItemExistsException          if an item at the specified path already
         *                                      exists, same-name siblings are not allowed and this implementation
         *                                      performs this validation immediately.
         * @throws PathNotFoundException        if the specified path implies intermediary
         *                                      <code>Node</code>s that do not exist or the last element of
         *                                      <code>relPath</code> has an index, and this implementation performs this
         *                                      validation immediately.
         * @throws ConstraintViolationException if a node type or
         *                                      implementation-specific constraint is violated or if an attempt is made
         *                                      to add a node as the child of a property and this implementation performs
         *                                      this validation immediately.
         * @throws VersionException             if the node to which the new child is being
         *                                      added is read-only due to a checked-in node and this implementation
         *                                      performs this validation immediately.
         * @throws LockException                if a lock prevents the addition of the node and
         *                                      this implementation performs this validation immediately.
         * @throws UnsupportedRepositoryOperationException
         *                                      if persistent queries are
         *                                      not supported.
         * @throws RepositoryException          if another error occurs or if the
         *                                      <code>absPath</code> provided has an index on its final element.
         */
        public Node storeAsNode(String absPath) throws ItemExistsException, PathNotFoundException, VersionException, ConstraintViolationException, LockException, UnsupportedRepositoryOperationException, RepositoryException;
    
        /**
         * Binds the given <code>value</code> to the variable named
         * <code>varName</code>.
         *
         * @param varName name of variable in query
         * @param value   value to bind
         * @throws IllegalArgumentException      if <code>varName</code> is not a valid
         *                                       variable in this query.
         * @throws javax.jcr.RepositoryException if an error occurs.
         * @since JCR 2.0
         */
        public void bindValue(String varName, Value value) throws IllegalArgumentException, RepositoryException;
    
        /**
         * Returns the names of the bind variables in this query. If this query does
         * not contains any bind variables then an empty array is returned.
         *
         * @return the names of the bind variables in this query.
         * @throws RepositoryException if an error occurs.
         * @since JCR 2.0
         */
        public String[] getBindVariableNames() throws RepositoryException;
    }

    抽象类AbstractQueryImpl只有一个init抽象方法,显然是要求子类实现

    /**
     * Defines common initialisation methods for all query implementations.
     */
    public abstract class AbstractQueryImpl implements Query {
    
        /**
         * Initialises a query instance from a query string.
         *
         * @param sessionContext component context of the current session
         * @param handler   the query handler of the search index.
         * @param statement the query statement.
         * @param language  the syntax of the query statement.
         * @param node      a nt:query node where the query was read from or
         *                  <code>null</code> if it is not a stored query.
         * @throws InvalidQueryException if the query statement is invalid according
         *                               to the specified <code>language</code>.
         */
        public abstract void init(
                SessionContext sessionContext, QueryHandler handler,
                String statement, String language, Node node)
                throws InvalidQueryException;
    }

    QueryImpl类的源码如下:

    /**
     * Provides the default implementation for a JCR query.
     */
    public class QueryImpl extends AbstractQueryImpl {
    
        /**
         * The logger instance for this class
         */
        private static final Logger log = LoggerFactory.getLogger(QueryImpl.class);
    
        /**
         * Component context of the current session
         */
        protected SessionContext sessionContext;
    
        /**
         * The query statement
         */
        protected String statement;
    
        /**
         * The syntax of the query statement
         */
        protected String language;
    
        /**
         * The actual query implementation that can be executed
         */
        protected ExecutableQuery query;
    
        /**
         * The node where this query is persisted. Only set when this is a persisted
         * query.
         */
        protected Node node;
    
        /**
         * The query handler for this query.
         */
        protected QueryHandler handler;
    
        /**
         * Flag indicating whether this query is initialized.
         */
        private boolean initialized = false;
    
        /**
         * The maximum result size
         */
        protected long limit = -1;
    
        /**
         * The offset in the total result set
         */
        protected long offset = 0;
    
        /**
         * @inheritDoc
         */
        public void init(
                SessionContext sessionContext, QueryHandler handler,
                String statement, String language, Node node)
                throws InvalidQueryException {
            checkNotInitialized();
            this.sessionContext = sessionContext;
            this.statement = statement;
            this.language = language;
            this.handler = handler;
            this.node = node;
            this.query = handler.createExecutableQuery(sessionContext, statement, language);
            setInitialized();
        }
    
        /**
         * This method simply forwards the <code>execute</code> call to the
         * {@link ExecutableQuery} object returned by
         * {@link QueryHandler#createExecutableQuery}.
         * {@inheritDoc}
         */
        public QueryResult execute() throws RepositoryException {
            checkInitialized();
            long time = System.currentTimeMillis();
            QueryResult result = sessionContext.getSessionState().perform(
                    new SessionOperation<QueryResult>() {
                        public QueryResult perform(SessionContext context)
                                throws RepositoryException {
                            return query.execute(offset, limit);
                        }
                        public String toString() {
                            return "query.execute(" + statement + ")";
                        }
                    });
            if (log.isDebugEnabled()) {
                time = System.currentTimeMillis() - time;
                NumberFormat format = NumberFormat.getNumberInstance();
                format.setMinimumFractionDigits(2);
                format.setMaximumFractionDigits(2);
                String seconds = format.format((double) time / 1000);
                log.debug("executed in " + seconds + " s. (" + statement + ")");
            }
            return result;
        }
    
        /**
         * {@inheritDoc}
         */
        public String getStatement() {
            checkInitialized();
            return statement;
        }
    
        /**
         * {@inheritDoc}
         */
        public String getLanguage() {
            checkInitialized();
            return language;
        }
    
        /**
         * {@inheritDoc}
         */
        public String getStoredQueryPath()
                throws ItemNotFoundException, RepositoryException {
            checkInitialized();
            if (node == null) {
                throw new ItemNotFoundException("not a persistent query");
            }
            return node.getPath();
        }
    
        /**
         * {@inheritDoc}
         */
        public Node storeAsNode(String absPath)
                throws ItemExistsException,
                PathNotFoundException,
                VersionException,
                ConstraintViolationException,
                LockException,
                UnsupportedRepositoryOperationException,
                RepositoryException {
    
            checkInitialized();
            try {
                Path p = sessionContext.getQPath(absPath).getNormalizedPath();
                if (!p.isAbsolute()) {
                    throw new RepositoryException(absPath + " is not an absolute path");
                }
    
                String relPath = sessionContext.getJCRPath(p).substring(1);
                Node queryNode =
                    sessionContext.getSessionImpl().getRootNode().addNode(
                            relPath, sessionContext.getJCRName(NT_QUERY));
                // set properties
                queryNode.setProperty(sessionContext.getJCRName(JCR_LANGUAGE), language);
                queryNode.setProperty(sessionContext.getJCRName(JCR_STATEMENT), statement);
                node = queryNode;
                return node;
            } catch (NameException e) {
                throw new RepositoryException(e.getMessage(), e);
            }
        }
    
        /**
         * {@inheritDoc}
         */
        public String[] getBindVariableNames() {
            return new String[0];
        }
    
        /**
         * Throws an {@link IllegalArgumentException} as XPath and SQL1 queries
         * have no bind variables.
         *
         * @throws IllegalArgumentException always thrown
         */
        public void bindValue(String varName, Value value)
                throws IllegalArgumentException {
            throw new IllegalArgumentException("No such bind variable: " + varName);
        }
    
        /**
         * Sets the maximum size of the result set.
         *
         * @param limit new maximum size of the result set
         */
        public void setLimit(long limit) {
            if (limit < 0) {
                throw new IllegalArgumentException("limit must not be negativ");
            }
            this.limit = limit;
        }
    
        /**
         * Sets the start offset of the result set.
         *
         * @param offset new start offset of the result set
         */
        public void setOffset(long offset) {
            if (offset < 0) {
                throw new IllegalArgumentException("offset must not be negativ");
            }
            this.offset = offset;
        }
    
        //-----------------------------< internal >---------------------------------
    
        /**
         * Sets the initialized flag.
         */
        protected void setInitialized() {
            initialized = true;
        }
    
        /**
         * Checks if this query is not yet initialized and throws an
         * <code>IllegalStateException</code> if it is already initialized.
         */
        protected void checkNotInitialized() {
            if (initialized) {
                throw new IllegalStateException("already initialized");
            }
        }
    
        /**
         * Checks if this query is initialized and throws an
         * <code>IllegalStateException</code> if it is not yet initialized.
         */
        protected void checkInitialized() {
            if (!initialized) {
                throw new IllegalStateException("not initialized");
            }
        }
    
    }

    先看一下它的初始化方法

     /**
         * @inheritDoc
         */
        public void init(
                SessionContext sessionContext, QueryHandler handler,
                String statement, String language, Node node)
                throws InvalidQueryException {
            checkNotInitialized();
            this.sessionContext = sessionContext;
            this.statement = statement;
            this.language = language;
            this.handler = handler;
            this.node = node;
            this.query = handler.createExecutableQuery(sessionContext, statement, language);
            setInitialized();
        }

    这里的handler还是SearchManager初始化该类时传过来的SearchIndex类型的对象,我们从这里可以看到,ExecutableQuery类型的query成员变量时通过handler(SearchIndex类型)创建的

    /**
         * The actual query implementation that can be executed
         */
        protected ExecutableQuery query;

    QueryImpl类的真正检索方法如下:

    /**
         * This method simply forwards the <code>execute</code> call to the
         * {@link ExecutableQuery} object returned by
         * {@link QueryHandler#createExecutableQuery}.
         * {@inheritDoc}
         */
        public QueryResult execute() throws RepositoryException {
            checkInitialized();
            long time = System.currentTimeMillis();
            QueryResult result = sessionContext.getSessionState().perform(
                    new SessionOperation<QueryResult>() {
                        public QueryResult perform(SessionContext context)
                                throws RepositoryException {
                            return query.execute(offset, limit);
                        }
                        public String toString() {
                            return "query.execute(" + statement + ")";
                        }
                    });
            if (log.isDebugEnabled()) {
                time = System.currentTimeMillis() - time;
                NumberFormat format = NumberFormat.getNumberInstance();
                format.setMinimumFractionDigits(2);
                format.setMaximumFractionDigits(2);
                String seconds = format.format((double) time / 1000);
                log.debug("executed in " + seconds + " s. (" + statement + ")");
            }
            return result;
        }

    我们可以看到是调用ExecutableQuery query成员变量的execute方法

    现在回顾头来查看一下SearchIndex的createExecutableQuery方法

    /**
         * Creates a new query by specifying the query statement itself and the
         * language in which the query is stated.  If the query statement is
         * syntactically invalid, given the language specified, an
         * InvalidQueryException is thrown. <code>language</code> must specify a query language
         * string from among those returned by QueryManager.getSupportedQueryLanguages(); if it is not
         * then an <code>InvalidQueryException</code> is thrown.
         *
         * @param sessionContext component context of the current session
         * @param statement the query statement.
         * @param language the syntax of the query statement.
         * @throws InvalidQueryException if statement is invalid or language is unsupported.
         * @return A <code>Query</code> object.
         */
        public ExecutableQuery createExecutableQuery(
                SessionContext sessionContext, String statement, String language)
                throws InvalidQueryException {
            QueryImpl query = new QueryImpl(
                    sessionContext, this, getContext().getPropertyTypeRegistry(),
                    statement, language, getQueryNodeFactory());
            query.setRespectDocumentOrder(documentOrder);
            return query;
        }

    可以看到,该方法实际返回的是org.apache.jackrabbit.core.query.lucene.QueryImpl类型的对象

    org.apache.jackrabbit.core.query.lucene.QueryImpl类继承自抽象类org.apache.jackrabbit.core.query.lucene.AbstractQueryImpl,而抽象类org.apache.jackrabbit.core.query.lucene.AbstractQueryImpl实现了org.apache.jackrabbit.core.query.ExecutableQuery接口

    (这里面命名与org.apache.jackrabbit.core.query包命名相同,容易使人混淆)

    ExecutableQuery接口的源码如下:

    /**
     * Specifies an interface for a query object implementation that can just be
     * executed.
     * @see QueryImpl
     */
    public interface ExecutableQuery {
    
        /**
         * Executes this query and returns a <code>{@link QueryResult}</code>.
         * @param offset the offset in the total result set
         * @param limit the maximum result size
         *
         * @return a <code>QueryResult</code>
         * @throws RepositoryException if an error occurs
         */
        QueryResult execute(long offset, long limit) throws RepositoryException;
    
    }

    抽象类org.apache.jackrabbit.core.query.lucene.AbstractQueryImpl源码如下:

    /**
     * <code>AbstractQueryImpl</code> provides a base class for executable queries
     * based on {@link SearchIndex}.
     */
    public abstract class AbstractQueryImpl implements ExecutableQuery {
    
        /**
         * Component context of the current session
         */
        protected final SessionContext sessionContext;
    
        /**
         * The actual search index
         */
        protected final SearchIndex index;
    
        /**
         * The property type registry for type lookup.
         */
        protected final PropertyTypeRegistry propReg;
    
        /**
         * If <code>true</code> the default ordering of the result nodes is in
         * document order.
         */
        private boolean documentOrder = true;
    
        protected final PerQueryCache cache = new PerQueryCache();
    
        /**
         * Creates a new query instance from a query string.
         *
         * @param sessionContext component context of the current session
         * @param index   the search index.
         * @param propReg the property type registry.
         */
        public AbstractQueryImpl(
                SessionContext sessionContext, SearchIndex index,
                PropertyTypeRegistry propReg) {
            this.sessionContext = sessionContext;
            this.index = index;
            this.propReg = propReg;
        }
    
        /**
         * If set <code>true</code> the result nodes will be in document order
         * per default (if no order by clause is specified). If set to
         * <code>false</code> the result nodes are returned in whatever sequence
         * the index has stored the nodes. That sequence is stable over multiple
         * invocations of the same query, but will change when nodes get added or
         * removed from the index.
         * <p/>
         * The default value for this property is <code>true</code>.
         * @return the current value of this property.
         */
        public boolean getRespectDocumentOrder() {
            return documentOrder;
        }
    
        /**
         * Sets a new value for this property.
         *
         * @param documentOrder if <code>true</code> the result nodes are in
         * document order per default.
         *
         * @see #getRespectDocumentOrder()
         */
        public void setRespectDocumentOrder(boolean documentOrder) {
            this.documentOrder = documentOrder;
        }
    
        /**
         * @return the query object model factory.
         * @throws RepositoryException if an error occurs.
         */
        protected QueryObjectModelFactory getQOMFactory()
                throws RepositoryException {
            Workspace workspace = sessionContext.getSessionImpl().getWorkspace();
            return workspace.getQueryManager().getQOMFactory();
        }
    
        /**
         * Returns <code>true</code> if this query node needs items under
         * /jcr:system to be queried.
         *
         * @return <code>true</code> if this query node needs content under
         *         /jcr:system to be queried; <code>false</code> otherwise.
         */
        public abstract boolean needsSystemTree();
    }

    org.apache.jackrabbit.core.query.lucene.QueryImpl类的源码如下:

    /**
     * Implements the {@link org.apache.jackrabbit.core.query.ExecutableQuery}
     * interface.
     */
    public class QueryImpl extends AbstractQueryImpl {
    
        /**
         * The logger instance for this class
         */
        private static final Logger log = LoggerFactory.getLogger(QueryImpl.class);
    
        /**
         * The default selector name 's'.
         */
        public static final Name DEFAULT_SELECTOR_NAME = NameFactoryImpl.getInstance().create("", "s");
    
        /**
         * The root node of the query tree
         */
        protected final QueryRootNode root;
    
        /**
         * Creates a new query instance from a query string.
         *
         * @param sessionContext component context of the current session
         * @param index     the search index.
         * @param propReg   the property type registry.
         * @param statement the query statement.
         * @param language  the syntax of the query statement.
         * @param factory   the query node factory.
         * @throws InvalidQueryException if the query statement is invalid according
         *                               to the specified <code>language</code>.
         */
        public QueryImpl(
                SessionContext sessionContext, SearchIndex index,
                PropertyTypeRegistry propReg, String statement, String language,
                QueryNodeFactory factory) throws InvalidQueryException {
            super(sessionContext, index, propReg);
            // parse query according to language
            // build query tree using the passed factory
            this.root = QueryParser.parse(
                    statement, language, sessionContext, factory);
        }
    
        /**
         * Executes this query and returns a <code>{@link QueryResult}</code>.
         *
         * @param offset the offset in the total result set
         * @param limit the maximum result size
         * @return a <code>QueryResult</code>
         * @throws RepositoryException if an error occurs
         */
        public QueryResult execute(long offset, long limit) throws RepositoryException {
            if (log.isDebugEnabled()) {
                log.debug("Executing query: \n" + root.dump());
            }
    
            // build lucene query
            Query query = LuceneQueryBuilder.createQuery(
                    root, sessionContext.getSessionImpl(),
                    index.getContext().getItemStateManager(),
                    index.getNamespaceMappings(), index.getTextAnalyzer(),
                    propReg, index.getSynonymProvider(),
                    index.getIndexFormatVersion(),
                    cache);
    
            OrderQueryNode orderNode = root.getOrderNode();
    
            OrderQueryNode.OrderSpec[] orderSpecs;
            if (orderNode != null) {
                orderSpecs = orderNode.getOrderSpecs();
            } else {
                orderSpecs = new OrderQueryNode.OrderSpec[0];
            }
            Path[] orderProperties = new Path[orderSpecs.length];
            boolean[] ascSpecs = new boolean[orderSpecs.length];
            for (int i = 0; i < orderSpecs.length; i++) {
                orderProperties[i] = orderSpecs[i].getPropertyPath();
                ascSpecs[i] = orderSpecs[i].isAscending();
            }
    
            return new SingleColumnQueryResult(
                    index, sessionContext, this, query,
                    new SpellSuggestion(index.getSpellChecker(), root),
                    getColumns(), orderProperties, ascSpecs,
                    orderProperties.length == 0 && getRespectDocumentOrder(),
                    offset, limit);
        }
    
        /**
         * Returns the columns for this query.
         *
         * @return array of columns.
         * @throws RepositoryException if an error occurs.
         */
        protected ColumnImpl[] getColumns() throws RepositoryException {
            SessionImpl session = sessionContext.getSessionImpl();
            QueryObjectModelFactory qomFactory =
                session.getWorkspace().getQueryManager().getQOMFactory();
            // get columns
            Map<Name, ColumnImpl> columns = new LinkedHashMap<Name, ColumnImpl>();
            for (Name name : root.getSelectProperties()) {
                String pn = sessionContext.getJCRName(name);
                ColumnImpl col = (ColumnImpl) qomFactory.column(
                        sessionContext.getJCRName(DEFAULT_SELECTOR_NAME), pn, pn);
                columns.put(name, col);
            }
            if (columns.size() == 0) {
                // use node type constraint
                LocationStepQueryNode[] steps = root.getLocationNode().getPathSteps();
                final Name[] ntName = new Name[1];
                steps[steps.length - 1].acceptOperands(new DefaultQueryNodeVisitor() {
    
                    public Object visit(AndQueryNode node, Object data) throws RepositoryException {
                        return node.acceptOperands(this, data);
                    }
    
                    public Object visit(NodeTypeQueryNode node, Object data) {
                        ntName[0] = node.getValue();
                        return data;
                    }
                }, null);
                if (ntName[0] == null) {
                    ntName[0] = NameConstants.NT_BASE;
                }
                NodeTypeImpl nt = session.getNodeTypeManager().getNodeType(ntName[0]);
                PropertyDefinition[] propDefs = nt.getPropertyDefinitions();
                for (PropertyDefinition pd : propDefs) {
                    QPropertyDefinition propDef = ((PropertyDefinitionImpl) pd).unwrap();
                    if (!propDef.definesResidual() && !propDef.isMultiple()) {
                        columns.put(propDef.getName(), columnForName(propDef.getName()));
                    }
                }
            }
    
            // add jcr:path and jcr:score if not selected already
            if (!columns.containsKey(NameConstants.JCR_PATH)) {
                columns.put(NameConstants.JCR_PATH, columnForName(NameConstants.JCR_PATH));
            }
            if (!columns.containsKey(NameConstants.JCR_SCORE)) {
                columns.put(NameConstants.JCR_SCORE, columnForName(NameConstants.JCR_SCORE));
            }
    
            return columns.values().toArray(new ColumnImpl[columns.size()]);
        }
    
        /**
         * Returns <code>true</code> if this query node needs items under
         * /jcr:system to be queried.
         *
         * @return <code>true</code> if this query node needs content under
         *         /jcr:system to be queried; <code>false</code> otherwise.
         */
        public boolean needsSystemTree() {
            return this.root.needsSystemTree();
        }
    
        /**
         * Returns a column for the given property name and the default selector
         * name.
         *
         * @param propertyName the name of the property as well as the column.
         * @return a column.
         * @throws RepositoryException if an error occurs while creating the column.
         */
        protected ColumnImpl columnForName(Name propertyName) throws RepositoryException {
            Workspace workspace = sessionContext.getSessionImpl().getWorkspace();
            QueryObjectModelFactory qomFactory =
                workspace.getQueryManager().getQOMFactory();
            String name = sessionContext.getJCRName(propertyName);
            return (ColumnImpl) qomFactory.column(
                    sessionContext.getJCRName(DEFAULT_SELECTOR_NAME), name, name);
        }
    }

    最重要的查询方法是

    /**
         * Executes this query and returns a <code>{@link QueryResult}</code>.
         *
         * @param offset the offset in the total result set
         * @param limit the maximum result size
         * @return a <code>QueryResult</code>
         * @throws RepositoryException if an error occurs
         */
        public QueryResult execute(long offset, long limit) throws RepositoryException {
            if (log.isDebugEnabled()) {
                log.debug("Executing query: \n" + root.dump());
            }
    
            // build lucene query
            Query query = LuceneQueryBuilder.createQuery(
                    root, sessionContext.getSessionImpl(),
                    index.getContext().getItemStateManager(),
                    index.getNamespaceMappings(), index.getTextAnalyzer(),
                    propReg, index.getSynonymProvider(),
                    index.getIndexFormatVersion(),
                    cache);
    
            OrderQueryNode orderNode = root.getOrderNode();
    
            OrderQueryNode.OrderSpec[] orderSpecs;
            if (orderNode != null) {
                orderSpecs = orderNode.getOrderSpecs();
            } else {
                orderSpecs = new OrderQueryNode.OrderSpec[0];
            }
            Path[] orderProperties = new Path[orderSpecs.length];
            boolean[] ascSpecs = new boolean[orderSpecs.length];
            for (int i = 0; i < orderSpecs.length; i++) {
                orderProperties[i] = orderSpecs[i].getPropertyPath();
                ascSpecs[i] = orderSpecs[i].isAscending();
            }
    
            return new SingleColumnQueryResult(
                    index, sessionContext, this, query,
                    new SpellSuggestion(index.getSpellChecker(), root),
                    getColumns(), orderProperties, ascSpecs,
                    orderProperties.length == 0 && getRespectDocumentOrder(),
                    offset, limit);
        }

    ---------------------------------------------------------------------------

    本系列Apache Jackrabbit源码研究系本人原创

    转载请注明出处 博客园 刺猬的温驯

    本文链接 http://www.cnblogs.com/chenying99/archive/2013/04/07/3003304.html

  • 相关阅读:
    Silverlight学习(五)图形标绘
    Silverlight学习(四) domainservice动态多条件查询
    MySQL之单表查询
    mysql外键的三种关系
    mysql之完整性约束
    接口类和抽象类的区别
    mysql中的sql_mode
    html5本地存储技术 localstorage
    mysql数值类型
    mysql
  • 原文地址:https://www.cnblogs.com/chenying99/p/3003304.html
Copyright © 2011-2022 走看看