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

你可能感兴趣的:(apache)