You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@unomi.apache.org by jk...@apache.org on 2022/01/28 15:37:52 UTC
[unomi] 01/01: UNOMI-550: handle negative past event condition and code cleaning
This is an automated email from the ASF dual-hosted git repository.
jkevan pushed a commit to branch handleNegativePastEventCondition
in repository https://gitbox.apache.org/repos/asf/unomi.git
commit a734dc7cad55fd2dc4cfe5a53b7aad9181960b4a
Author: Kevan <ke...@jahia.com>
AuthorDate: Fri Jan 28 16:37:37 2022 +0100
UNOMI-550: handle negative past event condition and code cleaning
---
.../java/org/apache/unomi/itests/SegmentIT.java | 45 +++++
.../conditions/IdsConditionESQueryBuilder.java | 57 ++++++
.../conditions/IdsConditionEvaluator.java | 36 ++++
.../PastEventConditionESQueryBuilder.java | 218 ++++++++++++---------
.../conditions/PastEventConditionEvaluator.java | 72 +------
.../META-INF/cxs/conditions/IdsCondition.json | 23 +++
.../cxs/conditions/pastEventCondition.json | 5 +
.../resources/OSGI-INF/blueprint/blueprint.xml | 17 ++
8 files changed, 314 insertions(+), 159 deletions(-)
diff --git a/itests/src/test/java/org/apache/unomi/itests/SegmentIT.java b/itests/src/test/java/org/apache/unomi/itests/SegmentIT.java
index 55f3ce9..781cce1 100644
--- a/itests/src/test/java/org/apache/unomi/itests/SegmentIT.java
+++ b/itests/src/test/java/org/apache/unomi/itests/SegmentIT.java
@@ -192,6 +192,51 @@ public class SegmentIT extends BaseIT {
}
@Test
+ public void testSegmentWithNegativePastEventCondition() throws InterruptedException {
+ // create Profile
+ Profile profile = new Profile();
+ profile.setItemId("test_profile_id");
+ profileService.save(profile);
+ persistenceService.refreshIndex(Profile.class, null); // wait for profile to be full persisted and index
+
+ // create the negative past event condition segment
+ Metadata segmentMetadata = new Metadata("negative-past-event-segment-test");
+ Segment segment = new Segment(segmentMetadata);
+ Condition segmentCondition = new Condition(definitionsService.getConditionType("pastEventCondition"));
+ segmentCondition.setParameter("numberOfDays", 10);
+ Condition pastEventEventCondition = new Condition(definitionsService.getConditionType("eventTypeCondition"));
+ pastEventEventCondition.setParameter("eventTypeId", "negative-test-event-type");
+ segmentCondition.setParameter("eventCondition", pastEventEventCondition);
+ segmentCondition.setParameter("operator", "eventsNotOccurred");
+ segment.setCondition(segmentCondition);
+ segmentService.setSegmentDefinition(segment);
+
+ // insure that profile is correctly engaged in sement since there is no events yet.
+ keepTrying("Profile should be engaged in the segment, there is no event for the past condition yet",
+ () -> profileService.load("test_profile_id"),
+ updatedProfile -> updatedProfile.getSegments().contains("negative-past-event-segment-test"),
+ 1000, 20);
+
+ // send event for profile from a previous date (today -3 days)
+ ZoneId defaultZoneId = ZoneId.systemDefault();
+ LocalDate localDate = LocalDate.now().minusDays(3);
+ Event testEvent = new Event("negative-test-event-type", null, profile, null, null, profile, Date.from(localDate.atStartOfDay(defaultZoneId).toInstant()));
+ testEvent.setPersistent(true);
+ int changes = eventService.send(testEvent);
+ if ((changes & EventService.PROFILE_UPDATED) == EventService.PROFILE_UPDATED) {
+ profileService.save(profile);
+ persistenceService.refreshIndex(Profile.class, null);
+ }
+ persistenceService.refreshIndex(Event.class, testEvent.getTimeStamp()); // wait for event to be fully persisted and indexed
+
+ // now Profile should be out of the segment since one event have been done and the past event is only valid for no events occurrences
+ keepTrying("Profile should not be engaged in the segment anymore, it have a least one event now",
+ () -> profileService.load("test_profile_id"),
+ updatedProfile -> !updatedProfile.getSegments().contains("negative-past-event-segment-test"),
+ 1000, 20);
+ }
+
+ @Test
public void testSegmentPastEventRecalculation() throws Exception {
// create Profile
Profile profile = new Profile();
diff --git a/plugins/baseplugin/src/main/java/org/apache/unomi/plugins/baseplugin/conditions/IdsConditionESQueryBuilder.java b/plugins/baseplugin/src/main/java/org/apache/unomi/plugins/baseplugin/conditions/IdsConditionESQueryBuilder.java
new file mode 100644
index 0000000..d535c1b
--- /dev/null
+++ b/plugins/baseplugin/src/main/java/org/apache/unomi/plugins/baseplugin/conditions/IdsConditionESQueryBuilder.java
@@ -0,0 +1,57 @@
+/*
+ * 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.unomi.api.conditions.Condition;
+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.IdsQueryBuilder;
+import org.elasticsearch.index.query.QueryBuilder;
+import org.elasticsearch.index.query.QueryBuilders;
+
+import java.util.Collection;
+import java.util.Map;
+
+public class IdsConditionESQueryBuilder implements ConditionESQueryBuilder {
+
+ private int maximumIdsQueryCount = 5000;
+
+ public void setMaximumIdsQueryCount(int maximumIdsQueryCount) {
+ this.maximumIdsQueryCount = maximumIdsQueryCount;
+ }
+
+ @Override
+ public QueryBuilder buildQuery(Condition condition, Map<String, Object> context, ConditionESQueryBuilderDispatcher dispatcher) {
+ Collection<String> ids = (Collection<String>) condition.getParameter("ids");
+ Boolean match = (Boolean) condition.getParameter("match");
+
+ if (ids.size() > maximumIdsQueryCount) {
+ // Avoid building too big ids query - throw exception instead
+ throw new UnsupportedOperationException("Too many profiles");
+ }
+
+ IdsQueryBuilder idsQueryBuilder = QueryBuilders.idsQuery().addIds(ids.toArray(new String[0]));
+ if (match) {
+ return idsQueryBuilder;
+ } else {
+ BoolQueryBuilder boolQuery = QueryBuilders.boolQuery();
+ boolQuery.mustNot(idsQueryBuilder);
+ return boolQuery;
+ }
+ }
+}
diff --git a/plugins/baseplugin/src/main/java/org/apache/unomi/plugins/baseplugin/conditions/IdsConditionEvaluator.java b/plugins/baseplugin/src/main/java/org/apache/unomi/plugins/baseplugin/conditions/IdsConditionEvaluator.java
new file mode 100644
index 0000000..d4f1ca8
--- /dev/null
+++ b/plugins/baseplugin/src/main/java/org/apache/unomi/plugins/baseplugin/conditions/IdsConditionEvaluator.java
@@ -0,0 +1,36 @@
+/*
+ * 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.unomi.api.Item;
+import org.apache.unomi.api.conditions.Condition;
+import org.apache.unomi.persistence.elasticsearch.conditions.ConditionEvaluator;
+import org.apache.unomi.persistence.elasticsearch.conditions.ConditionEvaluatorDispatcher;
+
+import java.util.Collection;
+import java.util.Map;
+
+public class IdsConditionEvaluator implements ConditionEvaluator {
+ @Override
+ public boolean eval(Condition condition, Item item, Map<String, Object> context, ConditionEvaluatorDispatcher dispatcher) {
+ Collection<String> ids = (Collection<String>) condition.getParameter("ids");
+ Boolean match = (Boolean) condition.getParameter("match");
+
+ boolean contained = ids != null && !ids.isEmpty() && ids.contains(item.getItemId());
+ return match == contained;
+ }
+}
diff --git a/plugins/baseplugin/src/main/java/org/apache/unomi/plugins/baseplugin/conditions/PastEventConditionESQueryBuilder.java b/plugins/baseplugin/src/main/java/org/apache/unomi/plugins/baseplugin/conditions/PastEventConditionESQueryBuilder.java
index c76a750..4491eb6 100644
--- a/plugins/baseplugin/src/main/java/org/apache/unomi/plugins/baseplugin/conditions/PastEventConditionESQueryBuilder.java
+++ b/plugins/baseplugin/src/main/java/org/apache/unomi/plugins/baseplugin/conditions/PastEventConditionESQueryBuilder.java
@@ -20,6 +20,7 @@ package org.apache.unomi.plugins.baseplugin.conditions;
import org.apache.unomi.api.Event;
import org.apache.unomi.api.Profile;
import org.apache.unomi.api.conditions.Condition;
+import org.apache.unomi.api.conditions.ConditionType;
import org.apache.unomi.api.services.DefinitionsService;
import org.apache.unomi.api.services.SegmentService;
import org.apache.unomi.persistence.elasticsearch.conditions.ConditionContextHelper;
@@ -28,9 +29,7 @@ import org.apache.unomi.persistence.elasticsearch.conditions.ConditionESQueryBui
import org.apache.unomi.persistence.spi.PersistenceService;
import org.apache.unomi.persistence.spi.aggregate.TermsAggregate;
import org.apache.unomi.scripting.ScriptExecutor;
-import org.elasticsearch.index.query.QueryBuilder;
-import org.elasticsearch.index.query.QueryBuilders;
-import org.elasticsearch.index.query.RangeQueryBuilder;
+import org.elasticsearch.index.query.*;
import java.util.*;
import java.util.stream.Collectors;
@@ -74,113 +73,137 @@ public class PastEventConditionESQueryBuilder implements ConditionESQueryBuilder
this.segmentService = segmentService;
}
+ @Override
public QueryBuilder buildQuery(Condition condition, Map<String, Object> context, ConditionESQueryBuilderDispatcher dispatcher) {
- Integer minimumEventCount = condition.getParameter("minimumEventCount") == null ? 1 : (Integer) condition.getParameter("minimumEventCount");
- Integer maximumEventCount = condition.getParameter("maximumEventCount") == null ? Integer.MAX_VALUE : (Integer) condition.getParameter("maximumEventCount");
+ boolean eventsOccurred = getStrategyFromOperator((String) condition.getParameter("operator"));
+ int minimumEventCount = !eventsOccurred || condition.getParameter("minimumEventCount") == null ? 1 : (Integer) condition.getParameter("minimumEventCount");
+ int maximumEventCount = !eventsOccurred || condition.getParameter("maximumEventCount") == null ? Integer.MAX_VALUE : (Integer) condition.getParameter("maximumEventCount");
String generatedPropertyKey = (String) condition.getParameter("generatedPropertyKey");
if (generatedPropertyKey != null && generatedPropertyKey.equals(segmentService.getGeneratedPropertyKey((Condition) condition.getParameter("eventCondition"), condition))) {
// A property is already set on profiles matching the past event condition, use it to check the numbers of occurrences
- RangeQueryBuilder builder = QueryBuilders.rangeQuery("systemProperties.pastEvents." + generatedPropertyKey);
- builder.gte(minimumEventCount);
- builder.lte(maximumEventCount);
- return builder;
+ return dispatcher.buildFilter(getProfileConditionForCounter(generatedPropertyKey, minimumEventCount, maximumEventCount, eventsOccurred), context);
} else {
// No property set - tries to build an idsQuery
- // Build past event condition
- Condition eventCondition = getEventCondition(condition, context);
-
- Set<String> ids = new HashSet<>();
-
- if (pastEventsDisablePartitions) {
- Map<String, Long> eventCountByProfile = persistenceService.aggregateWithOptimizedQuery(eventCondition, new TermsAggregate("profileId"), Event.ITEM_TYPE, maximumIdsQueryCount);
- ids = eventCountByProfile.entrySet().stream()
- .filter(x -> !x.getKey().equals("_filtered"))
- .filter(x -> x.getValue() >= minimumEventCount && x.getValue() <= maximumEventCount)
- .map(Map.Entry::getKey)
- .collect(Collectors.toSet());
- } else {
- // Get full cardinality to partition the terms aggreggation
- Map<String, Double> m = persistenceService.getSingleValuesMetrics(eventCondition, new String[]{"card"}, "profileId.keyword", Event.ITEM_TYPE);
- long card = m.get("_card").longValue();
-
- int numParts = (int) (card / aggregateQueryBucketSize) + 2;
- for (int i = 0; i < numParts; i++) {
- Map<String, Long> eventCountByProfile = persistenceService.aggregateWithOptimizedQuery(eventCondition, new TermsAggregate("profileId", i, numParts), Event.ITEM_TYPE);
- if (eventCountByProfile != null) {
- eventCountByProfile.remove("_filtered");
- for (Map.Entry<String, Long> entry : eventCountByProfile.entrySet()) {
- if (entry.getValue() < minimumEventCount) {
- // No more interesting buckets in this partition
- break;
- } else if (entry.getValue() <= maximumEventCount) {
- ids.add(entry.getKey());
+ // TODO see for deprecation, this should not happen anymore each past event condition should have a generatedPropertyKey
+ Condition eventCondition = getEventCondition(condition, context, null, definitionsService, scriptExecutor);
+ Set<String> ids = getProfileIdsMatchingEventCount(eventCondition, minimumEventCount, maximumEventCount);
+ return dispatcher.buildFilter(getProfileIdsCondition(ids, eventsOccurred), context);
+ }
+ }
- if (ids.size() > maximumIdsQueryCount) {
- // Avoid building too big ids query - throw exception instead
- throw new UnsupportedOperationException("Too many profiles");
- }
- }
- }
- }
- }
+ @Override
+ public long count(Condition condition, Map<String, Object> context, ConditionESQueryBuilderDispatcher dispatcher) {
+ boolean eventsOccurred = getStrategyFromOperator((String) condition.getParameter("operator"));
+ int minimumEventCount = !eventsOccurred || condition.getParameter("minimumEventCount") == null ? 1 : (Integer) condition.getParameter("minimumEventCount");
+ int maximumEventCount = !eventsOccurred || condition.getParameter("maximumEventCount") == null ? Integer.MAX_VALUE : (Integer) condition.getParameter("maximumEventCount");
+ String generatedPropertyKey = (String) condition.getParameter("generatedPropertyKey");
+
+ if (generatedPropertyKey != null && generatedPropertyKey.equals(segmentService.getGeneratedPropertyKey((Condition) condition.getParameter("eventCondition"), condition))) {
+ // query profiles directly
+ return persistenceService.queryCount(getProfileConditionForCounter(generatedPropertyKey, minimumEventCount, maximumEventCount, eventsOccurred), Profile.ITEM_TYPE);
+ } else {
+ // No count filter - simply get the full number of distinct profiles
+ // TODO see for deprecation, this should not happen anymore each past event condition should have a generatedPropertyKey
+ Condition eventCondition = getEventCondition(condition, context, null, definitionsService, scriptExecutor);
+ if (eventsOccurred && minimumEventCount == 1 && maximumEventCount == Integer.MAX_VALUE) {
+ return persistenceService.getSingleValuesMetrics(eventCondition, new String[]{"card"}, "profileId.keyword", Event.ITEM_TYPE).get("_card").longValue();
}
- return QueryBuilders.idsQuery().addIds(ids.toArray(new String[0]));
+ Set<String> profileIds = getProfileIdsMatchingEventCount(eventCondition, minimumEventCount, maximumEventCount);
+ return eventsOccurred ? profileIds.size() : persistenceService.queryCount(getProfileIdsCondition(profileIds, false), Profile.ITEM_TYPE);
}
}
- public long count(Condition condition, Map<String, Object> context, ConditionESQueryBuilderDispatcher dispatcher) {
- Condition eventCondition = getEventCondition(condition, context);
- Map<String, Double> aggResult = null;
+ protected static boolean getStrategyFromOperator(String operator) {
+ if (operator != null && !operator.equals("eventsOccurred") && !operator.equals("eventsNotOccurred")) {
+ throw new UnsupportedOperationException("Unsupported operator: " + operator + ", please use either 'eventsOccurred' or 'eventsNotOccurred'");
+ }
+ return operator == null || operator.equals("eventsOccurred");
+ }
+
+ private Condition getProfileIdsCondition(Set<String> ids, boolean shouldMatch) {
+ Condition idsCondition = new Condition();
+ idsCondition.setConditionType(definitionsService.getConditionType("idsCondition"));
+ idsCondition.setParameter("ids", ids);
+ idsCondition.setParameter("match", shouldMatch);
+ return idsCondition;
+ }
+
+ private Condition getProfileConditionForCounter(String generatedPropertyKey, Integer minimumEventCount, Integer maximumEventCount, boolean eventsOccurred) {
+ String generatedPropertyName = "systemProperties.pastEvents." + generatedPropertyKey;
+ ConditionType profilePropertyConditionType = definitionsService.getConditionType("profilePropertyCondition");
+ if (eventsOccurred) {
+ Condition counterIsBetweenBoundaries = new Condition();
+ counterIsBetweenBoundaries.setConditionType(profilePropertyConditionType);
+ counterIsBetweenBoundaries.setParameter("propertyName", generatedPropertyName);
+ counterIsBetweenBoundaries.setParameter("comparisonOperator", "between");
+ counterIsBetweenBoundaries.setParameter("propertyValuesInteger", Arrays.asList(minimumEventCount, maximumEventCount));
+ return counterIsBetweenBoundaries;
+ } else {
+ Condition counterMissing = new Condition();
+ counterMissing.setConditionType(profilePropertyConditionType);
+ counterMissing.setParameter("propertyName", generatedPropertyName);
+ counterMissing.setParameter("comparisonOperator", "missing");
- Integer minimumEventCount = condition.getParameter("minimumEventCount") == null ? 1 : (Integer) condition.getParameter("minimumEventCount");
- Integer maximumEventCount = condition.getParameter("maximumEventCount") == null ? Integer.MAX_VALUE : (Integer) condition.getParameter("maximumEventCount");
+ Condition counterZero = new Condition();
+ counterZero.setConditionType(profilePropertyConditionType);
+ counterZero.setParameter("propertyName", generatedPropertyName);
+ counterZero.setParameter("comparisonOperator", "equals");
+ counterZero.setParameter("propertyValueInteger", 0);
- // No count filter - simply get the full number of distinct profiles
- if (minimumEventCount == 1 && maximumEventCount == Integer.MAX_VALUE) {
- aggResult = persistenceService.getSingleValuesMetrics(eventCondition, new String[]{"card"}, "profileId.keyword", Event.ITEM_TYPE);
- return aggResult.get("_card").longValue();
+ Condition counterCondition = new Condition();
+ counterCondition.setConditionType(definitionsService.getConditionType("booleanCondition"));
+ counterCondition.setParameter("operator", "or");
+ counterCondition.setParameter("subConditions", Arrays.asList(counterMissing, counterZero));
+ return counterCondition;
}
+ }
+ private Set<String> getProfileIdsMatchingEventCount(Condition eventCondition, int minimumEventCount, int maximumEventCount) {
+ boolean noBoundaries = minimumEventCount == 1 && maximumEventCount == Integer.MAX_VALUE;
if (pastEventsDisablePartitions) {
Map<String, Long> eventCountByProfile = persistenceService.aggregateWithOptimizedQuery(eventCondition, new TermsAggregate("profileId"), Event.ITEM_TYPE, maximumIdsQueryCount);
- return eventCountByProfile.entrySet().stream()
- .filter(x -> x.getKey().equals("_filtered"))
- .filter(x -> x.getValue() >= minimumEventCount && x.getValue() <= maximumEventCount)
- .count();
+ eventCountByProfile.remove("_filtered");
+ return noBoundaries ?
+ eventCountByProfile.keySet() :
+ eventCountByProfile.entrySet()
+ .stream()
+ .filter(eventCountPerProfile -> (eventCountPerProfile.getValue() >= minimumEventCount && eventCountPerProfile.getValue() <= maximumEventCount))
+ .map(Map.Entry::getKey)
+ .collect(Collectors.toSet());
} else {
- // Get full cardinality to partition the terms aggreggation
- aggResult = persistenceService.getSingleValuesMetrics(eventCondition, new String[]{"card"}, "profileId.keyword", Event.ITEM_TYPE);
- long card = aggResult.get("_card").longValue();
- // Event count specified, must check occurences count for each profile
- int result = 0;
+ Set<String> result = new HashSet<>();
+ // Get full cardinality to partition the terms aggregation
+ Map<String, Double> m = persistenceService.getSingleValuesMetrics(eventCondition, new String[]{"card"}, "profileId.keyword", Event.ITEM_TYPE);
+ long card = m.get("_card").longValue();
+
int numParts = (int) (card / aggregateQueryBucketSize) + 2;
for (int i = 0; i < numParts; i++) {
Map<String, Long> eventCountByProfile = persistenceService.aggregateWithOptimizedQuery(eventCondition, new TermsAggregate("profileId", i, numParts), Event.ITEM_TYPE);
- int j = 0;
if (eventCountByProfile != null) {
eventCountByProfile.remove("_filtered");
- for (Map.Entry<String, Long> entry : eventCountByProfile.entrySet()) {
- if (entry.getValue() < minimumEventCount) {
- // No more interesting buckets in this partition
- break;
- } else if (entry.getValue() <= maximumEventCount && minimumEventCount == 1) {
- // Take all remaining elements
- result += eventCountByProfile.size() - j;
- break;
- } else if (entry.getValue() <= maximumEventCount) {
- result++;
+ if (noBoundaries) {
+ result.addAll(eventCountByProfile.keySet());
+ } else {
+ for (Map.Entry<String, Long> entry : eventCountByProfile.entrySet()) {
+ if (entry.getValue() < minimumEventCount) {
+ // No more interesting buckets in this partition
+ break;
+ } else if (entry.getValue() <= maximumEventCount) {
+ result.add(entry.getKey());
+ }
}
- j++;
}
}
}
+
return result;
}
}
- private Condition getEventCondition(Condition condition, Map<String, Object> context) {
+ protected static Condition getEventCondition(Condition condition, Map<String, Object> context, String profileId,
+ DefinitionsService definitionsService, ScriptExecutor scriptExecutor) {
Condition eventCondition;
try {
eventCondition = (Condition) condition.getParameter("eventCondition");
@@ -198,36 +221,37 @@ public class PastEventConditionESQueryBuilder implements ConditionESQueryBuilder
l.add(ConditionContextHelper.getContextualCondition(eventCondition, context, scriptExecutor));
+ if (profileId != null) {
+ Condition profileCondition = new Condition();
+ profileCondition.setConditionType(definitionsService.getConditionType("sessionPropertyCondition"));
+ profileCondition.setParameter("propertyName", "profileId");
+ profileCondition.setParameter("comparisonOperator", "equals");
+ profileCondition.setParameter("propertyValue", profileId);
+ l.add(profileCondition);
+ }
+
Integer numberOfDays = (Integer) condition.getParameter("numberOfDays");
String fromDate = (String) condition.getParameter("fromDate");
String toDate = (String) condition.getParameter("toDate");
if (numberOfDays != null) {
- Condition numberOfDaysCondition = new Condition();
- numberOfDaysCondition.setConditionType(definitionsService.getConditionType("sessionPropertyCondition"));
- numberOfDaysCondition.setParameter("propertyName", "timeStamp");
- numberOfDaysCondition.setParameter("comparisonOperator", "greaterThan");
- numberOfDaysCondition.setParameter("propertyValueDateExpr", "now-" + numberOfDays + "d");
- l.add(numberOfDaysCondition);
+ l.add(getTimeStampCondition("greaterThan", "propertyValueDateExpr", "now-" + numberOfDays + "d", definitionsService));
}
if (fromDate != null) {
- Condition startDateCondition = new Condition();
- startDateCondition.setConditionType(definitionsService.getConditionType("sessionPropertyCondition"));
- startDateCondition.setParameter("propertyName", "timeStamp");
- startDateCondition.setParameter("comparisonOperator", "greaterThanOrEqualTo");
- startDateCondition.setParameter("propertyValueDate", fromDate);
- l.add(startDateCondition);
+ l.add(getTimeStampCondition("greaterThanOrEqualTo", "propertyValueDate", fromDate, definitionsService));
}
if (toDate != null) {
- Condition endDateCondition = new Condition();
- endDateCondition.setConditionType(definitionsService.getConditionType("sessionPropertyCondition"));
- endDateCondition.setParameter("propertyName", "timeStamp");
- endDateCondition.setParameter("comparisonOperator", "lessThanOrEqualTo");
- endDateCondition.setParameter("propertyValueDate", toDate);
- l.add(endDateCondition);
+ l.add(getTimeStampCondition("lessThanOrEqualTo", "propertyValueDate", toDate, definitionsService));
}
-
return andCondition;
}
-}
+ private static Condition getTimeStampCondition(String operator, String propertyValueParameter, Object propertyValue, DefinitionsService definitionsService) {
+ Condition endDateCondition = new Condition();
+ endDateCondition.setConditionType(definitionsService.getConditionType("sessionPropertyCondition"));
+ endDateCondition.setParameter("propertyName", "timeStamp");
+ endDateCondition.setParameter("comparisonOperator", operator);
+ endDateCondition.setParameter(propertyValueParameter, propertyValue);
+ return endDateCondition;
+ }
+}
\ No newline at end of file
diff --git a/plugins/baseplugin/src/main/java/org/apache/unomi/plugins/baseplugin/conditions/PastEventConditionEvaluator.java b/plugins/baseplugin/src/main/java/org/apache/unomi/plugins/baseplugin/conditions/PastEventConditionEvaluator.java
index 5ca8e10..721d9a7 100644
--- a/plugins/baseplugin/src/main/java/org/apache/unomi/plugins/baseplugin/conditions/PastEventConditionEvaluator.java
+++ b/plugins/baseplugin/src/main/java/org/apache/unomi/plugins/baseplugin/conditions/PastEventConditionEvaluator.java
@@ -22,22 +22,17 @@ import org.apache.unomi.api.Item;
import org.apache.unomi.api.Profile;
import org.apache.unomi.api.conditions.Condition;
import org.apache.unomi.api.services.DefinitionsService;
-import org.apache.unomi.persistence.elasticsearch.conditions.ConditionContextHelper;
import org.apache.unomi.persistence.elasticsearch.conditions.ConditionEvaluator;
import org.apache.unomi.persistence.elasticsearch.conditions.ConditionEvaluatorDispatcher;
import org.apache.unomi.persistence.spi.PersistenceService;
import org.apache.unomi.scripting.ScriptExecutor;
-import java.util.ArrayList;
-import java.util.List;
import java.util.Map;
public class PastEventConditionEvaluator implements ConditionEvaluator {
private PersistenceService persistenceService;
-
private DefinitionsService definitionsService;
-
private ScriptExecutor scriptExecutor;
public void setPersistenceService(PersistenceService persistenceService) {
@@ -54,11 +49,7 @@ public class PastEventConditionEvaluator implements ConditionEvaluator {
@Override
public boolean eval(Condition condition, Item item, Map<String, Object> context, ConditionEvaluatorDispatcher dispatcher) {
-
final Map<String, Object> parameters = condition.getParameterValues();
-
- Condition eventCondition = (Condition) parameters.get("eventCondition");
-
long count;
if (parameters.containsKey("generatedPropertyKey")) {
@@ -71,61 +62,18 @@ public class PastEventConditionEvaluator implements ConditionEvaluator {
} else {
count = 0;
}
-
} else {
- if (eventCondition == null) {
- throw new IllegalArgumentException("No eventCondition");
- }
-
- List<Condition> l = new ArrayList<Condition>();
- Condition andCondition = new Condition();
- andCondition.setConditionType(definitionsService.getConditionType("booleanCondition"));
- andCondition.setParameter("operator", "and");
- andCondition.setParameter("subConditions", l);
-
- l.add(ConditionContextHelper.getContextualCondition(eventCondition, context, scriptExecutor));
-
- Condition profileCondition = new Condition();
- profileCondition.setConditionType(definitionsService.getConditionType("sessionPropertyCondition"));
- profileCondition.setParameter("propertyName", "profileId");
- profileCondition.setParameter("comparisonOperator", "equals");
- profileCondition.setParameter("propertyValue", item.getItemId());
- l.add(profileCondition);
-
- Integer numberOfDays = (Integer) condition.getParameter("numberOfDays");
- String fromDate = (String) condition.getParameter("fromDate");
- String toDate = (String) condition.getParameter("toDate");
-
- if (numberOfDays != null) {
- Condition numberOfDaysCondition = new Condition();
- numberOfDaysCondition.setConditionType(definitionsService.getConditionType("sessionPropertyCondition"));
- numberOfDaysCondition.setParameter("propertyName", "timeStamp");
- numberOfDaysCondition.setParameter("comparisonOperator", "greaterThan");
- numberOfDaysCondition.setParameter("propertyValueDateExpr", "now-" + numberOfDays + "d");
- l.add(numberOfDaysCondition);
- }
- if (fromDate != null) {
- Condition startDateCondition = new Condition();
- startDateCondition.setConditionType(definitionsService.getConditionType("sessionPropertyCondition"));
- startDateCondition.setParameter("propertyName", "timeStamp");
- startDateCondition.setParameter("comparisonOperator", "greaterThanOrEqualTo");
- startDateCondition.setParameter("propertyValueDate", fromDate);
- l.add(startDateCondition);
- }
- if (toDate != null) {
- Condition endDateCondition = new Condition();
- endDateCondition.setConditionType(definitionsService.getConditionType("sessionPropertyCondition"));
- endDateCondition.setParameter("propertyName", "timeStamp");
- endDateCondition.setParameter("comparisonOperator", "lessThanOrEqualTo");
- endDateCondition.setParameter("propertyValueDate", toDate);
- l.add(endDateCondition);
- }
- count = persistenceService.queryCount(andCondition, Event.ITEM_TYPE);
+ // TODO see for deprecation, this should not happen anymore each past event condition should have a generatedPropertyKey
+ count = persistenceService.queryCount(PastEventConditionESQueryBuilder.getEventCondition(condition, context, item.getItemId(), definitionsService, scriptExecutor), Event.ITEM_TYPE);
}
- Integer minimumEventCount = parameters.get("minimumEventCount") == null ? 0 : (Integer) parameters.get("minimumEventCount");
- Integer maximumEventCount = parameters.get("maximumEventCount") == null ? Integer.MAX_VALUE : (Integer) parameters.get("maximumEventCount");
-
- return count > 0 && (count >= minimumEventCount && count <= maximumEventCount);
+ boolean eventsOccurred = PastEventConditionESQueryBuilder.getStrategyFromOperator((String) condition.getParameter("operator"));
+ if (eventsOccurred) {
+ int minimumEventCount = parameters.get("minimumEventCount") == null ? 0 : (Integer) parameters.get("minimumEventCount");
+ int maximumEventCount = parameters.get("maximumEventCount") == null ? Integer.MAX_VALUE : (Integer) parameters.get("maximumEventCount");
+ return count > 0 && (count >= minimumEventCount && count <= maximumEventCount);
+ } else {
+ return count == 0;
+ }
}
}
diff --git a/plugins/baseplugin/src/main/resources/META-INF/cxs/conditions/IdsCondition.json b/plugins/baseplugin/src/main/resources/META-INF/cxs/conditions/IdsCondition.json
new file mode 100644
index 0000000..530e7ff
--- /dev/null
+++ b/plugins/baseplugin/src/main/resources/META-INF/cxs/conditions/IdsCondition.json
@@ -0,0 +1,23 @@
+{
+ "metadata": {
+ "id": "idsCondition",
+ "name": "idsCondition",
+ "description": "",
+ "systemTags": [],
+ "readOnly": true
+ },
+ "conditionEvaluator": "idsConditionEvaluator",
+ "queryBuilder": "idsConditionESQueryBuilder",
+ "parameters": [
+ {
+ "id": "ids",
+ "type": "string",
+ "multivalued": true
+ },
+ {
+ "id": "match",
+ "type": "boolean",
+ "multivalued": false
+ }
+ ]
+}
\ No newline at end of file
diff --git a/plugins/baseplugin/src/main/resources/META-INF/cxs/conditions/pastEventCondition.json b/plugins/baseplugin/src/main/resources/META-INF/cxs/conditions/pastEventCondition.json
index 6740a73..0d70acb 100644
--- a/plugins/baseplugin/src/main/resources/META-INF/cxs/conditions/pastEventCondition.json
+++ b/plugins/baseplugin/src/main/resources/META-INF/cxs/conditions/pastEventCondition.json
@@ -41,6 +41,11 @@
"multivalued": false
},
{
+ "id": "operator",
+ "type": "string",
+ "multivalued": false
+ },
+ {
"id": "eventCondition",
"type": "Condition",
"multivalued": false
diff --git a/plugins/baseplugin/src/main/resources/OSGI-INF/blueprint/blueprint.xml b/plugins/baseplugin/src/main/resources/OSGI-INF/blueprint/blueprint.xml
index f2c3846..b80f250 100644
--- a/plugins/baseplugin/src/main/resources/OSGI-INF/blueprint/blueprint.xml
+++ b/plugins/baseplugin/src/main/resources/OSGI-INF/blueprint/blueprint.xml
@@ -60,6 +60,16 @@
<service
interface="org.apache.unomi.persistence.elasticsearch.conditions.ConditionESQueryBuilder">
<service-properties>
+ <entry key="queryBuilderId" value="idsConditionESQueryBuilder"/>
+ </service-properties>
+ <bean class="org.apache.unomi.plugins.baseplugin.conditions.IdsConditionESQueryBuilder">
+ <property name="maximumIdsQueryCount" value="${es.maximumIdsQueryCount}"/>
+ </bean>
+ </service>
+
+ <service
+ interface="org.apache.unomi.persistence.elasticsearch.conditions.ConditionESQueryBuilder">
+ <service-properties>
<entry key="queryBuilderId" value="booleanConditionESQueryBuilder"/>
</service-properties>
<bean class="org.apache.unomi.plugins.baseplugin.conditions.BooleanConditionESQueryBuilder"/>
@@ -132,6 +142,13 @@
<service interface="org.apache.unomi.persistence.elasticsearch.conditions.ConditionEvaluator">
<service-properties>
+ <entry key="conditionEvaluatorId" value="idsConditionEvaluator"/>
+ </service-properties>
+ <bean class="org.apache.unomi.plugins.baseplugin.conditions.IdsConditionEvaluator"/>
+ </service>
+
+ <service interface="org.apache.unomi.persistence.elasticsearch.conditions.ConditionEvaluator">
+ <service-properties>
<entry key="conditionEvaluatorId" value="notConditionEvaluator"/>
</service-properties>
<bean class="org.apache.unomi.plugins.baseplugin.conditions.NotConditionEvaluator"/>