Support real time unavailable item constraint
diff --git a/README.md b/README.md
index d2ed8ce..3595257 100644
--- a/README.md
+++ b/README.md
@@ -22,16 +22,19 @@
normal:
```
-curl -H "Content-Type: application/json" \
+$ curl -H "Content-Type: application/json" \
-d '{
"user" : "u1",
"num" : 10 }' \
http://localhost:8000/queries.json \
-w %{time_connect}:%{time_starttransfer}:%{time_total}
+
+
+{"itemScores":[{"item":"i43","score":0.9987465791873191},{"item":"i21","score":0.9987465791873191},{"item":"i20","score":0.997849496769407},{"item":"i49","score":0.9969960024975738},{"item":"i41","score":0.9955226345451645},{"item":"i37","score":0.9953403563456491},{"item":"i8","score":0.9944547925923641},{"item":"i46","score":0.9928301245473273},{"item":"i1","score":0.9910679754780729},{"item":"i18","score":0.9903295848514115}]}
```
```
-curl -H "Content-Type: application/json" \
+$ curl -H "Content-Type: application/json" \
-d '{
"user" : "u1",
"num": 10,
@@ -113,3 +116,37 @@
}'
```
+
+## handle unavailable items
+
+Set the following items "unavailable" (need to specify complete list each time when this list is changed):
+
+```
+curl -i -X POST http://localhost:7070/events.json?accessKey=s49uNadpNh5C4cRGCCIp8NqK1UWkaQPsN1SN2y670pZfRVXsbUepzmZQmyfjWeHo \
+-H "Content-Type: application/json" \
+-d '{
+ "event" : "$set",
+ "entityType" : "itemConstraint"
+ "entityId" : "unavailable",
+ "properties" : {
+ "items": ["i43", "i20", "i37", "i3", "i4", "i5"],
+ }
+ "eventTime" : "2015-02-17T02:11:21.934Z"
+}'
+```
+
+No more items "unavailable":
+
+```
+curl -i -X POST http://localhost:7070/events.json?accessKey=s49uNadpNh5C4cRGCCIp8NqK1UWkaQPsN1SN2y670pZfRVXsbUepzmZQmyfjWeHo \
+-H "Content-Type: application/json" \
+-d '{
+ "event" : "$set",
+ "entityType" : "itemConstraint"
+ "entityId" : "unavailable",
+ "properties" : {
+ "items": [],
+ }
+ "eventTime" : "2015-02-18T02:11:21.934Z"
+}'
+```
diff --git a/engine.json b/engine.json
index 5748efa..a8f5576 100644
--- a/engine.json
+++ b/engine.json
@@ -4,7 +4,7 @@
"engineFactory": "org.template.ecommercerecommendation.ECommerceRecommendationEngine",
"datasource": {
"params" : {
- "appId": 16
+ "appId": 17
}
},
"algorithms": [
diff --git a/src/main/scala/ALSAlgorithm.scala b/src/main/scala/ALSAlgorithm.scala
index ea50e62..cd424fe 100644
--- a/src/main/scala/ALSAlgorithm.scala
+++ b/src/main/scala/ALSAlgorithm.scala
@@ -57,6 +57,7 @@
extends P2LAlgorithm[PreparedData, ALSModel, Query, PredictedResult] {
@transient lazy val logger = Logger[this.type]
+ // NOTE: use getLEvents() for local access
@transient lazy val lEventsDb = Storage.getLEvents()
def train(data: PreparedData): ALSModel = {
@@ -94,7 +95,8 @@
}.filter { case ((u, i), v) =>
// keep events with valid user and item index
(u != -1) && (i != -1)
- }.reduceByKey(_ + _) // aggregate all view events of same user-item pair
+ }
+ .reduceByKey(_ + _) // aggregate all view events of same user-item pair
.map { case ((u, i), v) =>
// MLlibRating requires integer index for user and item
MLlibRating(u, i, v)
@@ -173,7 +175,7 @@
event.targetEntityId.get
} catch {
case e => {
- logger.error("Can't get targetEntityId of event ${event}.")
+ logger.error(s"Can't get targetEntityId of event ${event}.")
throw e
}
}
@@ -182,10 +184,34 @@
Set[String]()
}
- // combine query's blackList and seenItems into final blackList
+ val unavailableItems: Set[String] = lEventsDb.find(
+ appId = ap.appId,
+ // entityType and entityId is specified for fast lookup
+ entityType = Some("itemConstraint"),
+ entityId = Some("unavailable"),
+ eventNames = Some(Seq("$set")),
+ limit = Some(1),
+ reversed = Some(true),
+ timeout = Duration(200, "millis")
+ ) match {
+ case Right(x) => {
+ if (x.hasNext) {
+ x.next.properties.get[Set[String]]("items")
+ } else {
+ Set[String]()
+ }
+ }
+ case Left(e) => {
+ logger.error(s"Error when read unavailable items: ${e}")
+ Set[String]()
+ }
+ }
+
+ // combine query's blackList,seenItems and unavailableItems
+ // into final blackList.
// convert seen Items list from String ID to interger Index
- val finalBlackList: Set[Int] = (blackList ++ seenItems).map( x =>
- model.itemStringIntMap.get(x)).flatten
+ val finalBlackList: Set[Int] = (blackList ++ seenItems ++
+ unavailableItems).map( x => model.itemStringIntMap.get(x)).flatten
val userFeature =
model.userStringIntMap.get(query.user).map { userIndex =>
@@ -225,8 +251,7 @@
} else {
// the user doesn't have feature vector.
- // For example, new user created after model is trained.
-
+ // For example, new user is created after model is trained.
logger.info(s"No userFeature found for user ${query.user}.")
predictNewUser(
model = model,