Predicates

The Query API includes several methods to filter search results based on specified conditions, such as where, and, or, and not. Conditions are expressed with compound, comparison, and other predicates, as described below:

Basic Comparisons
=, eq The left-hand expression is equal to the right-hand expression.
>=, => The left-hand expression is greater than or equal to the right-hand expression.
<=, =< The left-hand expression is less than or equal to the right-hand expression.
> The left-hand expression is greater than the right-hand expression.
< The left-hand expression is less than the right-hand expression.
!=, <> The left-hand expression is not equal to the right-hand expression.
Boolean Predicates
true A predicate that always evaluates to TRUE.
false A predicate that always evaluates to FALSE.
Basic Compound Predicates
AND, && Logical AND.
OR, || Logical OR.
NOT Logical NOT.
String Comparisons
startsWith The value of the field specified in the left-hand expression begins with the right-hand expression.
matches The left-hand expression matches right-hand expression using a full-text search.
contains The left-hand expression partially or completely matches right-hand expression. Use contains on short text fields such as a name or title. Use matches for large bodies of text.
Other Predicates
missing The left-hand expression is missing.

Predicates are used for testing field values. Note that fields evaluated with predicate operations must be annotated with @Recordable.Indexed.

The where method is the most common Query method for testing field values. For example, the following query uses a comparison operator = to return all Activity instances with an activityType field value equal to “checkin”.

   Query<Activity> query = Query.from(Activity.class);
   query.where("activityType = 'checkin'");
   List<Activity> activities = query.selectAll();

You can include logical operations in the where method.

 Query<Activity> query = Query.from(Activity.class);
 query.where("activityType = 'checkin' or activityType = 'cancel'");
 List<Activity> activities = query.selectAll();

Logical operations can also be expressed with Query compound clauses.

 Query<Activity> query = Query.from(Activity.class);
 query.where("activityType = 'checkin'");
 query.or("activityType = 'cancel'");
 List<Activity> activities = query.selectAll();

In the above examples, the Query object returned from the from method is used for all subsequent methods called on the object. As an alternative to creating a separate statement for each Query method, you can chain the methods in one statement.

List<Activity> activities = Query.from(Activity.class)
                            .where("activityType = 'checkin'")
                            .or("activityType = 'cancel'")
                            .selectAll();

Advanced Predicates

Predicates can be expressed as strings or objects. In the previous examples, strings are used in various Query methods. However, there are versions of these methods that take a Predicate object to express logical and comparison operations.

The following example contrasts the two versions of Query methods that take predicate arguments, those that take a predicate string and those take a Predicate object.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
 /* For user aHoward, return system activities recorded on 03/27/17 for project 'evans-26-b825".
    Exclude any logon activities. */

 // Activity class has fields of type User and Project. Get the User and Project objects to filter retrieved Activity objects.
 String name = "aHoward";
 String projCode = "evans-26-b825";
 User user = Query.from(User.class).where("userName = ?", name).first();
 Project project = Query.from(Project.class).where("code = ?", projCode).first();

 // Specify date to filter retrieved Activity objects.
 Date date = new SimpleDateFormat("MM/dd/yyyy").parse("03/27/2017");

 // Create query that retrieves all Activity objects.
 Query<Activity> query = Query.from(Activity.class);

 /* Use methods that take predicate strings to filter Activity objects.
 query.where("activityUser = ?", user);
 query.and("activityDate = ?", date);
 query.and("project = ?", project);
 query.not("activityType = ?", "logon");
 return query.selectAll(); */

 //////// Alternatively, use Predicate objects to set filtering conditions. ////////

 // Set variables to operators to be used in Predicate object.
 String comparisonOp = PredicateParser.EQUALS_ANY_OPERATOR;
 String compoundOp = PredicateParser.AND_OPERATOR;

 // Create ComparisonPredicate object that filters Activity objects by the specified user.
 Predicate predicate = new ComparisonPredicate(comparisonOp, true, "activityUser", Arrays.asList(user));

 // Create CompoundPredicate object that also filters Activity objects by the specified date.
 predicate = new CompoundPredicate(compoundOp,
                 Arrays.asList(predicate,
                               new ComparisonPredicate(comparisonOp, true, "activityDate", Arrays.asList(date))));

 // Create CompoundPredicate object that also filters Activity objects by the specified project.
 predicate = new CompoundPredicate(compoundOp,
                 Arrays.asList(predicate,
                               new ComparisonPredicate(comparisonOp, true, "project", Arrays.asList(project))));

 /* Create CompoundPredicate object that also filters Activity objects by the specified activity type.
    The value of the ``comparisonOp`` variable is changed so that objects with an activity type of "logon"
    are removed from the result set. */
 comparisonOp = PredicateParser.NOT_EQUALS_ALL_OPERATOR;
 predicate = new CompoundPredicate(compoundOp,
                 Arrays.asList(predicate,
                               new ComparisonPredicate(comparisonOp, true, "activityType", Arrays.asList("logon"))));

 return query.where(predicate).selectAll();

In the previous snippet—

  • Lines 5–11 initialize values that will be used in predicates to filter query results.
  • Line 14 creates the Query object.
  • Lines 17–20 show the use of predicate strings to filter the query results. Line 21 retrieves and returns all of the results.
  • Lines 25–50 show the use of Predicate objects to filter the query results.
    • Lines 26–27 initialize the comparison and compound operators that will be used with the Predicate objects. The PredicateParser class provides a set of constants to specify operators.
    • Line 30 creates a ComparisonPredicate object that filters Activity objects by user.
    • Lines 33–35 create a CompoundPredicate object that adds filtering by date. Note that previously constructed Predicate objects are used in the creation of a CompoundPredicate object.
    • Lines 38–40 create a CompoundPredicate object that adds filtering by project.
    • Lines 45–48 create a CompoundPredicate object that adds filtering to exclude objects with an activity type of “logon”.
    • Line 50 passes the Predicate object to retrieve and return the same results as the Query object on which predicate strings are used.

Equals All Condition

The PredicateParser class includes EQUALS_ANY_OPERATOR. Note that this operator uses OR logic, where foo = [ a, b, c ] translates to foo = a OR foo = b OR foo = c.

Consider an Article type with a categories list field, used to enter subject categories. You could use the PredicateParser.EQUALS_ANY_OPERATOR to find articles with any of the following category values: “environment” or “nature” or “politics”.

Predicate predicate = new ComparisonPredicate(PredicateParser.EQUALS_ANY_OPERATOR, true, "category", Arrays.asList("environment","nature","politics"));

However, to find articles with all of the category values of “environment” and “nature” and “politics”, you would need to apply “equalsall” logic. This operator uses AND logic, where foo = [ a, b, c ] translates to foo = a AND foo = b AND foo = c.

Because PredicateParser does not include an “equalsall” operator, you need to build a query that tests for the inclusion of each value in a list field. For example, the following for loop would build a query to find articles categorized with “environment” and “nature” and “politics”.

ArrayList<String> subjects = new ArrayList<String>();
subjects.add("environment");
subjects.add("nature");
subjects.add("politics");

 for (String subject : subjects) {
     query.and("categories = ?", subject);
}

Not Equals Any Condition

The PredicateParser class includes NOT_EQUALS_ALL_OPERATOR. Note that this operator uses AND logic, where foo != [ a, b, c ] translates to foo != a AND foo != b AND foo != c.

Consider an Article type with a categories list field, used to enter subject categories. You could use the PredicateParser.NOT_EQUALS_ALL_OPERATOR to find articles with none of the following category values: “environment” and “nature” and “politics”.

Predicate predicate = new ComparisonPredicate(PredicateParser.NOT_EQUALS_ALL_OPERATOR, true, "category", Arrays.asList("environment","nature","politics"));

However, to find articles with none of the category values of “environment” or “nature” or “politics”, you would need to apply “notequalsany” logic. This operator uses OR logic, where foo != [ a, b, c ] translates to foo != a OR foo != b OR foo != c.

Because PredicateParser does not include a “notequalsany” operator, you need to build a query that tests for the exclusion of each value in a list field. For example, the following for loop would build a query to find articles that are not categorized with “environment” or “nature” or “politics”.

ArrayList<String> subjects = new ArrayList<String>();
subjects.add("environment");
subjects.add("nature");
subjects.add("politics");

for (String subject : subjects) {
     query.and("categories != ?", subject);
}