您的位置:首页 > 运维架构 > Apache

Apache Jackrabbit源码研究(五)

2013-04-07 03:29 405 查看
上文最后提到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
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: