/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.exoplatform.services.jcr.impl.core.query.lucene;
import org.apache.lucene.search.Query;
import org.exoplatform.services.jcr.core.nodetype.NodeTypeData;
import org.exoplatform.services.jcr.core.nodetype.PropertyDefinitionData;
import org.exoplatform.services.jcr.datamodel.InternalQName;
import org.exoplatform.services.jcr.datamodel.QPath;
import org.exoplatform.services.jcr.impl.Constants;
import org.exoplatform.services.jcr.impl.core.SessionDataManager;
import org.exoplatform.services.jcr.impl.core.SessionImpl;
import org.exoplatform.services.jcr.impl.core.query.AndQueryNode;
import org.exoplatform.services.jcr.impl.core.query.DefaultQueryNodeVisitor;
import org.exoplatform.services.jcr.impl.core.query.LocationStepQueryNode;
import org.exoplatform.services.jcr.impl.core.query.NodeTypeQueryNode;
import org.exoplatform.services.jcr.impl.core.query.OrderQueryNode;
import org.exoplatform.services.jcr.impl.core.query.PropertyTypeRegistry;
import org.exoplatform.services.jcr.impl.core.query.QueryNodeFactory;
import org.exoplatform.services.jcr.impl.core.query.QueryParser;
import org.exoplatform.services.jcr.impl.core.query.QueryRootNode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import javax.jcr.RepositoryException;
import javax.jcr.query.InvalidQueryException;
import javax.jcr.query.QueryResult;
/**
* Implements the {@link org.exoplatform.services.jcr.impl.core.query.ExecutableQuery}
* interface.
*/
public class QueryImpl extends AbstractQueryImpl
{
/**
* The logger instance for this class
*/
private static final Logger log = LoggerFactory.getLogger("exo.jcr.component.core.QueryImpl");
/**
* The default selector name 's'.
*/
public static final InternalQName DEFAULT_SELECTOR_NAME = new InternalQName(Constants.NS_DEFAULT_URI, "s");
/**
* The root node of the query tree
*/
protected final QueryRootNode root;
/**
* Creates a new query instance from a query string.
*
* @param session
* the session of the user executing this query.
* @param itemMgr
* the item manager of the session executing this query.
* @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(SessionImpl session, SessionDataManager itemMgr, SearchIndex index, PropertyTypeRegistry propReg,
String statement, String language, QueryNodeFactory factory) throws InvalidQueryException
{
super(session, itemMgr, index, propReg);
// parse query according to language
// build query tree using the passed factory
// this.root = QueryParser.parse(statement, language, session, factory);
this.root = QueryParser.parse(statement, language, session.getLocationFactory(), factory);
}
/**
* {@inheritDoc}
*/
public QueryResult execute(long offset, long limit, boolean caseInsensitiveOrder) throws RepositoryException
{
if (log.isDebugEnabled())
{
log.debug("Executing query: \n" + root.dump());
}
setCaseInsensitiveOrder(caseInsensitiveOrder);
// build lucene query
Query query =
LuceneQueryBuilder.createQuery(root, session, index.getContext().getItemStateManager(),
index.getNamespaceMappings(), index.getTextAnalyzer(), propReg, index.getSynonymProvider(),
index.getIndexFormatVersion(), index.getContext().getVirtualTableResolver(), index.getIndexingConfig());
OrderQueryNode orderNode = root.getOrderNode();
OrderQueryNode.OrderSpec[] orderSpecs;
if (orderNode != null)
{
orderSpecs = orderNode.getOrderSpecs();
}
else
{
orderSpecs = new OrderQueryNode.OrderSpec[0];
}
QPath[] orderProperties = new QPath[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, itemMgr, session, session.getAccessManager(), this, query,
new SpellSuggestion(index.getSpellChecker(), root), getSelectProperties(), orderProperties, ascSpecs,
orderProperties.length == 0 && getRespectDocumentOrder(), offset, limit);
}
/**
* Returns the select properties for this query.
*
* @return array of select property names.
* @throws RepositoryException
* if an error occurs.
*/
protected InternalQName[] getSelectProperties() throws RepositoryException
{
// get select properties
List<InternalQName> selectProps = new ArrayList<InternalQName>();
selectProps.addAll(Arrays.asList(root.getSelectProperties()));
if (selectProps.size() == 0)
{
// use node type constraint
LocationStepQueryNode[] steps = root.getLocationNode().getPathSteps();
final InternalQName[] ntName = new InternalQName[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] = Constants.NT_BASE;
}
NodeTypeData nt = session.getWorkspace().getNodeTypesHolder().getNodeType(ntName[0]);
PropertyDefinitionData[] propDefs = nt.getDeclaredPropertyDefinitions();
for (int i = 0; i < propDefs.length; i++)
{
PropertyDefinitionData propDef = propDefs[i];
if (!propDef.isResidualSet() && !propDef.isMultiple())
{
selectProps.add(propDef.getName());
}
}
}
// add jcr:path and jcr:score if not selected already
if (!selectProps.contains(Constants.JCR_PATH))
{
selectProps.add(Constants.JCR_PATH);
}
if (!selectProps.contains(Constants.JCR_SCORE))
{
selectProps.add(Constants.JCR_SCORE);
}
return (InternalQName[])selectProps.toArray(new InternalQName[selectProps.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();
}
}