This is an automated email from the ASF dual-hosted git repository. jsinovassinnaik pushed a commit to branch UNOMI-817 in repository https://gitbox.apache.org/repos/asf/unomi.git
commit 3c3482c14e79ca3f0b221b910c58148992964156 Author: jsinovassin <jsinovassinn...@jahia.com> AuthorDate: Tue Apr 23 14:50:15 2024 +0200 UNOMI-817: transform pastEvents to nested property --- .../resources/META-INF/cxs/mappings/profile.json | 2 +- .../actions/SetEventOccurenceCountAction.java | 21 ++++++--- .../PastEventConditionESQueryBuilder.java | 55 +++++++++++++++------- .../conditions/PastEventConditionEvaluator.java | 15 ++++-- .../services/impl/segments/SegmentServiceImpl.java | 54 ++++++++++++++------- ...te-2.5.0-00-cleanPastEventProfileSession.groovy | 3 +- .../2.5.0/remove_pastEvents_session.painless | 1 - ...painless => update_pastEvents_profile.painless} | 13 +++-- 8 files changed, 113 insertions(+), 51 deletions(-) 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 48eab69f5..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 @@ -43,7 +43,7 @@ "systemProperties": { "properties": { "pastEvents": { - "type": "flattened" + "type": "nested" } } }, 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..2b195227f 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,7 @@ 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 javax.xml.bind.DatatypeConverter; @@ -123,14 +120,26 @@ 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) { + ArrayList<Map<String, Object>> pastEvents = (ArrayList<Map<String, Object>>) event.getProfile().getSystemProperties().get("pastEvents"); + if (pastEvents == null) { + pastEvents = new ArrayList<>(); + } + pastEvents.removeIf(pastEvent -> pastEvent.get("key").equals(generatedPropertyKey)); + Map<String, Object> pastEvent = new HashMap<>(); + pastEvent.put("key", generatedPropertyKey); + pastEvent.put("count", count); + pastEvents.add(pastEvent); + return PropertyHelper.setProperty(event.getProfile(), "systemProperties.pastEvents", pastEvents, "alwaysSet"); + } + 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..28e8c1939 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 @@ -131,33 +131,52 @@ public class PastEventConditionESQueryBuilder implements ConditionESQueryBuilder } private Condition getProfileConditionForCounter(String generatedPropertyKey, Integer minimumEventCount, Integer maximumEventCount, boolean eventsOccurred) { - String generatedPropertyName = "systemProperties.pastEvents." + generatedPropertyKey; + Condition countCondition = new Condition(); + + countCondition.setConditionType(definitionsService.getConditionType("nestedCondition")); + countCondition.setParameter("path", "systemProperties.pastEvents"); + + Condition subConditionCount = new Condition(definitionsService.getConditionType("profilePropertyCondition")); + + Condition subConditionKey = new Condition(definitionsService.getConditionType("profilePropertyCondition")); + subConditionKey.setParameter("propertyName", "systemProperties.pastEvents.key"); + subConditionKey.setParameter("comparisonOperator", "equals"); + subConditionKey.setParameter("propertyValue", 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; + subConditionCount.setParameter("propertyName", "systemProperties.pastEvents.count"); + subConditionCount.setParameter("comparisonOperator", "between"); + subConditionCount.setParameter("propertyValuesInteger", Arrays.asList(minimumEventCount, maximumEventCount)); + + Condition booleanCondition = new Condition(definitionsService.getConditionType("booleanCondition")); + booleanCondition.setParameter("operator", "and"); + booleanCondition.setParameter("subConditions", Arrays.asList(subConditionCount, subConditionKey)); + + countCondition.setParameter("subCondition", booleanCondition); } 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); + Condition keyMissing = new Condition(profilePropertyConditionType); + keyMissing.setParameter("propertyName", "systemProperties.pastEvents.key"); + keyMissing.setParameter("comparisonOperator", "missing"); + keyMissing.setParameter("propertyValue", generatedPropertyKey); + + Condition counterZero = new Condition(profilePropertyConditionType); + counterZero.setParameter("propertyName", "systemProperties.pastEvents.count"); counterZero.setParameter("comparisonOperator", "equals"); counterZero.setParameter("propertyValueInteger", 0); + Condition keyExistsAndCounterZero = new Condition(definitionsService.getConditionType("booleanCondition")); + keyExistsAndCounterZero.setParameter("operator", "and"); + keyExistsAndCounterZero.setParameter("subConditions", Arrays.asList(subConditionKey, counterZero)); + Condition counterCondition = new Condition(); counterCondition.setConditionType(definitionsService.getConditionType("booleanCondition")); counterCondition.setParameter("operator", "or"); - counterCondition.setParameter("subConditions", Arrays.asList(counterMissing, counterZero)); - return counterCondition; + counterCondition.setParameter("subConditions", Arrays.asList(keyMissing, keyExistsAndCounterZero)); + + countCondition.setParameter("subCondition", counterCondition); } + return countCondition; } private Set<String> getProfileIdsMatchingEventCount(Condition eventCondition, int minimumEventCount, int maximumEventCount) { @@ -254,4 +273,4 @@ 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..58d912b42 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,15 @@ 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"); - if (pastEvents != null) { - Number l = (Number) pastEvents.get(key); - count = l != null ? l.longValue() : 0L; + Object rawPastEvents = profile.getSystemProperties().get("pastEvents"); + if (rawPastEvents != null) { + List<Map<String, Object>> pastEvents = (ArrayList<Map<String, Object>>) rawPastEvents; + 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/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..04acfeee5 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 @@ -801,7 +801,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"); @@ -881,10 +881,25 @@ public class SegmentServiceImpl extends AbstractServiceImpl implements SegmentSe */ 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); + + countExistsCondition.setConditionType(definitionsService.getConditionType("nestedCondition")); + countExistsCondition.setParameter("path", "systemProperties.pastEvents"); + + Condition subConditionCount = new Condition(definitionsService.getConditionType("profilePropertyCondition")); + subConditionCount.setParameter("propertyName", "systemProperties.pastEvents.count"); + subConditionCount.setParameter("comparisonOperator", "greaterThan"); + subConditionCount.setParameter("propertyValueInteger", 0); + + Condition subConditionKey = new Condition(definitionsService.getConditionType("profilePropertyCondition")); + subConditionKey.setParameter("propertyName", "systemProperties.pastEvents.key"); + subConditionKey.setParameter("comparisonOperator", "equals"); + subConditionKey.setParameter("propertyValue", generatedPropertyKey); + + Condition booleanCondition = new Condition(definitionsService.getConditionType("booleanCondition")); + booleanCondition.setParameter("operator", "and"); + booleanCondition.setParameter("subConditions", Arrays.asList(subConditionCount, subConditionKey)); + + countExistsCondition.setParameter("subCondition", booleanCondition); Set<String> profileIds = new HashSet<>(); if (pastEventsDisablePartitions) { @@ -996,19 +1011,26 @@ public class SegmentServiceImpl extends AbstractServiceImpl implements SegmentSe 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)); - profilesUpdated.add(profileId); + Profile storedProfile = persistenceService.load(profileId, Profile.class); + if (storedProfile != null) { + List<Map<String, Object>> pastEvents = new ArrayList<>(); + Map<String, Object> systemProperties = storedProfile.getSystemProperties() != null ? storedProfile.getSystemProperties() : new HashMap<>(); + if (systemProperties.containsKey("pastEvents")) { + pastEvents = (ArrayList<Map<String, Object>>) storedProfile.getSystemProperties().get("pastEvents"); + pastEvents.removeIf(map -> map.get("key").equals(propertyKey)); + } + pastEvents.add(Map.of("key", propertyKey, "count", entry.getValue())); + systemProperties.put("pastEvents", pastEvents); + systemProperties.put("lastUpdated", new Date()); + + Profile profile = new Profile(); + profile.setItemId(profileId); + batch.put(profile, Collections.singletonMap("systemProperties", systemProperties)); + profilesUpdated.add(profileId); + } } - if (batch.size() == segmentUpdateBatchSize || (!entryIterator.hasNext() && batch.size() > 0)) { + if (batch.size() == segmentUpdateBatchSize || (!entryIterator.hasNext() && !batch.isEmpty())) { try { persistenceService.update(batch, Profile.class); } catch (Exception 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 index bcd43fde1..7b519ce47 100644 --- 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 @@ -29,9 +29,10 @@ 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, "", context) + MigrationUtils.reIndex(context.getHttpClient(), bundleContext, esAddress, indexPrefix + "-profile", newIndexSettings, updatePastEventScript, context) }) context.performMigrationStep("2.5.0-clean-session-mapping", () -> { 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 index 17f4443ca..6e3b6d248 100644 --- 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 @@ -16,7 +16,6 @@ */ if (ctx._source.profile != null) { - /* Look for empty scope */ 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/remove_pastEvents_session.painless b/tools/shell-commands/src/main/resources/requestBody/2.5.0/update_pastEvents_profile.painless similarity index 57% copy from tools/shell-commands/src/main/resources/requestBody/2.5.0/remove_pastEvents_session.painless copy to tools/shell-commands/src/main/resources/requestBody/2.5.0/update_pastEvents_profile.painless index 17f4443ca..e6981f033 100644 --- 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/update_pastEvents_profile.painless @@ -15,9 +15,14 @@ * limitations under the License. */ -if (ctx._source.profile != null) { - /* Look for empty scope */ - if (ctx._source.profile.systemProperties != null && ctx._source.profile.systemProperties.pastEvents != null) { - ctx._source.profile.systemProperties.remove('pastEvents'); +if (ctx._source.systemProperties != null && ctx._source.systemProperties.pastEvents != null && ctx._source.systemProperties.pastEvents instanceof Map) { + Map updatedPastEvents = new HashMap(); + 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; }