com.twitter.graphjet.algorithms.counting.tweet.TopSecondDegreeByCountTweetRecsGenerator.java Source code

Java tutorial

Introduction

Here is the source code for com.twitter.graphjet.algorithms.counting.tweet.TopSecondDegreeByCountTweetRecsGenerator.java

Source

/**
 * Copyright 2016 Twitter. All rights reserved.
 *
 * 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 com.twitter.graphjet.algorithms.counting.tweet;

import java.util.Collections;
import java.util.List;
import java.util.PriorityQueue;
import java.util.Set;

import com.google.common.collect.Lists;

import com.twitter.graphjet.algorithms.NodeInfo;
import com.twitter.graphjet.algorithms.RecommendationInfo;
import com.twitter.graphjet.algorithms.RecommendationRequest;
import com.twitter.graphjet.algorithms.RecommendationType;
import com.twitter.graphjet.algorithms.TweetIDMask;
import com.twitter.graphjet.algorithms.counting.GeneratorHelper;
import com.twitter.graphjet.hashing.SmallArrayBasedLongToDoubleMap;

import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
import it.unimi.dsi.fastutil.longs.LongSet;

public final class TopSecondDegreeByCountTweetRecsGenerator {
    private static final TweetIDMask TWEET_ID_MASK = new TweetIDMask();

    private TopSecondDegreeByCountTweetRecsGenerator() {
    }

    /**
     * Return tweet recommendations.
     *
     * @param request       topSecondDegreeByCount request
     * @param nodeInfoList  a list of node info containing engagement social proof and weights
     * @return a list of tweet recommendations
     */
    public static List<RecommendationInfo> generateTweetRecs(TopSecondDegreeByCountRequestForTweet request,
            List<NodeInfo> nodeInfoList) {
        int maxNumResults = GeneratorHelper.getMaxNumResults(request, RecommendationType.TWEET);
        int minUserSocialProofSize = GeneratorHelper.getMinUserSocialProofSize(request, RecommendationType.TWEET);
        byte[] validSocialProofs = request.getSocialProofTypes();

        PriorityQueue<NodeInfo> topResults = new PriorityQueue<NodeInfo>(maxNumResults);

        // handling specific rules of tweet recommendations
        for (NodeInfo nodeInfo : nodeInfoList) {
            // do not return if size of each social proof is less than minUserSocialProofSize.
            if (isLessThanMinUserSocialProofSize(nodeInfo.getSocialProofs(), validSocialProofs,
                    minUserSocialProofSize) &&
            // do not return if size of each social proof union is less than minUserSocialProofSize.
                    isLessThanMinUserSocialProofSizeCombined(nodeInfo.getSocialProofs(), minUserSocialProofSize,
                            request.getSocialProofTypeUnions())) {
                continue;
            }
            GeneratorHelper.addResultToPriorityQueue(topResults, nodeInfo, maxNumResults);
        }

        List<RecommendationInfo> outputResults = Lists.newArrayListWithCapacity(topResults.size());
        while (!topResults.isEmpty()) {
            NodeInfo nodeInfo = topResults.poll();
            outputResults.add(new TweetRecommendationInfo(TWEET_ID_MASK.restore(nodeInfo.getValue()),
                    nodeInfo.getWeight(), GeneratorHelper.pickTopSocialProofs(nodeInfo.getSocialProofs(),
                            request.getMaxUserSocialProofSize())));
        }
        Collections.reverse(outputResults);

        return outputResults;
    }

    private static boolean isSocialProofUnionSizeLessThanMin(SmallArrayBasedLongToDoubleMap[] socialProofs,
            int minUserSocialProofSize, Set<byte[]> socialProofTypeUnions) {
        long socialProofSizeSum = 0;

        for (byte[] socialProofTypeUnion : socialProofTypeUnions) {
            socialProofSizeSum = 0;
            for (byte socialProofType : socialProofTypeUnion) {
                if (socialProofs[socialProofType] != null) {
                    socialProofSizeSum += socialProofs[socialProofType].size();
                    if (socialProofSizeSum >= minUserSocialProofSize) {
                        return false;
                    }
                }
            }
        }
        return true;
    }

    private static boolean isLessThanMinUserSocialProofSizeCombined(SmallArrayBasedLongToDoubleMap[] socialProofs,
            int minUserSocialProofSize, Set<byte[]> socialProofTypeUnions) {
        if (socialProofTypeUnions.isEmpty() ||
        // check if the size of any social proof union is greater than minUserSocialProofSize before dedupping
                isSocialProofUnionSizeLessThanMin(socialProofs, minUserSocialProofSize, socialProofTypeUnions)) {
            return true;
        }

        LongSet uniqueNodes = new LongOpenHashSet(minUserSocialProofSize);

        for (byte[] socialProofTypeUnion : socialProofTypeUnions) {
            // Clear removes all elements, but does not change the size of the set.
            // Thus, we only use one LongOpenHashSet with at most a size of 2*minUserSocialProofSize
            uniqueNodes.clear();
            for (byte socialProofType : socialProofTypeUnion) {
                if (socialProofs[socialProofType] != null) {
                    for (int i = 0; i < socialProofs[socialProofType].size(); i++) {
                        uniqueNodes.add(socialProofs[socialProofType].keys()[i]);
                        if (uniqueNodes.size() >= minUserSocialProofSize) {
                            return false;
                        }
                    }
                }
            }
        }
        return true;
    }

    private static boolean isLessThanMinUserSocialProofSize(SmallArrayBasedLongToDoubleMap[] socialProofs,
            byte[] validSocialProofs, int minUserSocialProofSize) {
        int length = validSocialProofs.length;
        long authorId = getAuthorId(socialProofs);

        for (int i = 0; i < length; i++) {
            if (socialProofs[validSocialProofs[i]] != null) {
                int minUserSocialProofThreshold = minUserSocialProofSize;
                if (authorId != -1 &&
                // Skip tweet author social proof because its size can be only one
                        validSocialProofs[i] != RecommendationRequest.AUTHOR_SOCIAL_PROOF_TYPE
                        && socialProofs[validSocialProofs[i]].contains(authorId)) {
                    minUserSocialProofThreshold += 1;
                }
                if (socialProofs[validSocialProofs[i]].size() >= minUserSocialProofThreshold) {
                    return false;
                }
            }
        }
        return true;
    }

    // Return the authorId of the Tweet, if the author is in the leftSeedNodesWithWeight; otherwise, return -1.
    private static long getAuthorId(SmallArrayBasedLongToDoubleMap[] socialProofs) {
        int socialProofTypeTweet = RecommendationRequest.AUTHOR_SOCIAL_PROOF_TYPE;
        long authorId = -1;
        if (socialProofs[socialProofTypeTweet] != null) {
            // There cannot be more than one key associated with the Tweet socialProofType
            authorId = socialProofs[socialProofTypeTweet].keys()[0];
        }
        return authorId;
    }
}