docs: public List<TaskSummary> doCriteriaQuery(String userId, UserGroupCallback userGroupCallback, QueryWhere queryWhere) {
  
          // 1. create builder and query instances
          CriteriaBuilder builder = getCriteriaBuilder();
          CriteriaQuery<Tuple> criteriaQuery = builder.createTupleQuery();
  
          // 2. query base
          Root<TaskImpl> taskRoot = criteriaQuery.from(TaskImpl.class);
          criteriaQuery = criteriaQuery.multiselect(
                  taskRoot.get(TaskImpl_.id),                                                         // 0
                  taskRoot.get(TaskImpl_.name),                                                       // 1
                  taskRoot.get(TaskImpl_.subject),                                                    // 2
                  taskRoot.get(TaskImpl_.description),                                                // 3
  
                  taskRoot.get(TaskImpl_.taskData).get(TaskDataImpl_.status),                         // 4
                  taskRoot.get(TaskImpl_.priority),                                                   // 5
                  taskRoot.get(TaskImpl_.taskData).get(TaskDataImpl_.skipable),                       // 6
  
                  taskRoot.get(TaskImpl_.taskData).get(TaskDataImpl_.actualOwner).get(UserImpl_.id),  // 7
                  taskRoot.get(TaskImpl_.taskData).get(TaskDataImpl_.createdBy).get(UserImpl_.id),    // 8
  
                  taskRoot.get(TaskImpl_.taskData).get(TaskDataImpl_.createdOn),                      // 9
                  taskRoot.get(TaskImpl_.taskData).get(TaskDataImpl_.activationTime),                 // 10
                  taskRoot.get(TaskImpl_.taskData).get(TaskDataImpl_.expirationTime),                 // 11
  
                  taskRoot.get(TaskImpl_.taskData).get(TaskDataImpl_.processId),                      // 12
                  taskRoot.get(TaskImpl_.taskData).get(TaskDataImpl_.processSessionId),               // 13
                  taskRoot.get(TaskImpl_.taskData).get(TaskDataImpl_.processInstanceId),              // 14
                  taskRoot.get(TaskImpl_.taskData).get(TaskDataImpl_.deploymentId),                   // 15
  
                  taskRoot.get(TaskImpl_.subTaskStrategy),                                            // 16
                  taskRoot.get(TaskImpl_.taskData).get(TaskDataImpl_.parentId)                        // 17
                  );
          taskRoot.join(TaskImpl_.taskData); // added for convienence sake, since other logic expects to find this join
  
          addUserGroupsViaCallBackToQueryWhere(userGroupCallback, queryWhere.getCriteria());
  
          // 3. check to see if there's already a user(/security)-limitation in the search
          checkExistingCriteriaForUserBasedLimit(queryWhere, userId, userGroupCallback);
          for( QueryModificationService queryModificationService : queryModificationServiceLoader ) {
              queryModificationService.optimizeCriteria(queryWhere);
          }
  
          // 4. process query criteria
          fillCriteriaQuery(criteriaQuery, queryWhere, builder, TaskImpl.class);
  
          // 5. retrieve result (after also applying meta-criteria)
          useDistinctWhenLefOuterJoinsPresent(criteriaQuery);
          List<Tuple> result = createQueryAndCallApplyMetaCriteriaAndGetResult(queryWhere, criteriaQuery, builder);
  
          List<TaskSummary> taskSummaryList = new ArrayList<TaskSummary>(result.size());
          for( Tuple tupleRow : result ) {
              int i = 0;
              //@formatter:off
              TaskSummaryImpl taskSummaryImpl = new TaskSummaryImpl(
                 // id
                 tupleRow.get(i++, Long.class),
                 // name, subject, description
                 tupleRow.get(i++, String.class), tupleRow.get(i++, String.class), tupleRow.get(i++, String.class),
                 // status, prio, skippable
                 tupleRow.get(i++, Status.class), tupleRow.get(i++, Integer.class), tupleRow.get(i++, Boolean.class),
                 // actual owner, created by
                 tupleRow.get(i++, String.class), tupleRow.get(i++, String.class),
                 // created on, activation time, expiration time
                 tupleRow.get(i++, Date.class), tupleRow.get(i++, Date.class), tupleRow.get(i++, Date.class),
                 // process id, process session id, process inst id, deployment id
                 tupleRow.get(i++, String.class), tupleRow.get(i++, Long.class), tupleRow.get(i++, Long.class), tupleRow.get(i++, String.class),
                 tupleRow.get(i++, SubTasksStrategy.class),
                 tupleRow.get(i++, Long.class) );
              //@formatter:on
              taskSummaryList.add(taskSummaryImpl);
  
          }
          return taskSummaryList;
      }