You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by md...@apache.org on 2010/11/09 18:57:11 UTC
svn commit: r1033124 - in /jackrabbit/trunk:
jackrabbit-api/src/main/java/org/apache/jackrabbit/api/security/user/
jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/
jackrabbit-core/src/test/java/org/apache/jackrabbit/api/security/...
Author: mduerig
Date: Tue Nov 9 17:57:11 2010
New Revision: 1033124
URL: http://svn.apache.org/viewvc?rev=1033124&view=rev
Log:
JCR-2800: Implement search facility for users and groups
implemented limits, bounds and offsets
work in progress
Modified:
jackrabbit/trunk/jackrabbit-api/src/main/java/org/apache/jackrabbit/api/security/user/QueryBuilder.java
jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/XPathQueryBuilder.java
jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/XPathQueryEvaluator.java
jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/api/security/user/UserManagerSearchTest.java
Modified: jackrabbit/trunk/jackrabbit-api/src/main/java/org/apache/jackrabbit/api/security/user/QueryBuilder.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-api/src/main/java/org/apache/jackrabbit/api/security/user/QueryBuilder.java?rev=1033124&r1=1033123&r2=1033124&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-api/src/main/java/org/apache/jackrabbit/api/security/user/QueryBuilder.java (original)
+++ jackrabbit/trunk/jackrabbit-api/src/main/java/org/apache/jackrabbit/api/security/user/QueryBuilder.java Tue Nov 9 17:57:11 2010
@@ -25,18 +25,24 @@ public interface QueryBuilder<T> {
* The sort order of the result set of a query.
*/
enum Direction {
- ASCENDING("ascending"),
- DESCENDING("descending");
+ ASCENDING("ascending", RelationOp.GT),
+ DESCENDING("descending", RelationOp.LT);
private final String direction;
+ private RelationOp relOp;
- Direction(String direction) {
+ Direction(String direction, RelationOp relOp) {
this.direction = direction;
+ this.relOp = relOp;
}
public String getDirection() {
return direction;
}
+
+ public RelationOp getRelOp() {
+ return relOp;
+ }
}
/**
@@ -120,21 +126,31 @@ public interface QueryBuilder<T> {
void setSortOrder(String propertyName, Direction direction);
/**
- * Set the limit of the query. A limit consists of an offset, a maximal number of results
- * to include and a direction. The offset refers to the value of the sort order property.
- * If <code>forward</code> is <code>true</code> (<code>false</code>) the query returns
- * at most the <code>maxCount</code> {@link Authorizable}s whose value of the sort order
- * property follows (precedes)the specified <code>offset</code> in the given sort
- * {@link Direction direction}. This method has no effect if the
- * {@link #setSortOrder(String, Direction) sort order} is not also specified.
- * @see #setSortOrder(String, Direction)
- *
- * @param offset Offset from where to start returning results. <code>null</code> for no offset
- * @param maxCount Maximal number of results to return. -1 for all.
- * @param forward If <code>true</code> return the following <code>count</code> records.
- * Otherwise return the preceding <code>count</code> records wrt. <code>offset</code>.
+ * Set limits for the query. The limits consists of a bound and a maximal
+ * number of results. The bound refers to the value of the
+ * {@link #setSortOrder(String, Direction) sort order} property. The
+ * query returns at most <code>maxCount</code> {@link Authorizable}s whose
+ * values of the sort order property follow <code>bound</code> in the sort
+ * direction. This method has no effect if the sort order is not specified.
+ *
+ * @param bound Bound from where to start returning results. <code>null</code>
+ * for no bound
+ * @param maxCount Maximal number of results to return. -1 for no limit.
+ */
+ void setLimit(Value bound, long maxCount);
+
+ /**
+ * Set limits for the query. The limits consists of an offset and a maximal
+ * number of results. <code>offset</code> refers to the offset within the full
+ * result set at which the returned result set should start expressed in terms
+ * of the number of {@link Authorizable}s to skip. <code>limit</code> sets the
+ * maximum size of the result set expressed in terms of the number of authorizables
+ * to return.
+ *
+ * @param offset Offset from where to start returning results. <code>0</code> for no offset.
+ * @param maxCount Maximal number of results to return. -1 for no limit.
*/
- void setLimit(String offset, int maxCount, boolean forward);
+ void setLimit(long offset, long maxCount);
/**
* Create a condition which holds iff the node of an {@link Authorizable} has a
Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/XPathQueryBuilder.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/XPathQueryBuilder.java?rev=1033124&r1=1033123&r2=1033124&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/XPathQueryBuilder.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/XPathQueryBuilder.java Tue Nov 9 17:57:11 2010
@@ -46,9 +46,9 @@ public class XPathQueryBuilder implement
private Condition condition;
private String sortProperty;
private Direction sortDirection = Direction.ASCENDING;
- private String offset;
- private int maxCount = -1;
- private boolean forward = true;
+ private Value bound;
+ private long offset;
+ private long maxCount = -1;
Selector getSelector() {
return selector;
@@ -74,16 +74,16 @@ public class XPathQueryBuilder implement
return sortDirection;
}
- String getOffset() {
- return offset;
+ Value getBound() {
+ return bound;
}
- int getMaxCount() {
- return maxCount;
+ long getOffset() {
+ return offset;
}
- boolean isForward() {
- return forward;
+ long getMaxCount() {
+ return maxCount;
}
//------------------------------------------< QueryBuilder >---
@@ -106,11 +106,16 @@ public class XPathQueryBuilder implement
sortDirection = direction;
}
- public void setLimit(String offset, int maxCount, boolean forward) {
+ public void setLimit(Value bound, long maxCount) {
+ offset = 0; // Unset any previously set offset
+ this.bound = bound;
+ this.maxCount = maxCount;
+ }
+
+ public void setLimit(long offset, long maxCount) {
+ bound = null; // Unset any previously set bound
this.offset = offset;
this.maxCount = maxCount;
- this.forward = true;
- throw new UnsupportedOperationException("limit is not yet supported"); // todo implement: limit
}
public Condition property(String relPath, RelationOp op, Value value) {
Modified: jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/XPathQueryEvaluator.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/XPathQueryEvaluator.java?rev=1033124&r1=1033123&r2=1033124&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/XPathQueryEvaluator.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/main/java/org/apache/jackrabbit/core/security/user/XPathQueryEvaluator.java Tue Nov 9 17:57:11 2010
@@ -19,6 +19,8 @@ package org.apache.jackrabbit.core.secur
import org.apache.jackrabbit.api.security.user.Authorizable;
import org.apache.jackrabbit.api.security.user.Group;
+import org.apache.jackrabbit.core.security.user.XPathQueryBuilder.Condition;
+import org.apache.jackrabbit.api.security.user.QueryBuilder.Direction;
import org.apache.jackrabbit.api.security.user.QueryBuilder.RelationOp;
import org.apache.jackrabbit.core.NodeImpl;
import org.apache.jackrabbit.spi.commons.iterator.Iterators;
@@ -59,29 +61,53 @@ public class XPathQueryEvaluator impleme
.append(builder.getSelector().getNtName())
.append(')');
- XPathQueryBuilder.Condition condition = builder.getCondition();
+ Value bound = builder.getBound();
+ long offset = builder.getOffset();
+ if (bound != null && offset > 0) {
+ log.warn("Found bound {} and offset {} in limit. Discarding offset.", bound, offset);
+ offset = 0;
+ }
+
+ Condition condition = builder.getCondition();
+ String sortCol = builder.getSortProperty();
+ Direction sortDir = builder.getSortDirection();
+ if (bound != null) {
+ if (sortCol == null) {
+ log.warn("Ignoring bound {} since no sort order is specified");
+ }
+ else {
+ Condition boundCondition = builder.property(sortCol, sortDir.getRelOp(), bound);
+ condition = condition == null
+ ? boundCondition
+ : builder.and(condition, boundCondition);
+ }
+ }
+
if (condition != null) {
xPath.append('[');
condition.accept(this);
xPath.append(']');
}
- String sortCol = builder.getSortProperty();
if (sortCol != null) {
xPath.append(" order by ")
.append(sortCol)
.append(' ')
- .append(builder.getSortDirection().getDirection());
+ .append(sortDir.getDirection());
}
Query query = queryManager.createQuery(xPath.toString(), Query.XPATH);
- int count = builder.getMaxCount();
- if (count == 0) {
+ long maxCount = builder.getMaxCount();
+ if (maxCount == 0) {
return Iterators.empty();
}
- if (count > 0) {
- query.setLimit(count);
+ if (maxCount > 0) {
+ query.setLimit(maxCount);
+ }
+
+ if (offset > 0) {
+ query.setOffset(offset);
}
return filter(toAuthorizables(execute(query)), builder.getGroupName(), builder.isDeclaredMembersOnly());
@@ -123,7 +149,7 @@ public class XPathQueryEvaluator impleme
public void visit(XPathQueryBuilder.AndCondition condition) throws RepositoryException {
int count = 0;
- for (XPathQueryBuilder.Condition c : condition) {
+ for (Condition c : condition) {
xPath.append(count++ > 0 ? " and " : "");
c.accept(this);
}
@@ -133,7 +159,7 @@ public class XPathQueryEvaluator impleme
int pos = xPath.length();
int count = 0;
- for (XPathQueryBuilder.Condition c : condition) {
+ for (Condition c : condition) {
xPath.append(count++ > 0 ? " or " : "");
c.accept(this);
}
Modified: jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/api/security/user/UserManagerSearchTest.java
URL: http://svn.apache.org/viewvc/jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/api/security/user/UserManagerSearchTest.java?rev=1033124&r1=1033123&r2=1033124&view=diff
==============================================================================
--- jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/api/security/user/UserManagerSearchTest.java (original)
+++ jackrabbit/trunk/jackrabbit-core/src/test/java/org/apache/jackrabbit/api/security/user/UserManagerSearchTest.java Tue Nov 9 17:57:11 2010
@@ -17,17 +17,15 @@
package org.apache.jackrabbit.api.security.user;
+import org.apache.jackrabbit.api.security.user.QueryBuilder.Direction;
import org.apache.jackrabbit.api.security.user.QueryBuilder.RelationOp;
import org.apache.jackrabbit.api.security.user.QueryBuilder.Selector;
-import org.apache.jackrabbit.api.security.user.QueryBuilder.Direction;
import org.apache.jackrabbit.spi.commons.iterator.Iterators;
import org.apache.jackrabbit.spi.commons.iterator.Predicate;
import javax.jcr.RepositoryException;
import javax.jcr.Value;
-import java.util.HashSet;
-import java.util.Iterator;
-import java.util.Set;
+import java.util.*;
public class UserManagerSearchTest extends AbstractUserTest {
@@ -112,7 +110,7 @@ public class UserManagerSearchTest exten
jackrabbit = createUser("jackrabbit", "carrots", 2500, true);
deer = createUser("deer", "leaves", 120000, true);
opposum = createUser("opposum", "fruit", 1200, true);
- kangaroo = createUser("kangaroo", "grass", 80000, true);
+ kangaroo = createUser("kangaroo", "grass", 90000, true);
elephant = createUser("elephant", "leaves", 5000000, true);
addMembers(mammals, jackrabbit, deer, opposum, kangaroo, elephant);
@@ -122,11 +120,11 @@ public class UserManagerSearchTest exten
crocodile = createUser("crocodile", "meat", 80000, false);
turtle = createUser("turtle", "leaves", 10000, true);
- lizard = createUser("lizard", "leaves", 2000, false);
+ lizard = createUser("lizard", "leaves", 1900, false);
addMembers(reptiles, crocodile, turtle, lizard);
kestrel = createUser("kestrel", "mice", 2000, false);
- goose = createUser("goose", "snails", 10000, true);
+ goose = createUser("goose", "snails", 13000, true);
pelican = createUser("pelican", "fish", 15000, true);
dove = createUser("dove", "insects", 1600, false);
addMembers(birds, kestrel, goose, pelican, dove);
@@ -557,6 +555,85 @@ public class UserManagerSearchTest exten
}
}
+ public void testOffset() throws RepositoryException {
+ long[] offsets = {2, 0, 3, 0, 100000};
+ long[] counts = {4, 4, 0, 100000, 100000};
+
+ for (int k = 0; k < offsets.length; k++) {
+ final long offset = offsets[k];
+ final long count = counts[k];
+ Iterator<Authorizable> result = userMgr.findAuthorizables(new Query() {
+ public <T> void build(QueryBuilder<T> builder) {
+ builder.setSortOrder("profile/@weight", Direction.ASCENDING);
+ builder.setLimit(offset, count);
+ }
+ });
+
+ Iterator<Authorizable> expected = userMgr.findAuthorizables(new Query() {
+ public <T> void build(QueryBuilder<T> builder) {
+ builder.setSortOrder("profile/@weight", Direction.ASCENDING);
+ }
+ });
+
+ skip(expected, offset);
+ assertSame(expected, result, count);
+ assertFalse(result.hasNext());
+ }
+ }
+
+ public void testSetBound() throws RepositoryException {
+ List<User> sortedUsers = new ArrayList<User>(users);
+ Comparator<? super User> comp = new Comparator<User>() {
+ public int compare(User user1, User user2) {
+ try {
+ Value[] weight1 = user1.getProperty("profile/weight");
+ assertNotNull(weight1);
+ assertEquals(1, weight1.length);
+
+ Value[] weight2 = user2.getProperty("profile/weight");
+ assertNotNull(weight2);
+ assertEquals(1, weight2.length);
+
+ return weight1[0].getDouble() < weight2[0].getDouble() ? -1 : 1;
+ } catch (RepositoryException e) {
+ fail(e.getMessage());
+ return 0; // Make the compiler happy
+ }
+ }
+ };
+ Collections.sort(sortedUsers, comp);
+
+ long[] counts = {4, 0, 100000};
+ for (final long count : counts) {
+ Iterator<Authorizable> result = userMgr.findAuthorizables(new Query() {
+ public <T> void build(QueryBuilder<T> builder) {
+ builder.setCondition(builder.
+ property("profile/@cute", RelationOp.EQ, vf.createValue(true)));
+ builder.setSortOrder("profile/@weight", Direction.ASCENDING);
+ builder.setLimit(vf.createValue(1000.0), count);
+ }
+ });
+
+ Iterator<User> expected = Iterators.filterIterator(sortedUsers.iterator(), new Predicate<User>() {
+ public boolean evaluate(User user) {
+ try {
+ Value[] cute = user.getProperty("profile/cute");
+ Value[] weight = user.getProperty("profile/weight");
+ return cute != null && cute.length == 1 && cute[0].getBoolean() &&
+ weight != null && weight.length == 1 && weight[0].getDouble() > 1000.0;
+
+ } catch (RepositoryException e) {
+ fail(e.getMessage());
+ }
+ return false;
+ }
+ });
+
+ assertSame(expected, result, count);
+ assertFalse(result.hasNext());
+ }
+ }
+
//------------------------------------------< private >---
private static void addMembers(Group group, Authorizable... authorizables) throws RepositoryException {
@@ -624,5 +701,18 @@ public class UserManagerSearchTest exten
return set;
}
+ private static <T> void assertSame(Iterator<? extends T> expected, Iterator<? extends T> actual, long count) {
+ for (int k = 0; k < count && actual.hasNext(); k++) {
+ assertTrue(expected.hasNext());
+ assertEquals(expected.next(), actual.next());
+ }
+ }
+
+ private static <T> void skip(Iterator<T> iterator, long count) {
+ for (int k = 0; k < count && iterator.hasNext(); k++) {
+ iterator.next();
+ }
+ }
+
}