This is an automated email from the ASF dual-hosted git repository.
dgriffon pushed a commit to branch unomi-1.6.x
in repository https://gitbox.apache.org/repos/asf/unomi.git
The following commit(s) were added to refs/heads/unomi-1.6.x by this push:
new 45fe9aa UNOMI-523 : add date expression support to isDay / isNotDay
property comparison (#359)
45fe9aa is described below
commit 45fe9aad4e8537d61dc67ec77f60eef4b09464fc
Author: David Griffon <[email protected]>
AuthorDate: Mon Nov 8 13:48:06 2021 +0100
UNOMI-523 : add date expression support to isDay / isNotDay property
comparison (#359)
* UNOMI-523 : add date expression to isDay / isNotDay property comparison
operator. (bonus, fix double property type support)
* DMF-5016 : add support of date expression for multiple values
---
.../org/apache/unomi/itests/ConditionBuilder.java | 24 ++++++++++++
.../apache/unomi/itests/ConditionEvaluatorIT.java | 7 ++++
.../PropertyConditionESQueryBuilder.java | 26 ++++++++++++-
.../conditions/PropertyConditionEvaluator.java | 43 ++++++++++++++++++++--
4 files changed, 96 insertions(+), 4 deletions(-)
diff --git a/itests/src/test/java/org/apache/unomi/itests/ConditionBuilder.java
b/itests/src/test/java/org/apache/unomi/itests/ConditionBuilder.java
index 2d01f97..69a210f 100644
--- a/itests/src/test/java/org/apache/unomi/itests/ConditionBuilder.java
+++ b/itests/src/test/java/org/apache/unomi/itests/ConditionBuilder.java
@@ -141,6 +141,10 @@ public class ConditionBuilder {
return op("in").stringValues(values);
}
+ public ComparisonCondition inDateExpr(String... values) {
+ return op("in").dateExprValues(values);
+ }
+
public ComparisonCondition in(Date... values) {
return op("in").dateValues(values);
}
@@ -149,10 +153,18 @@ public class ConditionBuilder {
return op("isDay").dateValue(value);
}
+ public ComparisonCondition isDay(String expression) {
+ return op("isDay").dateValueExpr(expression);
+ }
+
public ComparisonCondition isNotDay(Date value) {
return op("isNotDay").dateValue(value);
}
+ public ComparisonCondition isNotDay(String expression) {
+ return op("isNotDay").dateValueExpr(expression);
+ }
+
public ComparisonCondition in(Integer... values) {
return op("in").integerValues(values);
}
@@ -221,6 +233,10 @@ public class ConditionBuilder {
return op("notIn").dateValues(values);
}
+ public ComparisonCondition notInDateExpr(String... values) {
+ return op("notIn").dateExprValues(values);
+ }
+
public ComparisonCondition notIn(Integer... values) {
return op("notIn").integerValues(values);
}
@@ -262,6 +278,10 @@ public class ConditionBuilder {
return parameter("propertyValueDate", value);
}
+ private ComparisonCondition dateValueExpr(String value) {
+ return parameter("propertyValueDateExpr", value);
+ }
+
private ComparisonCondition stringValues(String... values) {
return parameter("propertyValues", values != null ?
Arrays.asList(values) : null);
}
@@ -277,6 +297,10 @@ public class ConditionBuilder {
private ComparisonCondition dateValues(Date... values) {
return parameter("propertyValuesDate", values != null ?
Arrays.asList(values) : null);
}
+
+ private ComparisonCondition dateExprValues(String... values) {
+ return parameter("propertyValuesDateExpr", values != null ?
Arrays.asList(values) : null);
+ }
}
public class CompoundCondition extends ConditionItem {
diff --git
a/itests/src/test/java/org/apache/unomi/itests/ConditionEvaluatorIT.java
b/itests/src/test/java/org/apache/unomi/itests/ConditionEvaluatorIT.java
index a1f93a6..749fc71 100644
--- a/itests/src/test/java/org/apache/unomi/itests/ConditionEvaluatorIT.java
+++ b/itests/src/test/java/org/apache/unomi/itests/ConditionEvaluatorIT.java
@@ -17,6 +17,7 @@
package org.apache.unomi.itests;
+import org.apache.commons.lang3.time.DateUtils;
import org.apache.unomi.api.Item;
import org.apache.unomi.api.Profile;
import org.apache.unomi.api.conditions.Condition;
@@ -32,6 +33,7 @@ import org.ops4j.pax.exam.util.Filter;
import javax.inject.Inject;
import java.util.*;
+import java.util.concurrent.TimeUnit;
import static org.junit.Assert.*;
@@ -116,6 +118,11 @@ public class ConditionEvaluatorIT extends BaseIT {
assertTrue(eval(builder.profileProperty("properties.lastVisit").isDay(lastVisit).build()));
assertTrue(eval(builder.profileProperty("properties.lastVisit").isNotDay(new
Date(lastVisit.getTime() + (24*60*60*1000))).build()));
+ long daysFromToday =
TimeUnit.MILLISECONDS.toDays(DateUtils.truncate(new Date(),
Calendar.DAY_OF_MONTH).getTime() - DateUtils.truncate(lastVisit,
Calendar.DAY_OF_MONTH).getTime());
+
assertTrue(eval(builder.profileProperty("properties.lastVisit").isDay("now-" +
daysFromToday + "d").build()));
+
assertTrue(eval(builder.profileProperty("properties.lastVisit").isNotDay("now-"
+ (daysFromToday + 1) + "d").build()));
+
assertTrue(eval(builder.profileProperty("properties.lastVisit").inDateExpr("" +
lastVisit.getTime()).build()));
+
assertTrue(eval(builder.profileProperty("properties.lastVisit").notInDateExpr("now-"
+ (daysFromToday + 1) + "d").build()));
}
@Test
diff --git
a/plugins/baseplugin/src/main/java/org/apache/unomi/plugins/baseplugin/conditions/PropertyConditionESQueryBuilder.java
b/plugins/baseplugin/src/main/java/org/apache/unomi/plugins/baseplugin/conditions/PropertyConditionESQueryBuilder.java
index e9a5dfb..994924d 100644
---
a/plugins/baseplugin/src/main/java/org/apache/unomi/plugins/baseplugin/conditions/PropertyConditionESQueryBuilder.java
+++
b/plugins/baseplugin/src/main/java/org/apache/unomi/plugins/baseplugin/conditions/PropertyConditionESQueryBuilder.java
@@ -34,6 +34,8 @@ import java.util.Date;
import java.util.List;
import java.util.Map;
+import static
org.apache.unomi.plugins.baseplugin.conditions.PropertyConditionEvaluator.getDate;
+
public class PropertyConditionESQueryBuilder implements
ConditionESQueryBuilder {
DateTimeFormatter dateTimeFormatter;
@@ -144,9 +146,31 @@ public class PropertyConditionESQueryBuilder implements
ConditionESQueryBuilder
return boolQueryBuilder;
case "isDay":
checkRequiredValue(value, name, comparisonOperator, false);
- return getIsSameDayRange(value, name);
+ return getIsSameDayRange(getDate(value), name);
case "isNotDay":
checkRequiredValue(value, name, comparisonOperator, false);
+ return
QueryBuilders.boolQuery().mustNot(getIsSameDayRange(getDate(value), name));
+ case "distance":
+ final String unitString = (String)
condition.getParameter("unit");
+ final Object centerObj = condition.getParameter("center");
+ final Double distance = (Double)
condition.getParameter("distance");
+
+ if (centerObj != null && distance != null) {
+ String centerString;
+ if (centerObj instanceof org.apache.unomi.api.GeoPoint) {
+ centerString = ((org.apache.unomi.api.GeoPoint)
centerObj).asString();
+ } else if (centerObj instanceof String) {
+ centerString = (String) centerObj;
+ } else {
+ centerString = centerObj.toString();
+ }
+ DistanceUnit unit = unitString != null ?
DistanceUnit.fromString(unitString) : DistanceUnit.DEFAULT;
+
+ return QueryBuilders.geoDistanceQuery(name)
+ .ignoreUnmapped(true)
+ .distance(distance, unit)
+ .point(new GeoPoint(centerString));
+ }
return
QueryBuilders.boolQuery().mustNot(getIsSameDayRange(value, name));
}
return null;
diff --git
a/plugins/baseplugin/src/main/java/org/apache/unomi/plugins/baseplugin/conditions/PropertyConditionEvaluator.java
b/plugins/baseplugin/src/main/java/org/apache/unomi/plugins/baseplugin/conditions/PropertyConditionEvaluator.java
index 5dfee9f..2ecd3b7 100644
---
a/plugins/baseplugin/src/main/java/org/apache/unomi/plugins/baseplugin/conditions/PropertyConditionEvaluator.java
+++
b/plugins/baseplugin/src/main/java/org/apache/unomi/plugins/baseplugin/conditions/PropertyConditionEvaluator.java
@@ -41,6 +41,7 @@ import java.lang.reflect.Modifier;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.regex.Pattern;
+import java.util.stream.Collectors;
/**
* Evaluator for property comparison conditions
@@ -68,7 +69,7 @@ public class PropertyConditionEvaluator implements
ConditionEvaluator {
}
private int compare(Object actualValue, String expectedValue, Object
expectedValueDate, Object expectedValueInteger, Object expectedValueDateExpr,
Object expectedValueDouble) {
- if (expectedValue == null && expectedValueDate == null &&
expectedValueInteger == null && getDate(expectedValueDateExpr) == null) {
+ if (expectedValue == null && expectedValueDate == null &&
expectedValueInteger == null && getDate(expectedValueDateExpr) == null &&
expectedValueDouble == null) {
return actualValue == null ? 0 : 1;
} else if (actualValue == null) {
return -1;
@@ -87,8 +88,13 @@ public class PropertyConditionEvaluator implements
ConditionEvaluator {
}
}
- private boolean compareMultivalue(Object actualValue, List<?>
expectedValues, List<?> expectedValuesDate, List<?> expectedValuesNumber,
List<?> expectedValuesDateExpr, String op) {
+ private boolean compareValues(Object actualValue, Collection<?>
expectedValues, Collection<?> expectedValuesInteger, Collection<?>
expectedValuesDouble, Collection<?> expectedValuesDate, Collection<?>
expectedValuesDateExpr, String op) {
+ Collection<Object> expectedDateExpr = null;
+ if (expectedValuesDateExpr != null) {
+ expectedDateExpr =
expectedValuesDateExpr.stream().map(PropertyConditionEvaluator::getDate).collect(Collectors.toList());
+ }
@SuppressWarnings("unchecked")
+ Collection<?> expected = ObjectUtils.firstNonNull(expectedValues,
expectedValuesDate, expectedValuesInteger, expectedValuesDouble,
expectedDateExpr);
List<?> expected = ObjectUtils.firstNonNull(expectedValues,
expectedValuesDate, expectedValuesNumber);
if (actualValue == null) {
return expected == null;
@@ -196,10 +202,17 @@ public class PropertyConditionEvaluator implements
ConditionEvaluator {
actualValue = ConditionContextHelper.foldToASCII((String)
actualValue);
}
+ return isMatch(op, actualValue, expectedValue, expectedValueInteger,
expectedValueDouble, expectedValueDate,
+ expectedValueDateExpr, condition);
+ }
+
+ protected boolean isMatch(String op, Object actualValue, String
expectedValue, Object expectedValueInteger, Object expectedValueDouble,
+ Object expectedValueDate, Object
expectedValueDateExpr, Condition condition) {
if (op == null) {
return false;
} else if (actualValue == null) {
return op.equals("missing");
+ return op.equals("missing") || op.equals("notIn") ||
op.equals("notEquals") || op.equals("hasNoneOf");
} else if (op.equals("exists")) {
return true;
} else if (op.equals("equals")) {
@@ -252,11 +265,35 @@ public class PropertyConditionEvaluator implements
ConditionEvaluator {
} else if (op.equals("matchesRegex")) {
return expectedValue != null &&
Pattern.compile(expectedValue).matcher(actualValue.toString()).matches();
} else if (op.equals("in") || op.equals("inContains") ||
op.equals("notIn") || op.equals("hasSomeOf") || op.equals("hasNoneOf") ||
op.equals("all")) {
+ Collection<?> expectedValues =
ConditionContextHelper.foldToASCII((Collection<?>)
condition.getParameter("propertyValues"));
+ Collection<?> expectedValuesInteger = (Collection<?>)
condition.getParameter("propertyValuesInteger");
+ Collection<?> expectedValuesDate = (Collection<?>)
condition.getParameter("propertyValuesDate");
+ Collection<?> expectedValuesDateExpr = (Collection<?>)
condition.getParameter("propertyValuesDateExpr");
+ Collection<?> expectedValuesDouble = (Collection<?>)
condition.getParameter("propertyValuesDouble");
List<?> expectedValues =
ConditionContextHelper.foldToASCII((List<?>)
condition.getParameter("propertyValues"));
List<?> expectedValuesInteger = (List<?>)
condition.getParameter("propertyValuesInteger");
List<?> expectedValuesDate = (List<?>)
condition.getParameter("propertyValuesDate");
List<?> expectedValuesDateExpr = (List<?>)
condition.getParameter("propertyValuesDateExpr");
+ return compareValues(actualValue, expectedValues,
expectedValuesInteger, expectedValuesDouble, expectedValuesDate,
expectedValuesDateExpr, op);
+ } else if (op.equals("isDay") && (expectedValueDate != null ||
expectedValueDateExpr != null)) {
+ Object expectedDate = expectedValueDate == null ?
expectedValueDateExpr : expectedValueDate;
+ return
yearMonthDayDateFormat.format(getDate(actualValue)).equals(yearMonthDayDateFormat.format(getDate(expectedDate)));
+ } else if (op.equals("isNotDay") && (expectedValueDate != null ||
expectedValueDateExpr != null)) {
+ Object expectedDate = expectedValueDate == null ?
expectedValueDateExpr : expectedValueDate;
+ return
!yearMonthDayDateFormat.format(getDate(actualValue)).equals(yearMonthDayDateFormat.format(getDate(expectedDate)));
+ } else if (op.equals("distance")) {
+ GeoPoint actualCenter = null;
+ if (actualValue instanceof GeoPoint) {
+ actualCenter = (GeoPoint) actualValue;
+ } else if (actualValue instanceof Map) {
+ actualCenter = GeoPoint.fromMap((Map<String, Double>)
actualValue);
+ } else if (actualValue instanceof String) {
+ actualCenter = GeoPoint.fromString((String) actualValue);
+ }
+ if (actualCenter == null) {
+ return false;
+ }
return compareMultivalue(actualValue, expectedValues,
expectedValuesDate, expectedValuesInteger, expectedValuesDateExpr, op);
} else if (op.equals("isDay") && expectedValueDate != null) {
return
yearMonthDayDateFormat.format(getDate(actualValue)).equals(yearMonthDayDateFormat.format(getDate(expectedValueDate)));
@@ -399,7 +436,7 @@ public class PropertyConditionEvaluator implements
ConditionEvaluator {
return accessor;
}
- private Date getDate(Object value) {
+ protected static Date getDate(Object value) {
if (value == null) {
return null;
}