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
92 changes: 40 additions & 52 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
await em.query(`
WITH videos_with_weight AS (
SELECT
video.id as videoId,
c.id as channelId,
(ROUND((
(extract(epoch from now()) -
((
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} +
) / ${joystreamTimestampWeight} + ${ytTimestampWeight})) / ${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;
(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.cId = channel.id
GROUP BY channel.id)

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

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you maybe break down this complex query into smaller pieces for better readability? Even with Copilot is a bit complex to decipher


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