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 2023/03/09 18:28:17 UTC

[unomi] branch UNOMI-727-adapt-merge-rollover created (now 2d456a67f)

This is an automated email from the ASF dual-hosted git repository.

jkevan pushed a change to branch UNOMI-727-adapt-merge-rollover
in repository https://gitbox.apache.org/repos/asf/unomi.git


      at 2d456a67f UNOMI-727: adapt merge system to rollover (and cleanup too)

This branch includes the following new commits:

     new 2d456a67f UNOMI-727: adapt merge system to rollover (and cleanup too)

The 1 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



[unomi] 01/01: UNOMI-727: adapt merge system to rollover (and cleanup too)

Posted by jk...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

jkevan pushed a commit to branch UNOMI-727-adapt-merge-rollover
in repository https://gitbox.apache.org/repos/asf/unomi.git

commit 2d456a67fde9401d7c4a881acff1604d2be857ae
Author: Kevan <ke...@jahia.com>
AuthorDate: Thu Mar 9 19:28:01 2023 +0100

    UNOMI-727: adapt merge system to rollover (and cleanup too)
---
 .../actions/MergeProfilesOnPropertyAction.java     | 269 ++++++++++-----------
 1 file changed, 131 insertions(+), 138 deletions(-)

diff --git a/plugins/baseplugin/src/main/java/org/apache/unomi/plugins/baseplugin/actions/MergeProfilesOnPropertyAction.java b/plugins/baseplugin/src/main/java/org/apache/unomi/plugins/baseplugin/actions/MergeProfilesOnPropertyAction.java
index 2261a2a4b..49d9c7cc7 100644
--- a/plugins/baseplugin/src/main/java/org/apache/unomi/plugins/baseplugin/actions/MergeProfilesOnPropertyAction.java
+++ b/plugins/baseplugin/src/main/java/org/apache/unomi/plugins/baseplugin/actions/MergeProfilesOnPropertyAction.java
@@ -24,14 +24,15 @@ import org.apache.unomi.api.Profile;
 import org.apache.unomi.api.Session;
 import org.apache.unomi.api.actions.Action;
 import org.apache.unomi.api.actions.ActionExecutor;
-import org.apache.unomi.api.actions.ActionPostExecutor;
 import org.apache.unomi.api.conditions.Condition;
 import org.apache.unomi.api.services.*;
 import org.apache.unomi.persistence.spi.PersistenceService;
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
 
+import java.util.HashMap;
 import java.util.List;
+import java.util.Map;
 import java.util.UUID;
 
 public class MergeProfilesOnPropertyAction implements ActionExecutor {
@@ -46,171 +47,163 @@ public class MergeProfilesOnPropertyAction implements ActionExecutor {
 
     public int execute(Action action, Event event) {
 
-        Profile profile = event.getProfile();
-        if (profile instanceof Persona || profile.isAnonymousProfile()) {
+        Profile eventProfile = event.getProfile();
+        final String mergePropName = (String) action.getParameterValues().get("mergeProfilePropertyName");
+        final String mergePropValue = (String) action.getParameterValues().get("mergeProfilePropertyValue");
+        boolean forceEventProfileAsMaster = action.getParameterValues().containsKey("forceEventProfileAsMaster") ? (boolean) action.getParameterValues().get("forceEventProfileAsMaster") : false;
+        final String currentProfileMergeValue = (String) eventProfile.getSystemProperties().get(mergePropName);
+
+        if (eventProfile instanceof Persona || eventProfile.isAnonymousProfile() || StringUtils.isEmpty(mergePropName) ||
+                StringUtils.isEmpty(mergePropValue)) {
             return EventService.NO_CHANGE;
         }
 
-        final String mergeProfilePropertyName = (String) action.getParameterValues().get("mergeProfilePropertyName");
-        if (StringUtils.isEmpty(mergeProfilePropertyName)) {
-            return EventService.NO_CHANGE;
+        final List<Profile> profilesToBeMerge = getProfilesToBeMerge(mergePropName, mergePropValue);
+
+        // Check if the user switched to another profile
+        if (StringUtils.isNotEmpty(currentProfileMergeValue) && !currentProfileMergeValue.equals(mergePropValue)) {
+            reassignSession(event, profilesToBeMerge, forceEventProfileAsMaster, mergePropName, mergePropValue);
+            return EventService.PROFILE_UPDATED + EventService.SESSION_UPDATED;
         }
 
-        final String mergeProfilePropertyValue = (String) action.getParameterValues().get("mergeProfilePropertyValue");
-        if (StringUtils.isEmpty(mergeProfilePropertyValue)) {
-            return EventService.NO_CHANGE;
+        // Store merge prop on current profile
+        boolean profileUpdated = false;
+        if (StringUtils.isEmpty(currentProfileMergeValue)) {
+            profileUpdated = true;
+            eventProfile.getSystemProperties().put(mergePropName, mergePropValue);
         }
 
-        final String mergeProfilePreviousPropertyValue = profile.getSystemProperties().get(mergeProfilePropertyName) != null ? profile.getSystemProperties().get(mergeProfilePropertyName).toString() : "";
+        // If not profiles to merge we are done here.
+        if (profilesToBeMerge.isEmpty()) {
+            return profileUpdated ? EventService.PROFILE_UPDATED : EventService.NO_CHANGE;
+        }
 
-        final Session currentSession = event.getSession();
+        // add current Profile to profiles to be merged
+        if (profilesToBeMerge.stream().noneMatch(p -> StringUtils.equals(p.getItemId(), eventProfile.getItemId()))) {
+            profilesToBeMerge.add(eventProfile);
+        }
 
-        boolean forceEventProfileAsMaster = action.getParameterValues().containsKey("forceEventProfileAsMaster") ?
-                (boolean) action.getParameterValues().get("forceEventProfileAsMaster") : false;
+        final String eventProfileId = eventProfile.getItemId();
+        final Profile mergedProfile = profileService.mergeProfiles(forceEventProfileAsMaster ? eventProfile : profilesToBeMerge.get(0), profilesToBeMerge);
+        final String mergedProfileId = mergedProfile.getItemId();
 
-        // store the profile id in case the merge change it to a previous one
-        String profileId = profile.getItemId();
+        // Profile is still using the same profileId after being merged, no need to rewrite exists data, merge is done
+        if (!forceEventProfileAsMaster && mergedProfileId.equals(eventProfileId)) {
+            return profileUpdated ? EventService.PROFILE_UPDATED : EventService.NO_CHANGE;
+        }
 
-        Condition propertyCondition = new Condition(definitionsService.getConditionType("profilePropertyCondition"));
-        propertyCondition.setParameter("comparisonOperator", "equals");
-        propertyCondition.setParameter("propertyName", "systemProperties." + mergeProfilePropertyName);
-        propertyCondition.setParameter("propertyValue", mergeProfilePropertyValue);
+        // ProfileID changed we have a lot to do
+        // First check for privacy stuff (inherit from previous profile if necessary)
+        if (privacyService.isRequireAnonymousBrowsing(eventProfile)) {
+            privacyService.setRequireAnonymousBrowsing(mergedProfileId, true, event.getScope());
+        }
+        final boolean anonymousBrowsing = privacyService.isRequireAnonymousBrowsing(mergedProfileId);
 
-        final List<Profile> profiles = persistenceService.query(propertyCondition, "properties.firstVisit", Profile.class, 0, maxProfilesInOneMerge).getList();
+        // Modify current session:
+        if (event.getSession() != null) {
+            event.getSession().setProfile(anonymousBrowsing ? privacyService.getAnonymousProfile(mergedProfile) : mergedProfile);
+        }
 
-        // Check if the user switched to another profile
-        if (StringUtils.isNotEmpty(mergeProfilePreviousPropertyValue) && !mergeProfilePreviousPropertyValue.equals(mergeProfilePropertyValue)) {
-            if (profiles.size() > 0) {
-                // Take existing profile
-                profile = profiles.get(0);
-            } else {
-                if (forceEventProfileAsMaster) {
-                    profile = event.getProfile();
-                } else {
-                    // Create a new profile
-                    profile = new Profile(UUID.randomUUID().toString());
-                    profile.setProperty("firstVisit", event.getTimeStamp());
+        // Modify current event:
+        event.setProfileId(anonymousBrowsing ? null : mergedProfileId);
+        event.setProfile(mergedProfile);
+
+        event.getActionPostExecutors().add(() -> {
+            try {
+                // Save event, as we changed the profileId of the current event
+                if (event.isPersistent()) {
+                    persistenceService.save(event);
                 }
-                profile.getSystemProperties().put(mergeProfilePropertyName, mergeProfilePropertyValue);
-            }
 
-            logger.info("Different users, switch to " + profile.getItemId());
-            // At the end of the merge, we must set the merged profile as profile event to process other Actions
-            event.setProfileId(profile.getItemId());
-            event.setProfile(profile);
+                for (Profile profileToBeMerge : profilesToBeMerge) {
+                    String profileToBeMergeId = profileToBeMerge.getItemId();
+                    if (!StringUtils.equals(profileToBeMergeId, mergedProfileId)) {
 
-            if (currentSession != null) {
-                currentSession.setProfile(profile);
-                eventService.send(new Event("sessionReassigned", currentSession, profile, event.getScope(), event, currentSession,
-                        null, event.getTimeStamp(), false));
-            }
+                        // todo move in update by query + script
+                        List<Session> oldSessions = persistenceService.query("profileId", profileToBeMergeId, null, Session.class);
+                        for (Session oldSession : oldSessions) {
+                            if (!oldSession.getItemId().equals(event.getSession().getItemId())) {
+                                persistenceService.update(oldSession, Session.class, "profileId", anonymousBrowsing ? null : mergedProfileId);
+                            }
+                        }
 
-            return EventService.PROFILE_UPDATED + EventService.SESSION_UPDATED;
-        } else {
-            // Store the merge property identifier in the profile
-            profile.getSystemProperties().put(mergeProfilePropertyName, mergeProfilePropertyValue);
+                        // todo move in update by query + script
+                        List<Event> oldEvents = persistenceService.query("profileId", profileToBeMergeId, null, Event.class);
+                        for (Event oldEvent : oldEvents) {
+                            if (!oldEvent.getItemId().equals(event.getItemId())) {
+                                persistenceService.update(oldEvent, Event.class, "profileId", anonymousBrowsing ? null : mergedProfileId);
+                            }
+                        }
 
-            // add current Profile to profiles to be merged
-            boolean add = true;
-            for (Profile p : profiles) {
-                add = add && !StringUtils.equals(p.getItemId(), profile.getItemId());
-            }
-            if (add) {
-                profiles.add(profile);
+                        final String clientIdFromEvent = (String) event.getAttributes().get(Event.CLIENT_ID_ATTRIBUTE);
+                        String clientId = clientIdFromEvent != null ? clientIdFromEvent : "defaultClientId";
+                        profileService.addAliasToProfile(mergedProfileId, profileToBeMergeId, clientId);
+                        if (profileService.load(profileToBeMergeId) != null) {
+                            profileService.delete(profileToBeMergeId, false);
+                        }
+                    }
+                }
+            } catch (Exception e) {
+                logger.error("unable to execute callback action, profile and session will not be saved", e);
+                return false;
             }
+            return true;
+        });
 
-            if (profiles.size() == 1) {
-                return StringUtils.isEmpty(mergeProfilePreviousPropertyValue) ? EventService.PROFILE_UPDATED : EventService.NO_CHANGE;
-            }
+        return EventService.PROFILE_UPDATED + EventService.SESSION_UPDATED;
+    }
 
-            Profile markedMasterProfile;
-            if (forceEventProfileAsMaster)
-                markedMasterProfile = event.getProfile();
-            else
-                markedMasterProfile = profiles.get(0);// Use oldest profile for master profile
+    private List<Profile> getProfilesToBeMerge(String mergeProfilePropertyName, String mergeProfilePropertyValue) {
+        Condition propertyCondition = new Condition(definitionsService.getConditionType("profilePropertyCondition"));
+        propertyCondition.setParameter("comparisonOperator", "equals");
+        propertyCondition.setParameter("propertyName", "systemProperties." + mergeProfilePropertyName);
+        propertyCondition.setParameter("propertyValue", mergeProfilePropertyValue);
 
-            final Profile masterProfile = profileService.mergeProfiles(markedMasterProfile, profiles);
+        return persistenceService.query(propertyCondition, "properties.firstVisit", Profile.class, 0, maxProfilesInOneMerge).getList();
+    }
 
-            // Profile has changed
-            if (forceEventProfileAsMaster || !masterProfile.getItemId().equals(profileId)) {
+    private void reassignSession(Event event, List<Profile> existingMergedProfiles, boolean forceEventProfileAsMaster, String mergePropName, String mergePropValue) {
+        Profile eventProfile = event.getProfile();
 
-                final String masterProfileId = masterProfile.getItemId();
-                // At the end of the merge, we must set the merged profile as profile event to process other Actions
-                event.setProfileId(masterProfileId);
-                event.setProfile(masterProfile);
+        if (existingMergedProfiles.size() > 0) {
+            // Take existing profile
+            eventProfile = existingMergedProfiles.get(0);
+        } else {
+            if (!forceEventProfileAsMaster) {
+                // Create a new profile
+                eventProfile = new Profile(UUID.randomUUID().toString());
+                eventProfile.setProperty("firstVisit", event.getTimeStamp());
+            }
+            eventProfile.getSystemProperties().put(mergePropName, mergePropValue);
+        }
 
-                final Boolean anonymousBrowsing = privacyService.isRequireAnonymousBrowsing(masterProfileId);
+        logger.info("Different users, switch to " + eventProfile.getItemId());
+        // At the end of the merge, we must set the merged profile as profile event to process other Actions
+        event.setProfileId(eventProfile.getItemId());
+        event.setProfile(eventProfile);
 
-                if (currentSession != null) {
-                    currentSession.setProfile(masterProfile);
-                    if (privacyService.isRequireAnonymousBrowsing(profile)) {
-                        privacyService.setRequireAnonymousBrowsing(masterProfileId, true, event.getScope());
-                    }
+        if (event.getSession() != null) {
+            Session eventSession = event.getSession();
+            eventSession.setProfile(eventProfile);
+            eventService.send(new Event("sessionReassigned", eventSession, eventProfile, event.getScope(), event, eventSession,
+                    null, event.getTimeStamp(), false));
+        }
+    }
 
-                    if (anonymousBrowsing) {
-                        currentSession.setProfile(privacyService.getAnonymousProfile(masterProfile));
-                        event.setProfileId(null);
-                        persistenceService.save(event);
-                    }
-                }
+    private void updateAllSessionsForProfile(String newProfileId, String oldProfileId) {
+        String[] scripts = new String[]{"updateProfileId"};
 
-                event.getActionPostExecutors().add(new ActionPostExecutor() {
-                    @Override
-                    public boolean execute() {
-                        try {
-                            Event currentEvent = event;
-                            // Update current event explicitly, as it might not return from search query if there wasn't a refresh in ES
-                            if (!StringUtils.equals(profileId, masterProfileId)) {
-                                if (currentEvent.isPersistent()) {
-                                    persistenceService.update(currentEvent, Event.class, "profileId", anonymousBrowsing ? null : masterProfileId);
-                                }
-                            }
+        Map<String, Object>[] scriptParams = new HashMap[1];
+        scriptParams[0] = new HashMap<>();
+        scriptParams[0].put("profileId", newProfileId);
 
-                            for (Profile profile : profiles) {
-                                String profileId = profile.getItemId();
-                                if (!StringUtils.equals(profileId, masterProfileId)) {
-                                    // TODO consider udpate by query and/or script
-                                    List<Session> sessions = persistenceService.query("profileId", profileId, null, Session.class);
-                                    if (currentSession != null) {
-                                        if (masterProfileId.equals(profileId) && !sessions.contains(currentSession)) {
-                                            sessions.add(currentSession);
-                                        }
-                                    }
-
-                                    for (Session session : sessions) {
-                                        persistenceService.update(session, Session.class, "profileId", anonymousBrowsing ? null : masterProfileId);
-                                    }
-
-                                    // TODO consider udpate by query and/or script
-                                    List<Event> events = persistenceService.query("profileId", profileId, null, Event.class);
-                                    for (Event event : events) {
-                                        if (!event.getItemId().equals(currentEvent.getItemId())) {
-                                            persistenceService.update(event, Event.class, "profileId", anonymousBrowsing ? null : masterProfileId);
-                                        }
-                                    }
-
-                                    final String clientIdFromEvent = (String) event.getAttributes().get(Event.CLIENT_ID_ATTRIBUTE);
-                                    String clientId = clientIdFromEvent != null ? clientIdFromEvent : "defaultClientId";
-                                    profileService.addAliasToProfile(masterProfileId, profile.getItemId(), clientId);
-
-                                    boolean isExist = profileService.load(profile.getItemId()) != null;
-                                    if (isExist) {
-                                        profileService.delete(profileId, false);
-                                    }
-                                }
-                            }
-                        } catch (Exception e) {
-                            logger.error("unable to execute callback action, profile and session will not be saved", e);
-                            return false;
-                        }
-                        return true;
-                    }
-                });
-                return EventService.PROFILE_UPDATED + EventService.SESSION_UPDATED;
-            } else {
-                return StringUtils.isEmpty(mergeProfilePreviousPropertyValue) ? EventService.PROFILE_UPDATED : EventService.NO_CHANGE;
-            }
-        }
+        Condition[] conditions = new Condition[1];
+        conditions[0] = new Condition();
+        conditions[0].setConditionType(definitionsService.getConditionType("sessionPropertyCondition"));
+        conditions[0].setParameter("propertyName", "profileId");
+        conditions[0].setParameter("comparisonOperator", "equals");
+        conditions[0].setParameter("propertyValue", oldProfileId);
+        persistenceService.updateWithQueryAndStoredScript(Session.class, scripts, scriptParams, conditions);
     }
 
     public void setProfileService(ProfileService profileService) {
@@ -236,4 +229,4 @@ public class MergeProfilesOnPropertyAction implements ActionExecutor {
     public void setMaxProfilesInOneMerge(String maxProfilesInOneMerge) {
         this.maxProfilesInOneMerge = Integer.parseInt(maxProfilesInOneMerge);
     }
-}
+}
\ No newline at end of file