You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by mr...@apache.org on 2005/03/10 10:57:13 UTC
svn commit: r156923 - in incubator/jackrabbit/trunk/src:
java/org/apache/jackrabbit/core/search/
java/org/apache/jackrabbit/core/search/lucene/
java/org/apache/jackrabbit/core/search/sql/
java/org/apache/jackrabbit/core/search/xpath/
test/org/apache/jackrabbit/core/search/
test/org/apache/jackrabbit/test/api/query/
Author: mreutegg
Date: Thu Mar 10 01:57:09 2005
New Revision: 156923
URL: http://svn.apache.org/viewcvs?view=rev&rev=156923
Log:
Implement location step with context position index.
Modified:
incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/AbstractQueryHandler.java
incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/LocationStepQueryNode.java
incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/QueryConstants.java
incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/RelationQueryNode.java
incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/ChildAxisQuery.java
incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/LuceneQueryBuilder.java
incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/QueryImpl.java
incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/sql/JCRSQLQueryBuilder.java
incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/sql/QueryFormat.java
incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/xpath/QueryFormat.java
incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/xpath/XPathQueryBuilder.java
incubator/jackrabbit/trunk/src/test/org/apache/jackrabbit/core/search/SelectClauseTest.java
incubator/jackrabbit/trunk/src/test/org/apache/jackrabbit/core/search/XPathAxisTest.java
incubator/jackrabbit/trunk/src/test/org/apache/jackrabbit/test/api/query/XPathDocOrderTest.java
Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/AbstractQueryHandler.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/AbstractQueryHandler.java?view=diff&r1=156922&r2=156923
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/AbstractQueryHandler.java (original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/AbstractQueryHandler.java Thu Mar 10 01:57:09 2005
@@ -81,7 +81,7 @@
* @return the persistent <code>ItemStateProvider</code> of the current
* workspace.
*/
- protected ItemStateManager getItemStateProvider() {
+ public ItemStateManager getItemStateProvider() {
return stateProvider;
}
Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/LocationStepQueryNode.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/LocationStepQueryNode.java?view=diff&r1=156922&r2=156923
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/LocationStepQueryNode.java (original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/LocationStepQueryNode.java Thu Mar 10 01:57:09 2005
@@ -30,6 +30,12 @@
*/
public class LocationStepQueryNode extends NAryQueryNode {
+ /** Constant value for position index = last() */
+ public static final int LAST = Integer.MIN_VALUE;
+
+ /** Constant value to indicate no position index */
+ public static final int NONE = Integer.MIN_VALUE + 1;
+
/** Empty <code>QueryNode</code> array for us as return value */
private static final QueryNode[] EMPTY = new QueryNode[0];
@@ -46,10 +52,9 @@
private boolean includeDescendants;
/**
- * If <code>index</code> is larger than 0 this location step contains
- * a position index.
+ * The context position <code>index</code>. Initially {@link #NONE}.
*/
- private int index = 0;
+ private int index = NONE;
/**
* Creates a new <code>LocationStepQueryNode</code> with a reference to
@@ -111,9 +116,8 @@
}
/**
- * Returns the predicate nodes for this location step. This method will
- * not return a position predicate. Use {@link #getIndex} to retrieve
- * this information.
+ * Returns the predicate nodes for this location step. This method may
+ * also return a position predicate.
* @return the predicate nodes or an empty array if there are no predicates
* for this location step.
*/
@@ -126,21 +130,17 @@
}
/**
- * Sets the position index for this step. A value of 0 (zero) indicates
+ * Sets the position index for this step. A value of {@link #NONE} indicates
* that this location step has no position index assigned. That is, the
* step selects all same name siblings.
* @param index the position index.
- * @exception IllegalArgumentException if index < 0.
*/
public void setIndex(int index) {
- if (index < 0) {
- throw new IllegalArgumentException("index < 0");
- }
this.index = index;
}
/**
- * Returns the position index for this step. A value of 0 (zero) indicates
+ * Returns the position index for this step. A value of {@link #NONE} indicates
* that this location step has no position index assigned. That is, the
* step selects all same name siblings.
* @return the position index for this step.
Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/QueryConstants.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/QueryConstants.java?view=diff&r1=156922&r2=156923
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/QueryConstants.java (original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/QueryConstants.java Thu Mar 10 01:57:09 2005
@@ -70,6 +70,11 @@
*/
public static int TYPE_TIMESTAMP = 5;
+ /**
+ * position index type
+ */
+ public static int TYPE_POSITION = 6;
+
public static int OPERATIONS = 10;
/**
Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/RelationQueryNode.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/RelationQueryNode.java?view=diff&r1=156922&r2=156923
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/RelationQueryNode.java (original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/RelationQueryNode.java Thu Mar 10 01:57:09 2005
@@ -37,6 +37,11 @@
private long valueLong;
/**
+ * The <code>int</code> value of the position index.
+ */
+ private int valuePosition;
+
+ /**
* The <code>double</code> value of the relation if this is a query is of
* type <code>double</code>
*/
@@ -62,7 +67,7 @@
/**
* The value type of this relation. One of {@link #TYPE_DATE}, {@link
- * #TYPE_DOUBLE}, {@link #TYPE_LONG}, {@link #TYPE_STRING}.
+ * #TYPE_DOUBLE}, {@link #TYPE_LONG}, {@link #TYPE_STRING}, {@link #TYPE_POSITION}.
*/
private int type;
@@ -206,6 +211,25 @@
public void setLongValue(long value) {
valueLong = value;
type = TYPE_LONG;
+ }
+
+ /**
+ * Returns the <code>int</code> position index value if this relation is
+ * of type {@link #TYPE_POSITION}.
+ * @return the position index value.
+ */
+ public int getPositionValue() {
+ return valuePosition;
+ }
+
+ /**
+ * Sets a new value for the position index.
+ *
+ * @param value the new value.
+ */
+ public void setPositionValue(int value) {
+ valuePosition = value;
+ type = TYPE_POSITION;
}
/**
Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/ChildAxisQuery.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/ChildAxisQuery.java?view=diff&r1=156922&r2=156923
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/ChildAxisQuery.java (original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/ChildAxisQuery.java Thu Mar 10 01:57:09 2005
@@ -26,6 +26,14 @@
import org.apache.lucene.search.Searcher;
import org.apache.lucene.search.Similarity;
import org.apache.lucene.search.Weight;
+import org.apache.lucene.search.TermQuery;
+import org.apache.lucene.document.Document;
+import org.apache.jackrabbit.core.state.ItemStateManager;
+import org.apache.jackrabbit.core.state.NodeState;
+import org.apache.jackrabbit.core.state.ItemStateException;
+import org.apache.jackrabbit.core.NodeId;
+import org.apache.jackrabbit.core.QName;
+import org.apache.jackrabbit.core.search.LocationStepQueryNode;
import java.io.IOException;
import java.util.ArrayList;
@@ -40,23 +48,67 @@
class ChildAxisQuery extends Query {
/**
+ * The item state manager containing persistent item states.
+ */
+ private final ItemStateManager itemMgr;
+
+ /**
* The context query
*/
private final Query contextQuery;
/**
+ * The nameTest to apply on the child axis, or <code>null</code> if all
+ * child nodes should be selected.
+ */
+ private final String nameTest;
+
+ /**
+ * The context position for the selected child node, or
+ * {@link LocationStepQueryNode#NONE} if no position is specified.
+ */
+ private final int position;
+
+ /**
* The scorer of the context query
*/
private Scorer contextScorer;
/**
+ * The scorer of the name test query
+ */
+ private Scorer nameTestScorer;
+
+ /**
+ * Creates a new <code>ChildAxisQuery</code> based on a <code>context</code>
+ * query.
+ *
+ * @param itemMgr the item state manager.
+ * @param context the context for this query.
+ * @param nameTest a name test or <code>null</code> if any child node is
+ * selected.
+ */
+ ChildAxisQuery(ItemStateManager itemMgr, Query context, String nameTest) {
+ this(itemMgr, context, nameTest, LocationStepQueryNode.NONE);
+ }
+
+ /**
* Creates a new <code>ChildAxisQuery</code> based on a <code>context</code>
* query.
*
+ * @param itemMgr the item state manager.
* @param context the context for this query.
+ * @param nameTest a name test or <code>null</code> if any child node is
+ * selected.
+ * @param position the context position of the child node to select. If
+ * <code>position</code> is {@link LocationStepQueryNode#NONE}, the context
+ * position of the child node is not checked.
*/
- ChildAxisQuery(Query context) {
+ ChildAxisQuery(ItemStateManager itemMgr, Query context, String nameTest, int position) {
+ this.itemMgr = itemMgr;
this.contextQuery = context;
+ this.nameTest = nameTest;
+ this.position = position;
}
/**
@@ -142,6 +194,9 @@
*/
public Scorer scorer(IndexReader reader) throws IOException {
contextScorer = contextQuery.weight(searcher).scorer(reader);
+ if (nameTest != null) {
+ nameTestScorer = new TermQuery(new Term(FieldNames.LABEL, nameTest)).weight(searcher).scorer(reader);
+ }
return new ChildAxisScorer(searcher.getSimilarity(), reader);
}
@@ -256,17 +311,102 @@
// @todo maintain cache of doc id hierarchy
hits.set(doc);
}
- }); // find all
+ });
+
+ // collect nameTest hits
+ final BitSet nameTestHits = new BitSet();
+ if (nameTestScorer != null) {
+ nameTestScorer.score(new HitCollector() {
+ public void collect(int doc, float score) {
+ nameTestHits.set(doc);
+ }
+ });
+ }
+
+ // read the uuids of the context nodes
for (int i = hits.nextSetBit(0); i >= 0; i = hits.nextSetBit(i + 1)) {
String uuid = reader.document(i).get(FieldNames.UUID);
uuids.add(uuid);
}
+ // collect the doc ids of all child nodes. we reuse the existing
+ // bitset.
hits.clear();
for (Iterator it = uuids.iterator(); it.hasNext();) {
TermDocs children = reader.termDocs(new Term(FieldNames.PARENT, (String) it.next()));
while (children.next()) {
hits.set(children.doc());
+ }
+ }
+ // filter out the child nodes that do not match the name test
+ // if there is any name test at all.
+ if (nameTestScorer != null) {
+ hits.and(nameTestHits);
+ }
+
+ // filter by index
+ if (position != LocationStepQueryNode.NONE) {
+ for (int i = hits.nextSetBit(0); i >= 0; i = hits.nextSetBit(i + 1)) {
+ Document node = reader.document(i);
+ String parentUUID = node.get(FieldNames.PARENT);
+ String uuid = node.get(FieldNames.UUID);
+ try {
+ NodeState state = (NodeState) itemMgr.getItemState(new NodeId(parentUUID));
+ if (nameTest == null) {
+ // only select this node if it is the child at
+ // specified position
+ if (position == LocationStepQueryNode.LAST) {
+ // only select last
+ List childNodes = state.getChildNodeEntries();
+ if (childNodes.size() == 0
+ || !((NodeState.ChildNodeEntry) childNodes.get(childNodes.size() - 1)).getUUID().equals(uuid)) {
+ hits.flip(i);
+ }
+ } else {
+ List childNodes = state.getChildNodeEntries();
+ if (position < 1
+ || childNodes.size() < position
+ || !((NodeState.ChildNodeEntry) childNodes.get(position - 1)).getUUID().equals(uuid)) {
+ hits.flip(i);
+ }
+ }
+ } else {
+ // select the node when its index is equal to
+ // specified position
+ if (position == LocationStepQueryNode.LAST) {
+ // only select last
+ List childNodes = state.getChildNodeEntries(uuid);
+ if (childNodes.size() == 0) {
+ // no such child node, probably deleted meanwhile
+ hits.flip(i);
+ } else {
+ // only use the last one
+ QName name = ((NodeState.ChildNodeEntry) childNodes.get(0)).getName();
+ childNodes = state.getChildNodeEntries(name);
+ if (childNodes.size() == 0
+ || !((NodeState.ChildNodeEntry) childNodes.get(childNodes.size() - 1)).getUUID().equals(uuid)) {
+ hits.flip(i);
+ }
+ }
+ } else {
+ List childNodes = state.getChildNodeEntries(uuid);
+ if (childNodes.size() == 0) {
+ // no such child node, probably has been deleted meanwhile
+ hits.flip(i);
+ } else {
+ for (int j = 0; j < childNodes.size(); j++) {
+ NodeState.ChildNodeEntry entry = (NodeState.ChildNodeEntry) childNodes.get(j);
+ if (entry.getIndex() != position) {
+ hits.flip(i);
+ }
+ }
+ }
+ }
+ }
+ } catch (ItemStateException e) {
+ // ignore this node, probably has been deleted meanwhile
+ hits.flip(i);
+ }
}
}
}
Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/LuceneQueryBuilder.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/LuceneQueryBuilder.java?view=diff&r1=156922&r2=156923
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/LuceneQueryBuilder.java (original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/LuceneQueryBuilder.java Thu Mar 10 01:57:09 2005
@@ -24,6 +24,7 @@
import org.apache.jackrabbit.core.QName;
import org.apache.jackrabbit.core.SessionImpl;
import org.apache.jackrabbit.core.UnknownPrefixException;
+import org.apache.jackrabbit.core.state.ItemStateManager;
import org.apache.jackrabbit.core.search.AndQueryNode;
import org.apache.jackrabbit.core.search.ExactQueryNode;
import org.apache.jackrabbit.core.search.LocationStepQueryNode;
@@ -38,6 +39,7 @@
import org.apache.jackrabbit.core.search.QueryRootNode;
import org.apache.jackrabbit.core.search.RelationQueryNode;
import org.apache.jackrabbit.core.search.TextsearchQueryNode;
+import org.apache.jackrabbit.core.search.QueryNode;
import org.apache.log4j.Logger;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.index.Term;
@@ -90,6 +92,11 @@
private SessionImpl session;
/**
+ * The shared item state manager of the workspace.
+ */
+ private ItemStateManager sharedItemMgr;
+
+ /**
* Namespace mappings to internal prefixes
*/
private NamespaceMappings nsMappings;
@@ -114,17 +121,20 @@
*
* @param root the root node of the abstract query tree.
* @param session of the user executing this query.
+ * @param sharedItemMgr the shared item state manager of the workspace.
* @param nsMappings namespace resolver for internal prefixes.
* @param analyzer for parsing the query statement of the contains function.
* @param propReg the property type registry.
*/
private LuceneQueryBuilder(QueryRootNode root,
SessionImpl session,
+ ItemStateManager sharedItemMgr,
NamespaceMappings nsMappings,
Analyzer analyzer,
PropertyTypeRegistry propReg) {
this.root = root;
this.session = session;
+ this.sharedItemMgr = sharedItemMgr;
this.nsMappings = nsMappings;
this.analyzer = analyzer;
this.propRegistry = propReg;
@@ -136,6 +146,7 @@
*
* @param root the root node of the abstract query tree.
* @param session of the user executing the query.
+ * @param sharedItemMgr the shared item state manager of the workspace.
* @param nsMappings namespace resolver for internal prefixes.
* @param analyzer for parsing the query statement of the contains function.
* @param propReg the property type registry to lookup type information.
@@ -144,13 +155,14 @@
*/
public static Query createQuery(QueryRootNode root,
SessionImpl session,
+ ItemStateManager sharedItemMgr,
NamespaceMappings nsMappings,
Analyzer analyzer,
PropertyTypeRegistry propReg)
throws RepositoryException {
- LuceneQueryBuilder builder = new LuceneQueryBuilder(root,
- session, nsMappings, analyzer, propReg);
+ LuceneQueryBuilder builder = new LuceneQueryBuilder(root, session,
+ sharedItemMgr, nsMappings, analyzer, propReg);
Query q = builder.createLuceneQuery();
if (builder.exceptions.size() > 0) {
@@ -385,6 +397,17 @@
andQuery.add((Query) predicates[i], true, false);
}
+ // check for position predicate
+ QueryNode[] pred = node.getPredicates();
+ for (int i = 0; i < pred.length; i++) {
+ if (pred[i].getType() == QueryNode.TYPE_RELATION) {
+ RelationQueryNode pos = (RelationQueryNode) pred[i];
+ if (pos.getValueType() == QueryConstants.TYPE_POSITION) {
+ node.setIndex(pos.getPositionValue());
+ }
+ }
+ }
+
TermQuery nameTest = null;
if (node.getNameTest() != null) {
try {
@@ -408,19 +431,19 @@
andQuery = new BooleanQuery();
andQuery.add(subQuery, true, false);
} else {
- // @todo this will traverse the whole index, optimize!
+ // todo this will traverse the whole index, optimize!
Query subQuery = new MatchAllQuery(FieldNames.UUID);
context = new DescendantSelfAxisQuery(context, subQuery);
- andQuery.add(new ChildAxisQuery(context), true, false);
+ andQuery.add(new ChildAxisQuery(sharedItemMgr, context, null, node.getIndex()), true, false);
}
}
} else {
- // select child nodes
- andQuery.add(new ChildAxisQuery(context), true, false);
-
// name test
if (nameTest != null) {
- andQuery.add(nameTest, true, false);
+ andQuery.add(new ChildAxisQuery(sharedItemMgr, context, nameTest.getTerm().text(), node.getIndex()), true, false);
+ } else {
+ // select child nodes
+ andQuery.add(new ChildAxisQuery(sharedItemMgr, context, null, node.getIndex()), true, false);
}
}
@@ -452,6 +475,9 @@
stringValues = getStringValues(node.getProperty(), node.getStringValue());
}
break;
+ case QueryConstants.TYPE_POSITION:
+ // ignore position. is handled in the location step
+ return null;
default:
throw new IllegalArgumentException("Unknown relation type: "
+ node.getValueType());
Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/QueryImpl.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/QueryImpl.java?view=diff&r1=156922&r2=156923
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/QueryImpl.java (original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/lucene/QueryImpl.java Thu Mar 10 01:57:09 2005
@@ -115,8 +115,9 @@
*/
public QueryResult execute() throws RepositoryException {
// build lucene query
- Query query = LuceneQueryBuilder.createQuery(root,
- session, index.getNamespaceMappings(), index.getAnalyzer(), propReg);
+ Query query = LuceneQueryBuilder.createQuery(root, session,
+ index.getItemStateProvider(), index.getNamespaceMappings(),
+ index.getAnalyzer(), propReg);
OrderQueryNode orderNode = root.getOrderNode();
Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/sql/JCRSQLQueryBuilder.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/sql/JCRSQLQueryBuilder.java?view=diff&r1=156922&r2=156923
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/sql/JCRSQLQueryBuilder.java (original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/sql/JCRSQLQueryBuilder.java Thu Mar 10 01:57:09 2005
@@ -501,7 +501,7 @@
} else {
int idx = names[i].indexOf('[');
String name = null;
- int index = 0;
+ int index = LocationStepQueryNode.NONE;
if (idx > -1) {
// contains index
name = names[i].substring(0, idx);
@@ -509,7 +509,7 @@
String indexStr = suffix.substring(1, suffix.length() - 1);
if (indexStr.equals("%")) {
// select all same name siblings
- index = 0;
+ index = LocationStepQueryNode.NONE;
} else {
try {
index = Integer.parseInt(indexStr);
@@ -517,14 +517,19 @@
log.warn("Unable to parse index for path element: " + names[i]);
}
}
+ if (name.equals("%")) {
+ name = null;
+ }
} else {
- // no index
+ // no index specified
+ // - index defaults to 1 if there is an explicit name test
+ // - index defaults to NONE if name test is %
name = names[i];
- // in SQL this means index 1
- index = 1;
- }
- if (name.equals("%")) {
- name = null;
+ if (name.equals("%")) {
+ name = null;
+ } else {
+ index = 1;
+ }
}
QName qName = null;
if (name != null) {
Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/sql/QueryFormat.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/sql/QueryFormat.java?view=diff&r1=156922&r2=156923
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/sql/QueryFormat.java (original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/sql/QueryFormat.java Thu Mar 10 01:57:09 2005
@@ -338,7 +338,7 @@
} catch (NoPrefixDeclaredException e) {
exceptions.add(e);
}
- if (node.getIndex() == 0) {
+ if (node.getIndex() == LocationStepQueryNode.NONE) {
sb.append("[%]");
} else if (node.getIndex() == 1) {
// do nothing
Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/xpath/QueryFormat.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/xpath/QueryFormat.java?view=diff&r1=156922&r2=156923
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/xpath/QueryFormat.java (original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/xpath/QueryFormat.java Thu Mar 10 01:57:09 2005
@@ -252,7 +252,7 @@
exceptions.add(e);
}
}
- if (node.getIndex() > 0) {
+ if (node.getIndex() != LocationStepQueryNode.NONE) {
sb.append('[').append(node.getIndex()).append(']');
}
QueryNode[] predicates = node.getPredicates();
@@ -268,7 +268,13 @@
StringBuffer sb = (StringBuffer) data;
try {
- String propName = "@" + ISO9075.encode(node.getProperty()).toJCRName(resolver);
+ String propName = "@";
+ // only encode if not position function
+ if (node.getProperty().equals(XPathQueryBuilder.FN_POSITION_FULL)) {
+ propName += node.getProperty().toJCRName(resolver);
+ } else {
+ propName += ISO9075.encode(node.getProperty()).toJCRName(resolver);
+ }
if (node.getOperation() == OPERATION_EQ_VALUE) {
sb.append(propName).append(" eq ");
@@ -358,6 +364,12 @@
cal.setTime(node.getDateValue());
b.append(XPathQueryBuilder.XS_DATETIME.toJCRName(resolver));
b.append("('").append(ISO8601.format(cal)).append("')");
+ } else if (node.getValueType() == TYPE_POSITION) {
+ if (node.getPositionValue() == LocationStepQueryNode.LAST) {
+ b.append("last()");
+ } else {
+ b.append(node.getPositionValue());
+ }
} else {
exceptions.add(new InvalidQueryException("Invalid type: " + node.getValueType()));
}
Modified: incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/xpath/XPathQueryBuilder.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/xpath/XPathQueryBuilder.java?view=diff&r1=156922&r2=156923
==============================================================================
--- incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/xpath/XPathQueryBuilder.java (original)
+++ incubator/jackrabbit/trunk/src/java/org/apache/jackrabbit/core/search/xpath/XPathQueryBuilder.java Thu Mar 10 01:57:09 2005
@@ -36,6 +36,7 @@
import org.apache.jackrabbit.core.search.QueryRootNode;
import org.apache.jackrabbit.core.search.RelationQueryNode;
import org.apache.jackrabbit.core.search.TextsearchQueryNode;
+import org.apache.jackrabbit.core.search.QueryConstants;
import org.apache.jackrabbit.core.util.ISO9075;
import javax.jcr.query.InvalidQueryException;
@@ -61,6 +62,36 @@
static final QName FN_NOT_10 = new QName("", "not");
/**
+ * QName for true function.
+ */
+ static final QName FN_TRUE = new QName("", "true");
+
+ /**
+ * QName for false function.
+ */
+ static final QName FN_FALSE = new QName("", "false");
+
+ /**
+ * QName for position function.
+ */
+ static final QName FN_POSITION = new QName("", "position");
+
+ /**
+ * QName for the full position function including bracket
+ */
+ static final QName FN_POSITION_FULL = new QName("", "position()");
+
+ /**
+ * QName for last function.
+ */
+ static final QName FN_LAST = new QName("", "last");
+
+ /**
+ * QName for first function.
+ */
+ static final QName FN_FIRST = new QName("", "first");
+
+ /**
* QName for xs:dateTime
*/
static final QName XS_DATETIME = new QName(SearchManager.NS_XS_URI, "dateTime");
@@ -71,6 +102,11 @@
static final QName JCRFN_LIKE = new QName(SearchManager.NS_JCRFN_URI, "like");
/**
+ * QName for jcrfn:deref
+ */
+ static final QName JCRFN_DEREF = new QName(SearchManager.NS_JCRFN_URI, "deref");
+
+ /**
* QName for jcrfn:contains
*/
static final QName JCRFN_CONTAINS = new QName(SearchManager.NS_JCRFN_URI, "contains");
@@ -518,7 +554,13 @@
} else if (node.getId() == JJTDOUBLELITERAL) {
queryNode.setDoubleValue(Double.parseDouble(node.getValue()));
} else if (node.getId() == JJTINTEGERLITERAL) {
- queryNode.setLongValue(Long.parseLong(node.getValue()));
+ // if this is an expression that contains position() do not change
+ // the type.
+ if (queryNode.getValueType() == QueryConstants.TYPE_POSITION) {
+ queryNode.setPositionValue(Integer.parseInt(node.getValue()));
+ } else {
+ queryNode.setLongValue(Long.parseLong(node.getValue()));
+ }
} else {
exceptions.add(new InvalidQueryException("Unsupported literal type:" + node.toString()));
}
@@ -634,6 +676,52 @@
// wrong number of arguments
exceptions.add(new InvalidQueryException("Wrong number of arguments for jcrfn:like"));
}
+ } else if (FN_TRUE.toJCRName(resolver).equals(fName)) {
+ if (queryNode.getType() == QueryNode.TYPE_RELATION) {
+ RelationQueryNode rel = (RelationQueryNode) queryNode;
+ rel.setStringValue("true");
+ } else {
+ exceptions.add(new InvalidQueryException("Unsupported location for true()"));
+ }
+ } else if (FN_FALSE.toJCRName(resolver).equals(fName)) {
+ if (queryNode.getType() == QueryNode.TYPE_RELATION) {
+ RelationQueryNode rel = (RelationQueryNode) queryNode;
+ rel.setStringValue("false");
+ } else {
+ exceptions.add(new InvalidQueryException("Unsupported location for false()"));
+ }
+ } else if (FN_POSITION.toJCRName(resolver).equals(fName)) {
+ if (queryNode.getType() == QueryNode.TYPE_RELATION) {
+ RelationQueryNode rel = (RelationQueryNode) queryNode;
+ if (rel.getOperation() == RelationQueryNode.OPERATION_EQ_GENERAL) {
+ // set dummy value to set type of relation query node
+ // will be overwritten when the tree is furhter parsed.
+ rel.setPositionValue(1);
+ rel.setProperty(FN_POSITION_FULL);
+ } else {
+ exceptions.add(new InvalidQueryException("Unsupported expression with position(). Only = is supported."));
+ }
+ } else {
+ exceptions.add(new InvalidQueryException("Unsupported location for position()"));
+ }
+ } else if (FN_FIRST.toJCRName(resolver).equals(fName)) {
+ if (queryNode.getType() == QueryNode.TYPE_RELATION) {
+ ((RelationQueryNode) queryNode).setPositionValue(1);
+ } else if (queryNode.getType() == QueryNode.TYPE_LOCATION) {
+ ((LocationStepQueryNode) queryNode).setIndex(1);
+ } else {
+ exceptions.add(new InvalidQueryException("Unsupported location for first()"));
+ }
+ } else if (FN_LAST.toJCRName(resolver).equals(fName)) {
+ if (queryNode.getType() == QueryNode.TYPE_RELATION) {
+ ((RelationQueryNode) queryNode).setPositionValue(LocationStepQueryNode.LAST);
+ } else if (queryNode.getType() == QueryNode.TYPE_LOCATION) {
+ ((LocationStepQueryNode) queryNode).setIndex(LocationStepQueryNode.LAST);
+ } else {
+ exceptions.add(new InvalidQueryException("Unsupported location for last()"));
+ }
+ } else if (JCRFN_DEREF.toJCRName(resolver).equals(fName)) {
+ exceptions.add(new InvalidQueryException("Unsupported function: " + fName));
} else {
exceptions.add(new InvalidQueryException("Unsupported function: " + fName));
}
Modified: incubator/jackrabbit/trunk/src/test/org/apache/jackrabbit/core/search/SelectClauseTest.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/test/org/apache/jackrabbit/core/search/SelectClauseTest.java?view=diff&r1=156922&r2=156923
==============================================================================
--- incubator/jackrabbit/trunk/src/test/org/apache/jackrabbit/core/search/SelectClauseTest.java (original)
+++ incubator/jackrabbit/trunk/src/test/org/apache/jackrabbit/core/search/SelectClauseTest.java Thu Mar 10 01:57:09 2005
@@ -102,7 +102,7 @@
testRootNode.save();
String sql = "SELECT myvalue FROM " + ntBase + " WHERE " +
- "jcr:path LIKE '" + testRoot + "/node' AND myvalue IS NOT NULL";
+ "jcr:path LIKE '" + testRoot + "/node[%]' AND myvalue IS NOT NULL";
Query q = superuser.getWorkspace().getQueryManager().createQuery(sql, Query.SQL);
QueryResult result = q.execute();
checkResult(result, 2, 2);
Modified: incubator/jackrabbit/trunk/src/test/org/apache/jackrabbit/core/search/XPathAxisTest.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/test/org/apache/jackrabbit/core/search/XPathAxisTest.java?view=diff&r1=156922&r2=156923
==============================================================================
--- incubator/jackrabbit/trunk/src/test/org/apache/jackrabbit/core/search/XPathAxisTest.java (original)
+++ incubator/jackrabbit/trunk/src/test/org/apache/jackrabbit/core/search/XPathAxisTest.java Thu Mar 10 01:57:09 2005
@@ -167,4 +167,25 @@
String xpath = testPath + "/node1";
executeXPathQuery(xpath, new Node[]{n1});
}
+
+ public void testIndex0Descendant() throws RepositoryException {
+ String xpath = "/jcr:root" + testRoot + "//*[0]";
+ executeXPathQuery(xpath, new Node[0]);
+ }
+
+ public void testIndex1Descendant() throws RepositoryException {
+ String xpath = "/jcr:root" + testRoot + "//*[1]";
+ executeXPathQuery(xpath, new Node[]{n1, n11, n21});
+ }
+
+ public void testIndex2Descendant() throws RepositoryException {
+ String xpath = "/jcr:root" + testRoot + "//*[2]";
+ executeXPathQuery(xpath, new Node[]{n2, n12, n22});
+ }
+
+ public void testIndex3Descendant() throws RepositoryException {
+ String xpath = "/jcr:root" + testRoot + "//*[3]";
+ executeXPathQuery(xpath, new Node[0]);
+ }
+
}
Modified: incubator/jackrabbit/trunk/src/test/org/apache/jackrabbit/test/api/query/XPathDocOrderTest.java
URL: http://svn.apache.org/viewcvs/incubator/jackrabbit/trunk/src/test/org/apache/jackrabbit/test/api/query/XPathDocOrderTest.java?view=diff&r1=156922&r2=156923
==============================================================================
--- incubator/jackrabbit/trunk/src/test/org/apache/jackrabbit/test/api/query/XPathDocOrderTest.java (original)
+++ incubator/jackrabbit/trunk/src/test/org/apache/jackrabbit/test/api/query/XPathDocOrderTest.java Thu Mar 10 01:57:09 2005
@@ -64,6 +64,23 @@
}
/**
+ * Tests if position index and document order on child axis returns the
+ * correct node.
+ * <p/>
+ * For configuration description see {@link XPathDocOrderTest}.
+ */
+ public void testDocOrderPositionIndex() throws Exception {
+ StringBuffer tmp = new StringBuffer("/");
+ tmp.append(jcrRoot).append(testRoot).append("/*");
+ tmp.append("[2]");
+ String resultPath = "";
+ for (NodeIterator nodes = testRootNode.getNodes(); nodes.hasNext() && nodes.getPos() < 2;) {
+ resultPath = nodes.nextNode().getPath();
+ }
+ docOrderTest(new Statement(tmp.toString(), Query.XPATH), resultPath);
+ }
+
+ /**
* Tests the <code>last()</code> function.
* <p/>
* For configuration description see {@link XPathDocOrderTest}.