You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@unomi.apache.org by jk...@apache.org on 2022/09/16 16:13:20 UTC
[unomi] 01/01: UNOMI-666: fix personalization that use score sorting and interests (new interests data structure)
This is an automated email from the ASF dual-hosted git repository.
jkevan pushed a commit to branch fixPersoScoreInterestsSort
in repository https://gitbox.apache.org/repos/asf/unomi.git
commit 418b498e30980a165818a658dd8c483c684940b8
Author: Kevan <ke...@jahia.com>
AuthorDate: Fri Sep 16 18:12:37 2022 +0200
UNOMI-666: fix personalization that use score sorting and interests (new interests data structure)
---
.../org/apache/unomi/itests/ContextServletIT.java | 69 ++++++++
.../resources/personalization-score-interests.json | 178 +++++++++++++++++++++
.../unomi/rest/endpoints/ContextJsonEndpoint.java | 2 -
.../sorts/ScorePersonalizationStrategy.java | 15 +-
4 files changed, 257 insertions(+), 7 deletions(-)
diff --git a/itests/src/test/java/org/apache/unomi/itests/ContextServletIT.java b/itests/src/test/java/org/apache/unomi/itests/ContextServletIT.java
index 6559c4660..cfa9275cd 100644
--- a/itests/src/test/java/org/apache/unomi/itests/ContextServletIT.java
+++ b/itests/src/test/java/org/apache/unomi/itests/ContextServletIT.java
@@ -550,6 +550,75 @@ public class ContextServletIT extends BaseIT {
assertEquals("Invalid response code", 200, response.getStatusCode());
}
+ @Test
+ public void testScorePersonalizationStrategy_Interests() throws Exception {
+ // Test request before adding interests to current profile.
+ HttpPost request = new HttpPost(getFullUrl(CONTEXT_URL));
+ request.setEntity(new StringEntity(getValidatedBundleJSON("personalization-score-interests.json", null), ContentType.APPLICATION_JSON));
+ TestUtils.RequestResponse response = TestUtils.executeContextJSONRequest(request);
+ ContextResponse contextResponse = response.getContextResponse();
+ List<String> variants = contextResponse.getPersonalizations().get("perso-by-interest");
+ assertEquals("Invalid response code", 200, response.getStatusCode());
+ assertEquals("Perso should be empty, profile is empty", 0, variants.size());
+
+ // set profile for matching
+ Profile profile = profileService.load(TEST_PROFILE_ID);
+ profile.setProperty("age", 30);
+ profileService.save(profile);
+ keepTrying("Profile " + TEST_PROFILE_ID + " not found in the required time", () -> profileService.load(TEST_PROFILE_ID),
+ savedProfile -> (savedProfile != null && savedProfile.getProperty("age").equals(30)), DEFAULT_TRYING_TIMEOUT, DEFAULT_TRYING_TRIES);
+
+ // check results of the perso now
+ request = new HttpPost(getFullUrl(CONTEXT_URL));
+ request.setEntity(new StringEntity(getValidatedBundleJSON("personalization-score-interests.json", null), ContentType.APPLICATION_JSON));
+ response = TestUtils.executeContextJSONRequest(request);
+ contextResponse = response.getContextResponse();
+ variants = contextResponse.getPersonalizations().get("perso-by-interest");
+ assertEquals("Invalid response code", 200, response.getStatusCode());
+ assertEquals("Perso should contains the good number of variants", 1, variants.size());
+ assertEquals("Variant is not the expected one", "matching-fishing-interests-custom-score-100-variant-expected-score-120", variants.get(0));
+
+ // modify profile to add interests
+ profile = profileService.load(TEST_PROFILE_ID);
+ List<Map<String, Object>> interests = new ArrayList<>();
+ Map<String, Object> interest1 = new HashMap<>();
+ interest1.put("key", "cars");
+ interest1.put("value", 50);
+ interests.add(interest1);
+ Map<String, Object> interest2 = new HashMap<>();
+ interest2.put("key", "football");
+ interest2.put("value", 40);
+ interests.add(interest2);
+ Map<String, Object> interest3 = new HashMap<>();
+ interest3.put("key", "tennis");
+ interest3.put("value", 30);
+ interests.add(interest3);
+ Map<String, Object> interest4 = new HashMap<>();
+ interest4.put("key", "fishing");
+ interest4.put("value", 20);
+ interests.add(interest4);
+ profile.setProperty("interests", interests);
+ profileService.save(profile);
+ keepTrying("Profile " + TEST_PROFILE_ID + " not found in the required time", () -> profileService.load(TEST_PROFILE_ID),
+ savedProfile -> (savedProfile != null && savedProfile.getProperty("interests") != null), DEFAULT_TRYING_TIMEOUT, DEFAULT_TRYING_TRIES);
+
+ // re test now that profiles has interests
+ request = new HttpPost(getFullUrl(CONTEXT_URL));
+ request.setEntity(new StringEntity(getValidatedBundleJSON("personalization-score-interests.json", null), ContentType.APPLICATION_JSON));
+ response = TestUtils.executeContextJSONRequest(request);
+ contextResponse = response.getContextResponse();
+ variants = contextResponse.getPersonalizations().get("perso-by-interest");
+ assertEquals("Invalid response code", 200, response.getStatusCode());
+ assertEquals("Perso should contains the good number of variants", 7, variants.size());
+ assertEquals("Variant is not the expected one", "matching-fishing-interests-custom-score-100-variant-expected-score-120", variants.get(0));
+ assertEquals("Variant is not the expected one", "matching-football-cars-interests-variant-expected-score-91", variants.get(1));
+ assertEquals("Variant is not the expected one", "not-matching-football-cars-interests-variant-expected-score-90", variants.get(2));
+ assertEquals("Variant is not the expected one", "not-matching-tennis-fishing-interests-variant-expected-score-50", variants.get(3));
+ assertEquals("Variant is not the expected one", "matching-football-interests-variant-expected-score-51", variants.get(4));
+ assertEquals("Variant is not the expected one", "matching-tennis-interests-variant-expected-score-31", variants.get(5));
+ assertEquals("Variant is not the expected one", "not-matching-tennis-interests-custom-score-100-variant-expected-score-30", variants.get(6));
+ }
+
@Test
public void testPersonalizationWithControlGroup() throws Exception {
diff --git a/itests/src/test/resources/personalization-score-interests.json b/itests/src/test/resources/personalization-score-interests.json
new file mode 100644
index 000000000..0f19d62a5
--- /dev/null
+++ b/itests/src/test/resources/personalization-score-interests.json
@@ -0,0 +1,178 @@
+{
+ "source": {
+ "itemId": "CMSServer",
+ "itemType": "custom",
+ "scope": "acme",
+ "properties": {}
+ },
+ "requireSegments": true,
+ "requiredProfileProperties": [
+ "*"
+ ],
+ "requiredSessionProperties": [
+ "*"
+ ],
+ "personalizations": [
+ {
+ "id": "perso-by-interest",
+ "strategy": "score-sorted",
+ "strategyOptions": {
+ "threshold": 25
+ },
+ "contents": [
+ {
+ "id": "matching-football-cars-interests-variant-expected-score-91",
+ "filters": [
+ {
+ "condition": {
+ "type": "profilePropertyCondition",
+ "parameterValues": {
+ "propertyName": "properties.age",
+ "propertyValueInteger": "25",
+ "comparisonOperator": "greaterThan"
+ }
+ }
+ }
+ ],
+ "properties": {
+ "interests": "football cars"
+ }
+ },
+ {
+ "id": "not-matching-football-cars-interests-variant-expected-score-90",
+ "filters": [
+ {
+ "condition": {
+ "type": "profilePropertyCondition",
+ "parameterValues": {
+ "propertyName": "properties.age",
+ "propertyValueInteger": "35",
+ "comparisonOperator": "greaterThan"
+ }
+ }
+ }
+ ],
+ "properties": {
+ "interests": "football cars"
+ }
+ },
+ {
+ "id": "matching-football-interests-variant-expected-score-51",
+ "filters": [
+ {
+ "condition": {
+ "type": "profilePropertyCondition",
+ "parameterValues": {
+ "propertyName": "properties.age",
+ "propertyValueInteger": "25",
+ "comparisonOperator": "greaterThan"
+ }
+ }
+ }
+ ],
+ "properties": {
+ "interests": "football"
+ }
+ },
+ {
+ "id": "less-threshold-fishing-interests-variant-expected-score-21",
+ "filters": [
+ {
+ "condition": {
+ "type": "profilePropertyCondition",
+ "parameterValues": {
+ "propertyName": "properties.age",
+ "propertyValueInteger": "25",
+ "comparisonOperator": "greaterThan"
+ }
+ }
+ }
+ ],
+ "properties": {
+ "interests": "fishing"
+ }
+ },
+ {
+ "id": "matching-tennis-interests-variant-expected-score-31",
+ "filters": [
+ {
+ "condition": {
+ "type": "profilePropertyCondition",
+ "parameterValues": {
+ "propertyName": "properties.age",
+ "propertyValueInteger": "25",
+ "comparisonOperator": "greaterThan"
+ }
+ }
+ }
+ ],
+ "properties": {
+ "interests": "tennis"
+ }
+ },
+ {
+ "id": "not-matching-tennis-fishing-interests-variant-expected-score-50",
+ "filters": [
+ {
+ "condition": {
+ "type": "profilePropertyCondition",
+ "parameterValues": {
+ "propertyName": "properties.age",
+ "propertyValueInteger": "35",
+ "comparisonOperator": "greaterThan"
+ }
+ }
+ }
+ ],
+ "properties": {
+ "interests": "tennis fishing"
+ }
+ },
+ {
+ "id": "not-matching-tennis-interests-custom-score-100-variant-expected-score-30",
+ "filters": [
+ {
+ "condition": {
+ "type": "profilePropertyCondition",
+ "parameterValues": {
+ "propertyName": "properties.age",
+ "propertyValueInteger": "35",
+ "comparisonOperator": "greaterThan"
+ }
+ },
+ "properties": {
+ "score": 100
+ }
+ }
+ ],
+ "properties": {
+ "interests": "tennis"
+ }
+ },
+ {
+ "id": "matching-fishing-interests-custom-score-100-variant-expected-score-120",
+ "filters": [
+ {
+ "condition": {
+ "type": "profilePropertyCondition",
+ "parameterValues": {
+ "propertyName": "properties.age",
+ "propertyValueInteger": "25",
+ "comparisonOperator": "greaterThan"
+ }
+ },
+ "properties": {
+ "score": 100
+ }
+ }
+ ],
+ "properties": {
+ "interests": "fishing"
+ }
+ }
+ ]
+ }
+ ],
+ "sessionId": "dummy-session",
+ "profileId": "test-profile-id"
+}
\ No newline at end of file
diff --git a/rest/src/main/java/org/apache/unomi/rest/endpoints/ContextJsonEndpoint.java b/rest/src/main/java/org/apache/unomi/rest/endpoints/ContextJsonEndpoint.java
index 9d2a3c661..6207845f8 100644
--- a/rest/src/main/java/org/apache/unomi/rest/endpoints/ContextJsonEndpoint.java
+++ b/rest/src/main/java/org/apache/unomi/rest/endpoints/ContextJsonEndpoint.java
@@ -157,12 +157,10 @@ public class ContextJsonEndpoint {
// init ids
String profileId = null;
String scope = null;
- List<Event> events = null;
if (contextRequest != null) {
scope = contextRequest.getSource() != null ? contextRequest.getSource().getScope() : scope;
sessionId = contextRequest.getSessionId() != null ? contextRequest.getSessionId() : sessionId;
profileId = contextRequest.getProfileId();
- events = contextRequest.getEvents();
}
// build public context, profile + session creation/anonymous etc ...
diff --git a/services/src/main/java/org/apache/unomi/services/sorts/ScorePersonalizationStrategy.java b/services/src/main/java/org/apache/unomi/services/sorts/ScorePersonalizationStrategy.java
index 7e636d32f..82bcc165b 100644
--- a/services/src/main/java/org/apache/unomi/services/sorts/ScorePersonalizationStrategy.java
+++ b/services/src/main/java/org/apache/unomi/services/sorts/ScorePersonalizationStrategy.java
@@ -49,10 +49,15 @@ public class ScorePersonalizationStrategy implements PersonalizationStrategy {
String interestList = (String) (personalizedContent.getProperties() != null ? personalizedContent.getProperties().get("interests") : null);
if (interestList != null) {
- Map<String,Integer> interestValues = (Map<String, Integer>) profile.getProperties().get("interests");
- for (String interest : interestList.split(" ")) {
- if (interestValues != null && interestValues.get(interest) != null) {
- score += interestValues.get(interest);
+ List<Map<String, Object>> profileInterests = (List<Map<String, Object>>) profile.getProperties().get("interests");
+ if (profileInterests != null) {
+ for (String interest : interestList.split(" ")) {
+ for (Map<String, Object> profileInterest : profileInterests) {
+ if (profileInterest.get("key").equals(interest)){
+ score += ((Number) profileInterest.get("value")).intValue();
+ break;
+ }
+ }
}
}
}
@@ -74,7 +79,7 @@ public class ScorePersonalizationStrategy implements PersonalizationStrategy {
Condition condition = filter.getCondition();
if (condition != null && condition.getConditionTypeId() != null) {
if (profileService.matchCondition(condition, profile, session)) {
- if (filter.getProperties().get("score") != null) {
+ if (filter.getProperties() != null && filter.getProperties().get("score") != null) {
score += (int) filter.getProperties().get("score");
} else {
score += 1;