/* * Sifarish: Recommendation Engine * Author: Pranab Ghosh * * Licensed under the Apache License, Version 2.0 (the "License"); you * may not use this file except in compliance with the License. You may * obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or * implied. See the License for the specific language governing * permissions and limitations under the License. */ package org.sifarish.realtime; import java.util.HashMap; import java.util.List; import java.util.Map; import org.apache.log4j.Level; import org.apache.log4j.Logger; import org.chombo.storm.Cache; import org.chombo.storm.GenericBolt; import org.chombo.storm.MessageHolder; import org.chombo.storm.MessageQueue; import org.chombo.util.ConfigUtility; import redis.clients.jedis.Jedis; import backtype.storm.task.TopologyContext; import backtype.storm.tuple.Tuple; /** * Makes real time recommendation based collaborative filtering * @author pranab * */ public class RecommenderBolt extends GenericBolt { private Map<String, UserItemRatings> userItemRatings = new HashMap<String, UserItemRatings>(); private Map stormConf; private boolean writeRecommendationToQueue; private String recommendationQueue; private String recommendationCache; private MessageQueue recQueue; private Cache recCache; public static final String USER_ID = "userID"; public static final String SESSION_ID = "sessionID"; public static final String ITEM_ID = "itemID"; public static final String EVENT_ID = "eventID"; public static final String TS_ID = "timeStamp"; public static final String FIELD_DELIM = ","; public static final String SUB_FIELD_DELIM = ":"; private static final Logger LOG = Logger.getLogger(RecommenderBolt.class); private static final long serialVersionUID = -8339901835031581335L; @Override public Map<String, Object> getComponentConfiguration() { // TODO Auto-generated method stub return null; } @Override public void intialize(Map stormConf, TopologyContext context) { this.stormConf = stormConf; writeRecommendationToQueue = ConfigUtility.getBoolean(stormConf,"write.recommendation.to.queue"); if (writeRecommendationToQueue) { recommendationQueue = ConfigUtility.getString(stormConf, "redis.recommendation.queue"); recQueue = MessageQueue.createMessageQueue(stormConf, recommendationQueue); }else { recommendationCache = ConfigUtility.getString(stormConf, "redis.recommendation.cache"); recCache = Cache.createCache(stormConf, recommendationCache); } debugOn = ConfigUtility.getBoolean(stormConf,"debug.on", false); if (debugOn) { LOG.setLevel(Level.INFO);; LOG.info("bolt intialized " ); } } @Override public boolean process(Tuple input) { boolean status = true; String userID = input.getStringByField(USER_ID); String sessionID = input.getStringByField(SESSION_ID); String itemID = input.getStringByField(ITEM_ID); int eventID = input.getIntegerByField(EVENT_ID); long timeStamp = input.getLongByField(TS_ID); if (debugOn) LOG.info("got tuple:" + userID + " " + sessionID + " " + itemID + " " + eventID + " " + timeStamp); try { UserItemRatings itemRatings = userItemRatings.get(userID); if (null == itemRatings) { Cache cache = Cache.createCache(stormConf, null); itemRatings = new UserItemRatings(userID, sessionID, cache, stormConf); userItemRatings.put(userID, itemRatings); } //add event and get predicted ratings itemRatings.addEvent(sessionID, itemID, eventID, timeStamp / 1000); List<UserItemRatings.ItemRating> predictedRatings = itemRatings.getPredictedRatings(); if (debugOn) { LOG.info("num of recommended items:" + predictedRatings.size()); } //write to queue or cache StringBuilder stBld = new StringBuilder(userID); for (UserItemRatings.ItemRating itemRating : predictedRatings) { stBld.append(FIELD_DELIM).append(itemRating.getItem()).append(SUB_FIELD_DELIM). append(itemRating.getRating()); } String itemRatingList = stBld.toString(); if (writeRecommendationToQueue) { //jedis.lpush(recommendationQueue, itemRatingList); recQueue.send(itemRatingList); if (debugOn) { LOG.info("wrote to recommendation queue"); } } else { //jedis.hset(recommendationCache, userID, itemRatingList); recCache.set(userID, itemRatingList); } } catch (Exception e) { //TODO LOG.info("got error " + e); status = false; } return status; } @Override public List<MessageHolder> getOutput() { // TODO Auto-generated method stub return null; } }