package org.codelibs.elasticsearch.taste.rest.handler;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import org.codelibs.elasticsearch.taste.common.LongPrimitiveArrayIterator;
import org.codelibs.elasticsearch.taste.common.LongPrimitiveIterator;
import org.codelibs.elasticsearch.taste.eval.RecommenderBuilder;
import org.codelibs.elasticsearch.taste.exception.TasteException;
import org.codelibs.elasticsearch.taste.model.DataModel;
import org.codelibs.elasticsearch.taste.model.ElasticsearchDataModel;
import org.codelibs.elasticsearch.taste.model.IndexInfo;
import org.codelibs.elasticsearch.taste.recommender.Recommender;
import org.codelibs.elasticsearch.taste.recommender.UserBasedRecommenderBuilder;
import org.codelibs.elasticsearch.taste.service.TasteService;
import org.codelibs.elasticsearch.taste.util.ClusterUtils;
import org.codelibs.elasticsearch.taste.util.SettingsUtils;
import org.codelibs.elasticsearch.taste.worker.RecommendedItemsWorker;
import org.codelibs.elasticsearch.taste.writer.ItemWriter;
import org.elasticsearch.client.Client;
import org.elasticsearch.common.settings.Settings;
import org.elasticsearch.common.xcontent.XContentBuilder;
import org.elasticsearch.common.xcontent.XContentFactory;
import org.elasticsearch.threadpool.ThreadPool;
import com.google.common.cache.Cache;
import com.google.common.cache.CacheBuilder;
public class ItemsFromUserHandler extends RecommendationHandler {
public ItemsFromUserHandler(final Settings settings,
final Map<String, Object> sourceMap, final Client client, final ThreadPool pool,
final TasteService tasteService) {
super(settings, sourceMap, client, pool, tasteService);
}
@Override
public void execute() {
final int numOfItems = SettingsUtils.get(rootSettings, "num_of_items",
10);
final int maxDuration = SettingsUtils.get(rootSettings, "max_duration",
0);
final int numOfThreads = getNumOfThreads();
final Map<String, Object> indexInfoSettings = SettingsUtils.get(
rootSettings, "index_info");
final IndexInfo indexInfo = new IndexInfo(indexInfoSettings);
final Map<String, Object> modelInfoSettings = SettingsUtils.get(
rootSettings, "data_model");
final ElasticsearchDataModel dataModel = createDataModel(client,
indexInfo, modelInfoSettings);
ClusterUtils.waitForAvailable(client, indexInfo.getUserIndex(),
indexInfo.getItemIndex(), indexInfo.getPreferenceIndex(),
indexInfo.getRecommendationIndex());
final long[] userIDs = getTargetIDs(indexInfo.getUserIndex(),
indexInfo.getUserType(), indexInfo.getUserIdField(), "users");
final UserBasedRecommenderBuilder recommenderBuilder = new UserBasedRecommenderBuilder(
indexInfo, rootSettings);
final ItemWriter writer = createRecommendedItemsWriter(indexInfo,
rootSettings);
compute(userIDs, dataModel, recommenderBuilder, writer, numOfItems,
numOfThreads, maxDuration);
}
protected void compute(final long[] userIDs, final DataModel dataModel,
final RecommenderBuilder recommenderBuilder,
final ItemWriter writer, final int numOfRecommendedItems,
final int degreeOfParallelism, final int maxDuration) {
Recommender recommender = null;
try {
final ExecutorService executorService = Executors
.newFixedThreadPool(degreeOfParallelism);
recommender = recommenderBuilder.buildRecommender(dataModel);
logger.info("Recommender: {}", recommender);
logger.info("NumOfRecommendedItems: {}", numOfRecommendedItems);
logger.info("MaxDuration: {}", maxDuration);
final LongPrimitiveIterator userIdIter = userIDs == null ? dataModel
.getUserIDs() : new LongPrimitiveArrayIterator(userIDs);
for (int n = 0; n < degreeOfParallelism; n++) {
final RecommendedItemsWorker worker = new RecommendedItemsWorker(
n, recommender, userIdIter, numOfRecommendedItems,
writer);
executorService.execute(worker);
}
waitFor(executorService, maxDuration);
} catch (final TasteException e) {
logger.error("Recommender {} is failed.", e, recommender);
} finally {
if (writer != null) {
try {
writer.close();
} catch (final IOException e) {
// ignore
}
}
}
}
protected ItemWriter createRecommendedItemsWriter(
final IndexInfo indexInfo, final Map<String, Object> rootSettings) {
final ItemWriter writer = new ItemWriter(client,
indexInfo.getRecommendationIndex(),
indexInfo.getRecommendationType(), indexInfo.getUserIdField(),
indexInfo.getMaxNumOfWriters());
writer.setTargetIndex(indexInfo.getUserIndex());
writer.setTargetType(indexInfo.getUserType());
writer.setItemIndex(indexInfo.getItemIndex());
writer.setItemType(indexInfo.getItemType());
writer.setItemIdField(indexInfo.getItemIdField());
writer.setItemsField(indexInfo.getItemsField());
writer.setValueField(indexInfo.getValueField());
writer.setTimestampField(indexInfo.getTimestampField());
try (XContentBuilder jsonBuilder = XContentFactory.jsonBuilder()) {
final XContentBuilder builder = jsonBuilder//
.startObject()//
.startObject(indexInfo.getRecommendationType())//
.startObject("properties")//
// @timestamp
.startObject(indexInfo.getTimestampField())//
.field("type", "date")//
.field("format", "date_optional_time")//
.endObject()//
// user_id
.startObject(indexInfo.getUserIdField())//
.field("type", "long")//
.endObject()//
// items
.startObject(indexInfo.getItemsField())//
.startObject("properties")//
// item_id
.startObject(indexInfo.getItemIdField())//
.field("type", "long")//
.endObject()//
// value
.startObject(indexInfo.getValueField())//
.field("type", "double")//
.endObject()//
.endObject()//
.endObject()//
.endObject()//
.endObject()//
.endObject();
writer.setMapping(builder);
final Map<String, Object> writerSettings = SettingsUtils.get(
rootSettings, "writer");
final boolean verbose = SettingsUtils.get(writerSettings, "verbose",
false);
if (verbose) {
writer.setVerbose(verbose);
final int maxCacheSize = SettingsUtils.get(writerSettings,
"cache_size", 1000);
final Cache<Long, Map<String, Object>> cache = CacheBuilder
.newBuilder().maximumSize(maxCacheSize).build();
writer.setCache(cache);
}
writer.open();
} catch (final IOException e) {
logger.info("Failed to create a mapping {}/{}.", e,
indexInfo.getReportIndex(), indexInfo.getReportType());
}
return writer;
}
@Override
public void close() {
}
}