This is an automated email from the ASF dual-hosted git repository.
jsinovassinnaik pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/unomi.git
The following commit(s) were added to refs/heads/master by this push:
new a1c4f774b UNOMI-817: set pastEvents as nested and clean existing
mapping (#659)
a1c4f774b is described below
commit a1c4f774bc0be136c3c13921f4a630eb5afd2e5c
Author: jsinovassin <[email protected]>
AuthorDate: Fri May 31 12:43:51 2024 +0200
UNOMI-817: set pastEvents as nested and clean existing mapping (#659)
* UNOMI-817: set pastEvents as flattened and clean existing mapping
* UNOMI-817: transform pastEvents to nested property
---
.../unomi/api/services/DefinitionsService.java | 11 +++-
.../apache/unomi/api/utils}/ConditionBuilder.java | 36 +++++++----
.../apache/unomi/itests/ConditionEvaluatorIT.java | 5 +-
.../org/apache/unomi/itests/RuleServiceIT.java | 3 +-
.../java/org/apache/unomi/itests/SegmentIT.java | 10 +--
.../unomi/itests/migration/Migrate16xTo220IT.java | 14 +++++
.../resources/migration/snapshots_repository.zip | Bin 3908941 -> 7807889
bytes
.../ElasticSearchPersistenceServiceImpl.java | 2 +-
.../resources/META-INF/cxs/mappings/profile.json | 7 +++
.../resources/OSGI-INF/blueprint/blueprint.xml | 7 +--
.../actions/SetEventOccurenceCountAction.java | 33 ++++++++--
.../PastEventConditionESQueryBuilder.java | 70 ++++++++++-----------
.../conditions/PastEventConditionEvaluator.java | 12 +++-
.../painless/updatePastEventOccurences.painless | 47 ++++++++++++++
.../impl/definitions/DefinitionsServiceImpl.java | 9 +++
.../services/impl/profiles/ProfileServiceImpl.java | 9 ++-
.../services/impl/segments/SegmentServiceImpl.java | 60 +++++++++---------
...te-2.5.0-00-cleanPastEventProfileSession.groovy | 48 ++++++++++++++
.../2.5.0/remove_pastEvents_session.painless | 22 +++++++
.../2.5.0/update_pastEvents_profile.painless | 27 ++++++++
20 files changed, 325 insertions(+), 107 deletions(-)
diff --git
a/api/src/main/java/org/apache/unomi/api/services/DefinitionsService.java
b/api/src/main/java/org/apache/unomi/api/services/DefinitionsService.java
index 816f8831a..b4cf75a68 100644
--- a/api/src/main/java/org/apache/unomi/api/services/DefinitionsService.java
+++ b/api/src/main/java/org/apache/unomi/api/services/DefinitionsService.java
@@ -23,6 +23,7 @@ import org.apache.unomi.api.ValueType;
import org.apache.unomi.api.actions.ActionType;
import org.apache.unomi.api.conditions.Condition;
import org.apache.unomi.api.conditions.ConditionType;
+import org.apache.unomi.api.utils.ConditionBuilder;
import java.util.Collection;
import java.util.List;
@@ -210,4 +211,12 @@ public interface DefinitionsService {
* so it is recommended to use this in specific cases such as for example
in integration tests.
*/
void refresh();
-}
\ No newline at end of file
+
+
+ /**
+ * Retrieves a new instance of a ConditionBuilder to help to build
conditions.
+ *
+ * @return a new instance of a ConditionBuilder
+ */
+ ConditionBuilder getConditionBuilder();
+}
diff --git a/itests/src/test/java/org/apache/unomi/itests/ConditionBuilder.java
b/api/src/main/java/org/apache/unomi/api/utils/ConditionBuilder.java
similarity index 92%
rename from itests/src/test/java/org/apache/unomi/itests/ConditionBuilder.java
rename to api/src/main/java/org/apache/unomi/api/utils/ConditionBuilder.java
index 69a210fe1..aceb1ffe5 100644
--- a/itests/src/test/java/org/apache/unomi/itests/ConditionBuilder.java
+++ b/api/src/main/java/org/apache/unomi/api/utils/ConditionBuilder.java
@@ -12,34 +12,33 @@
* 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
+ * limitations under the License.
*/
-
-package org.apache.unomi.itests;
+package org.apache.unomi.api.utils;
import org.apache.unomi.api.conditions.Condition;
import org.apache.unomi.api.services.DefinitionsService;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Date;
+import java.util.*;
/**
- * Utility class for building conditions
- *
- * @author Sergiy Shyrkov
+ * Utility class for creating various types of {@link Condition} objects.
+ * This class provides methods to easily construct conditions used for
querying data based on specific criteria.
*/
public class ConditionBuilder {
private DefinitionsService definitionsService;
/**
- * Initializes an instance of this class.
+ * Constructs a new Builder with a specified DefinitionsService.
*
- * @param definitionsService an instance of the {@link DefinitionsService}
+ * @param definitionsService the DefinitionsService to use for obtaining
condition types.
*/
public ConditionBuilder(DefinitionsService definitionsService) {
- super();
+ this.definitionsService = definitionsService;
+ }
+
+ public void setDefinitionsService(DefinitionsService definitionsService) {
this.definitionsService = definitionsService;
}
@@ -55,6 +54,10 @@ public class ConditionBuilder {
return new CompoundCondition(condition1, condition2, "or");
}
+ public NestedCondition nested(ConditionItem subCondition, String path) {
+ return new NestedCondition(subCondition, path);
+ }
+
public PropertyCondition profileProperty(String propertyName) {
return new PropertyCondition("profilePropertyCondition", propertyName,
definitionsService);
}
@@ -315,6 +318,14 @@ public class ConditionBuilder {
}
}
+ public class NestedCondition extends ConditionItem {
+ NestedCondition(ConditionItem subCondition, String path) {
+ super("nestedCondition", subCondition.definitionsService);
+ parameter("path", path);
+ parameter("subCondition", subCondition.build());
+ }
+ }
+
public class ConditionItem {
protected Condition condition;
@@ -359,4 +370,5 @@ public class ConditionBuilder {
}
}
+
}
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 d74672ca3..e9dd76ca0 100644
--- a/itests/src/test/java/org/apache/unomi/itests/ConditionEvaluatorIT.java
+++ b/itests/src/test/java/org/apache/unomi/itests/ConditionEvaluatorIT.java
@@ -21,8 +21,7 @@ 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;
-import org.apache.unomi.api.services.DefinitionsService;
-import org.apache.unomi.persistence.spi.PersistenceService;
+import org.apache.unomi.api.utils.ConditionBuilder;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -62,7 +61,7 @@ public class ConditionEvaluatorIT extends BaseIT {
public void setUp() {
assertNotNull("Definition service should be available",
definitionsService);
assertNotNull("Persistence service should be available",
persistenceService);
- builder = new ConditionBuilder(definitionsService);
+ builder = definitionsService.getConditionBuilder();
lastVisit = new GregorianCalendar(2015,
Calendar.FEBRUARY,1,20,30,0).getTime();
diff --git a/itests/src/test/java/org/apache/unomi/itests/RuleServiceIT.java
b/itests/src/test/java/org/apache/unomi/itests/RuleServiceIT.java
index e605c039b..764980fe6 100644
--- a/itests/src/test/java/org/apache/unomi/itests/RuleServiceIT.java
+++ b/itests/src/test/java/org/apache/unomi/itests/RuleServiceIT.java
@@ -22,6 +22,7 @@ import org.apache.unomi.api.conditions.ConditionType;
import org.apache.unomi.api.rules.Rule;
import org.apache.unomi.api.services.EventService;
import org.apache.unomi.api.services.RulesService;
+import org.apache.unomi.api.utils.ConditionBuilder;
import org.apache.unomi.persistence.spi.CustomObjectMapper;
import org.junit.Assert;
import org.junit.Before;
@@ -105,7 +106,7 @@ public class RuleServiceIT extends BaseIT {
@Test
public void testRuleEventTypeOptimization() throws InterruptedException {
- ConditionBuilder builder = new ConditionBuilder(definitionsService);
+ ConditionBuilder builder = definitionsService.getConditionBuilder();
Rule simpleEventTypeRule = new Rule(new Metadata(TEST_SCOPE,
"simple-event-type-rule", "Simple event type rule", "A rule with a simple
condition to match an event type"));
simpleEventTypeRule.setCondition(builder.condition("eventTypeCondition").parameter("eventTypeId",
"view").build());
createAndWaitForRule(simpleEventTypeRule);
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 3b3aa9314..59375b8c9 100644
--- a/itests/src/test/java/org/apache/unomi/itests/SegmentIT.java
+++ b/itests/src/test/java/org/apache/unomi/itests/SegmentIT.java
@@ -547,9 +547,8 @@ public class SegmentIT extends BaseIT {
// insure the profile is engaged;
try {
- Assert.assertTrue("Profile should have 2 events in the scoring",
- (Long) ((Map)
testEvent.getProfile().getSystemProperties().get("pastEvents"))
-
.get(pastEventCondition.getParameterValues().get("generatedPropertyKey")) == 2);
+ Map<String, Object> pastEvent = ((List<Map<String,
Object>>)testEvent.getProfile().getSystemProperties().get("pastEvents")).stream().filter(profilePastEvent
->
profilePastEvent.get("key").equals(pastEventCondition.getParameterValues().get("generatedPropertyKey"))).findFirst().get();
+ Assert.assertEquals("Profile should have 2 events in the scoring",
2, (long) pastEvent.get("count"));
Assert.assertTrue("Profile is engaged",
testEvent.getProfile().getScores().containsKey("past-event-scoring-test")
&&
testEvent.getProfile().getScores().get("past-event-scoring-test") == 50);
} catch (Exception e) {
@@ -562,8 +561,9 @@ public class SegmentIT extends BaseIT {
// insure the profile is still engaged after recalculate;
keepTrying("Profile should have 2 events in the scoring", () ->
profileService.load("test_profile_id"), updatedProfile -> {
try {
- boolean eventCounted = (Integer) ((Map)
updatedProfile.getSystemProperties().get("pastEvents"))
-
.get(pastEventCondition.getParameterValues().get("generatedPropertyKey")) == 2;
+ Map<String, Object> pastEvent = ((List<Map<String,
Object>>)updatedProfile.getSystemProperties().get("pastEvents")).stream().filter(profilePastEvent
->
profilePastEvent.get("key").equals(pastEventCondition.getParameterValues().get("generatedPropertyKey"))).findFirst().get();
+
+ boolean eventCounted = (Integer) pastEvent.get("count") == 2;
boolean profileEngaged =
updatedProfile.getScores().containsKey("past-event-scoring-test")
&&
updatedProfile.getScores().get("past-event-scoring-test") == 50;
return eventCounted && profileEngaged;
diff --git
a/itests/src/test/java/org/apache/unomi/itests/migration/Migrate16xTo220IT.java
b/itests/src/test/java/org/apache/unomi/itests/migration/Migrate16xTo220IT.java
index cddb44f49..725f0fa4c 100644
---
a/itests/src/test/java/org/apache/unomi/itests/migration/Migrate16xTo220IT.java
+++
b/itests/src/test/java/org/apache/unomi/itests/migration/Migrate16xTo220IT.java
@@ -99,6 +99,7 @@ public class Migrate16xTo220IT extends BaseIT {
checkEventSessionRollover2_2_0();
checkIndexReductions2_2_0();
checkPagePathForEventView();
+ checkPastEvents();
}
/**
@@ -342,4 +343,17 @@ public class Migrate16xTo220IT extends BaseIT {
Assert.assertEquals(2,
persistenceService.query("target.properties.pageInfo.pagePath",
"/path/to/migrate/to/pageInfo", null, Event.class).size());
Assert.assertEquals(0, persistenceService.query("properties.path",
"/path/to/migrate/to/pageInfo", null, Event.class).size());
}
+
+
+ /**
+ * Data set contains a profile (id: 164adad8-6885-45b6-8e9d-512bf4a7d10d)
with a system property pastEvents that contains 5 events with key
eventTriggeredabcdefgh
+ * This test ensures that the pastEvents have been migrated to the new
data structure
+ */
+ private void checkPastEvents() {
+ Profile profile =
persistenceService.load("164adad8-6885-45b6-8e9d-512bf4a7d10d", Profile.class);
+ List<Map<String, Object>> pastEvents = ((List<Map<String,
Object>>)profile.getSystemProperties().get("pastEvents"));
+ Assert.assertEquals(1, pastEvents.size());
+ Assert.assertEquals("eventTriggeredabcdefgh",
pastEvents.get(0).get("key"));
+ Assert.assertEquals(5, (int) pastEvents.get(0).get("count"));
+ }
}
diff --git a/itests/src/test/resources/migration/snapshots_repository.zip
b/itests/src/test/resources/migration/snapshots_repository.zip
index 705a6241f..675e710cc 100644
Binary files a/itests/src/test/resources/migration/snapshots_repository.zip and
b/itests/src/test/resources/migration/snapshots_repository.zip differ
diff --git
a/persistence-elasticsearch/core/src/main/java/org/apache/unomi/persistence/elasticsearch/ElasticSearchPersistenceServiceImpl.java
b/persistence-elasticsearch/core/src/main/java/org/apache/unomi/persistence/elasticsearch/ElasticSearchPersistenceServiceImpl.java
index 0da7bc14b..d2a11ed52 100644
---
a/persistence-elasticsearch/core/src/main/java/org/apache/unomi/persistence/elasticsearch/ElasticSearchPersistenceServiceImpl.java
+++
b/persistence-elasticsearch/core/src/main/java/org/apache/unomi/persistence/elasticsearch/ElasticSearchPersistenceServiceImpl.java
@@ -1063,7 +1063,7 @@ public class ElasticSearchPersistenceServiceImpl
implements PersistenceService,
@Override
public List<String> update(final Map<Item, Map> items, final Date
dateHint, final Class clazz) {
- if (items.size() == 0)
+ if (items.isEmpty())
return new ArrayList<>();
List<String> result = new
InClassLoaderExecute<List<String>>(metricsService, this.getClass().getName() +
".updateItems", this.bundleContext, this.fatalIllegalStateErrors,
throwExceptions) {
diff --git
a/persistence-elasticsearch/core/src/main/resources/META-INF/cxs/mappings/profile.json
b/persistence-elasticsearch/core/src/main/resources/META-INF/cxs/mappings/profile.json
index 81cc14d0f..6e650a178 100644
---
a/persistence-elasticsearch/core/src/main/resources/META-INF/cxs/mappings/profile.json
+++
b/persistence-elasticsearch/core/src/main/resources/META-INF/cxs/mappings/profile.json
@@ -40,6 +40,13 @@
}
}
},
+ "systemProperties": {
+ "properties": {
+ "pastEvents": {
+ "type": "nested"
+ }
+ }
+ },
"consents": {
"properties": {
"statusDate": {
diff --git
a/persistence-elasticsearch/core/src/main/resources/OSGI-INF/blueprint/blueprint.xml
b/persistence-elasticsearch/core/src/main/resources/OSGI-INF/blueprint/blueprint.xml
index 32efdd022..d14235171 100644
---
a/persistence-elasticsearch/core/src/main/resources/OSGI-INF/blueprint/blueprint.xml
+++
b/persistence-elasticsearch/core/src/main/resources/OSGI-INF/blueprint/blueprint.xml
@@ -20,12 +20,7 @@
xmlns:cm="http://aries.apache.org/blueprint/xmlns/blueprint-cm/v1.1.0"
xmlns="http://www.osgi.org/xmlns/blueprint/v1.0.0"
xsi:schemaLocation="http://www.osgi.org/xmlns/blueprint/v1.0.0
http://www.osgi.org/xmlns/blueprint/v1.0.0/blueprint.xsd
-
-
-
-
-
- http://aries.apache.org/blueprint/xmlns/blueprint-cm/v1.1.0
http://aries.apache.org/schemas/blueprint-cm/blueprint-cm-1.1.0.xsd">
+ http://aries.apache.org/blueprint/xmlns/blueprint-cm/v1.1.0
http://aries.apache.org/schemas/blueprint-cm/blueprint-cm-1.1.0.xsd">
<cm:property-placeholder
persistent-id="org.apache.unomi.persistence.elasticsearch"
update-strategy="reload"
placeholder-prefix="${es.">
diff --git
a/plugins/baseplugin/src/main/java/org/apache/unomi/plugins/baseplugin/actions/SetEventOccurenceCountAction.java
b/plugins/baseplugin/src/main/java/org/apache/unomi/plugins/baseplugin/actions/SetEventOccurenceCountAction.java
index aa269762e..cb5e78f59 100644
---
a/plugins/baseplugin/src/main/java/org/apache/unomi/plugins/baseplugin/actions/SetEventOccurenceCountAction.java
+++
b/plugins/baseplugin/src/main/java/org/apache/unomi/plugins/baseplugin/actions/SetEventOccurenceCountAction.java
@@ -29,10 +29,8 @@ import org.apache.unomi.persistence.spi.PropertyHelper;
import java.time.Duration;
import java.time.LocalDateTime;
import java.time.ZoneId;
-import java.util.ArrayList;
-import java.util.Calendar;
-import java.util.LinkedHashMap;
-import java.util.Map;
+import java.util.*;
+import java.util.stream.Collectors;
import javax.xml.bind.DatatypeConverter;
@@ -123,14 +121,37 @@ public class SetEventOccurenceCountAction implements
ActionExecutor {
count++;
}
- String generatedPropertyKey = (String)
pastEventCondition.getParameter("generatedPropertyKey");
- if (PropertyHelper.setProperty(event.getProfile(),
"systemProperties.pastEvents." + generatedPropertyKey, count, "alwaysSet")) {
+ if (updatePastEvents(event, (String)
pastEventCondition.getParameter("generatedPropertyKey"), count)) {
return EventService.PROFILE_UPDATED;
}
return EventService.NO_CHANGE;
}
+ private boolean updatePastEvents(Event event, String generatedPropertyKey,
long count) {
+ List<Map<String, Object>> existingPastEvents = (List<Map<String,
Object>>) event.getProfile().getSystemProperties().get("pastEvents");
+ if (existingPastEvents == null) {
+ existingPastEvents = new ArrayList<>();
+ event.getProfile().getSystemProperties().put("pastEvents",
existingPastEvents);
+ }
+
+ for (Map<String, Object> pastEvent : existingPastEvents) {
+ if (generatedPropertyKey.equals(pastEvent.get("key"))) {
+ if (pastEvent.containsKey("count") &&
pastEvent.get("count").equals(count)) {
+ return false;
+ }
+ pastEvent.put("count", count);
+ return true;
+ }
+ }
+
+ Map<String, Object> newPastEvent = new HashMap<>();
+ newPastEvent.put("key", generatedPropertyKey);
+ newPastEvent.put("count", count);
+ existingPastEvents.add(newPastEvent);
+ return true;
+ }
+
private boolean inTimeRange(LocalDateTime eventTime, Integer numberOfDays,
LocalDateTime fromDate, LocalDateTime toDate) {
boolean inTimeRange = true;
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 4491eb678..6c40f9f87 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,9 +20,9 @@ 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.api.utils.ConditionBuilder;
import
org.apache.unomi.persistence.elasticsearch.conditions.ConditionContextHelper;
import
org.apache.unomi.persistence.elasticsearch.conditions.ConditionESQueryBuilder;
import
org.apache.unomi.persistence.elasticsearch.conditions.ConditionESQueryBuilderDispatcher;
@@ -88,7 +88,8 @@ public class PastEventConditionESQueryBuilder implements
ConditionESQueryBuilder
// 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);
+ ConditionBuilder conditionBuilder =
definitionsService.getConditionBuilder();
+ return
dispatcher.buildFilter(conditionBuilder.condition("idsCondition").parameter("ids",
ids).parameter("match", eventsOccurred).build(), context);
}
}
@@ -111,7 +112,8 @@ public class PastEventConditionESQueryBuilder implements
ConditionESQueryBuilder
}
Set<String> profileIds =
getProfileIdsMatchingEventCount(eventCondition, minimumEventCount,
maximumEventCount);
- return eventsOccurred ? profileIds.size() :
persistenceService.queryCount(getProfileIdsCondition(profileIds, false),
Profile.ITEM_TYPE);
+ ConditionBuilder conditionBuilder =
definitionsService.getConditionBuilder();
+ return eventsOccurred ? profileIds.size() :
persistenceService.queryCount(conditionBuilder.condition("idsCondition").parameter("ids",
profileIds).parameter("match", false).build(), Profile.ITEM_TYPE);
}
}
@@ -122,44 +124,38 @@ public class PastEventConditionESQueryBuilder implements
ConditionESQueryBuilder
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;
+ return createEventOccurredCondition(generatedPropertyKey,
minimumEventCount, maximumEventCount);
} else {
- Condition counterMissing = new Condition();
- counterMissing.setConditionType(profilePropertyConditionType);
- counterMissing.setParameter("propertyName", generatedPropertyName);
- counterMissing.setParameter("comparisonOperator", "missing");
-
- Condition counterZero = new Condition();
- counterZero.setConditionType(profilePropertyConditionType);
- counterZero.setParameter("propertyName", generatedPropertyName);
- counterZero.setParameter("comparisonOperator", "equals");
- counterZero.setParameter("propertyValueInteger", 0);
-
- Condition counterCondition = new Condition();
-
counterCondition.setConditionType(definitionsService.getConditionType("booleanCondition"));
- counterCondition.setParameter("operator", "or");
- counterCondition.setParameter("subConditions",
Arrays.asList(counterMissing, counterZero));
- return counterCondition;
+ return createEventNotOccurredCondition(generatedPropertyKey);
}
}
+ private Condition createEventOccurredCondition(String
generatedPropertyKey, Integer minimumEventCount, Integer maximumEventCount) {
+ ConditionBuilder conditionBuilder =
definitionsService.getConditionBuilder();
+ ConditionBuilder.ConditionItem subConditionCount =
conditionBuilder.profileProperty("systemProperties.pastEvents.count").between(minimumEventCount,
maximumEventCount);
+ ConditionBuilder.ConditionItem subConditionKey =
conditionBuilder.profileProperty("systemProperties.pastEvents.key").equalTo(generatedPropertyKey);
+ ConditionBuilder.ConditionItem booleanCondition =
conditionBuilder.and(subConditionCount, subConditionKey);
+ return conditionBuilder.nested(booleanCondition,
"systemProperties.pastEvents").build();
+ }
+
+ private Condition createEventNotOccurredCondition(String
generatedPropertyKey) {
+ ConditionBuilder.ConditionItem counterMissing =
createPastEventMustNotExistCondition(generatedPropertyKey);
+ ConditionBuilder conditionBuilder =
definitionsService.getConditionBuilder();
+ ConditionBuilder.ConditionItem counterZero =
conditionBuilder.profileProperty("systemProperties.pastEvents.count").equalTo(0);
+ ConditionBuilder.ConditionItem keyEquals =
conditionBuilder.profileProperty("systemProperties.pastEvents.key").equalTo(generatedPropertyKey);
+ ConditionBuilder.ConditionItem keyExistsAndCounterZero =
conditionBuilder.and(counterZero, keyEquals);
+ ConditionBuilder.ConditionItem nestedKeyExistsAndCounterZero =
conditionBuilder.nested(keyExistsAndCounterZero, "systemProperties.pastEvents");
+ return conditionBuilder.or(counterMissing,
nestedKeyExistsAndCounterZero).build();
+ }
+
+ private ConditionBuilder.ConditionItem
createPastEventMustNotExistCondition(String generatedPropertyKey) {
+ ConditionBuilder conditionBuilder =
definitionsService.getConditionBuilder();
+ ConditionBuilder.ConditionItem keyEquals =
conditionBuilder.profileProperty("systemProperties.pastEvents.key").equalTo(generatedPropertyKey);
+ return conditionBuilder.not(keyEquals);
+ }
+
private Set<String> getProfileIdsMatchingEventCount(Condition
eventCondition, int minimumEventCount, int maximumEventCount) {
boolean noBoundaries = minimumEventCount == 1 && maximumEventCount ==
Integer.MAX_VALUE;
if (pastEventsDisablePartitions) {
@@ -254,4 +250,6 @@ public class PastEventConditionESQueryBuilder implements
ConditionESQueryBuilder
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 721d9a78a..f14f768f6 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
@@ -27,6 +27,8 @@ import
org.apache.unomi.persistence.elasticsearch.conditions.ConditionEvaluatorD
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 {
@@ -55,10 +57,14 @@ public class PastEventConditionEvaluator implements
ConditionEvaluator {
if (parameters.containsKey("generatedPropertyKey")) {
String key = (String) parameters.get("generatedPropertyKey");
Profile profile = (Profile) item;
- Map<String,Object> pastEvents = (Map<String, Object>)
profile.getSystemProperties().get("pastEvents");
+ List<Map<String, Object>> pastEvents = (ArrayList<Map<String,
Object>>) profile.getSystemProperties().get("pastEvents");
if (pastEvents != null) {
- Number l = (Number) pastEvents.get(key);
- count = l != null ? l.longValue() : 0L;
+ Number l = (Number) pastEvents
+ .stream()
+ .filter(pastEvent -> pastEvent.get("key").equals(key))
+ .findFirst()
+ .map(pastEvent -> pastEvent.get("count")).orElse(0L);
+ count = l.longValue();
} else {
count = 0;
}
diff --git
a/plugins/baseplugin/src/main/resources/META-INF/cxs/painless/updatePastEventOccurences.painless
b/plugins/baseplugin/src/main/resources/META-INF/cxs/painless/updatePastEventOccurences.painless
new file mode 100644
index 000000000..37563e35e
--- /dev/null
+++
b/plugins/baseplugin/src/main/resources/META-INF/cxs/painless/updatePastEventOccurences.painless
@@ -0,0 +1,47 @@
+/*
+ * 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.
+ */
+
+if (params.containsKey(ctx._source.itemId)) {
+ Map pastEventKeyValue = params.get(ctx._source.itemId);
+ String pastEventKey = pastEventKeyValue.get('pastEventKey');
+ Long valueToAdd = (Long) pastEventKeyValue.get('valueToAdd');
+
+ if (ctx._source.systemProperties == null) {
+ ctx._source.systemProperties = new HashMap();
+ }
+
+ if (ctx._source.systemProperties.pastEvents == null) {
+ ctx._source.systemProperties.pastEvents = new ArrayList();
+ }
+
+ boolean exists = false;
+ for (pastEvent in ctx._source.systemProperties.pastEvents) {
+ if (pastEvent.get('key') == pastEventKey) {
+ pastEvent.put('count', valueToAdd);
+ exists = true;
+ }
+ }
+
+ if (!exists) {
+ Map newPastEvent = new HashMap();
+ newPastEvent.put('key', pastEventKey);
+ newPastEvent.put('count', valueToAdd);
+ ctx._source.systemProperties.pastEvents.add(newPastEvent);
+ }
+
+ ctx._source.systemProperties.put('lastUpdated', new Date());
+}
diff --git
a/services/src/main/java/org/apache/unomi/services/impl/definitions/DefinitionsServiceImpl.java
b/services/src/main/java/org/apache/unomi/services/impl/definitions/DefinitionsServiceImpl.java
index 9a098b63e..32ccdfc6f 100644
---
a/services/src/main/java/org/apache/unomi/services/impl/definitions/DefinitionsServiceImpl.java
+++
b/services/src/main/java/org/apache/unomi/services/impl/definitions/DefinitionsServiceImpl.java
@@ -25,6 +25,7 @@ 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.SchedulerService;
+import org.apache.unomi.api.utils.ConditionBuilder;
import org.apache.unomi.persistence.spi.CustomObjectMapper;
import org.apache.unomi.persistence.spi.PersistenceService;
import org.apache.unomi.api.utils.ParserHelper;
@@ -67,6 +68,7 @@ public class DefinitionsServiceImpl implements
DefinitionsService, SynchronousBu
private long definitionsRefreshInterval = 10000;
+ private ConditionBuilder conditionBuilder;
private BundleContext bundleContext;
public DefinitionsServiceImpl() {
}
@@ -101,6 +103,7 @@ public class DefinitionsServiceImpl implements
DefinitionsService, SynchronousBu
bundleContext.addBundleListener(this);
scheduleTypeReloads();
+ conditionBuilder = new ConditionBuilder(this);
logger.info("Definitions service initialized.");
}
@@ -523,4 +526,10 @@ public class DefinitionsServiceImpl implements
DefinitionsService, SynchronousBu
public void refresh() {
reloadTypes(true);
}
+
+ @Override
+ public ConditionBuilder getConditionBuilder() {
+ return conditionBuilder;
+ }
+
}
diff --git
a/services/src/main/java/org/apache/unomi/services/impl/profiles/ProfileServiceImpl.java
b/services/src/main/java/org/apache/unomi/services/impl/profiles/ProfileServiceImpl.java
index 56bf686d8..dd30ba863 100644
---
a/services/src/main/java/org/apache/unomi/services/impl/profiles/ProfileServiceImpl.java
+++
b/services/src/main/java/org/apache/unomi/services/impl/profiles/ProfileServiceImpl.java
@@ -881,8 +881,13 @@ public class ProfileServiceImpl implements ProfileService,
SynchronousBundleList
if (session.getItemId() == null) {
return null;
}
- if (session.getProfile() != null &&
session.getProfile().getProperties() != null) {
-
session.getProfile().setProperties(removePersonalIdentifiersFromSessionProfile(session.getProfile().getProperties()));
+ if (session.getProfile() != null) {
+ if (session.getProfile().getProperties() != null){
+
session.getProfile().setProperties(removePersonalIdentifiersFromSessionProfile(session.getProfile().getProperties()));
+ }
+ if (session.getProfile().getSystemProperties() != null){
+
session.getProfile().getSystemProperties().entrySet().removeIf(entry ->
entry.getKey().equals("pastEvents"));
+ }
}
return persistenceService.save(session) ? session : null;
}
diff --git
a/services/src/main/java/org/apache/unomi/services/impl/segments/SegmentServiceImpl.java
b/services/src/main/java/org/apache/unomi/services/impl/segments/SegmentServiceImpl.java
index 0aac270c6..fee5ef0fb 100644
---
a/services/src/main/java/org/apache/unomi/services/impl/segments/SegmentServiceImpl.java
+++
b/services/src/main/java/org/apache/unomi/services/impl/segments/SegmentServiceImpl.java
@@ -20,11 +20,7 @@ package org.apache.unomi.services.impl.segments;
import com.fasterxml.jackson.core.JsonProcessingException;
import net.jodah.failsafe.Failsafe;
import net.jodah.failsafe.RetryPolicy;
-import org.apache.unomi.api.Event;
-import org.apache.unomi.api.Item;
-import org.apache.unomi.api.Metadata;
-import org.apache.unomi.api.PartialList;
-import org.apache.unomi.api.Profile;
+import org.apache.unomi.api.*;
import org.apache.unomi.api.actions.Action;
import org.apache.unomi.api.conditions.Condition;
import org.apache.unomi.api.conditions.ConditionType;
@@ -35,6 +31,7 @@ import org.apache.unomi.api.services.EventService;
import org.apache.unomi.api.services.RulesService;
import org.apache.unomi.api.services.SchedulerService;
import org.apache.unomi.api.services.SegmentService;
+import org.apache.unomi.api.utils.ConditionBuilder;
import org.apache.unomi.persistence.spi.CustomObjectMapper;
import org.apache.unomi.persistence.spi.aggregate.TermsAggregate;
import org.apache.unomi.services.impl.AbstractServiceImpl;
@@ -51,6 +48,7 @@ import org.slf4j.LoggerFactory;
import java.io.IOException;
import java.net.URL;
import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
import java.time.Duration;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;
@@ -801,7 +799,7 @@ public class SegmentServiceImpl extends AbstractServiceImpl
implements SegmentSe
private void recalculatePastEventOccurrencesOnProfiles(Condition
eventCondition, Condition parentCondition,
boolean
forceRefresh, boolean resetExistingProfilesNotMatching) {
long t = System.currentTimeMillis();
- List<Condition> l = new ArrayList<Condition>();
+ List<Condition> l = new ArrayList<>();
Condition andCondition = new Condition();
andCondition.setConditionType(definitionsService.getConditionType("booleanCondition"));
andCondition.setParameter("operator", "and");
@@ -877,25 +875,25 @@ public class SegmentServiceImpl extends
AbstractServiceImpl implements SegmentSe
* Return the list of profile ids, for profiles that already have an event
count matching the generated property key
*
* @param generatedPropertyKey the generated property key of the generated
rule for the given past event condition.
- * @return the list of profile ids.
+ * @return the set of profile ids.
*/
private Set<String> getExistingProfilesWithPastEventOccurrenceCount(String
generatedPropertyKey) {
- Condition countExistsCondition = new Condition();
-
countExistsCondition.setConditionType(definitionsService.getConditionType("profilePropertyCondition"));
- countExistsCondition.setParameter("propertyName",
"systemProperties.pastEvents." + generatedPropertyKey);
- countExistsCondition.setParameter("comparisonOperator", "greaterThan");
- countExistsCondition.setParameter("propertyValueInteger", 0);
+ ConditionBuilder conditionBuilder =
definitionsService.getConditionBuilder();
+ ConditionBuilder.ConditionItem subConditionCount =
conditionBuilder.profileProperty("systemProperties.pastEvents.count").greaterThan(0);
+ ConditionBuilder.ConditionItem subConditionKey =
conditionBuilder.profileProperty("systemProperties.pastEvents.key").equalTo(generatedPropertyKey);
+ ConditionBuilder.ConditionItem booleanCondition =
conditionBuilder.and(subConditionCount, subConditionKey);
+ Condition condition = conditionBuilder.nested(booleanCondition,
"systemProperties.pastEvents").build();
Set<String> profileIds = new HashSet<>();
if (pastEventsDisablePartitions) {
-
profileIds.addAll(persistenceService.aggregateWithOptimizedQuery(countExistsCondition,
new TermsAggregate("itemId"),
+
profileIds.addAll(persistenceService.aggregateWithOptimizedQuery(condition, new
TermsAggregate("itemId"),
Profile.ITEM_TYPE, maximumIdsQueryCount).keySet());
} else {
- Map<String, Double> m =
persistenceService.getSingleValuesMetrics(countExistsCondition, new
String[]{"card"}, "itemId.keyword", Profile.ITEM_TYPE);
+ Map<String, Double> m =
persistenceService.getSingleValuesMetrics(condition, new String[]{"card"},
"itemId.keyword", Profile.ITEM_TYPE);
long card = m.get("_card").longValue();
int numParts = (int) (card / aggregateQueryBucketSize) + 2;
for (int i = 0; i < numParts; i++) {
-
profileIds.addAll(persistenceService.aggregateWithOptimizedQuery(countExistsCondition,
new TermsAggregate("itemId", i, numParts),
+
profileIds.addAll(persistenceService.aggregateWithOptimizedQuery(condition, new
TermsAggregate("itemId", i, numParts),
Profile.ITEM_TYPE).keySet());
}
}
@@ -986,35 +984,35 @@ public class SegmentServiceImpl extends
AbstractServiceImpl implements SegmentSe
*
* @param eventCountByProfile the events count per profileId map
* @param propertyKey the generate property key for this past
event condition, to keep track of the count in the profile
- * @return the list of profiles for witch the count of event occurrences
have been updated.
+ * @return the set of profiles for witch the count of event occurrences
have been updated.
*/
private Set<String> updatePastEventOccurrencesOnProfiles(Map<String, Long>
eventCountByProfile, String propertyKey) {
Set<String> profilesUpdated = new HashSet<>();
- Map<Item, Map> batch = new HashMap<>();
+ Set<String> batchProfilesToUpdate = new HashSet<>();
Iterator<Map.Entry<String, Long>> entryIterator =
eventCountByProfile.entrySet().iterator();
+ Map<String, Map<String, Object>> paramPerProfile = new HashMap<>();
+
while (entryIterator.hasNext()) {
Map.Entry<String, Long> entry = entryIterator.next();
String profileId = entry.getKey();
if (!profileId.startsWith("_")) {
- Map<String, Long> pastEventCounts = new HashMap<>();
- pastEventCounts.put(propertyKey, entry.getValue());
- Map<String, Object> systemProperties = new HashMap<>();
- systemProperties.put("pastEvents", pastEventCounts);
- systemProperties.put("lastUpdated", new Date());
-
- Profile profile = new Profile();
- profile.setItemId(profileId);
- batch.put(profile,
Collections.singletonMap("systemProperties", systemProperties));
+ Map<String, Object> pastEventKeyValue = new HashMap<>();
+ pastEventKeyValue.put("pastEventKey", propertyKey);
+ pastEventKeyValue.put("valueToAdd", entry.getValue());
+ paramPerProfile.put(profileId, pastEventKeyValue);
profilesUpdated.add(profileId);
+ batchProfilesToUpdate.add(profileId);
}
- if (batch.size() == segmentUpdateBatchSize ||
(!entryIterator.hasNext() && batch.size() > 0)) {
+ if (batchProfilesToUpdate.size() == segmentUpdateBatchSize ||
(!entryIterator.hasNext() && !batchProfilesToUpdate.isEmpty())) {
try {
- persistenceService.update(batch, Profile.class);
+ Condition profileIdCondition =
definitionsService.getConditionBuilder().condition("idsCondition").parameter("ids",
batchProfilesToUpdate).parameter("match", true).build();
+
persistenceService.updateWithQueryAndStoredScript(Profile.class, new
String[]{"updatePastEventOccurences"}, new Map[]{paramPerProfile}, new
Condition[]{profileIdCondition});
} catch (Exception e) {
- logger.error("Error updating {} profiles for past event
system properties", batch.size(), e);
+ logger.error("Error updating {} profiles for past event
system properties", paramPerProfile.size(), e);
} finally {
- batch.clear();
+ paramPerProfile.clear();
+ batchProfilesToUpdate.clear();
}
}
}
@@ -1030,7 +1028,7 @@ public class SegmentServiceImpl extends
AbstractServiceImpl implements SegmentSe
sb.append(Integer.toHexString((array[i] & 0xFF) |
0x100).substring(1, 3));
}
return sb.toString();
- } catch (java.security.NoSuchAlgorithmException e) {
+ } catch (NoSuchAlgorithmException e) {
throw new RuntimeException(e);
}
}
diff --git
a/tools/shell-commands/src/main/resources/META-INF/cxs/migration/migrate-2.5.0-00-cleanPastEventProfileSession.groovy
b/tools/shell-commands/src/main/resources/META-INF/cxs/migration/migrate-2.5.0-00-cleanPastEventProfileSession.groovy
new file mode 100644
index 000000000..7b519ce47
--- /dev/null
+++
b/tools/shell-commands/src/main/resources/META-INF/cxs/migration/migrate-2.5.0-00-cleanPastEventProfileSession.groovy
@@ -0,0 +1,48 @@
+import org.apache.unomi.shell.migration.service.MigrationContext
+import org.apache.unomi.shell.migration.utils.HttpUtils
+import org.apache.unomi.shell.migration.utils.MigrationUtils
+import org.osgi.framework.BundleContext
+import org.osgi.framework.Bundle
+
+/*
+ * 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.
+ */
+
+MigrationContext context = migrationContext
+String esAddress = context.getConfigString("esAddress")
+String indexPrefix = context.getConfigString("indexPrefix")
+String rolloverPolicyName = indexPrefix + "-unomi-rollover-policy"
+String rolloverEventAlias = indexPrefix + "-session"
+
+context.performMigrationStep("2.5.0-clean-profile-mapping", () -> {
+ String baseSettings = MigrationUtils.resourceAsString(bundleContext,
"requestBody/2.0.0/base_index_mapping.json")
+ String updatePastEventScript =
MigrationUtils.getFileWithoutComments(bundleContext,
"requestBody/2.5.0/update_pastEvents_profile.painless")
+ String mapping = MigrationUtils.extractMappingFromBundles(bundleContext,
"profile.json")
+ String newIndexSettings =
MigrationUtils.buildIndexCreationRequest(baseSettings, mapping, context, false)
+ MigrationUtils.reIndex(context.getHttpClient(), bundleContext, esAddress,
indexPrefix + "-profile", newIndexSettings, updatePastEventScript, context)
+})
+
+context.performMigrationStep("2.5.0-clean-session-mapping", () -> {
+ String baseSettings = MigrationUtils.resourceAsString(bundleContext,
"requestBody/2.2.0/base_index_withRollover_request.json")
+ String cleanPastEventScript =
MigrationUtils.getFileWithoutComments(bundleContext,
"requestBody/2.5.0/remove_pastEvents_session.painless")
+ String mapping = MigrationUtils.extractMappingFromBundles(bundleContext,
"session.json")
+ String newIndexSettings =
MigrationUtils.buildIndexCreationRequestWithRollover(baseSettings, mapping,
context, rolloverPolicyName, rolloverEventAlias)
+ Set<String> sessionIndices =
MigrationUtils.getIndexesPrefixedBy(context.getHttpClient(), esAddress,
"${indexPrefix}-session-")
+
+ sessionIndices.each { sessionIndex ->
+ MigrationUtils.reIndex(context.getHttpClient(), bundleContext,
esAddress, sessionIndex, newIndexSettings, cleanPastEventScript, context)
+ }
+})
diff --git
a/tools/shell-commands/src/main/resources/requestBody/2.5.0/remove_pastEvents_session.painless
b/tools/shell-commands/src/main/resources/requestBody/2.5.0/remove_pastEvents_session.painless
new file mode 100644
index 000000000..6e3b6d248
--- /dev/null
+++
b/tools/shell-commands/src/main/resources/requestBody/2.5.0/remove_pastEvents_session.painless
@@ -0,0 +1,22 @@
+/*
+ * 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.
+ */
+
+if (ctx._source.profile != null) {
+ if (ctx._source.profile.systemProperties != null &&
ctx._source.profile.systemProperties.pastEvents != null) {
+ ctx._source.profile.systemProperties.remove('pastEvents');
+ }
+}
diff --git
a/tools/shell-commands/src/main/resources/requestBody/2.5.0/update_pastEvents_profile.painless
b/tools/shell-commands/src/main/resources/requestBody/2.5.0/update_pastEvents_profile.painless
new file mode 100644
index 000000000..9c6768084
--- /dev/null
+++
b/tools/shell-commands/src/main/resources/requestBody/2.5.0/update_pastEvents_profile.painless
@@ -0,0 +1,27 @@
+/*
+ * 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.
+ */
+
+if (ctx._source.systemProperties != null &&
ctx._source.systemProperties.pastEvents != null &&
ctx._source.systemProperties.pastEvents instanceof Map) {
+ List listOfPastEvent = new ArrayList();
+ for (pastEventKey in ctx._source.systemProperties.pastEvents.keySet()) {
+ Map pastEvent = new HashMap();
+ pastEvent.put('key', pastEventKey);
+ pastEvent.put('count',
ctx._source.systemProperties.pastEvents.get(pastEventKey));
+ listOfPastEvent.add(pastEvent);
+ }
+ ctx._source.systemProperties.pastEvents = listOfPastEvent;
+}