All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.camunda.operate.webapp.elasticsearch.QueryHelper Maven / Gradle / Ivy

The newest version!
/*
 * Copyright Camunda Services GmbH and/or licensed to Camunda Services GmbH under
 * one or more contributor license agreements. See the NOTICE file distributed
 * with this work for additional information regarding copyright ownership.
 * Licensed under the Camunda License 1.0. You may not use this file
 * except in compliance with the Camunda License 1.0.
 */
package io.camunda.operate.webapp.elasticsearch;

import static io.camunda.operate.schema.templates.ListViewTemplate.*;
import static io.camunda.operate.util.ElasticsearchUtil.QueryType.ALL;
import static io.camunda.operate.util.ElasticsearchUtil.QueryType.ONLY_RUNTIME;
import static io.camunda.operate.util.ElasticsearchUtil.createMatchNoneQuery;
import static io.camunda.operate.util.ElasticsearchUtil.joinWithAnd;
import static io.camunda.operate.util.ElasticsearchUtil.joinWithOr;
import static org.apache.lucene.search.join.ScoreMode.None;
import static org.elasticsearch.index.query.QueryBuilders.boolQuery;
import static org.elasticsearch.index.query.QueryBuilders.constantScoreQuery;
import static org.elasticsearch.index.query.QueryBuilders.existsQuery;
import static org.elasticsearch.index.query.QueryBuilders.rangeQuery;
import static org.elasticsearch.index.query.QueryBuilders.termQuery;
import static org.elasticsearch.index.query.QueryBuilders.termsQuery;
import static org.elasticsearch.join.query.JoinQueryBuilders.hasChildQuery;

import io.camunda.operate.conditions.ElasticsearchCondition;
import io.camunda.operate.entities.FlowNodeState;
import io.camunda.operate.entities.FlowNodeType;
import io.camunda.operate.entities.listview.ProcessInstanceState;
import io.camunda.operate.property.OperateProperties;
import io.camunda.operate.schema.templates.ListViewTemplate;
import io.camunda.operate.util.CollectionUtil;
import io.camunda.operate.util.ElasticsearchUtil;
import io.camunda.operate.webapp.rest.dto.listview.ListViewQueryDto;
import io.camunda.operate.webapp.rest.dto.listview.VariablesQueryDto;
import io.camunda.operate.webapp.rest.exception.InvalidRequestException;
import io.camunda.operate.webapp.security.identity.IdentityPermission;
import io.camunda.operate.webapp.security.identity.PermissionsService;
import java.time.format.DateTimeFormatter;
import org.apache.commons.lang3.ArrayUtils;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.index.query.ConstantScoreQueryBuilder;
import org.elasticsearch.index.query.ExistsQueryBuilder;
import org.elasticsearch.index.query.Operator;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.index.query.RangeQueryBuilder;
import org.elasticsearch.index.query.TermQueryBuilder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Conditional;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

@Conditional(ElasticsearchCondition.class)
@Component
public class QueryHelper {
  public static final String WILD_CARD = "*";

  @Autowired private OperateProperties operateProperties;

  @Autowired private DateTimeFormatter dateTimeFormatter;

  @Autowired(required = false)
  private PermissionsService permissionsService;

  @Autowired private ListViewTemplate listViewTemplate;

  public SearchRequest createSearchRequest(ListViewQueryDto processInstanceRequest) {
    if (processInstanceRequest.isFinished()) {
      return ElasticsearchUtil.createSearchRequest(listViewTemplate, ALL);
    }
    return ElasticsearchUtil.createSearchRequest(listViewTemplate, ONLY_RUNTIME);
  }

  public QueryBuilder createRequestQuery(ListViewQueryDto request) {
    final QueryBuilder query = createQueryFragment(request);

    final TermQueryBuilder isProcessInstanceQuery =
        termQuery(JOIN_RELATION, PROCESS_INSTANCE_JOIN_RELATION);
    final QueryBuilder queryBuilder = joinWithAnd(isProcessInstanceQuery, query);

    return constantScoreQuery(queryBuilder);
  }

  public ConstantScoreQueryBuilder createProcessInstancesQuery(ListViewQueryDto query) {
    final TermQueryBuilder isProcessInstanceQuery =
        termQuery(JOIN_RELATION, PROCESS_INSTANCE_JOIN_RELATION);
    final QueryBuilder queryBuilder =
        joinWithAnd(isProcessInstanceQuery, createQueryFragment(query));
    return constantScoreQuery(queryBuilder);
  }

  public QueryBuilder createQueryFragment(ListViewQueryDto query) {
    return joinWithAnd(
        createRunningFinishedQuery(query),
        createRetriesLeftQuery(query),
        createActivityIdQuery(query),
        createIdsQuery(query),
        createErrorMessageQuery(query),
        createStartDateQuery(query),
        createEndDateQuery(query),
        createProcessDefinitionKeysQuery(query),
        createBpmnProcessIdQuery(query),
        createExcludeIdsQuery(query),
        createVariablesQuery(query),
        createVariablesInQuery(query),
        createBatchOperationIdQuery(query),
        createParentInstanceIdQuery(query),
        // TODO Elasticsearch changes
        createTenantIdQuery(query),
        createReadPermissionQuery());
  }

  private QueryBuilder createReadPermissionQuery() {
    if (permissionsService == null) {
      return null;
    }
    final var allowed = permissionsService.getProcessesWithPermission(IdentityPermission.READ);
    if (allowed == null) {
      return null;
    }
    return allowed.isAll()
        ? QueryBuilders.matchAllQuery()
        : termsQuery(ListViewTemplate.BPMN_PROCESS_ID, allowed.getIds());
  }

  private QueryBuilder createBatchOperationIdQuery(ListViewQueryDto query) {
    if (query.getBatchOperationId() != null) {
      return termQuery(ListViewTemplate.BATCH_OPERATION_IDS, query.getBatchOperationId());
    }
    return null;
  }

  private QueryBuilder createParentInstanceIdQuery(ListViewQueryDto query) {
    if (query.getParentInstanceId() != null) {
      return termQuery(ListViewTemplate.PARENT_PROCESS_INSTANCE_KEY, query.getParentInstanceId());
    }
    return null;
  }

  private QueryBuilder createTenantIdQuery(ListViewQueryDto query) {
    if (query.getTenantId() != null) {
      return termQuery(ListViewTemplate.TENANT_ID, query.getTenantId());
    }
    return null;
  }

  private QueryBuilder createProcessDefinitionKeysQuery(ListViewQueryDto query) {
    if (CollectionUtil.isNotEmpty(query.getProcessIds())) {
      return termsQuery(ListViewTemplate.PROCESS_KEY, query.getProcessIds());
    }
    return null;
  }

  private QueryBuilder createBpmnProcessIdQuery(ListViewQueryDto query) {
    if (!StringUtils.isEmpty(query.getBpmnProcessId())) {
      final TermQueryBuilder bpmnProcessIdQ =
          termQuery(ListViewTemplate.BPMN_PROCESS_ID, query.getBpmnProcessId());
      TermQueryBuilder versionQ = null;
      if (query.getProcessVersion() != null) {
        versionQ = termQuery(ListViewTemplate.PROCESS_VERSION, query.getProcessVersion());
      }
      return joinWithAnd(bpmnProcessIdQ, versionQ);
    }
    return null;
  }

  private QueryBuilder createVariablesQuery(ListViewQueryDto query) {
    final VariablesQueryDto variablesQuery = query.getVariable();
    if (variablesQuery != null && StringUtils.hasLength(variablesQuery.getValue())) {
      if (variablesQuery.getName() == null) {
        throw new InvalidRequestException("Variables query must provide not-null variable name.");
      }
      return hasChildQuery(
          VARIABLES_JOIN_RELATION,
          joinWithAnd(
              termQuery(VAR_NAME, variablesQuery.getName()),
              termQuery(VAR_VALUE, variablesQuery.getValue())),
          None);
    }
    return null;
  }

  private QueryBuilder createVariablesInQuery(ListViewQueryDto query) {
    final VariablesQueryDto variablesQuery = query.getVariable();
    if (variablesQuery != null && !ArrayUtils.isEmpty(variablesQuery.getValues())) {
      if (variablesQuery.getName() == null) {
        throw new InvalidRequestException("Variables query must provide not-null variable name.");
      }
      return hasChildQuery(
          VARIABLES_JOIN_RELATION,
          joinWithAnd(
              termQuery(VAR_NAME, variablesQuery.getName()),
              termsQuery(VAR_VALUE, variablesQuery.getValues())),
          None);
    }
    return null;
  }

  private QueryBuilder createExcludeIdsQuery(ListViewQueryDto query) {
    if (CollectionUtil.isNotEmpty(query.getExcludeIds())) {
      return boolQuery().mustNot(termsQuery(ListViewTemplate.ID, query.getExcludeIds()));
    }
    return null;
  }

  private QueryBuilder createEndDateQuery(ListViewQueryDto query) {
    if (query.getEndDateAfter() != null || query.getEndDateBefore() != null) {
      final RangeQueryBuilder rangeQueryBuilder = rangeQuery(ListViewTemplate.END_DATE);
      if (query.getEndDateAfter() != null) {
        rangeQueryBuilder.gte(dateTimeFormatter.format(query.getEndDateAfter()));
      }
      if (query.getEndDateBefore() != null) {
        rangeQueryBuilder.lt(dateTimeFormatter.format(query.getEndDateBefore()));
      }
      rangeQueryBuilder.format(operateProperties.getElasticsearch().getElsDateFormat());
      return rangeQueryBuilder;
    }
    return null;
  }

  private QueryBuilder createStartDateQuery(ListViewQueryDto query) {
    if (query.getStartDateAfter() != null || query.getStartDateBefore() != null) {
      final RangeQueryBuilder rangeQueryBuilder = rangeQuery(ListViewTemplate.START_DATE);
      if (query.getStartDateAfter() != null) {
        rangeQueryBuilder.gte(dateTimeFormatter.format(query.getStartDateAfter()));
      }
      if (query.getStartDateBefore() != null) {
        rangeQueryBuilder.lt(dateTimeFormatter.format(query.getStartDateBefore()));
      }
      rangeQueryBuilder.format(operateProperties.getElasticsearch().getElsDateFormat());

      return rangeQueryBuilder;
    }
    return null;
  }

  private QueryBuilder createErrorMessageAsAndMatchQuery(String errorMessage) {
    return hasChildQuery(
        ACTIVITIES_JOIN_RELATION,
        QueryBuilders.matchQuery(ERROR_MSG, errorMessage).operator(Operator.AND),
        None);
  }

  private QueryBuilder createErrorMessageAsWildcardQuery(String errorMessage) {
    return hasChildQuery(
        ACTIVITIES_JOIN_RELATION, QueryBuilders.wildcardQuery(ERROR_MSG, errorMessage), None);
  }

  private QueryBuilder createErrorMessageQuery(ListViewQueryDto query) {
    final String errorMessage = query.getErrorMessage();
    if (!StringUtils.isEmpty(errorMessage)) {
      if (errorMessage.contains(WILD_CARD)) {
        return createErrorMessageAsWildcardQuery(errorMessage.toLowerCase());
      } else {
        return createErrorMessageAsAndMatchQuery(errorMessage);
      }
    }
    return null;
  }

  private QueryBuilder createIdsQuery(ListViewQueryDto query) {
    if (CollectionUtil.isNotEmpty(query.getIds())) {
      return termsQuery(ListViewTemplate.ID, query.getIds());
    }
    return null;
  }

  private QueryBuilder createRunningFinishedQuery(ListViewQueryDto query) {

    final boolean active = query.isActive();
    final boolean incidents = query.isIncidents();
    final boolean running = query.isRunning();

    final boolean completed = query.isCompleted();
    final boolean canceled = query.isCanceled();
    final boolean finished = query.isFinished();

    if (!running && !finished) {
      // empty list should be returned
      return createMatchNoneQuery();
    }

    if (running && finished && active && incidents && completed && canceled) {
      // select all
      return null;
    }

    QueryBuilder runningQuery = null;

    if (running && (active || incidents)) {
      // running query
      runningQuery = boolQuery().mustNot(existsQuery(END_DATE));

      final QueryBuilder activeQuery = createActiveQuery(query);
      final QueryBuilder incidentsQuery = createIncidentsQuery(query);

      if (query.getActivityId() == null && query.isActive() && query.isIncidents()) {
        // we request all running instances
      } else {
        // some of the queries may be null
        runningQuery = joinWithAnd(runningQuery, joinWithOr(activeQuery, incidentsQuery));
      }
    }

    QueryBuilder finishedQuery = null;

    if (finished && (completed || canceled)) {

      // add finished query
      finishedQuery = existsQuery(END_DATE);

      final QueryBuilder completedQuery = createCompletedQuery(query);
      final QueryBuilder canceledQuery = createCanceledQuery(query);

      if (query.getActivityId() == null && query.isCompleted() && query.isCanceled()) {
        // we request all finished instances
      } else {
        finishedQuery = joinWithAnd(finishedQuery, joinWithOr(completedQuery, canceledQuery));
      }
    }

    final QueryBuilder processInstanceQuery = joinWithOr(runningQuery, finishedQuery);

    if (processInstanceQuery == null) {
      return createMatchNoneQuery();
    }

    return processInstanceQuery;
  }

  private QueryBuilder createRetriesLeftQuery(ListViewQueryDto query) {
    if (query.isRetriesLeft()) {
      final QueryBuilder retriesLeftQuery = termQuery(JOB_FAILED_WITH_RETRIES_LEFT, true);
      return hasChildQuery(ACTIVITIES_JOIN_RELATION, retriesLeftQuery, None);
    }
    return null;
  }

  private QueryBuilder createActivityIdQuery(ListViewQueryDto query) {
    if (StringUtils.isEmpty(query.getActivityId())) {
      return null;
    }
    QueryBuilder activeActivityIdQuery = null;
    if (query.isActive()) {
      activeActivityIdQuery = createActivityIdQuery(query.getActivityId(), FlowNodeState.ACTIVE);
    }
    QueryBuilder incidentActivityIdQuery = null;
    if (query.isIncidents()) {
      incidentActivityIdQuery = createActivityIdIncidentQuery(query.getActivityId());
    }
    QueryBuilder completedActivityIdQuery = null;
    if (query.isCompleted()) {
      completedActivityIdQuery =
          createActivityIdQuery(query.getActivityId(), FlowNodeState.COMPLETED);
    }
    QueryBuilder canceledActivityIdQuery = null;
    if (query.isCanceled()) {
      canceledActivityIdQuery =
          createActivityIdQuery(query.getActivityId(), FlowNodeState.TERMINATED);
    }
    return joinWithOr(
        activeActivityIdQuery,
        incidentActivityIdQuery,
        completedActivityIdQuery,
        canceledActivityIdQuery);
  }

  private QueryBuilder createCanceledQuery(ListViewQueryDto query) {
    if (query.isCanceled()) {
      return termQuery(STATE, ProcessInstanceState.CANCELED.toString());
    }
    return null;
  }

  private QueryBuilder createCompletedQuery(ListViewQueryDto query) {
    if (query.isCompleted()) {
      return termQuery(STATE, ProcessInstanceState.COMPLETED.toString());
    }
    return null;
  }

  private QueryBuilder createIncidentsQuery(ListViewQueryDto query) {
    if (query.isIncidents()) {
      return termQuery(INCIDENT, true);
    }
    return null;
  }

  private QueryBuilder createActiveQuery(ListViewQueryDto query) {
    if (query.isActive()) {
      return termQuery(INCIDENT, false);
    }
    return null;
  }

  private QueryBuilder createActivityIdQuery(String activityId, FlowNodeState state) {
    final QueryBuilder activitiesQuery = termQuery(ACTIVITY_STATE, state.name());
    final QueryBuilder activityIdQuery = termQuery(ACTIVITY_ID, activityId);
    QueryBuilder activityIsEndNodeQuery = null;
    if (state.equals(FlowNodeState.COMPLETED)) {
      activityIsEndNodeQuery = termQuery(ACTIVITY_TYPE, FlowNodeType.END_EVENT.name());
    }

    return hasChildQuery(
        ACTIVITIES_JOIN_RELATION,
        joinWithAnd(activitiesQuery, activityIdQuery, activityIsEndNodeQuery),
        None);
  }

  private QueryBuilder createActivityIdIncidentQuery(String activityId) {
    final QueryBuilder activitiesQuery = termQuery(ACTIVITY_STATE, FlowNodeState.ACTIVE.name());
    final QueryBuilder activityIdQuery = termQuery(ACTIVITY_ID, activityId);
    final ExistsQueryBuilder incidentExists = existsQuery(ERROR_MSG);

    return hasChildQuery(
        ACTIVITIES_JOIN_RELATION,
        joinWithAnd(activitiesQuery, activityIdQuery, incidentExists),
        None);
  }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy