io.seldon.api.resource.service.ItemService.java Source code

Java tutorial

Introduction

Here is the source code for io.seldon.api.resource.service.ItemService.java

Source

/*
 * 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.api.resource.service;

import io.seldon.api.APIException;
import io.seldon.api.Constants;
import io.seldon.api.Util;
import io.seldon.api.caching.ClientIdCacheStore;
import io.seldon.api.resource.ActionBean;
import io.seldon.api.resource.ConsumerBean;
import io.seldon.api.resource.DimensionBean;
import io.seldon.api.resource.ItemBean;
import io.seldon.api.resource.ItemTypeBean;
import io.seldon.api.resource.ListBean;
import io.seldon.api.resource.ResourceBean;
import io.seldon.general.Dimension;
import io.seldon.general.Item;
import io.seldon.general.ItemAttr;
import io.seldon.general.ItemStorage;
import io.seldon.general.ItemType;
import io.seldon.general.RecommendationStorage;
import io.seldon.memcache.MemCacheKeys;
import io.seldon.memcache.MemCachePeer;
import io.seldon.recommendation.RecommendationPeer;

import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
 * @author claudio
 */

@Service
public class ItemService {

    private static Logger logger = Logger.getLogger(ItemService.class.getName());
    public static final int ITEM_NAME_LENGTH = 255;
    private static final int ITEMS_CACHING_TIME_SECS = 300;

    @Autowired
    private RecommendationPeer recommendationPeer;

    @Autowired
    private RecommendationStorage recommendationStorage;

    @Autowired
    private ItemStorage itemStorage;

    @Autowired
    private ClientIdCacheStore idCache;

    public ItemBean getItem(final ConsumerBean c, final String iid, final boolean full) throws APIException {
        String memKey = MemCacheKeys.getItemBeanKey(c.getShort_name(), iid, full);
        ItemBean bean = (ItemBean) MemCachePeer.get(memKey);
        if (bean == null) {
            Item i = Util.getItemPeer(c).getItem(iid);
            if (i == null) {
                // TODO We should throw a checked exception (using APIException for now; minimal surprises).
                throw new APIException(APIException.ITEM_NOT_FOUND);
            }
            bean = new ItemBean(i, full, c);
            if (Constants.CACHING)
                MemCachePeer.put(memKey, bean, Constants.CACHING_TIME);
        }
        return bean;
    }

    public ItemBean getItemLocalized(final ConsumerBean c, final String iid, final boolean full, String locale)
            throws APIException {
        String memKey = MemCacheKeys.getItemBeanKeyWithLocale(c.getShort_name(), iid, full, locale);
        ItemBean bean = (ItemBean) MemCachePeer.get(memKey);
        if (bean == null) {
            Item i = Util.getItemPeer(c).getItem(iid);
            if (i == null) {
                // TODO We should throw a checked exception (using APIException for now; minimal surprises).
                throw new APIException(APIException.ITEM_NOT_FOUND);
            }
            bean = new ItemBean(i, full, c, locale);
            if (Constants.CACHING)
                MemCachePeer.put(memKey, bean, Constants.CACHING_TIME);
        }
        return bean;
    }

    public static ListBean getItems(ConsumerBean c, int limit, boolean full, String sort, int dimension)
            throws APIException {
        ListBean bean = (ListBean) MemCachePeer
                .get(MemCacheKeys.getItemsBeanKey(c.getShort_name(), full, sort, dimension));
        bean = Util.getLimitedBean(bean, limit);
        if (bean == null) {
            bean = new ListBean();
            bean.setRequested(limit);
            Collection<Item> res = null;
            if (sort == null || sort.length() == 0 || sort.toLowerCase().equals(Constants.SORT_ID)) {
                res = Util.getItemPeer(c).getItems(limit, dimension, c);
            } else if (sort.toLowerCase().equals(Constants.SORT_NAME)) {
                res = Util.getItemPeer(c).getAlphabeticItems(limit, dimension, c);
            } else if (sort.toLowerCase().equals(Constants.SORT_LAST_ACTION)) {
                res = Util.getItemPeer(c).getRecentItems(limit, dimension, c);
            }
            if (res != null) {
                for (Item i : res) {
                    bean.addBean(new ItemBean(i, full, c));
                }
            }
            if (bean != null)
                bean.setSize(bean.getList().size());
            if (Constants.CACHING)
                MemCachePeer.put(MemCacheKeys.getItemsBeanKey(c.getShort_name(), full, sort, dimension), bean,
                        ITEMS_CACHING_TIME_SECS);
        }
        return bean;
    }

    public static ListBean getItemsByName(ConsumerBean c, int limit, boolean full, String name, int dimension)
            throws APIException {
        ListBean bean = (ListBean) MemCachePeer
                .get(MemCacheKeys.getItemsBeanKeyByName(c.getShort_name(), full, name, dimension));
        bean = Util.getLimitedBean(bean, limit);
        if (bean == null) {
            bean = new ListBean();
            Collection<Item> res = Util.getItemPeer(c).getItemsByName(name, limit, dimension, c);
            for (Item i : res) {
                bean.addBean(new ItemBean(i, full, c));
            }
            bean.setRequested(limit);
            bean.setSize(res.size());
            if (Constants.CACHING)
                MemCachePeer.put(MemCacheKeys.getItemsBeanKeyByName(c.getShort_name(), full, name, dimension), bean,
                        ITEMS_CACHING_TIME_SECS);
        }
        return bean;
    }

    public String[] getDimensionName(ConsumerBean c, int dimension) {
        return Util.getItemPeer(c).getAttributesNames(dimension);
    }

    public static DimensionBean getDimension(ConsumerBean c, int dimension) throws APIException {
        if (dimension == Constants.DEFAULT_DIMENSION)
            return null;
        DimensionBean bean = (DimensionBean) MemCachePeer
                .get(MemCacheKeys.getDimensionBeanKey(c.getShort_name(), dimension));
        if (bean == null) {
            bean = new DimensionBean();
            bean.setDimId(dimension);
            int itemType = Util.getItemPeer(c).getDimensionItemType(dimension);
            Integer[] attr = Util.getItemPeer(c).getAttributes(dimension);
            String[] attrName = Util.getItemPeer(c).getAttributesNames(dimension);
            bean.setItemType(itemType);
            bean.setAttr(attr[0]);
            bean.setVal(attr[1]);
            // TODO address this properly:
            if (attrName == null) {
                bean.setAttrName("<all>");
                bean.setValName("<all>");
            } else {
                bean.setAttrName(attrName[0]);
                bean.setValName(attrName[1]);
            }
            MemCachePeer.put(MemCacheKeys.getDimensionBeanKey(c.getShort_name(), dimension), bean,
                    Constants.CACHING_TIME);
        }
        return bean;
    }

    public static DimensionBean getDimension(ConsumerBean c, int attr, int val) throws APIException {
        DimensionBean bean = (DimensionBean) MemCachePeer
                .get(MemCacheKeys.getDimensionBeanKey(c.getShort_name(), attr, val));
        if (bean == null) {
            bean = new DimensionBean();
            bean.setAttr(attr);
            bean.setVal(val);
            int dimension = Util.getItemPeer(c).getDimension(attr, val);
            int itemType = Util.getItemPeer(c).getDimensionItemType(dimension);
            String[] attrName = Util.getItemPeer(c).getAttributesNames(dimension);
            bean.setDimId(dimension);
            bean.setItemType(itemType);
            bean.setAttrName(attrName[0]);
            bean.setValName(attrName[1]);
            MemCachePeer.put(MemCacheKeys.getDimensionBeanKey(c.getShort_name(), attr, val), bean,
                    Constants.CACHING_TIME);
        }
        return bean;
    }

    public static DimensionBean getDimension(ConsumerBean c, String attrName, String valName) throws APIException {
        DimensionBean bean = (DimensionBean) MemCachePeer
                .get(MemCacheKeys.getDimensionBeanKey(c.getShort_name(), attrName, valName));
        if (bean == null) {
            int dimension = Util.getItemPeer(c).getDimension(attrName, valName);
            if (dimension != Constants.DEFAULT_DIMENSION) {
                bean = new DimensionBean();
                bean.setAttrName(attrName);
                bean.setValName(valName);
                int itemType = Util.getItemPeer(c).getDimensionItemType(dimension);
                Integer[] attr = Util.getItemPeer(c).getAttributes(dimension);
                bean.setDimId(dimension);
                bean.setItemType(itemType);
                bean.setAttr(attr[0]);
                bean.setVal(attr[1]);
                MemCachePeer.put(MemCacheKeys.getDimensionBeanKey(c.getShort_name(), attrName, valName), bean,
                        Constants.CACHING_TIME);
            } else
                return null;
        }
        return bean;
    }

    public static ListBean getDimensions(ConsumerBean c) throws APIException {
        ListBean bean = (ListBean) MemCachePeer.get(MemCacheKeys.getDimensionsBeanKey(c.getShort_name()));
        if (bean == null) {
            bean = new ListBean();
            Collection<Dimension> dims = Util.getItemPeer(c).getDimensions();
            for (Dimension d : dims) {
                bean.addBean(new DimensionBean(d));
            }
            MemCachePeer.put(MemCacheKeys.getDimensionsBeanKey(c.getShort_name()), bean, Constants.CACHING_TIME);
        }
        return bean;
    }

    public static Integer getDimensionbyItemType(ConsumerBean c, int itemType) throws APIException {
        Integer res = (Integer) MemCachePeer
                .get(MemCacheKeys.getDimensionBeanByItemTypeKey(c.getShort_name(), itemType));
        if (res == null) {
            res = Util.getItemPeer(c).getDimensionByItemType(itemType);
            if (res != null)
                MemCachePeer.put(MemCacheKeys.getDimensionBeanByItemTypeKey(c.getShort_name(), itemType), res,
                        Constants.CACHING_TIME);
            else
                logger.warn("Can't get dimension for item type " + itemType);
        }
        return res;
    }

    public Long getInternalItemId(ConsumerBean c, String id) throws APIException {
        Long res = null;
        res = idCache.getInternalItemId(c.getShort_name(), id);
        if (res == null)
            res = (Long) MemCachePeer.get(MemCacheKeys.getItemInternalId(c.getShort_name(), id));
        if (res == null) {
            Item i = Util.getItemPeer(c).getItem(id);
            if (i != null) {
                res = i.getItemId();
                idCache.putItemId(c.getShort_name(), id, res);
                cacheInternalItemId(c, id, res);
            } else {
                logger.info("getInternalItemId(" + id + "): ITEM NOT FOUND");
                throw new APIException(APIException.ITEM_NOT_FOUND);
            }
        }
        return res;
    }

    /**
     * Cache an item's internal ID keyed by client ID
     * @param consumerBean -
     * @param clientId the client item ID (cache key)
     * @param internalId the internal item ID (cache value)
     */
    public static void cacheInternalItemId(ConsumerBean consumerBean, String clientId, Long internalId) {
        final String clientIdKey = MemCacheKeys.getItemInternalId(consumerBean.getShort_name(), clientId);
        MemCachePeer.put(clientIdKey, internalId, Constants.CACHING_TIME);
    }

    public String getClientItemId(ConsumerBean c, Long id) throws APIException {
        if (id == null) {
            throw new APIException(APIException.ITEM_NOT_FOUND);
        }
        String res = null;
        res = idCache.getExternalItemId(c.getShort_name(), id);
        if (res == null)
            res = (String) MemCachePeer.get(MemCacheKeys.getItemClientId(c.getShort_name(), id));
        if (res == null) {
            Item i = Util.getItemPeer(c).getItem(id);
            if (i != null) {
                res = i.getClientItemId();
                idCache.putItemId(c.getShort_name(), res, id);
                cacheClientItemId(c, id, res);
            } else {
                logger.info("getClientItemId(" + id + "): ITEM NOT FOUND");
                throw new APIException(APIException.ITEM_NOT_FOUND);
            }
        }
        return res;
    }

    /**
     * Cache an item's client ID keyed by item ID
     * @param consumerBean -
     * @param internalId the internal item ID (cache key)
     * @param clientId the client item ID (cache value)
     */
    public static void cacheClientItemId(ConsumerBean consumerBean, Long internalId, String clientId) {
        final String internalItemKey = MemCacheKeys.getItemClientId(consumerBean.getShort_name(), internalId);
        MemCachePeer.put(internalItemKey, clientId, Constants.CACHING_TIME);
    }

    /**
     * Bidirectionally cache the supplied item
     * (see {@link ItemService#cacheClientItemId(io.seldon.api.resource.ConsumerBean, Long, String)}
     * and {@link ItemService#cacheInternalItemId(io.seldon.api.resource.ConsumerBean, String, Long)}).
     * @param consumerBean -
     * @param internalId user ID
     * @param clientId client ID
     */
    public static void cacheItem(ConsumerBean consumerBean, Long internalId, String clientId) {
        cacheClientItemId(consumerBean, internalId, clientId);
        cacheInternalItemId(consumerBean, clientId, internalId);
    }

    public static String getAttrType(ConsumerBean c, int itemType, String attrName) {
        String res = (String) MemCachePeer.get(MemCacheKeys.getItemAttrType(c.getShort_name(), itemType, attrName));
        if (res == null) {
            ItemAttr a = Util.getItemPeer(c).getItemAttr(itemType, attrName);
            if (a != null) {
                res = a.getType();
                MemCachePeer.put(MemCacheKeys.getItemAttrType(c.getShort_name(), itemType, attrName), res,
                        Constants.CACHING_TIME);
            }
        }
        return res;
    }

    private static void truncateItemName(ItemBean itemBean, int newLength) {
        final String name = itemBean.getName();
        final String truncatedName = StringUtils.left(name, newLength);
        logger.info("Truncating itemBean name '" + name + "' to '" + truncatedName + "'");
        itemBean.setName(truncatedName);
    }

    public Item addItem(ConsumerBean c, ItemBean bean) {
        //check if the item is already in the system
        try {
            getInternalItemId(c, bean.getId());
            throw new APIException(APIException.ITEM_DUPLICATED);
        } catch (APIException e) {
            if (e.getError_id() != APIException.ITEM_NOT_FOUND) {
                throw e;
            }
        }
        truncateItemName(bean, ITEM_NAME_LENGTH);
        Item i = bean.createItem(c);
        // double check the type is valid,
        ItemType type = ItemService.getItemType(c, i.getType());
        if (type == null) {
            throw new APIException(APIException.ITEM_TYPE_NOT_FOUND);
        }
        i = Util.getItemPeer(c).addItem(i, c);
        long itemId = i.getItemId();
        if (bean.getAttributesName() != null && bean.getAttributesName().size() > 0) {
            Util.getItemPeer(c).addItemAttributeNames(itemId, type.getTypeId(), bean.getAttributesName(), c);
        } else if (bean.getAttributes() != null && bean.getAttributes().size() > 0) {
            Util.getItemPeer(c).addItemAttribute(itemId, type.getTypeId(), bean.getAttributes(), c);
        }
        return i;
    }

    public void updateItem(ConsumerBean c, ItemBean bean) {
        Long itemId = null;
        truncateItemName(bean, ITEM_NAME_LENGTH);
        //check if the item is already in the system
        try {
            itemId = getInternalItemId(c, bean.getId());
            Item i = bean.createItem(c);
            i.setItemId(itemId);
            //         i = Util.getItemPeer(c).addItem(i,c);
            ItemType type = ItemService.getItemType(c, i.getType());
            if (type == null) {
                throw new APIException(APIException.ITEM_TYPE_NOT_FOUND);
            }
            i = Util.getItemPeer(c).saveOrUpdate(i, c);
            // TODO:
            if (bean.getAttributesName() != null && bean.getAttributesName().size() > 0) {
                Util.getItemPeer(c).addItemAttributeNames(itemId, bean.getType(), bean.getAttributesName(), c);
            } else if (bean.getAttributes() != null && bean.getAttributes().size() > 0) {
                Util.getItemPeer(c).addItemAttribute(itemId, bean.getType(), bean.getAttributes(), c);
            }
            //invalidate memcache entry (both full and short version)
            MemCachePeer.delete(MemCacheKeys.getItemBeanKey(c.getShort_name(), bean.getId(), true));
            MemCachePeer.delete(MemCacheKeys.getItemBeanKey(c.getShort_name(), bean.getId(), false));
            MemCachePeer.delete(MemCacheKeys.getItemDimensions(c.getShort_name(), itemId));
        } catch (APIException e) {
            if (e.getError_id() == APIException.ITEM_NOT_FOUND) {
                try {
                    addItem(c, bean);
                } catch (APIException additionException) {
                    if (additionException.getError_id() == APIException.ITEM_DUPLICATED) {
                        throw new APIException(APIException.CONCURRENT_ITEM_UPDATE);
                    } else {
                        throw additionException;
                    }
                }
            } else {
                throw e;
            }
        }
    }

    public static Collection<Integer> getItemDimensions(ConsumerBean c, long itemId) {
        Collection<Integer> res = (Collection<Integer>) MemCachePeer
                .get(MemCacheKeys.getItemDimensions(c.getShort_name(), itemId));
        if (res == null) {
            res = Util.getItemPeer(c).getItemDimensions(itemId);
            MemCachePeer.put(MemCacheKeys.getItemDimensions(c.getShort_name(), itemId), res,
                    Constants.CACHING_TIME);
        }
        return res;
    }

    public static Integer getItemCluster(ConsumerBean c, long itemId) {
        Integer res = (Integer) MemCachePeer.get(MemCacheKeys.getItemCluster(c.getShort_name(), itemId));
        if (res == null) {
            res = Util.getItemPeer(c).getItemCluster(itemId);
            MemCachePeer.put(MemCacheKeys.getItemCluster(c.getShort_name(), itemId), res, ITEMS_CACHING_TIME_SECS);
        }
        return res;
    }

    public static ItemType getItemType(ConsumerBean c, String name) {
        ItemType res = (ItemType) MemCachePeer.get(MemCacheKeys.getItemTypeByName(c.getShort_name(), name));
        if (res == null) {
            ItemType t = Util.getItemPeer(c).getItemType(name);
            if (t == null) {
                throw new APIException(APIException.ITEM_TYPE_NOT_FOUND);
            }
            MemCachePeer.put(MemCacheKeys.getItemTypeByName(c.getShort_name(), name), t, ITEMS_CACHING_TIME_SECS);
            res = t;
        }
        return res;
    }

    public static ItemType getItemType(ConsumerBean c, int typeId) {
        ItemType res = (ItemType) MemCachePeer.get(MemCacheKeys.getItemTypeById(c.getShort_name(), typeId));
        if (res == null) {
            ItemType t = Util.getItemPeer(c).getItemType(typeId);
            MemCachePeer.put(MemCacheKeys.getItemTypeById(c.getShort_name(), typeId), t, ITEMS_CACHING_TIME_SECS);
            res = t;
        }
        return res;
    }

    public static ResourceBean getItemTypes(ConsumerBean c) {
        ListBean bean = (ListBean) MemCachePeer.get(MemCacheKeys.getItemTypes(c.getShort_name()));
        if (bean == null) {
            bean = new ListBean();
            Collection<ItemType> types = Util.getItemPeer(c).getItemTypes();
            for (ItemType t : types) {
                bean.addBean(new ItemTypeBean(t));
            }
            MemCachePeer.put(MemCacheKeys.getItemTypes(c.getShort_name()), bean, ITEMS_CACHING_TIME_SECS);
        }
        return bean;
    }

    public static List<String> getItemSemanticAttributes(ConsumerBean c, long itemId) {
        List<String> res = (List<String>) MemCachePeer
                .get(MemCacheKeys.getItemSemanticAttributes(c.getShort_name(), itemId));
        if (res == null) {
            res = Util.getItemPeer(c).getItemSemanticAttributes(itemId);
            MemCachePeer.put(MemCacheKeys.getItemSemanticAttributes(c.getShort_name(), itemId), res,
                    Constants.CACHING_TIME);
        }
        return res;
    }

    public static ItemBean filter(ItemBean itemBean, List<String> attributeList) {
        if (attributeList != null && attributeList.size() > 0) {
            Map<String, String> oldAttributes = itemBean.getAttributesName();
            Map<String, String> newAttributes = new HashMap<>();
            for (String attribute : attributeList) {
                newAttributes.put(attribute, oldAttributes.get(attribute));
            }
            itemBean.setAttributesName(newAttributes);
        }
        return itemBean;
    }

    public void updateIgnoredItems(ConsumerBean consumerBean, ActionBean actionBean,
            List<Long> ignoredFromLastRecs) {
        Set<Long> resultingSet = new HashSet<>();
        Set<Long> ignoredItems = itemStorage.retrieveIgnoredItems(consumerBean.getShort_name(),
                actionBean.getUser());
        if (ignoredItems != null) {
            resultingSet.addAll(ignoredItems);
        }
        resultingSet.addAll(ignoredFromLastRecs);
        itemStorage.persistIgnoredItems(consumerBean.getShort_name(), actionBean.getUser(), resultingSet);

    }

    public Map<String, Integer> getDimensionIdsForItem(ConsumerBean c, long itemId) {
        final String key = MemCacheKeys.getItemAttrDims(c.getShort_name(), itemId);
        Map<String, Integer> res = (Map<String, Integer>) MemCachePeer.get(key);
        if (res == null) {
            res = Util.getItemPeer(c).getDimensionIdsForItem(itemId);
            MemCachePeer.put(key, res, Constants.CACHING_TIME);
        }
        return res;
    }
}