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

Reply via email to