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/11 18:02:49 UTC

svn commit: r1033998 - 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: Thu Nov 11 17:02:49 2010
New Revision: 1033998

URL: http://svn.apache.org/viewvc?rev=1033998&view=rev
Log:
JCR-2800: Implement search facility for users and groups
improved conditions, added like condition
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=1033998&r1=1033997&r2=1033998&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 Thu Nov 11 17:02:49 2010
@@ -25,49 +25,18 @@ public interface QueryBuilder<T> {
      * The sort order of the result set of a query.
      */
     enum Direction {
-        ASCENDING("ascending", RelationOp.GT),
-        DESCENDING("descending", RelationOp.LT);
+        ASCENDING("ascending"),
+        DESCENDING("descending");
 
         private final String direction;
-        private final RelationOp collation;
 
-        Direction(String direction, RelationOp collation) {
+        Direction(String direction) {
             this.direction = direction;
-            this.collation = collation;
         }
 
         public String getDirection() {
             return direction;
         }
-
-        public RelationOp getCollation() {
-            return collation;
-        }
-    }
-
-    /**
-     * Relational operators for comparing a property to a value. Correspond
-     * to the general comparison operators as define in JSR-170.
-     * The {@link #EX} tests for existence of a property.   
-     */
-    enum RelationOp { 
-        NE("!="),
-        EQ("="),
-        LT("<"),
-        LE("<="),
-        GT(">"),
-        GE("=>"),
-        EX("");
-
-        private final String op;
-
-        RelationOp(String op) {
-            this.op = op;
-        }
-
-        public String getOp() {
-            return op;
-        }
     }
 
     /**
@@ -136,19 +105,109 @@ public interface QueryBuilder<T> {
 
     /**
      * Create a condition which holds iff the node of an {@link Authorizable} has a
-     * property at <code>relPath</code> which relates to <code>value</code> through
-     * <code>op</<code>. The format of the <code>relPath</code> argument is the same
-     * as in XPath: <code>@attributeName</code> for an attribute on this node and
+     * property at <code>relPath</code> which is not equal to <code>value</code>.
+     * The format of the <code>relPath</code> argument is the same as in XPath:
+     * <code>@attributeName</code> for an attribute on this node and
+     * <code>relative/path/@attributeName</code> for an attribute of a descendant node.
+     *
+     * @param relPath  Relative path from the authorizable's node to the property
+     * @param value  Value to compare the property at <code>relPath</code> to
+     * @return  A condition
+     */
+    T neq(String relPath, Value value);
+
+    /**
+     * Create a condition which holds iff the node of an {@link Authorizable} has a
+     * property at <code>relPath</code> which is equal to <code>value</code>.
+     * The format of the <code>relPath</code> argument is the same as in XPath:
+     * <code>@attributeName</code> for an attribute on this node and
+     * <code>relative/path/@attributeName</code> for an attribute of a descendant node.
+     *
+     * @param relPath  Relative path from the authorizable's node to the property
+     * @param value  Value to compare the property at <code>relPath</code> to
+     * @return  A condition
+     */
+    T eq(String relPath, Value value);
+
+    /**
+     * Create a condition which holds iff the node of an {@link Authorizable} has a
+     * property at <code>relPath</code> which is smaller than <code>value</code>.
+     * The format of the <code>relPath</code> argument is the same as in XPath:
+     * <code>@attributeName</code> for an attribute on this node and
      * <code>relative/path/@attributeName</code> for an attribute of a descendant node.
-     * {@link RelationOp#EX} tests for existence of a property. In this case the
-     * <code>value</code> argument is ignored. 
      *
      * @param relPath  Relative path from the authorizable's node to the property
-     * @param op  Comparison operator
      * @param value  Value to compare the property at <code>relPath</code> to
      * @return  A condition
      */
-    T property(String relPath, RelationOp op, Value value); 
+    T lt(String relPath, Value value);
+
+    /**
+     * Create a condition which holds iff the node of an {@link Authorizable} has a
+     * property at <code>relPath</code> which is smaller than or equal to <code>value</code>.
+     * The format of the <code>relPath</code> argument is the same as in XPath:
+     * <code>@attributeName</code> for an attribute on this node and
+     * <code>relative/path/@attributeName</code> for an attribute of a descendant node.
+     *
+     * @param relPath  Relative path from the authorizable's node to the property
+     * @param value  Value to compare the property at <code>relPath</code> to
+     * @return  A condition
+     */
+    T le(String relPath, Value value);
+
+    /**
+     * Create a condition which holds iff the node of an {@link Authorizable} has a
+     * property at <code>relPath</code> which is greater than <code>value</code>.
+     * The format of the <code>relPath</code> argument is the same as in XPath:
+     * <code>@attributeName</code> for an attribute on this node and
+     * <code>relative/path/@attributeName</code> for an attribute of a descendant node.
+     *
+     * @param relPath  Relative path from the authorizable's node to the property
+     * @param value  Value to compare the property at <code>relPath</code> to
+     * @return  A condition
+     */
+    T gt(String relPath, Value value);
+
+    /**
+     * Create a condition which holds iff the node of an {@link Authorizable} has a
+     * property at <code>relPath</code> which is greater than or equal to <code>value</code>.
+     * The format of the <code>relPath</code> argument is the same as in XPath:
+     * <code>@attributeName</code> for an attribute on this node and
+     * <code>relative/path/@attributeName</code> for an attribute of a descendant node.
+     *
+     * @param relPath  Relative path from the authorizable's node to the property
+     * @param value  Value to compare the property at <code>relPath</code> to
+     * @return  A condition
+     */
+    T ge(String relPath, Value value);
+
+    /**
+     * Create a condition which holds iff the node of an {@link Authorizable} has a
+     * property at <code>relPath</code>.
+     * The format of the <code>relPath</code> argument is the same as in XPath:
+     * <code>@attributeName</code> for an attribute on this node and
+     * <code>relative/path/@attributeName</code> for an attribute of a descendant node.
+     *
+     * @param relPath  Relative path from the authorizable's node to the property
+     * @return  A condition
+     */
+    T exists(String relPath);
+
+    /**
+     * Create a condition which holds iff the node of an {@link Authorizable} has a
+     * property at <code>relPath</code> which matches the pattern in <code>pattern</code>.
+     * The percent character Ô%Õ represents any string of zero or more characters and the
+     * underscore character Ô_Õ represents any single character. Any literal use of these characters
+     * and the backslash character Ô\Õ must be escaped with a backslash character.
+     * The format of the <code>relPath</code> argument is the same as in XPath:
+     * <code>@attributeName</code> for an attribute on this node and
+     * <code>relative/path/@attributeName</code> for an attribute of a descendant node.
+     *
+     * @param relPath  Relative path from the authorizable's node to the property
+     * @param pattern  Pattern to match the property at <code>relPath</code> against
+     * @return  A condition
+     */
+    T like(String relPath, String pattern);
 
     /**
      * Create a full text search condition. The condition holds iff the node of an

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=1033998&r1=1033997&r2=1033998&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 Thu Nov 11 17:02:49 2010
@@ -28,6 +28,32 @@ import java.util.List;
 
 public class XPathQueryBuilder implements QueryBuilder<XPathQueryBuilder.Condition> {
 
+    /**
+     * Relational operators for comparing a property to a value. Correspond
+     * to the general comparison operators as define in JSR-170.
+     * The {@link #EX} tests for existence of a property.
+     */
+    enum RelationOp {
+        NE("!="),
+        EQ("="),
+        LT("<"),
+        LE("<="),
+        GT(">"),
+        GE("=>"),
+        EX(""),
+        LIKE("like");
+
+        private final String op;
+
+        RelationOp(String op) {
+            this.op = op;
+        }
+
+        public String getOp() {
+            return op;
+        }
+    }
+    
     interface Condition {
         void accept(ConditionVisitor visitor) throws RepositoryException;
     }
@@ -123,6 +149,38 @@ public class XPathQueryBuilder implement
         return new PropertyCondition(relPath, op, value);
     }
 
+    public Condition neq(String relPath, Value value) {
+        return new PropertyCondition(relPath, RelationOp.NE, value);
+    }
+
+    public Condition eq(String relPath, Value value) {
+        return new PropertyCondition(relPath, RelationOp.EQ, value);
+    }
+
+    public Condition lt(String relPath, Value value) {
+        return new PropertyCondition(relPath, RelationOp.LT, value);
+    }
+
+    public Condition le(String relPath, Value value){
+        return new PropertyCondition(relPath, RelationOp.LE, value);
+    }
+
+    public Condition gt(String relPath, Value value) {
+        return new PropertyCondition(relPath, RelationOp.GT, value);
+    }
+
+    public Condition ge(String relPath, Value value) {
+        return new PropertyCondition(relPath, RelationOp.GE, value);
+    }
+
+    public Condition exists(String relPath) {
+        return new PropertyCondition(relPath, RelationOp.EX);
+    }
+
+    public Condition like(String relPath, String pattern) {
+        return new PropertyCondition(relPath, RelationOp.LIKE, pattern);
+    }
+
     public Condition contains(String relPath, String searchExpr) {
         return new ContainsCondition(relPath, searchExpr);
     }
@@ -149,11 +207,27 @@ public class XPathQueryBuilder implement
         private final String relPath;
         private final RelationOp op;
         private final Value value;
+        private final String pattern;
 
         public PropertyCondition(String relPath, RelationOp op, Value value) {
             this.relPath = relPath;
             this.op = op;
             this.value = value;
+            pattern = null;
+        }
+
+        public PropertyCondition(String relPath, RelationOp op, String pattern) {
+            this.relPath = relPath;
+            this.op = op;
+            value = null;
+            this.pattern = pattern;
+        }
+
+        public PropertyCondition(String relPath, RelationOp op) {
+            this.relPath = relPath;
+            this.op = op;
+            value = null;
+            pattern = null;
         }
 
         public String getRelPath() {
@@ -168,6 +242,10 @@ public class XPathQueryBuilder implement
             return value;
         }
 
+        public String getPattern() {
+            return pattern;
+        }
+
         public void accept(ConditionVisitor visitor) throws RepositoryException {
             visitor.visit(this);
         }

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=1033998&r1=1033997&r2=1033998&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 Thu Nov 11 17:02:49 2010
@@ -20,11 +20,11 @@ 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.api.security.user.QueryBuilder.Direction;
-import org.apache.jackrabbit.api.security.user.QueryBuilder.RelationOp;
 import org.apache.jackrabbit.api.security.user.User;
 import org.apache.jackrabbit.core.NodeImpl;
 import org.apache.jackrabbit.core.SessionImpl;
 import org.apache.jackrabbit.core.security.user.XPathQueryBuilder.Condition;
+import org.apache.jackrabbit.core.security.user.XPathQueryBuilder.RelationOp;
 import org.apache.jackrabbit.spi.commons.iterator.Iterators;
 import org.apache.jackrabbit.spi.commons.iterator.Predicate;
 import org.apache.jackrabbit.spi.commons.iterator.Predicates;
@@ -78,7 +78,7 @@ public class XPathQueryEvaluator impleme
                 log.warn("Ignoring bound {} since no sort order is specified");
             }
             else {
-                Condition boundCondition = builder.property(sortCol, sortDir.getCollation(), bound);
+                Condition boundCondition = builder.property(sortCol, getCollation(sortDir), bound);
                 condition = condition == null 
                         ? boundCondition
                         : builder.and(condition, boundCondition);
@@ -123,6 +123,13 @@ public class XPathQueryEvaluator impleme
         if (relOp == RelationOp.EX) {
             xPath.append(condition.getRelPath());
         }
+        else if (relOp == RelationOp.LIKE) {
+            xPath.append("jcr:like(")
+                 .append(condition.getRelPath())
+                 .append(",'")
+                 .append(condition.getPattern())
+                 .append("')");
+        }
         else {
             xPath.append(condition.getRelPath())
                  .append(condition.getOp().getOp())
@@ -207,6 +214,19 @@ public class XPathQueryEvaluator impleme
         }
     }
 
+    private static RelationOp getCollation(Direction direction) throws RepositoryException {
+        switch (direction) {
+            case ASCENDING:
+                return RelationOp.GT;
+
+            case DESCENDING:
+                return RelationOp.LT;
+
+            default:
+                throw new RepositoryException("Unknown sort order " + direction);
+        }
+    }
+
     @SuppressWarnings("unchecked")
     private static Iterator<Node> execute(Query query) throws RepositoryException {
         return query.execute().getNodes();

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=1033998&r1=1033997&r2=1033998&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 Thu Nov 11 17:02:49 2010
@@ -18,7 +18,6 @@
 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.spi.commons.iterator.Iterators;
 import org.apache.jackrabbit.spi.commons.iterator.Predicate;
 
@@ -296,7 +295,7 @@ public class UserManagerSearchTest exten
         Iterator<Authorizable> result = userMgr.findAuthorizables(new Query() {
             public <T> void build(QueryBuilder<T> builder) {
                 builder.setCondition(builder.
-                        property("@canFly", RelationOp.EQ, vf.createValue(true)));
+                        eq("@canFly", vf.createValue(true)));
             }
         });
 
@@ -320,7 +319,7 @@ public class UserManagerSearchTest exten
         Iterator<Authorizable> result = userMgr.findAuthorizables(new Query() {
             public <T> void build(QueryBuilder<T> builder) {
                 builder.setCondition(builder.
-                        property("profile/@weight", RelationOp.GT, vf.createValue(2000.0)));
+                        gt("profile/@weight", vf.createValue(2000.0)));
             }
         });
 
@@ -344,7 +343,7 @@ public class UserManagerSearchTest exten
         Iterator<Authorizable> result = userMgr.findAuthorizables(new Query() {
             public <T> void build(QueryBuilder<T> builder) {
                 builder.setCondition(builder.
-                        property("@numberOfLegs", RelationOp.EQ, vf.createValue(8)));
+                        eq("@numberOfLegs", vf.createValue(8)));
             }
         });
 
@@ -368,7 +367,7 @@ public class UserManagerSearchTest exten
         Iterator<Authorizable> result = userMgr.findAuthorizables(new Query() {
             public <T> void build(QueryBuilder<T> builder) {
                 builder.setCondition(builder.
-                        property("@poisonous", RelationOp.EX, null));
+                        exists("@poisonous"));
             }
         });
 
@@ -388,6 +387,36 @@ public class UserManagerSearchTest exten
         assertSameElements(result, expected);
     }
 
+    public void testPropertyLike() throws RepositoryException {
+        Iterator<Authorizable> result = userMgr.findAuthorizables(new Query() {
+            public <T> void build(QueryBuilder<T> builder) {
+                builder.setCondition(builder.
+                        like("profile/@food", "m%"));
+            }
+        });
+
+        Iterator<User> expected = Iterators.filterIterator(users.iterator(), new Predicate<User>() {
+            public boolean evaluate(User user) {
+                try {
+                    Value[] food = user.getProperty("profile/food");
+                    if (food == null || food.length != 1) {
+                        return false;
+                    }
+                    else {
+                        String value = food[0].getString();
+                        return value.length() > 0 && value.charAt(0) == 'm';
+                    }
+                } catch (RepositoryException e) {
+                    fail(e.getMessage());
+                }
+                return false;
+            }
+        });
+
+        assertTrue(result.hasNext());
+        assertSameElements(result, expected);
+    }
+
     public void testContains1() throws RepositoryException {
         Iterator<Authorizable> result = userMgr.findAuthorizables(new Query() {
             public <T> void build(QueryBuilder<T> builder) {
@@ -445,9 +474,9 @@ public class UserManagerSearchTest exten
             public <T> void build(QueryBuilder<T> builder) {
                 builder.setCondition(builder.
                         and(builder.
-                            property("profile/@cute", RelationOp.EQ, vf.createValue(true)), builder.
+                            eq("profile/@cute", vf.createValue(true)), builder.
                             not(builder.
-                                property("@color", RelationOp.EQ, vf.createValue("black")))));
+                                eq("@color", vf.createValue("black")))));
             }
         });
 
@@ -474,8 +503,8 @@ public class UserManagerSearchTest exten
             public <T> void build(QueryBuilder<T> builder) {
                 builder.setCondition(builder.
                         or(builder.
-                            property("profile/@food", RelationOp.EQ, vf.createValue("mice")), builder.
-                            property("profile/@food", RelationOp.EQ, vf.createValue("nectar"))));
+                            eq("profile/@food", vf.createValue("mice")), builder.
+                            eq("profile/@food", vf.createValue("nectar"))));
             }
         });
 
@@ -512,8 +541,8 @@ public class UserManagerSearchTest exten
     public void testSortOrder1() throws RepositoryException {
         Iterator<Authorizable> result = userMgr.findAuthorizables(new Query() {
             public <T> void build(QueryBuilder<T> builder) {
-                builder.setCondition(
-                        builder.property("@color", RelationOp.EX, null));
+                builder.setCondition(builder.
+                        exists("@color"));
                 builder.setSortOrder("@color", Direction.DESCENDING);
             }
         });
@@ -533,8 +562,8 @@ public class UserManagerSearchTest exten
     public void testSortOrder2() throws RepositoryException {
         Iterator<Authorizable> result = userMgr.findAuthorizables(new Query() {
             public <T> void build(QueryBuilder<T> builder) {
-                builder.setCondition(
-                        builder.property("profile/@weight", RelationOp.EX, null));
+                builder.setCondition(builder.
+                        exists("profile/@weight"));
                 builder.setSortOrder("profile/@weight", Direction.ASCENDING);
             }
         });
@@ -604,7 +633,7 @@ public class UserManagerSearchTest exten
             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)));
+                            eq("profile/@cute", vf.createValue(true)));
                     builder.setSortOrder("profile/@weight", Direction.ASCENDING);
                     builder.setLimit(vf.createValue(1000.0), count);
                 }