Since KIE Server’s BPM capability is based on jbpm services api, it does provide access to QueryService and its advanced (DashBuilder DataSets based) operations.
We are going to use the same use case, product sale with 10 000 loaded process and task instances. Next we show how you can query data both via KIE Server client and directly via raw REST api.
KIE Server capabilities when it comes to advanced queries mirrors what’s available in services api, so users can:
- register query definitions
- replace query definitions
- unregister query definitions
- get list of queries or individual query definition
- execute queries on top of query definitions with
- paging and sorting
- filter parameters
- query with custom param builder and mappers
KieServicesConfiguration configuration = KieServicesFactory.newRestConfiguration(serverUrl, user, password);
Set<Class<?>> extraClasses = new HashSet<Class<?>>();
extraClasses.add(Date.class); // for JSON only to properly map dates
configuration.setMarshallingFormat(MarshallingFormat.JSON);
configuration.addJaxbClasses(extraClasses);
KieServicesClient kieServicesClient = KieServicesFactory.newKieServicesClient(configuration);
QueryServicesClient queryClient = kieServicesClient.getServicesClient(QueryServicesClient.class);
List available query definitions available in the system
List<QueryDefinition> queryDefs = queryClient.getQueries(0, 10);
System.out.println(queryDefs);
QueryDefinition query = new QueryDefinition();
query.setName("getAllTaskInstancesWithCustomVariables");
query.setSource("java:jboss/datasources/ExampleDS");
query.setExpression("select ti.*, c.country, c.productCode, c.quantity, c.price, c.saleDate " +
"from AuditTaskImpl ti " +
" inner join (select mv.map_var_id, mv.taskid from MappedVariable mv) mv " +
" on (mv.taskid = ti.taskId) " +
" inner join ProductSale c " +
" on (c.id = mv.map_var_id)");
queryClient.registerQuery(query);
List<TaskInstance> tasks = queryClient.query("getAllTaskInstancesWithCustomVariables", "UserTasks", 0, 10, TaskInstance.class);
System.out.println(tasks);
this will return task instances directly from the data set without any filtering and use UserTasks mapper to build up object representation and apply paging – first page and 10 results at most.
Now it’s time to use more advanced queries capabilities and start filtering by process variables. As described in the Advanced queries in jBPM 6.4 article to be able to map custom variables we need to provide their column mapping – name and type. Following is an example that searches for tasks that:
- processInstanceId is between 1000 and 2000 – number range condition
- price is over 800 – number comparison condition
- sale date is between 01.02.2016 and 01.03.2016 – date range condition
- product in sale are EAP or Wildfly – logical and group condition
- order descending by saleDate and country
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
Date from = sdf.parse("2016-02-01");
Date to = sdf.parse("2016-03-01");
QueryFilterSpec spec = new QueryFilterSpecBuilder()
.between("processInstanceId", 1000, 2000)
.greaterThan("price", 800)
.between("saleDate", from, to)
.in("productCode", Arrays.asList("EAP", "WILDFLY"))
.oderBy("saleDate, country", false)
.addColumnMapping("COUNTRY", "string")
.addColumnMapping("PRODUCTCODE", "string")
.addColumnMapping("QUANTITY", "integer")
.addColumnMapping("PRICE", "double")
.addColumnMapping("SALEDATE", "date")
.get();
List<TaskInstance> tasks = queryClient.query("getAllTaskInstancesWithCustomVariables", "UserTasksWithCustomVariables", spec, 0, 10, TaskInstance.class);
System.out.println(tasks);
To overcome the problem, services api introduced QueryParamBuilder so users can build advanced filters. Similar is on KIE Server, though they need to be built and included in one of following:
- KIE Server itself (like in WEB-INF/lib)
- Inside a project – kjar
- Inside a project’s dependency
Using QueryParamBuilder in KIE Server
- Implement QueryParamBuilder that will produce new instance every time is requested and given a map of parameters
public class TestQueryParamBuilder implements QueryParamBuilder<ColumnFilter> {
private Map<String, Object> parameters;
private boolean built = false;
public TestQueryParamBuilder(Map<String, Object> parameters) {
this.parameters = parameters;
}
@Override
public ColumnFilter build() {
// return null if it was already invoked
if (built) {
return null;
}
String columnName = "processInstanceId";
ColumnFilter filter = FilterFactory.OR(
FilterFactory.greaterOrEqualsTo(((Number)parameters.get("min")).longValue()),
FilterFactory.lowerOrEqualsTo(((Number)parameters.get("max")).longValue()));
filter.setColumnId(columnName);
built = true;
return filter;
}
}
- Implement QueryParamBuilderFactory
public class TestQueryParamBuilderFactory implements QueryParamBuilderFactory {
@Override
public boolean accept(String identifier) {
if ("test".equalsIgnoreCase(identifier)) {
return true;
}
return false;
}
@Override
public QueryParamBuilder newInstance(Map<String, Object> parameters) {
return new TestQueryParamBuilder(parameters);
}
}
There is last tiny bit required to make this to work – we need to make it discoverable so let’s add service file into META-INF folder of the jar that will package these implementation.
META-INF/services/org.jbpm.services.api.query.QueryParamBuilderFactory
where the content of this file is fully qualified class name of the factory.
with this we can issue a request that will make use of newly created query builder for advanced filters:
Map<String, Object> params = new HashMaplt;String, Object>();
params.put("min", 10);
params.put("max", 20);
Listlt;TaskInstance> instances = queryClient.query("getAllTaskInstancesWithCustomVariables", "UserTasksWithCustomVariables", "test", params, 0, 10, TaskInstance.class);
So what we have done here:
- reference registered query by name – getAllTaskInstancesWithCustomVariables
- reference mapper by name – UserTasksWithCustomVariables
- reference query param builder identifier – test
- sent params (min and max) that will be used by new instance of query builder before query is executed
public class ProductSaleQueryMapper extends UserTaskInstanceWithCustomVarsQueryMapper {
private static final long serialVersionUID = 3299692663640707607L;
public ProductSaleQueryMapper() {
super(getVariableMapping());
}
protected static Map<String, String> getVariableMapping() {
Map<String, String> variablesMap = new HashMap<String, String>();
variablesMap.put("COUNTRY", "string");
variablesMap.put("PRODUCTCODE", "string");
variablesMap.put("QUANTITY", "integer");
variablesMap.put("PRICE", "double");
variablesMap.put("SALEDATE", "date");
return variablesMap;
}
@Override
public String getName() {
return "ProductSale";
}
}
Here we simply extend the UserTaskInstanceWithCustomVarsQueryMapper and provide directly column mapping so it can be used without column mapping on request level. To be able to use it, mapper needs to be made discoverable so we need to create service file within META-INF folder of the jar that will package this implementation.
META-INF/services/org.jbpm.services.api.query.QueryResultMapper
where the content of this file is fully qualified class name of the mapper.
Now we can directly use it by referencing it by name:
List<TaskInstance> tasks = queryClient.query("getAllTaskInstancesWithCustomVariables", "ProductSale", 0, 10, TaskInstance.class);
System.out.println(tasks);
Raw REST API use of described examples
Get query definitions
- http://localhost:8230/kie-server/services/rest/server/queries/definitions?page=0&pageSize=10
- GET
Register query definition
- http://localhost:8230/kie-server/services/rest/server/queries/definitions/getAllTaskInstancesWithCustomVariables
- POST
Query for tasks – no filtering
- http://localhost:8230/kie-server/services/rest/server/queries/definitions/getAllTaskInstancesWithCustomVariables/data?mapper=UserTasks&orderBy=&page=0&pageSize=10
- GET
Query with filter spec
- http://localhost:8230/kie-server/services/rest/server/queries/definitions/getAllTaskInstancesWithCustomVariables/filtered-data?mapper=UserTasksWithCustomVariables&page=0&pageSize=10
- POST
Query with custom query param builder
- http://localhost:8230/kie-server/services/rest/server/queries/definitions/getAllTaskInstancesWithCustomVariables/filtered-data?mapper=UserTasksWithCustomVariables&builder=test&page=0&pageSize=10
- POST
Query for tasks – custom mapper
- http://localhost:8230/kie-server/services/rest/server/queries/definitions/getAllTaskInstancesWithCustomVariables/data?mapper=ProductSale&orderBy=&page=0&pageSize=10
- GET
With this, we have went over support for advanced queries in KIE Server for BPM capability.
As usual, feedback is welcome 🙂