Manually setting vote count for posts
-
Is there any way I can set vote counts for imported posts without having to import the users that created them? Perhaps assign them to an 'anoymous' or 'generic' user? I can create a proxy user, but since a user can only upvote a post once, I'm not sure if there is a way to set the starting vote count to the existing votes in the imported posts.
-
If you know the values you can just set them directly on the post objects. The fields are
upvotes
&downvotes
on post objects.So an upgrade script can do
await db.setObject(`post:${pid}`, { upvotes: numberOfUpvotesFromImport, downvotes: numberOfDownvotesFromImport, });
The value displayed on nodebb UI is calculated from
upvotes - downvotes
-
@baris Thanks, that works, but only for static imports. If I let current users vote on an imported post the vote count gets reset. I do have a custom field that stores the imported vote counts, is there a hook I can use to update the displayed votes to include this value along with the 'real' count?
Also, assuming I lock down the imported posts so no one can vote on them, is there a way to see the statically set vote count reflected on the category view? The topic still shows the 'real' vote count.
-
On the category page each topic object has upvotes/downvotes/votes fields as well this comes from the main post of the topic.
You are right about votes resetting if someone votes on it. It is hard to fix that without setting the
pid:<pid>:upvote
&pid:<pid>:downvote
sets. These sets contain uids who have upvoted/downvoted the post. If you don't import the users then these don't exists and if someone votes on a post again then the count gets reset.The best hook to modify post objects loaded in a topic is
filter:post.getPosts
-
I think I can sort of get what I need by using that hook and just displaying the adjusted vote count
hookData.posts.map( post => { post.votes = post.votes + post.importedVotes})
I didn't know that the category view only displays the vote count for the main post. Is there an option to use the total votes for the posts in the topic? My thinking is that if a user is sorting by 'Most Votes', it makes sense to use the cumulative votes.
-
On category pages it's in handled by
Categories.getTopicIds
it uses a sorted set based on how the user wants the topics to be sorted. If you want to sort by votes it usescid:<cid>:tids:votes
the scores in there determine how they are sorted. In this case the scores don't have your imported values so that's why it doesn't work. -
Apologies for all the questions, but I am trying to understand how simulated sorted sets work in MongoDB. In Redis, inserting an element into a sorted set is handled by the DB as it gets placed in the right ranking order based on the score. How does this work with MongoDB? Is the entire set recreated when a score changes or a new element needs to be added? I can lock down the voting on imported topics, but I do need to set the correct values in the
cid:<cid>:tids:votes
set on import. -
It works the same as redis in mongodb you just add an element with
db.sortedSetAdd('someSet', 123, 'item1');
and when you retrieve it withdb.getSortedSetRange('someSet', 0, -1)
it will be in the correct order. It is handled by the_key, score
compound index on the objects collection. -
Cool, and I assume that adding an element that already exists is handled transparently ( score gets updated ). By the way, have you, or anyone else, tried nodeBB with tidis https://github.com/tidb-incubator/tidis
-
@razibal said in Manually setting vote count for posts:
Cool, and I assume that adding an element that already exists is handled transparently ( score gets updated )
Yeah it just updates the score if it's already in the zset.
I haven't looked at tidis, to be honest we have enough database adapters to deal with already
-
@baris said in Manually setting vote count for posts:
I haven't looked at tidis, to be honest we have enough database adapters to deal with already
Understandable, only reason I brought it up is because it appears that all the adapters are essentially emulating Redis, and Tidis promises 100% protocol compatibility while addressing the negatives of Redis. I will admit using MongoDB as a Redis replacement has been a little bit frustrating for me because none of the collection structures look like what I would expect them to in MongoDB
-
I was able to set the imported vote count in the import script by adding
await db.sortedSetAdd(`cid:${topic.cid}:tids:votes`, post.importedVotes, post.tid)
to the scriptHowever, it doesn't look like there is any way to get 'Sort by Most Votes' (using all votes rather that just the main post votes) to work without modifying the
updateTopicVoteCount
function in thesrc/posts/votes.js
module. It's going to need to sum all the votes in the topic and set that as thecid:${topicData.cid}:tids:votes
value.This is an example of a use case where being to override a core method or function would be really useful.
-
Thanks, that works perfectly. I'm handling the hook in my custom theme, but this probably needs to be in the https://community.nodebb.org/post/95161 plugin as well (without the importedVotes)
library.setTopicVotes = async function(hookData) { const topicData = await topics.getTopicFields(hookData.post.tid, ['cid', 'importedVotes']); const importedVotes = topicData.importedVotes || 0 const voteData = await db.getSortedSetRangeByScoreWithScores(`tid:${hookData.post.tid}:posts:votes`, 0, -1, 1, '+inf'); let allvotes = voteData.reduce((acc, cur) => acc + cur.score, 0); allvotes = allvotes + importedVotes await db.sortedSetAdd(`cid:${topicData.cid}:tids:votes`, allvotes, hookData.post.tid) }