blob: b06c94ec407ef328ac84c13f42bdcef780078140 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.unomi.plugins.baseplugin.conditions;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.unomi.api.conditions.Condition;
import org.apache.unomi.persistence.elasticsearch.conditions.ConditionContextHelper;
import org.apache.unomi.persistence.elasticsearch.conditions.ConditionESQueryBuilder;
import org.apache.unomi.persistence.elasticsearch.conditions.ConditionESQueryBuilderDispatcher;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.QueryBuilder;
import org.elasticsearch.index.query.QueryBuilders;
import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormatter;
import org.joda.time.format.ISODateTimeFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
public class PropertyConditionESQueryBuilder implements ConditionESQueryBuilder {
DateTimeFormatter dateTimeFormatter;
public PropertyConditionESQueryBuilder() {
dateTimeFormatter = ISODateTimeFormat.dateTime();
}
@Override
public QueryBuilder buildQuery(Condition condition, Map<String, Object> context, ConditionESQueryBuilderDispatcher dispatcher) {
String comparisonOperator = (String) condition.getParameter("comparisonOperator");
String name = (String) condition.getParameter("propertyName");
if (comparisonOperator == null || name == null) {
throw new IllegalArgumentException("Impossible to build ES filter, condition is not valid, comparisonOperator and propertyName properties should be provided");
}
String expectedValue = ConditionContextHelper.foldToASCII((String) condition.getParameter("propertyValue"));
Object expectedValueInteger = condition.getParameter("propertyValueInteger");
Object expectedValueDate = convertDateToISO(condition.getParameter("propertyValueDate"));
Object expectedValueDateExpr = condition.getParameter("propertyValueDateExpr");
List<?> expectedValues = ConditionContextHelper.foldToASCII((List<?>) condition.getParameter("propertyValues"));
List<?> expectedValuesInteger = (List<?>) condition.getParameter("propertyValuesInteger");
List<?> expectedValuesDate = convertDatesToISO((List<?>) condition.getParameter("propertyValuesDate"));
List<?> expectedValuesDateExpr = (List<?>) condition.getParameter("propertyValuesDateExpr");
Object value = ObjectUtils.firstNonNull(expectedValue, expectedValueInteger, expectedValueDate, expectedValueDateExpr);
@SuppressWarnings("unchecked")
List<?> values = ObjectUtils.firstNonNull(expectedValues, expectedValuesInteger, expectedValuesDate, expectedValuesDateExpr);
switch (comparisonOperator) {
case "equals":
checkRequiredValue(value, name, comparisonOperator, false);
return QueryBuilders.termQuery(name, value);
case "notEquals":
checkRequiredValue(value, name, comparisonOperator, false);
return QueryBuilders.boolQuery().mustNot(QueryBuilders.termQuery(name, value));
case "greaterThan":
checkRequiredValue(value, name, comparisonOperator, false);
return QueryBuilders.rangeQuery(name).gt(value);
case "greaterThanOrEqualTo":
checkRequiredValue(value, name, comparisonOperator, false);
return QueryBuilders.rangeQuery(name).gte(value);
case "lessThan":
checkRequiredValue(value, name, comparisonOperator, false);
return QueryBuilders.rangeQuery(name).lt(value);
case "lessThanOrEqualTo":
checkRequiredValue(value, name, comparisonOperator, false);
return QueryBuilders.rangeQuery(name).lte(value);
case "between":
checkRequiredValuesSize(values, name, comparisonOperator, 2);
return QueryBuilders.rangeQuery(name).gte(values.get(0)).lte(values.get(1));
case "exists":
return QueryBuilders.existsQuery(name);
case "missing":
return QueryBuilders.boolQuery().mustNot(QueryBuilders.existsQuery((name)));
case "contains":
checkRequiredValue(expectedValue, name, comparisonOperator, false);
return QueryBuilders.regexpQuery(name, ".*" + expectedValue + ".*");
case "notContains":
checkRequiredValue(expectedValue, name, comparisonOperator, false);
return QueryBuilders.boolQuery().mustNot(QueryBuilders.regexpQuery(name, ".*" + expectedValue + ".*"));
case "startsWith":
checkRequiredValue(expectedValue, name, comparisonOperator, false);
return QueryBuilders.prefixQuery(name, expectedValue);
case "endsWith":
checkRequiredValue(expectedValue, name, comparisonOperator, false);
return QueryBuilders.regexpQuery(name, ".*" + expectedValue);
case "matchesRegex":
checkRequiredValue(expectedValue, name, comparisonOperator, false);
return QueryBuilders.regexpQuery(name, expectedValue);
case "in":
checkRequiredValue(values, name, comparisonOperator, true);
return QueryBuilders.termsQuery(name, values.toArray());
case "notIn":
checkRequiredValue(values, name, comparisonOperator, true);
return QueryBuilders.boolQuery().mustNot(QueryBuilders.termsQuery(name, values.toArray()));
case "all":
checkRequiredValue(values, name, comparisonOperator, true);
BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
for (Object curValue : values) {
boolQueryBuilder.must(QueryBuilders.termQuery(name, curValue));
}
return boolQueryBuilder;
case "inContains":
checkRequiredValue(values, name, comparisonOperator, true);
BoolQueryBuilder boolQueryBuilderInContains = QueryBuilders.boolQuery();
for (Object curValue : values) {
boolQueryBuilderInContains.must(QueryBuilders.regexpQuery(name, ".*" + curValue + ".*"));
}
return boolQueryBuilderInContains;
case "hasSomeOf":
checkRequiredValue(values, name, comparisonOperator, true);
boolQueryBuilder = QueryBuilders.boolQuery();
for (Object curValue : values) {
boolQueryBuilder.should(QueryBuilders.termQuery(name, curValue));
}
return boolQueryBuilder;
case "hasNoneOf":
checkRequiredValue(values, name, comparisonOperator, true);
boolQueryBuilder = QueryBuilders.boolQuery();
for (Object curValue : values) {
boolQueryBuilder.mustNot(QueryBuilders.termQuery(name, curValue));
}
return boolQueryBuilder;
case "isDay":
checkRequiredValue(value, name, comparisonOperator, false);
return getIsSameDayRange(value, name);
case "isNotDay":
checkRequiredValue(value, name, comparisonOperator, false);
return QueryBuilders.boolQuery().mustNot(getIsSameDayRange(value, name));
}
return null;
}
private void checkRequiredValuesSize(List<?> values, String name, String operator, int expectedSize) {
if (values == null || values.size() != expectedSize) {
throw new IllegalArgumentException("Impossible to build ES filter, missing " + expectedSize + " values for a condition using comparisonOperator: " + operator + ", and propertyName: " + name);
}
}
private void checkRequiredValue(Object value, String name, String operator, boolean multiple) {
if (value == null) {
throw new IllegalArgumentException("Impossible to build ES filter, missing value" + (multiple ? "s" : "") + " for condition using comparisonOperator: " + operator + ", and propertyName: " + name);
}
}
private QueryBuilder getIsSameDayRange(Object value, String name) {
DateTime date = new DateTime(value);
DateTime dayStart = date.withTimeAtStartOfDay();
DateTime dayAfterStart = date.plusDays(1).withTimeAtStartOfDay();
return QueryBuilders.rangeQuery(name).gte(convertDateToISO(dayStart.toDate())).lte(convertDateToISO(dayAfterStart.toDate()));
}
private Object convertDateToISO(Object dateValue) {
if (dateValue == null) {
return dateValue;
}
if (dateValue instanceof Date) {
return dateTimeFormatter.print(new DateTime(dateValue));
} else {
return dateValue;
}
}
private List<?> convertDatesToISO(List<?> datesValues) {
List<Object> results = new ArrayList<>();
if (datesValues == null) {
return null;
}
for (Object dateValue : datesValues) {
if (dateValue != null) {
results.add(convertDateToISO(dateValue));
}
}
return results;
}
}