| /* |
| * 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; |
| } |
| } |