NodeBB/NodeBB

View on GitHub
src/topics/suggested.js

Summary

Maintainability
A
1 hr
Test Coverage

'use strict';

const _ = require('lodash');

const db = require('../database');
const user = require('../user');
const privileges = require('../privileges');
const search = require('../search');

module.exports = function (Topics) {
    Topics.getSuggestedTopics = async function (tid, uid, start, stop, cutoff = 0) {
        let tids;
        tid = parseInt(tid, 10);
        cutoff = cutoff === 0 ? cutoff : (cutoff * 2592000000);
        const [tagTids, searchTids] = await Promise.all([
            getTidsWithSameTags(tid, cutoff),
            getSearchTids(tid, uid, cutoff),
        ]);

        tids = _.uniq(tagTids.concat(searchTids));

        let categoryTids = [];
        if (stop !== -1 && tids.length < stop - start + 1) {
            categoryTids = await getCategoryTids(tid, cutoff);
        }
        tids = _.shuffle(_.uniq(tids.concat(categoryTids)));
        tids = await privileges.topics.filterTids('topics:read', tids, uid);

        let topicData = await Topics.getTopicsByTids(tids, uid);
        topicData = topicData.filter(topic => topic && topic.tid !== tid);
        topicData = await user.blocks.filter(uid, topicData);
        topicData = topicData.slice(start, stop !== -1 ? stop + 1 : undefined)
            .sort((t1, t2) => t2.timestamp - t1.timestamp);
        Topics.calculateTopicIndices(topicData, start);
        return topicData;
    };

    async function getTidsWithSameTags(tid, cutoff) {
        const tags = await Topics.getTopicTags(tid);
        let tids = cutoff === 0 ?
            await db.getSortedSetRevRange(tags.map(tag => `tag:${tag}:topics`), 0, -1) :
            await db.getSortedSetRevRangeByScore(tags.map(tag => `tag:${tag}:topics`), 0, -1, '+inf', Date.now() - cutoff);
        tids = tids.filter(_tid => _tid !== tid); // remove self
        return _.shuffle(_.uniq(tids)).slice(0, 10).map(Number);
    }

    async function getSearchTids(tid, uid, cutoff) {
        const topicData = await Topics.getTopicFields(tid, ['title', 'cid']);
        const data = await search.search({
            query: topicData.title,
            searchIn: 'titles',
            matchWords: 'any',
            categories: [topicData.cid],
            uid: uid,
            returnIds: true,
            timeRange: cutoff !== 0 ? cutoff / 1000 : 0,
            timeFilter: 'newer',
        });
        data.tids = data.tids.filter(_tid => _tid !== tid); // remove self
        return _.shuffle(data.tids).slice(0, 10).map(Number);
    }

    async function getCategoryTids(tid, cutoff) {
        const cid = await Topics.getTopicField(tid, 'cid');
        const tids = cutoff === 0 ?
            await db.getSortedSetRevRange(`cid:${cid}:tids:lastposttime`, 0, 9) :
            await db.getSortedSetRevRangeByScore(`cid:${cid}:tids:lastposttime`, 0, 10, '+inf', Date.now() - cutoff);
        return _.shuffle(tids.map(Number).filter(_tid => _tid !== tid));
    }
};