Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix/homepage scoring #251

Merged
merged 14 commits into from
Dec 6, 2023
5 changes: 0 additions & 5 deletions src/mappings/content/commentsAndReactions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,6 @@ import {
genericEventFields,
metaprotocolTransactionFailure,
commentCountersManager,
videoRelevanceManager,
} from '../utils'
import { getChannelOwnerMemberByChannelId } from './utils'

Expand Down Expand Up @@ -257,8 +256,6 @@ export async function processReactVideoMessage(

await processVideoReaction(overlay, block, memberId, video, reactionType, existingReaction)

videoRelevanceManager.scheduleRecalcForVideo(video.id)

return new MetaprotocolTransactionResultOK()
}

Expand Down Expand Up @@ -403,7 +400,6 @@ export async function processCreateCommentMessage(
// schedule comment counters update
commentCountersManager.scheduleRecalcForComment(comment.parentCommentId)
commentCountersManager.scheduleRecalcForVideo(comment.videoId)
videoRelevanceManager.scheduleRecalcForVideo(comment.videoId)

// add CommentCreated event
const event = overlay.getRepository(Event).new({
Expand Down Expand Up @@ -537,7 +533,6 @@ export async function processDeleteCommentMessage(
// schedule comment counters update
commentCountersManager.scheduleRecalcForComment(comment.parentCommentId)
commentCountersManager.scheduleRecalcForVideo(comment.videoId)
videoRelevanceManager.scheduleRecalcForVideo(comment.videoId)

// update the comment
comment.text = ''
Expand Down
2 changes: 0 additions & 2 deletions src/mappings/content/metadata.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,6 @@ import {
genericEventFields,
invalidMetadata,
metaprotocolTransactionFailure,
videoRelevanceManager,
} from '../utils'
import { AsDecoded, ASSETS_MAP, EntityAssetProps, EntityAssetsMap, MetaNumberProps } from './utils'

Expand Down Expand Up @@ -572,7 +571,6 @@ export async function processModerateCommentMessage(
// schedule comment counters updates
commentCountersManager.scheduleRecalcForComment(comment.parentCommentId)
commentCountersManager.scheduleRecalcForVideo(comment.videoId)
videoRelevanceManager.scheduleRecalcForVideo(comment.videoId)

comment.text = ''
comment.status = CommentStatus.MODERATED
Expand Down
4 changes: 1 addition & 3 deletions src/mappings/content/video.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { DecodedMetadataObject } from '@joystream/metadata-protobuf/types'
import { integrateMeta } from '@joystream/metadata-protobuf/utils'
import { Channel, Video, VideoViewEvent } from '../../model'
import { EventHandlerContext } from '../../utils/events'
import { deserializeMetadata, u8aToBytes, videoRelevanceManager } from '../utils'
import { deserializeMetadata, u8aToBytes } from '../utils'
import { processVideoMetadata } from './metadata'
import { deleteVideo, encodeAssets, processAppActionMetadata, processNft } from './utils'
import { generateAppActionCommitment } from '@joystream/js/utils'
Expand Down Expand Up @@ -43,8 +43,6 @@ export async function processVideoCreatedEvent({
videoRelevance: 0,
})

videoRelevanceManager.scheduleRecalcForVideo(videoId)

// fetch related channel and owner
const channel = await overlay.getRepository(Channel).getByIdOrFail(channelId.toString())

Expand Down
2 changes: 1 addition & 1 deletion src/mappings/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import { VideoRelevanceManager } from '../utils/VideoRelevanceManager'

export const commentCountersManager = new CommentCountersManager()
export const videoRelevanceManager = new VideoRelevanceManager()
videoRelevanceManager.init(1000 * 60 * 60)
videoRelevanceManager.init(1000 * 60 * 10)

export const JOYSTREAM_SS58_PREFIX = 126

Expand Down
2 changes: 1 addition & 1 deletion src/processor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -322,7 +322,7 @@ processor.run(new TypeormDatabase({ isolationLevel: 'READ COMMITTED' }), async (
await offchainState.import(em)
await commentCountersManager.updateVideoCommentsCounters(em, true)
await commentCountersManager.updateParentRepliesCounters(em, true)
await videoRelevanceManager.updateVideoRelevanceValue(em, true)
await videoRelevanceManager.updateVideoRelevanceValue(em)
ctx.log.info(`Offchain state successfully imported!`)
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/server-extension/resolvers/AdminResolver/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ export class AdminResolver {
],
em
)
await videoRelevanceManager.updateVideoRelevanceValue(em, true)
await videoRelevanceManager.updateVideoRelevanceValue(em)
return { isApplied: true }
}

Expand Down
4 changes: 0 additions & 4 deletions src/server-extension/resolvers/AdminResolver/utils.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,14 @@
import { EntityManager, In } from 'typeorm'
import { CommentCountersManager } from '../../../utils/CommentsCountersManager'
import { Comment } from '../../../model'
import { VideoRelevanceManager } from '../../../utils/VideoRelevanceManager'

export async function processCommentsCensorshipStatusUpdate(em: EntityManager, ids: string[]) {
const manager = new CommentCountersManager()
const videoRelevanceManager = new VideoRelevanceManager()
const comments = await em.getRepository(Comment).find({ where: { id: In(ids) } })
comments.forEach((c) => {
manager.scheduleRecalcForComment(c.parentCommentId)
manager.scheduleRecalcForVideo(c.videoId)
videoRelevanceManager.scheduleRecalcForVideo(c.videoId)
})
await manager.updateVideoCommentsCounters(em)
await manager.updateParentRepliesCounters(em)
await videoRelevanceManager.updateVideoRelevanceValue(em)
}
6 changes: 0 additions & 6 deletions src/server-extension/resolvers/VideosResolver/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ import { config, ConfigVariable } from '../../../utils/config'
import { Context } from '../../check'
import { isObject } from 'lodash'
import { has } from '../../../utils/misc'
import { videoRelevanceManager } from '../../../mappings/utils'
import { uniqueId } from '../../../utils/crypto'
import { UserOnly } from '../middleware'

Expand Down Expand Up @@ -237,12 +236,7 @@ export class VideosResolver {
videoId,
})

const tick = await config.get(ConfigVariable.VideoRelevanceViewsTick, em)
if (video.viewsNum % tick === 0) {
videoRelevanceManager.scheduleRecalcForVideo(videoId)
}
await em.save([video, video.channel, newView])
await videoRelevanceManager.updateVideoRelevanceValue(em)
return {
videoId,
viewsNum: video.viewsNum,
Expand Down
96 changes: 42 additions & 54 deletions src/utils/VideoRelevanceManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@ import { globalEm } from './globalEm'
export const NEWNESS_SECONDS_DIVIDER = 60 * 60 * 24

export class VideoRelevanceManager {
private videosToUpdate: Set<string> = new Set()

init(intervalMs: number): void {
this.updateLoop(intervalMs)
.then(() => {
Expand All @@ -19,66 +17,56 @@ export class VideoRelevanceManager {
})
}

scheduleRecalcForVideo(id: string | null | undefined) {
id && this.videosToUpdate.add(id)
}

async updateVideoRelevanceValue(em: EntityManager, forceUpdateAll?: boolean) {
if (this.videosToUpdate.size || forceUpdateAll) {
const [
newnessWeight,
viewsWeight,
commentsWeight,
reactionsWeight,
[joystreamTimestampWeight, ytTimestampWeight] = [7, 3],
defaultChannelWeight,
] = await config.get(ConfigVariable.RelevanceWeights, em)
const channelWeight = defaultChannelWeight ?? 1
await em.query(`
WITH weighted_timestamp AS (
SELECT
"video"."id",
(
async updateVideoRelevanceValue(em: EntityManager) {
const [
newnessWeight,
viewsWeight,
commentsWeight,
reactionsWeight,
[joystreamTimestampWeight, ytTimestampWeight] = [7, 3],
defaultChannelWeight,
] = await config.get(ConfigVariable.RelevanceWeights, em)
const channelWeight = defaultChannelWeight ?? 1
const wtEpoch = `((
extract(epoch from video.created_at)*${joystreamTimestampWeight} +
COALESCE(extract(epoch from video.published_before_joystream), extract(epoch from video.created_at))*${ytTimestampWeight}
) / ${joystreamTimestampWeight} + ${ytTimestampWeight} as wtEpoch,
"channel"."channel_weight" as CW
FROM
"video"
INNER JOIN
"channel" ON "video"."channel_id" = "channel"."id"
${
forceUpdateAll
? ''
: `WHERE "video"."id" IN (${[...this.videosToUpdate.values()]
.map((id) => `'${id}'`)
.join(', ')})`
}
)
UPDATE
"video"
SET
"video_relevance" = ROUND(
(
(extract(epoch from now()) - wtEpoch) / ${NEWNESS_SECONDS_DIVIDER} * ${newnessWeight * -1} +
(views_num * ${viewsWeight}) +
(comments_count * ${commentsWeight}) +
(reactions_count * ${reactionsWeight})
) * COALESCE(CW, ${channelWeight}),
2)
FROM
weighted_timestamp
WHERE
"video".id = weighted_timestamp.id;
) / ${joystreamTimestampWeight} + ${ytTimestampWeight})`

await em.query(`
WITH videos_with_weight AS (
SELECT
video.id as videoId,
channel.id as channelId,
(ROUND((
(extract(epoch from now()) - ${wtEpoch})
/ ${NEWNESS_SECONDS_DIVIDER} * ${newnessWeight * -1}
+ (views_num * ${viewsWeight})
+ (comments_count * ${commentsWeight})
+ (reactions_count * ${reactionsWeight}))
* COALESCE(channel.channel_weight, ${channelWeight}),2)) as videoRelevance
FROM video
INNER JOIN channel ON video.channel_id = channel.id),

top_channel_score as (
SELECT
channel.id as channelId,
MAX(videoCte.videoRelevance) as maxChannelRelevance
FROM channel
INNER JOIN videos_with_weight as videoCte on videoCte.channelId = channel.id
GROUP BY channel.id)

UPDATE video
SET video_relevance = COALESCE(topChannelVideo.maxChannelRelevance, 1)
FROM videos_with_weight as videoCte
LEFT JOIN top_channel_score as topChannelVideo on topChannelVideo.channelId = videoCte.channelId and topChannelVideo.maxChannelRelevance = videoCte.videoRelevance
WHERE video.id = videoCte.videoId;
`)
this.videosToUpdate.clear()
}
}

private async updateLoop(intervalMs: number): Promise<void> {
const em = await globalEm
while (true) {
await this.updateVideoRelevanceValue(em, true)
await this.updateVideoRelevanceValue(em)
await new Promise((resolve) => setTimeout(resolve, intervalMs))
}
}
Expand Down
Loading