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
6 changes: 3 additions & 3 deletions src/mappings/content/commentsAndReactions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -257,7 +257,7 @@ export async function processReactVideoMessage(

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

videoRelevanceManager.scheduleRecalcForVideo(video.id)
videoRelevanceManager.scheduleRecalcForChannel(channelId)

return new MetaprotocolTransactionResultOK()
}
Expand Down Expand Up @@ -403,7 +403,7 @@ export async function processCreateCommentMessage(
// schedule comment counters update
commentCountersManager.scheduleRecalcForComment(comment.parentCommentId)
commentCountersManager.scheduleRecalcForVideo(comment.videoId)
videoRelevanceManager.scheduleRecalcForVideo(comment.videoId)
videoRelevanceManager.scheduleRecalcForChannel(video.channelId)

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

// 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
2 changes: 1 addition & 1 deletion src/mappings/content/video.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ export async function processVideoCreatedEvent({
videoRelevance: 0,
})

videoRelevanceManager.scheduleRecalcForVideo(videoId)
videoRelevanceManager.scheduleRecalcForChannel(channelId.toString())

// 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 * 60 * 12)

export const JOYSTREAM_SS58_PREFIX = 126

Expand Down
9 changes: 9 additions & 0 deletions src/processor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -314,9 +314,18 @@ processor.run(new TypeormDatabase({ isolationLevel: 'READ COMMITTED' }), async (
}
}
}

if (
!videoRelevanceManager.isVideoRelevanceEnabled &&
block.header.height >= exportBlockNumber
) {
videoRelevanceManager.turnOnVideoRelevanceManager()
}

// Importing exported offchain state
if (block.header.height >= exportBlockNumber && !offchainState.isImported) {
ctx.log.info(`Export block ${exportBlockNumber} reached, importing offchain state...`)
// there is no need to recalc video relevance before orion is synced
await overlay.updateDatabase()
const em = overlay.getEm()
await offchainState.import(em)
Expand Down
1 change: 0 additions & 1 deletion src/server-extension/resolvers/AdminResolver/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ export async function processCommentsCensorshipStatusUpdate(em: EntityManager, i
comments.forEach((c) => {
manager.scheduleRecalcForComment(c.parentCommentId)
manager.scheduleRecalcForVideo(c.videoId)
videoRelevanceManager.scheduleRecalcForVideo(c.videoId)
})
await manager.updateVideoCommentsCounters(em)
await manager.updateParentRepliesCounters(em)
Expand Down
4 changes: 2 additions & 2 deletions src/server-extension/resolvers/VideosResolver/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -239,10 +239,10 @@ export class VideosResolver {

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

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

public get isVideoRelevanceEnabled(): boolean {
return this._isVideoRelevanceEnabled
}

init(intervalMs: number): void {
this.updateLoop(intervalMs)
Expand All @@ -19,66 +24,77 @@ export class VideoRelevanceManager {
})
}

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

scheduleRecalcForChannel(id: string | null | undefined) {
id && this.channelsToUpdate.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",
(
if (!this._isVideoRelevanceEnabled || !(this.channelsToUpdate.size || forceUpdateAll)) {
return
}

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"
) / ${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
${
forceUpdateAll
? ''
: `WHERE "video"."id" IN (${[...this.videosToUpdate.values()]
: `WHERE video.channel_id in (${[...this.channelsToUpdate.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;
ORDER BY video.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()
}
this.channelsToUpdate.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