/* * Seldon -- open source prediction engine * ======================================= * Copyright 2011-2015 Seldon Technologies Ltd and Rummble Ltd (http://www.seldon.io/) * ********************************************************************************************** * * 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 io.seldon.recommendation.filters.tag; import io.seldon.clustering.recommender.RecommendationContext.OptionsHolder; import io.seldon.general.ItemStorage; import io.seldon.recommendation.ItemFilter; import io.seldon.recommendation.filters.FilteredItems; import io.seldon.recommendation.filters.tag.TagAffinityFilterModelManager.TagAffinityFilterModel; import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.apache.log4j.Logger; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component public class TagAffinityFilter implements ItemFilter { private static Logger logger = Logger.getLogger(TagAffinityFilter.class.getName()); private static final String TAG_ATTR_ID_OPTION_NAME = "io.seldon.algorithm.tags.attrid"; private static final String TAG_NUM_ITEMS_OPTION_NAME = "io.seldon.filter.tags.numitems"; private final ItemStorage itemStorage; private final TagAffinityFilterModelManager modelManager; @Autowired public TagAffinityFilter(ItemStorage itemStorage,TagAffinityFilterModelManager modelManager){ this.itemStorage = itemStorage; this.modelManager = modelManager; } private boolean hasNonEmptyIntersection(Set<Integer> s1,Set<Integer> s2) { for(Integer i : s1) { if (s2.contains(i)) return true; } return false; } @Override public List<Long> produceExcludedItems(String client, Long user, String clientUserId, OptionsHolder optsHolder, Long currentItem, String lastRecListUUID, int numRecommendations) { TagAffinityFilterModel model = modelManager.getClientStore(client, optsHolder); if (model != null) { Set<Integer> userClusters = model.userToClustersMap.get(user); if (userClusters != null) { if (logger.isDebugEnabled()) logger.debug("User "+user+" tag clusters "+userClusters.toString()); Integer tagAttrId = optsHolder.getIntegerOption(TAG_ATTR_ID_OPTION_NAME); Integer numItemsToGet = optsHolder.getIntegerOption(TAG_NUM_ITEMS_OPTION_NAME); Set<Long> itemsToExclude = new HashSet<Long>(); Set<Long> itemsToInclude = new HashSet<Long>(); for(Map.Entry<Integer,Set<Integer>> e : model.groupToClustersMap.entrySet()) { Set<Integer> clusters = e.getValue(); if (hasNonEmptyIntersection(userClusters, clusters)) // user is in this group of clusters { for(Integer cluster : clusters) { if (logger.isDebugEnabled()) logger.debug("Getting recent items from cluster "+cluster+" with tags "+model.clusterToTagsMap.get(cluster).toString()+" with tagAttrId "+tagAttrId+" numItemsToGet "+numItemsToGet); FilteredItems fItems = itemStorage.retrieveRecentlyAddedItemsWithTags(client, numItemsToGet, tagAttrId, model.clusterToTagsMap.get(cluster), cluster); if (logger.isDebugEnabled()) logger.debug("Found filtered items "+fItems.getItems().size()); if (!userClusters.contains(cluster)) // if user is NOT in this cluster get items to exclude for it { itemsToExclude.addAll(fItems.getItems()); } else { itemsToInclude.addAll(fItems.getItems()); } } } } if (itemsToExclude.size()>0) { int exSize = itemsToExclude.size(); int inSize = itemsToInclude.size(); itemsToExclude.removeAll(itemsToInclude); int exSize2 = itemsToExclude.size(); logger.info("Filtering user "+user+" with data before,incl,after :"+exSize+","+inSize+","+exSize2+": "+itemsToExclude.toString()); return new ArrayList<Long>(itemsToExclude); } } } return new ArrayList<Long>(); } }