This is the fifth day of my participation in the November Gwen Challenge. Check out the details: The last Gwen Challenge 2021

“The ideas I wrote last time are still in my last article.”

Short video recommendation scatter algorithm. In order to ensure the layering of recommendation videos and prevent the videos of the same authors from piling up together, springBoot + Java is used to simply implement a single poll scatter algorithm.

The following is only their own logic to break up, there may be a pile of pit 😓, just a realization of the idea.

Code implementation

@Slf4j
@Service
public class FirmVideoService {

    @Autowired
    private RedisList redisList;
    @Autowired
    private RedisHash redisHash;
    @Autowired
    private FixSortMapper fixSortMapper;

    
    /** * Query the list of recommended video ids *@paramVideoId cursor: videoId, starting from scratch (0) by default. The video cursor can get the unwatched video frequency every time it gets the video. Can solve the paging query video, there will be new video, old video is squeezed to the next page, resulting in repeated video problems. *@paramIsFix Determines whether to insert fixed video *@paramSize Page size */
    public List<VideoInfo> queryVideo(Integer videoId,int size,boolean isFix) {
        List<VideoInfo> videos = new ArrayList<>();
        LinkedHashSet dataSet = new LinkedHashSet<>();
        // Query the video id corresponding to the author ID
        Map<Object, Object> objectMap = redisHash.hmGet("authors");

        // Put the fixed video into the collection
        List<Integer> fixList = new ArrayList<>();
        LinkedHashSet<Integer> authors = new LinkedHashSet<>();

        // videoId has a default value or a fixed identifier
        if(0 == videoId || isFix){
            this.queryfixIds(fixList,objectMap,authors,size);
        }else{
            Integer[] fixIds = this.initVideo(size);
            fixList = Arrays.asList(fixIds);
        }

        // Query the calculated video ID set
        dataSet.addAll(this.queryFirmByRedis(videoId,size));

        // Build the result set
        List<Integer> result = this.buildData(dataSet,fixList,authors,objectMap);
        result.forEach(s->{
            VideoInfo videoInfo = new VideoInfo();
            videoInfo.setVideoId(s.longValue());
            videos.add(videoInfo);
        });

        return videos;
    }

    /** * Query fixed video id *@paramFixList List of video ids *@paramObjectMap Mapping between video authors *@paramAuthors authors record set *@paramSize Page size */
    private void queryfixIds(List<Integer> fixList,Map<Object, Object> objectMap, LinkedHashSet<Integer> authors,int size){
        Integer[] fixIds = this.initVideo(size);
        this.addFixSorts(fixIds);
        fixList.addAll(Arrays.asList(fixIds));

        // Place the fixed video in the result set and record the author information
        fixList.forEach(h->{
            AtomicInteger userIdAto = new AtomicInteger(0);
            Optional.ofNullable(objectMap.get(h.toString())).ifPresent(u->{
                userIdAto.set(Integer.parseInt(objectMap.get(h.toString()).toString()));
            });
            int authorId = userIdAto.get();
            // Record the author information
            authors.add(authorId);
        });
    }

    /** * query the calculated video *@paramVideoId Indicates the ID * of the last video you watched@paramSize Size of a page *@return* /
    private List queryFirmByRedis(Integer videoId,int size){
        // Query all video ids
        Object msi = redisList.lRange("videos".0, -1);

        try {
            Optional.ofNullable(msi).orElseThrow(()->new Exception("firm video is null"));
        } catch (Exception e) {
            log.error("firm video is null");
            return new ArrayList();
        }
        ArrayList videoList = JSON.parseObject(msi.toString(), ArrayList.class);
        
        int index = 1;
        if(0! = videoId){// Get the index of the last video viewed
            index += videoList.indexOf(videoId);
        }

        // Paging to get the list of videos
        return videoList.subList(index,index+size);
    }


    /** ** Fixed popular videos to add *@paramFixIds returns a list of video ids */
    private void addFixSorts(Integer[] fixIds){
        // If strong plug is configured, the remaining videos are selected according to their priorities, and the number of remaining videos is supplemented to 4
        List<FixSorts> fixSorts = fixSortMapper.select();

        // The data is not empty, replace the original video
        if(! CollectionUtils.isEmpty(fixSorts)){ fixSorts.forEach(h->{int index = h.getMediafixSort().intValue() - 1;
                intvideoId = h.getTargetId().intValue(); fixIds[index] = videoId; }); }}/** * Recursively build a list of videos *@paramUnused Indicates the ID * of a video not added to the result set@paramResult Result list *@paramTMP temporary table, save author ID *@paramObjectMap Video corresponds to author mapping *@return* /
    private List<Integer> buildData(LinkedHashSet<Integer> unused,List<Integer> result,LinkedHashSet<Integer> tmp,Map<Object, Object> objectMap){

        // Continue the insert logic as long as there are videos that are not in the result set
        Optional.ofNullable(unused).filter(u->u.size()>0).ifPresent(s->{
            s.forEach(videoId->{
                AtomicInteger userIdAto = new AtomicInteger(0);
                // Find the author ID according to videoId
                Optional.ofNullable(objectMap.get(videoId.toString())).ifPresent(u->{
                    userIdAto.set(Integer.parseInt(objectMap.get(videoId.toString()).toString()));
                });
                int userId = userIdAto.get();
                // Check if the author ID does not appear this time
                if(! tmp.contains(userId)){// Insert the video ID into the index without data
                    for (int i = 0; i < result.size(); i++) {
                        if(0 == result.get(i)){
                            result.set(i,videoId);
                            tmp.add(userId);
                            break; }}}});if(tmp.isEmpty()){
                return;
            }
            // Clear the author information
            tmp.clear();
            log.info("buildData result:{}",result);

            // Filter only the ids that have not been placed in the result setLinkedHashSet<Integer> next = unused.stream().filter(e->! result.contains(e)).collect(Collectors.toCollection(LinkedHashSet::new));
            // Continue to insert the result the next time
            buildData(next,result,tmp,objectMap);
        });
        return result;
    }

    /** * Initializes the list *@paramSize Size of a page *@return* /
    private Integer[] initVideo(int size){
        Integer[] arr = new Integer[size];
        for (int i = 0; i < size; i++) {
            arr[i] = 0;
        }
        returnarr; }}Copy the code

Code logic interpretation

QueryVideo is an entry method for obtaining recommended videos. The sorted lists of videos (videos) and the relational mappings between videos and author ids (authors) are cached in Redis.

First determine whether the fixed-bit video is displayed, and if so, place the corresponding slot in the fixList result list into the fixed-bit video and record the author in the authors set.

Query all videos to be recommended and put them into the collection dataSet.

Once we’ve got the data ready, we’re going to go into buildData and we’re going to recursively put the video ID that’s not in the result set into the result set, and we’re going to recursively exit with no temporary record of the author information TMP, TMP being empty means result is full.

reference

  • Redis tool class reference: SpringBoot operation redis details