/*******************************************************************************
* Copyright French Prime minister Office/SGMAP/DINSIC/Vitam Program (2015-2019)
*
* contact.vitam@culture.gouv.fr
*
* This software is a computer program whose purpose is to implement a digital archiving back-office system managing
* high volumetry securely and efficiently.
*
* This software is governed by the CeCILL 2.1 license under French law and abiding by the rules of distribution of free
* software. You can use, modify and/ or redistribute the software under the terms of the CeCILL 2.1 license as
* circulated by CEA, CNRS and INRIA at the following URL "http://www.cecill.info".
*
* As a counterpart to the access to the source code and rights to copy, modify and redistribute granted by the license,
* users are provided only with a limited warranty and the software's author, the holder of the economic rights, and the
* successive licensors have only limited liability.
*
* In this respect, the user's attention is drawn to the risks associated with loading, using, modifying and/or
* developing or reproducing the software by the user in light of its specific status of free software, that may mean
* that it is complicated to manipulate, and that also therefore means that it is reserved for developers and
* experienced professionals having in-depth computer knowledge. Users are therefore encouraged to load and test the
* software's suitability as regards their requirements in conditions enabling the security of their systems and/or data
* to be ensured and, more generally, to use and operate it in the same conditions as regards security.
*
* The fact that you are presently reading this means that you have had knowledge of the CeCILL 2.1 license and that you
* accept its terms.
*******************************************************************************/
package fr.gouv.vitam.ihmdemo.core;
import static fr.gouv.vitam.common.database.builder.query.QueryHelper.and;
import static fr.gouv.vitam.common.database.builder.query.QueryHelper.eq;
import static fr.gouv.vitam.common.database.builder.query.QueryHelper.exists;
import static fr.gouv.vitam.common.database.builder.query.QueryHelper.gte;
import static fr.gouv.vitam.common.database.builder.query.QueryHelper.in;
import static fr.gouv.vitam.common.database.builder.query.QueryHelper.lte;
import static fr.gouv.vitam.common.database.builder.query.QueryHelper.match;
import static fr.gouv.vitam.common.database.builder.query.QueryHelper.or;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import com.fasterxml.jackson.databind.JsonNode;
import com.google.common.base.Strings;
import fr.gouv.vitam.common.database.builder.query.BooleanQuery;
import fr.gouv.vitam.common.database.builder.query.action.SetAction;
import fr.gouv.vitam.common.database.builder.request.exception.InvalidCreateOperationException;
import fr.gouv.vitam.common.database.builder.request.multiple.Select;
import fr.gouv.vitam.common.database.builder.request.multiple.Update;
import fr.gouv.vitam.common.exception.InvalidParseOperationException;
import fr.gouv.vitam.common.logging.VitamLogger;
import fr.gouv.vitam.common.logging.VitamLoggerFactory;
/**
* Helper class to create DSL queries
*
*/
public final class DslQueryHelper {
private static final VitamLogger LOGGER = VitamLoggerFactory.getInstance(DslQueryHelper.class);
public static final String PROJECTION_DSL = "projection_";
private static final String EVENT_TYPE_PROCESS = "evTypeProc";
private static final String ALL = "All";
private static final String EVENT_ID_PROCESS = "evIdProc";
private static final String DESCRIPTION = "Description";
private static final String TITLE = "Title";
private static final String EVENT_DATE_TIME = "evDateTime";
private static final String DEFAULT_EVENT_TYPE_PROCESS = "INGEST";
private static final String EVENT_OUT_DETAIL = "events.outDetail";
private static final String DEFAULT_EVENT_TYPE_PROCESS_TEST = "INGEST_TEST";
private static final String PUID = "PUID";
private static final String RULEVALUE = "RuleValue";
private static final String OBJECT_IDENTIFIER_INCOME = "obIdIn";
private static final String FORMAT = "FORMAT";
private static final String FORMAT_NAME = "FormatName";
private static final String RULE_VALUE = "RuleValue";
private static final String EVENTID = "EventID";
private static final String EVENTTYPE = "EventType";
private static final String RULES = "RULES";
private static final String ACCESSION_REGISTER = "ACCESSIONREGISTER";
private static final String RULETYPE = "RuleType";
private static final String ORDER_BY = "orderby";
private static final String TITLE_AND_DESCRIPTION = "titleAndDescription";
private static final String PROJECTION_PREFIX = "projection_";
private static final int DEPTH_LIMIT = 20;
private static final String START_PREFIX = "Start";
private static final String END_PREFIX = "End";
private static final String START_DATE = "StartDate";
private static final String END_DATE = "EndDate";
private static final String TRANSACTED_DATE = "TransactedDate";
private static final String ADVANCED_SEARCH_FLAG = "isAdvancedSearchFlag";
private static final String YES = "yes";
private static final String ORIGINATING_AGENCY = "OriginatingAgency";
private static final String DATEOPERATION = "EvDateTime";
private static final String TRACEABILITY_OK = "TraceabilityOk";
private static final String TRACEABILITY_ID = "TraceabilityId";
private static final String TRACEABILITY_LOG_TYPE = "TraceabilityLogType";
private static final String TRACEABILITY_START_DATE = "TraceabilityStartDate";
private static final String TRACEABILITY_END_DATE = "TraceabilityEndDate";
private static final String TRACEABILITY_EV_DET_DATA = "events.evDetData";
// FIXME when id will be implemented on traceability, change me
private static final String TRACEABILITY_FIELD_ID = "FileName";
private static final String TRACEABILITY_FIELD_LOG_TYPE = "LogType";
// empty constructor
private DslQueryHelper() {
// empty constructor
}
/**
* generate the DSL query after receiving the search criteria
*
*
* @param searchCriteriaMap the map containing the criteria
* @return DSL request
* @throws InvalidParseOperationException if a parse exception is encountered
* @throws InvalidCreateOperationException if an Invalid create operation is encountered
*/
public static JsonNode createSingleQueryDSL(Map<String, String> searchCriteriaMap)
throws InvalidParseOperationException, InvalidCreateOperationException {
final fr.gouv.vitam.common.database.builder.request.single.Select select =
new fr.gouv.vitam.common.database.builder.request.single.Select();
final BooleanQuery query = and();
BooleanQuery queryOr = null;
for (final Entry<String, String> entry : searchCriteriaMap.entrySet()) {
final String searchKeys = entry.getKey();
final String searchValue = entry.getValue();
switch (searchKeys) {
case ORDER_BY:
if (EVENT_DATE_TIME.equals(searchValue)) {
select.addOrderByDescFilter(searchValue);
} else {
select.addOrderByAscFilter(searchValue);
}
break;
case DEFAULT_EVENT_TYPE_PROCESS:
query.add(or().add(eq(EVENT_TYPE_PROCESS, DEFAULT_EVENT_TYPE_PROCESS),
eq(EVENT_TYPE_PROCESS, DEFAULT_EVENT_TYPE_PROCESS_TEST)));
break;
case OBJECT_IDENTIFIER_INCOME:
query.add(eq("events.obIdIn", searchValue));
break;
case FORMAT:
query.add(exists(PUID));
break;
case FORMAT_NAME:
if (!searchValue.trim().isEmpty()) {
query.add(match("Name", searchValue));
}
break;
case RULE_VALUE:
if (!searchValue.trim().isEmpty()) {
query.add(match(RULE_VALUE, searchValue));
}
break;
case ACCESSION_REGISTER:
query.add(exists(ORIGINATING_AGENCY));
break;
case ORIGINATING_AGENCY:
query.add(eq(ORIGINATING_AGENCY, searchValue));
break;
case RULES:
query.add(exists(RULEVALUE));
break;
case RULETYPE:
if (searchValue.contains(ALL)) {
break;
}
if (searchValue.contains(",")) {
queryOr = or();
final String[] ruleTypeArray = searchValue.split(",");
for (final String s : ruleTypeArray) {
queryOr.add(eq("RuleType", s));
}
break;
}
if (!searchValue.isEmpty()) {
query.add(eq("RuleType", searchValue));
}
break;
case EVENTID:
if ("all".equals(searchValue)) {
query.add(exists(EVENT_ID_PROCESS));
} else {
query.add(eq(EVENT_ID_PROCESS, searchValue));
}
break;
case EVENTTYPE:
if (!searchValue.isEmpty()) {
if ("all".equals(searchValue)) {
query.add(exists(EVENT_TYPE_PROCESS));
} else {
query.add(eq(EVENT_TYPE_PROCESS, searchValue.toUpperCase()));
}
}
break;
case DATEOPERATION:
if (!searchValue.isEmpty()) {
query.add(gte(EVENT_DATE_TIME, searchValue));
}
break;
case TRACEABILITY_OK:
// FIXME : check if it is normal that the end event is a step event for a traceability
if ("true".equals(searchValue)) {
query.add(eq(EVENT_OUT_DETAIL, "STP_OP_SECURISATION.OK"));
}
break;
case TRACEABILITY_ID:
// FIXME : No real ID for now, search on fileName
if (!searchValue.isEmpty()) {
query.add(eq(TRACEABILITY_EV_DET_DATA + '.' + TRACEABILITY_FIELD_ID, searchValue));
}
break;
case TRACEABILITY_LOG_TYPE:
if (!searchValue.isEmpty()) {
query.add(eq(TRACEABILITY_EV_DET_DATA + '.' + TRACEABILITY_FIELD_LOG_TYPE, searchValue));
}
break;
case TRACEABILITY_START_DATE:
if (!searchValue.isEmpty()) {
query.add(gte(TRACEABILITY_EV_DET_DATA + '.' + START_DATE, searchValue));
}
break;
case TRACEABILITY_END_DATE:
if (!searchValue.isEmpty()) {
query.add(lte(TRACEABILITY_EV_DET_DATA + '.' + END_DATE, searchValue));
}
break;
default:
if (!searchValue.isEmpty()) {
query.add(eq(searchKeys, searchValue));
}
}
}
if (queryOr != null) {
query.add(queryOr);
}
select.setQuery(query);
LOGGER.debug("{}", select.getFinalSelect());
return select.getFinalSelect();
}
/**
* @param searchCriteriaMap Criteria received from The IHM screen Empty Keys or Value is not allowed
* @return the JSONDSL File
* @throws InvalidParseOperationException thrown when an error occurred during parsing
* @throws InvalidCreateOperationException thrown when an error occurred during creation
*/
public static JsonNode createSelectDSLQuery(Map<String, String> searchCriteriaMap)
throws InvalidParseOperationException, InvalidCreateOperationException {
final Select select = new Select();
// AND by default
final BooleanQuery booleanQueries = and();
for (final Entry<String, String> entry : searchCriteriaMap.entrySet()) {
final String searchKeys = entry.getKey();
final String searchValue = entry.getValue();
if (searchKeys.isEmpty() || searchValue.isEmpty()) {
throw new InvalidParseOperationException("Parameters should not be empty or null");
}
// Add projection for fields prefixed by projection_
if (searchKeys.startsWith(PROJECTION_PREFIX)) {
select.addUsedProjection(searchValue);
continue;
}
// Add order by
if (searchKeys.equals(ORDER_BY)) {
select.addOrderByAscFilter(searchValue);
continue;
}
// Add root
if (searchKeys.equals(UiConstants.SELECT_BY_ID.toString())) {
select.addRoots(searchValue);
continue;
}
// By default add equals query
booleanQueries.add(eq(searchKeys, searchValue));
}
if (booleanQueries.isReady()) {
booleanQueries.setDepthLimit(DEPTH_LIMIT);
select.addQueries(booleanQueries);
}
return select.getFinalSelect();
}
/**
* @param searchCriteriaMap Criteria received from The IHM screen Empty Keys or Value is not allowed
* @return the JSONDSL File
* @throws InvalidParseOperationException thrown when an error occurred during parsing
* @throws InvalidCreateOperationException thrown when an error occurred during creation
*/
public static JsonNode createSelectElasticsearchDSLQuery(Map<String, String> searchCriteriaMap)
throws InvalidParseOperationException, InvalidCreateOperationException {
final Select select = new Select();
final BooleanQuery andQuery = and();
final BooleanQuery booleanQueries = or();
String startDate = null;
String endDate = null;
String advancedSearchFlag = "";
for (final Entry<String, String> entry : searchCriteriaMap.entrySet()) {
final String searchKeys = entry.getKey();
final String searchValue = entry.getValue();
if (searchKeys.isEmpty() || searchValue.isEmpty()) {
throw new InvalidParseOperationException("Parameters should not be empty or null");
}
// Add projection for fields prefixed by projection_
if (searchKeys.startsWith(PROJECTION_PREFIX)) {
select.addUsedProjection(searchValue);
continue;
}
// Add order by
if (searchKeys.equals(ORDER_BY)) {
select.addOrderByAscFilter(searchValue);
continue;
}
// Add root
if (searchKeys.equals(UiConstants.SELECT_BY_ID.toString())) {
select.addRoots(searchValue);
continue;
}
if (searchKeys.equals(TITLE_AND_DESCRIPTION)) {
booleanQueries.add(match(TITLE, searchValue));
booleanQueries.add(match(DESCRIPTION, searchValue));
continue;
}
if (searchKeys.equalsIgnoreCase(UiConstants.ID.getReceivedCriteria())) {
andQuery.add(eq(UiConstants.ID.getResultCriteria(), searchValue));
continue;
}
if (searchKeys.equalsIgnoreCase(TITLE)) {
andQuery.add(match(TITLE, searchValue));
continue;
}
if (searchKeys.equalsIgnoreCase(DESCRIPTION)) {
andQuery.add(match(DESCRIPTION, searchValue));
continue;
}
if (searchKeys.startsWith(START_PREFIX)) {
startDate = searchValue;
continue;
}
if (searchKeys.startsWith(END_PREFIX)) {
endDate = searchValue;
continue;
}
if (searchKeys.equalsIgnoreCase(ADVANCED_SEARCH_FLAG)) {
advancedSearchFlag = searchValue;
continue;
}
// By default add equals query
booleanQueries.add(match(searchKeys, searchValue));
}
// US 509:start AND end date must be filled.
if (!Strings.isNullOrEmpty(endDate) && !Strings.isNullOrEmpty(startDate)) {
andQuery.add(createSearchUntisQueryByDate(startDate, endDate));
}
if (advancedSearchFlag.equalsIgnoreCase(YES)) {
if (andQuery.isReady()) {
andQuery.setDepthLimit(DEPTH_LIMIT);
select.addQueries(andQuery);
}
} else {
if (booleanQueries.isReady()) {
booleanQueries.setDepthLimit(DEPTH_LIMIT);
select.addQueries(booleanQueries);
}
}
return select.getFinalSelect();
}
/**
* @param searchCriteriaMap Criteria received from The IHM screen Empty Keys or Value is not allowed
* @return the JSONDSL File
* @throws InvalidParseOperationException thrown when an error occurred during parsing
* @throws InvalidCreateOperationException thrown when an error occurred during creation
*/
public static JsonNode createUpdateDSLQuery(Map<String, String> searchCriteriaMap)
throws InvalidParseOperationException, InvalidCreateOperationException {
final Update update = new Update();
for (final Entry<String, String> entry : searchCriteriaMap.entrySet()) {
final String searchKeys = entry.getKey();
final String searchValue = entry.getValue();
if (searchKeys.isEmpty()) {
throw new InvalidParseOperationException("Parameters should not be empty or null");
}
// Add root
if (searchKeys.equals(UiConstants.SELECT_BY_ID.toString())) {
update.addRoots(searchValue);
continue;
}
// Add Actions
update.addActions(new SetAction(searchKeys, searchValue));
}
return update.getFinalUpdate();
}
/**
* Creates Select Query to retrieve all parents relative to the unit specified by its id
*
* @param unitId the unit id
* @param immediateParents immediate parents (_up field value)
* @return DSL Select Query
* @throws InvalidParseOperationException
* @throws InvalidCreateOperationException
*/
public static JsonNode createSelectUnitTreeDSLQuery(String unitId, List<String> immediateParents)
throws InvalidParseOperationException, InvalidCreateOperationException {
final Select selectParentsDetails = new Select();
// Add projections
// Title
selectParentsDetails.addUsedProjection(UiConstants.TITLE.getResultCriteria());
// id
selectParentsDetails.addUsedProjection(UiConstants.ID.getResultCriteria());
// _up
selectParentsDetails.addUsedProjection(UiConstants.UNITUPS.getResultCriteria());
// Add query
// Initialize immediateParents if it is null
if (immediateParents == null) {
immediateParents = new ArrayList<>();
}
immediateParents.add(unitId);
final String[] allParentsArray = immediateParents.stream().toArray(size -> new String[size]);
final BooleanQuery inParentsIdListQuery = and();
inParentsIdListQuery.add(in(UiConstants.ID.getResultCriteria(), allParentsArray))
.setDepthLimit(DEPTH_LIMIT);
if (inParentsIdListQuery.isReady()) {
selectParentsDetails.addQueries(inParentsIdListQuery);
}
return selectParentsDetails.getFinalSelect();
}
private static BooleanQuery createSearchUntisQueryByDate(String startDate, String endDate)
throws InvalidCreateOperationException {
LOGGER.debug("in createSearchUntisQueryByDate / beginDate:" + startDate + "/ endDate:" + endDate);
final BooleanQuery query = or();
if (!Strings.isNullOrEmpty(endDate) && !Strings.isNullOrEmpty(startDate)) {
final BooleanQuery transactedDateBetween = and();
// search by transacted date
transactedDateBetween.add(gte(TRANSACTED_DATE, startDate));
transactedDateBetween.add(lte(TRANSACTED_DATE, endDate));
query.add(transactedDateBetween);
// search by begin and end date
final BooleanQuery queryAroundDate = and();
queryAroundDate.add(gte(END_DATE, startDate));
queryAroundDate.add(lte(START_DATE, endDate));
query.add(queryAroundDate);
}
LOGGER.debug("in createSearchUntisQueryByDate / query:" + query.toString());
return query;
}
}