Building Queries

This chapter describes the query API Infinity provides for building queries and how it is used.

Query API

The Infinity Process Engine offers a rich query API for querying runtime elements. The following concrete Query classes located in the org.eclipse.stardust.engine.api.query package are available:

They all extend the main Query class, which provides the basic functionality for creating query containers for complex queries. For details on query objects refer to section Query Objects of chapter Objects and Model Elements.

Defining Order

While in most situations it is completely sufficient to obtain runtime objects satisfying certain filter criteria in arbitrary order, in some cases it may be necessary to exactly specify a relative order of retrieved items.

As an example, to address certain quality of service aspects you may like to retrieve the ActivityInstance of the oldest activity instance belonging to instances of the process INTERNET_ORDER. To achieve this you would have to write code like:

ActivityInstanceQuery query = ActivityInstanceQuery.findAlive("INTERNET_ORDER");
query.orderBy(ActivityInstanceQuery.START_TIME); 
QueryService qs = ...;
ActivityInstance oldest = qs.findFirstActivityInstance(query);
This sample combines the predefined query for finding activity instances belonging to a certain process with a single additional order criterion.

The methods used to define a sorting order are:

public abstract class Query
{
...
public OrderCriteria orderBy(FilterableAttribute attribute);
public OrderCriteria orderBy(FilterableAttribute attribute, boolean ascending);
public OrderCriteria orderBy(OrderCriterion criterion);
...
}

The first two are used to define an order according to attributes of the objects queried for. The first method implicitly requests ascending order, while the second lets the caller choose between ascending or descending order.

Attributes you can use for ordering are defined in the scope of the concrete query. For instance, when performing an ActivityInstanceQuery you may select from the following:

public class ActivityInstanceQuery extends Query
{
  public static final Attribute STATE = ...;
  public static final Attribute START_TIME = ...;
  public static final Attribute LAST_MODIFICATION_TIME = ...;
  public static final Attribute ACTIVITY_OID = ...;
  public static final Attribute CURRENT_PERFORMER_OID = ...;
  public static final Attribute CURRENT_USER_PERFORMER_OID = ...;
  public static final Attribute PERFORMED_BY_OID = ...;
  public static final Attribute PROCESS_INSTANCE_OID = ...;
  public static final Attribute CRITICALITY = ...;
  public static final Attribute BENCHMARK_VALUE = ...;
  public static final Attribute BENCHMARK_OID = ...;
  ...
}

Which attributes are supported by the concrete query you want to perform can be found in the query class's JavaDoc. Attributes are always static instances of an inner class Attribute.

In case you need a combination of more than one order criterion you may make use of having the orderBy methods returning an instance of class OrderCriteria. As the name implies, instances of OrderCriteria are used to group lists of single order criteria into one object. The most important methods are:

public final class OrderCriteria implements OrderCriterion
{
...
public OrderCriteria and(FilterableAttribute attribute);
public OrderCriteria and(FilterableAttribute attribute, boolean ascending);
public OrderCriteria and(OrderCriterion criterion);
...
}

You may notice the similarity to the three orderBy methods shown before. Calling one of the methods appends another order criterion to the list already set. The list of criteria will later be evaluated in order of appearance.

To extend the former example we now add another criterion to still find the oldest activity instance, but now additionally requesting to get the instance with the longest idle time in case of multiple instances of the same age.

ActivityInstanceQuery query = ActivityInstanceQuery.findAlive("INTERNET_ORDER");
query.orderBy(ActivityInstanceQuery.START_TIME).and(ActivityInstanceQuery.LAST_MODIFICATION_TIME);
QueryService qs = ...;
ActivityInstance oldest = qs.findFirstActivityInstance(query);

To finish the quick order walk-through, let's come back to the third orderBy method. Its OrderCriterion parameter may be used to set a pre-configured complex list of order criteria at once. For instance, when having a GUI for defining order criteria it may be useful to evaluate its state once and later reuse the resulting OrderCriteria object for multiple queries. In terms of our last example this would mean:

OrderCriteria ordering = new OrderCriteria();
ordering.and(ActivityInstanceQuery.START_TIME).and(ActivityInstanceQuery.LAST_MODIFICATION_TIME);
...
ActivityInstanceQuery query = ActivityInstanceQuery.findAlive("INTERNET_ORDER");
query.orderBy(ordering); 
QueryService qs = ...;
ActivityInstance oldest = qs.findFirstActivityInstance(query);

Using Policies

Policies allow to set special options for queries. Using policies you can fine tune the behavior for more complex queries.

The EvaluationPolicy is the interface to be implemented by custom query evaluation policies. The following policies are available:

Determining if Process Instances are considered critical depending on specified Limits

The policies CriticalCostPerExecutionPolicy, CriticalExecutionTimePolicy, CriticalProcessingTimePolicy and PerformanceCriticalityPolicy can be used to determine if process instances are considered critical. They use the following different criteria:

Cost per execution

The CriticalCostPerExecutionPolicy determines if process instances are considered critical if their execution costs exceeds a certain limit.

The limits can be defined per priority. The parameters for this policy are the following:

  1. a parameter determining the percentage of the "target cost per execution" a process with priority LOW must exceed to be considered critical
  2. a parameter determining the percentage of the "target processing time" a process with priority HIGH must exceed to be considered critical

The following example code demonstrates how to use the policy in the Control Center to determine if process instances are critical depending on their cost per execution:

UserWorktimeStatisticsQuery wsQuery = UserWorktimeStatisticsQuery.forAllUsersWithoutWaitTime();

wsQuery.setPolicy(CriticalCostPerExecutionPolicy.criticalityByCost(
   BusinessControlCenterConstants.getInstanceCostThreshold(BusinessControlCenterConstants.YELLOW_THRESHOLD, 1.0f),
   BusinessControlCenterConstants.getInstanceCostThreshold(BusinessControlCenterConstants.RED_THRESHOLD,1.0f)));

Execution time

The CriticalExecutionTimePolicy determines if process instances are critical if their execution time exceeds a certain limit.

The limits can be defined per priority. The policy uses the following three parameters:

  1. a parameter determining the percentage of the "target execution time" a process with priority LOW must exceed to be considered critical
  2. a parameter determining the percentage of the "target execution time" a process with priority NORMAL must exceed to be considered critical
  3. a parameter determining the percentage of the "target execution time" a process with priority HIGH must exceed to be considered critical

The following example code demonstrates the usage of the CriticalExecutionTimePolicy to determine if process instances are critical by using the thresholds of the process instance priorities.

OpenActivitiesStatisticsQuery query = OpenActivitiesStatisticsQuery.forAllProcesses();

query.setPolicy(new CriticalExecutionTimePolicy(
   Constants.getCriticalDurationThreshold(ProcessInstancePriority.LOW, 1.0f), 
   Constants.getCriticalDurationThreshold(ProcessInstancePriority.NORMAL, 1.0f),
   Constants.getCriticalDurationThreshold(ProcessInstancePriority.HIGH, 1.0f)));

Processing time

Performance

Similar to the CriticalExecutionTimePolicy, the PerformanceCriticalityPolicy determines if process instances are considered critical if their duration exceeds a certain limit defined per priority.

Retrieving Activity Instances based on Workitem Table

The Infinity Process Platform provides a policy EvaluateByWorkitemsPolicy to retrieve activity instances based on the workitem table, which contains only manual/interactive activity instances. This policy is available for the ActivityInstanceQuery. If this policy is set, any ActivityInstanceQuery for getAllActivityInstance or getActivityInstanceCount is executed on the workitem table instead of the activity_instance table.

Note that based on this fact, this policy is relevant only for queries on interactive and manual activities that are active (in state application or suspended). Other activities will not be part of the result set.

According to the nature of the ActivityInstanceQuery, the use of other filters and/or policies should be handled with care as the workitems may not contain the same details as ActivityInstances.

Extending a DocumentQuery to multiple Repository Instances

You can extend a document search from the default repository to either all repositories or to all specified repositories by setting a RepositoryPolicy.

package org.eclipse.stardust.engine.api.query;

import java.util.Collections;
import java.util.List;

public class RepositoryPolicy implements EvaluationPolicy
{
   private static final long serialVersionUID = -4922399462085360884L;

   private List<String< repositoryIds;

   public RepositoryPolicy()
   {
      repositoryIds = Collections.EMPTY_LIST;
   }

   public static RepositoryPolicy includeAllRepositories()
   {
      return new RepositoryPolicy(Collections.EMPTY_LIST);
   }

   public static RepositoryPolicy includeRepositories(List<String< repositoryIds)
   {
      return new RepositoryPolicy(repositoryIds);
   }

   public RepositoryPolicy(List<String< repositoryIds)
   {
      this.repositoryIds = repositoryIds;
   }

   public List<String< getRepositoryIds()
   {
      return repositoryIds;
   }
   
}

Method includeAllRepositories() configures a policy to includes all available repositories into the search. To create a policy to include a list of repositories, use includeRepositories(List<String< repositoryIds).

The following example uses a RepositoryPolicy to search for documents in a set of repositories, which is specified as list of repository Ids:

DocumentQuery query = DocumentQuery.findAll();
Documents findDocuments(DocumentQuery query);
query.setPolicy(new RepositoryPolicy(<list of repository Ids>));

Specifying a List of Date Ranges to select multiple Intervals for Query Evaluation

The following example code shows how to use the StatisticsDateRangePolicy to specify a date range for a user worktime statistics query.

   UserWorktimeStatisticsQuery query = UserWorktimeStatisticsQuery.forAllUsers();
   
   List<DateRange< dateRanges = new ArrayList<DateRange<();
   
   dateRanges.add(DateRange.TODAY);
   dateRanges.add(DateRange.YESTERDAY);
   dateRanges.add(DateRange.THIS_WEEK);
   dateRanges.add(DateRange.LAST_WEEK);
   dateRanges.add(DateRange.THIS_MONTH);
   dateRanges.add(DateRange.LAST_MONTH);
   
   query.setPolicy(new StatisticsDateRangePolicy(dateRanges));
   
   ServiceFactory serviceFactory = getServiceFactory();
   userWorktimeStatistics = (UserWorktimeStatistics) serviceFactory.getQueryService().getAllUsers(query);

Retrieving User Details with the UserDetailsPolicy

To control the level of detail for user details, you can use the following policy:

UserQuery query = UserQuery.findAll();
query.setPolicy(new UserDetailsPolicy(<level>));

whereby <level> can be one of the following:

This policy applies to any query referencing user details in its results. If no policy is set then UserDetailsLevel.Full will be used.

The method

UserDetailsLevel getDetailsLevel();

in the interface User retrieves the level of details for users.

The method UserService.modifyUser(User changes) will throw the exception

new IllegalOperationException("Operation requires fully initialized user.")

in case the following predicate is not true:

UserDetailsLevel.Full != changes.getDetailsLevel();

Excluding Activity Instances for excluded Users

To exclude activity instances in activity instance queries, where the user is an excluded user for that activity instance, you can use the ExcludeUserPolicy policy:

ActivityInstanceQuery query = ActivityInstanceQuery.findAll();
query.setPolicy(ExcludeUserPolicy.EXCLUDE_USER);

Refer to class ActivityInstanceQuery for details.

You have the option to determine if the exclude user evaluation is performed in queries. Set the property Stardust.Engine.Tuning.Query.ExcludeUser.Evaluation in your server-side carnot.properties file accordingly. If set to true, ActivityInstanceQueries evaluate if activities have ExcludeUser actions applied (in case the ExcludeUserPolicy is applied). If set to false, which is the default value, no evaluation takes place.

To limit the runtime effort associated with the worklist count retrieval, in case exclude user evaluation is performed, you can configure a threshold up to which the total count is counted exactly. Set the property Stardust.Engine.Tuning.Query.ExcludeUser.MaximumWorklistCount to a maximum count value to be used as threshold. Refer to the Tuning section of chapter Server Side Properties for details on these properties.

Including or excluding Historical Data from a Process Instance Query

Policy HistoricalDataPolicy determines if historical data values are included or excluded in process instance details. The following options are provided:

Please refer to section Querying Process Instance Details including Historical Data of chapter Querying Process Instance Details for an example usage of this policy in a process instance details query.

Determining the Inclusion of Event Types in Process and Activity Instances

To determine the inclusion of given event types in process and activity instances, use the HistoricalEventPolicy. The possible event types of type HistoricalEventType are:

The predefined policy ALL_EVENTS includes all event types in the process or activity instance:

   public final static HistoricalEventPolicy ALL_EVENTS = new HistoricalEventPolicy(
         HistoricalEventType.EXCEPTION | HistoricalEventType.STATE_CHANGE | 
         HistoricalEventType.NOTE | HistoricalEventType.DELEGATION);

To create a policy for a given event type respectively event types, use

public HistoricalEventPolicy(int eventTypes)

To add more than one event type, concatenate the types via the logical or operator, e.g.:

new HistoricalEventPolicy(HistoricalEventType.EXCEPTION | HistoricalEventType.DELEGATION)

To create a policy for given events, use

public HistoricalEventPolicy(HistoricalEventType[] eventTypes)

Queries with EVENT_EXECUTION policy

Activity instance queries having set a HistoricalEventPolicy with HistoricalEventType.EVENT_EXECUTION will return in ActivityInstance.getHistoricalEvents() additional HistoricalEvents based on the log entries for that activity instance with:

    LogType.Info.equals(logType) AND
    LogCode.EVENT.equals(logCode)

HistoricalEvent.getDetails() returns an instance of org.eclipse.stardust.engine.api.model.EventHandler which describes the event handler associated to the executed event.

Determine the Level of Details for Process Definition Details

You can set the level of detail in which the process definition object is initialized, e.g. if you like to provide general information about a process definition only, not including all details about activities. The following levels are available:

Note that the declared and implemented ProcessInterface is always contained. The default level of detail is FULL.

Example Usage

For example, the following code retrieves all process definitions excluding information on activities, data paths, triggers or event handlers:

   ServiceFactory sf = ServiceFactoryLocator.get("motu", "motu");
   QueryService qs = sf.getQueryService();

   ProcessDefinitionDetailsLevel detailsLevel = ProcessDefinitionDetailsLevel.CORE;
   ProcessDefinitionQuery query = ProcessDefinitionQuery.findAll();
   query.setPolicy(new ProcessDefinitionDetailsPolicy(detailsLevel));
   ProcessDefinitions processDefinitions = qs.getProcessDefinitions(query);

Specifying the Layout of the Result for Worklist Queries

The following policies can be used to specify the result for worklist queries:

MERGED_DEPUTY

The value MERGED_DEPUTY merges deputy relates activity instances into standard layout. This is the default value. One worklist is displayed with several sub participant worklists, as illustrated in the following example:

(top level) UserWorklist containing own workitems and those of private worklists user is being deputy of
        (sub) ParticipantWorklist1 (e.g own or inherited grant)
        (sub) ParticipantWorklist2 (...)
        (sub) ParticipantWorklist3 (...)

SEPARATE_DEPUTY

Policy value SEPARATE_DEPUTY determines to split the result into one own user worklist with own sub participant worklists. User sub worklists of those users the user is deputy of with their own participant worklists, as shown in the following example:

(top level) UserWorklist containing own workitems

    (sub) ParticipantWorklist1 (own or inherited grant)
    (sub) ParticipantWorklist2 (...)
    (sub) ParticipantWorklist3 (...)
    ....
    (sub) userWorklist1 (for 1st user being deputy of)
        (sub) ParticipantWorklist1_1 (inherited grant)
        (sub) ParticipantWorklist1_2 (...)
        (sub) ParticipantWorklist1_3 (...)
        ....
    (sub) userWorklist2 (for 2nd user being deputy of)
        (sub) ParticipantWorklist2_1 (inherited grant)
        (sub) ParticipantWorklist2_2 (...)
        (sub) ParticipantWorklist2_3 (...)
        ....

Applying Custom Filters

Why do you need a Custom Filter

Infinity Process Platform provides various factory methods to execute queries on objects at runtime. Sometimes these well-defined query semantics do not meet specific requirements and return a huge amount of data, or do not retrieve the accurate data. Custom Filters help you to get the appropriate data by fetching only selected portion of the query results. You can also use Custom Filters to extend the existing query capabilities.

What is a Custom Filter

A Custom Filter is a part of the query. It dynamically intercepts all requests and responses, and transforms or uses the information contained in the requests or responses. It does not create responses by itself. You can also specify one or more filter criteria if required. All filters are implementations of the interface FilterCriterion.

Types of Custom Filters

Currently, Infinity Process Platform provides three types of Custom Filters - attribute filters, structural filters and filter terms.

Attribute filter

Attribute filter is the simplest one. You can use it to filter the attribute values of the objects. You can use a subset of Java's operators in the filter expression. For instance, you can test for equality, inequality, pattern matches, range matches, or NULL. For criteria, you can use any attribute that is valid for defining order criteria.

Attributes are defined as static constants of an inner class Attribute in scope of the actual query class. Attribute itself is a specialization of FilterableAttribute, which in turn defines several factory methods for creating filter criteria against the actual attribute. You can refer to the JavaDoc of class FilterableAttribute for a complete list.

An easiest way to use the Custom Filters is to use it with a predefined query. We can use a Custom Filter in the previous example to find activity instances started after a certain date.

ActivityInstanceQuery query = ActivityInstanceQuery.findAlive("INTERNET_ORDER");
Date date = ...;
query.getFilter().and(ActivityInstanceQuery.START_TIME.greaterOrEqual(date.getTime()));
query.orderBy(ActivityInstanceQuery.START_TIME).and(ActivityInstanceQuery.LAST_MODIFICATION_TIME);
QueryService qs = ...;
ActivityInstance oldest = qs.findFirstActivityInstance(query);

Note that query.getFilter().and(filter) is functionally equivalent to query.where(filter).

Structural Filters

This is a more advanced way of filtering the data. In this method, the data may not be directly attributed to the query object. For instance, items related to workflow data of a specific value, or items related to specific process definitions. You can specify the actual relation between these items in the query. Note that the details of a process instance directly relate to workflow data and process definitions. The details of activity instances relate indirectly via the process instance they belong to, but additionally relate to an activity definition. Refer to the query specific javadoc for actual set of filters available.

As an example, we can modify the previous query to additionally filter for activity instances belonging to process instances containing a certain workflow data identifying the ordered item:

ActivityInstanceQuery query = ActivityInstanceQuery.findAlive("INTERNET_ORDER");
Date date = ...;
query.where(ActivityInstanceQuery.START_TIME.greaterOrEqual(date.getTime()));
query.where(DataFilter.isEqual("ORDERED_ITEM_ID", "CARNOT_MUG"));
query.orderBy(ActivityInstanceQuery.START_TIME).and(ActivityInstanceQuery.LAST_MODIFICATION_TIME);
QueryService qs = ...;
ActivityInstance oldest = qs.findFirstActivityInstance(query);

Applying structural filters is as easy as applying attribute filters. It's the job of the process engine to perform all joins and traversals needed for the query.

Note that some of the structural filters require an additional post-processing step. This adds a little overhead to the query execution. Please consult the JavaDoc to get additional information.

Filter terms

In this method, you can combine several simple filter criteria to build a complex and efficient filter. You can do this in two ways: You can either use FilterAndTerm, or use FilterOrTerm. Both of them are specializations of FilterTerm, which has some interesting methods as follows:

public abstract class FilterTerm
{
...
public FilterTerm add(FilterCriterion filter);
public FilterAndTerm addAndTerm();
public FilterOrTerm addOrTerm();
...
}

You can add arbitrary filter criteria by calling the add() method, which returns a FilterTerm class. This method calls itself recursively, thus allowing for chained calling like add(filter1).add(filter2). You can use the other two methods to create sub-terms and build deeply nested filter trees. FilterAndTerm and FilterOrTerm are provided for convenience, to improve type safety while building filter terms.

Note that every query implicitly contains a top-level AND term. Any filter criterion you add with the where() method is added to the top-level AND term. Adding sub-terms resembles the practice of using braces when building complex logical terms. You may have noticed that adding another AND term to the query's top-level AND term doesn't produce an efficient query. You can add an OR sub-term for making it more flexible and effective. We can extend the previous example by not looking for order processes for one, but one out of two possible items:

ActivityInstanceQuery query = ActivityInstanceQuery.findAlive("INTERNET_ORDER");
Date date = ...;
query.where(ActivityInstanceQuery.START_TIME.greaterOrEqual(date.getTime()));
FilterOrTerm itemFilter = query.getFilter().addOrTerm();
itemFilter.or(DataFilter.isEqual("ORDERED_ITEM_ID", "CARNOT_MUG")).or(DataFilter.isEqual("ORDERED_ITEM_ID", "CARNOT_SHIRT"));
query.orderBy(ActivityInstanceQuery.START_TIME).and(ActivityInstanceQuery.LAST_MODIFICATION_TIME);
QueryService qs = ...;
ActivityInstance oldest = qs.findFirstActivityInstance(query);

Data filters are not restricted to simple equality tests. They also provide rich operators for filtering the attributes. The following example is an improved way of writing the previous one, which also has improved performance.

ActivityInstanceQuery query = ActivityInstanceQuery.findAlive("INTERNET_ORDER");
Date date = ...;
query.where(ActivityInstanceQuery.START_TIME.greaterOrEqual(date.getTime())).and(DataFilter.in("ORDERED_ITEM_ID", Arrays.asList(
   new String[] {"CARNOT_MUG", "CARNOT_SHIRT"})));
query.orderBy(ActivityInstanceQuery.START_TIME).and(ActivityInstanceQuery.LAST_MODIFICATION_TIME);
QueryService qs = ...;
ActivityInstance oldest = qs.findFirstActivityInstance(query);

You can use arbitrary complex sub-terms to build complex filter criteria for runtime queries.

Restricting the Result in Size

Often it is not needed to get all items resulting from a query but the top n. If combined with a well known order of items you may want to fetch further chunks of data on demand.

You can do so by specifying a SubsetPolicy for your query. Any subset defines a size restriction, and optionally a number of items to be skipped. For specifying subsets you set the appropriate policy via the query's setPolicy method. To extend the last example to retrieve five instead of one item you have to write code like:

ActivityInstanceQuery query = ActivityInstanceQuery.findAlive("INTERNET_ORDER");
Date date = ...;
query.where(ActivityInstanceQuery.START_TIME.greaterOrEqual(date.getTime()));
FilterOrTerm itemFilter = query.getFilter().addOrTerm();
itemFilter.or(DataFilter.isEqual("ORDERED_ITEM_ID", "CARNOT_MUG")).or(DataFilter.isEqual("ORDERED_ITEM_ID", "CARNOT_SHIRT"));
query.orderBy(ActivityInstanceQuery.START_TIME).and(ActivityInstanceQuery.LAST_MODIFICATION_TIME);
query.setPolicy(new SubsetPolicy(5));
QueryService qs = ...;
ActivityInstances oldest = qs.getAllActivityInstances(query);

This will get you up to five items matching the given filter criteria. If you later decide to fetch the next chunk you may simply write code like:

ActivityInstanceQuery query = oldest.getQuery();
SubsetPolicy subset = (SubsetPolicy) query.getPolicy(SubsetPolicy.class);
query.setPolicy(SubsetPolicy.nextChunk(subset));
ActivityInstances nextChunk = qs.getAllActivityInstances(query);

Especially as any query result object carries its query the last code snippet allows to generically page through result sets, for instance triggered by GUI operations.

Using Structured Data in Queries

Structured data can be used in queries to retrieve process and activity instances. To specify query conditions against structured data, XPath expressions can be specified as a third parameter to queries.

Attribute Filters

The query

query.where(DataFilter.isEqual("struct_1", "order/qty", new Integer("77"))).and(DataFilter.isEqual("struct_1", "order/ordernr", "N2"));

searches for orders, that have both qty=77 and ordernr=N2. If in some instance of structured data orderbook has two orders

this structured data will not match the search criteria. The query

query.where(DataFilter.isEqual("struct_1", "order/qty", new Integer("77"))).and(DataFilter.isEqual("struct_1", "order/customer/address/street", "North")));

The query

query.where(DataFilter.isEqual("struct_1", "order/customer/address/street", "North"));

filters for one specific attribute. The query

query.where(DataFilter.isEqual("struct_1", "order/qty", new Integer("77"))).and(DataFilter.isEqual("struct_1", "order/ordernr", "N2"));

filters for two attributes - the same entity is assumed. The query

query.where(DataFilter.isEqual("struct_1", "order/qty", new Integer("77"))).and(DataFilter.isEqual("struct_1", "order/customer/address/street", "North")).and(DataFilter.isEqual("struct_1", "order/customer/name", "James"))
   .and(DataFilter.isEqual("struct_1", "status", "OPEN"));

filters for four different attributes in the hierarchy.

OR Filters

The query

query.getFilter().addOrTerm().add(DataFilter.isEqual("struct_1", "order/qty", new Integer("2"))).add(DataFilter.isEqual("struct_1", "order/qty", new Integer("100")));

applies an OR filter in one entity, whereby

query.getFilter().addOrTerm().add(DataFilter.isEqual("struct_1", "order/qty", new Integer("77"))).add(DataFilter.isEqual("struct_1", "order/ordernr", "N1"));

applies and OR filter in different entities.

Multiple Data Filters

The query

query.where(DataFilter.isEqual("struct_1", "order/customer/address/street", "North")).and(DataFilter.isEqual("struct_2", "qty", new Integer("1001")));

filters for two data in the process.