Compute recommendation scores from updateUserFeed

This commit is contained in:
James Grugett 2022-04-30 11:54:44 -04:00
parent 43a0fc6581
commit f008971bd1
4 changed files with 22 additions and 56 deletions

View File

@ -142,36 +142,18 @@ export const getWordScores = (
return normalizedWordScores return normalizedWordScores
} }
export function getContractScores( export function getContractScore(
contracts: Contract[], contract: Contract,
wordScores: { [word: string]: number } wordScores: { [word: string]: number }
) { ) {
const scorePairs = contracts.map((contract) => { const wordFrequency = contractToWordFrequency(contract)
const wordFrequency = contractToWordFrequency(contract) const score = _.sumBy(Object.keys(wordFrequency), (word) => {
const wordFreq = wordFrequency[word] ?? 0
const score = _.sumBy(Object.keys(wordFrequency), (word) => { const weight = wordScores[word] ?? 0
const wordFreq = wordFrequency[word] ?? 0 return wordFreq * weight
const weight = wordScores[word] ?? 0
return wordFreq * weight
})
return [contract, score] as [Contract, number]
}) })
/* return score
const questionPairs = _.sortBy(
scorePairs.map(
([contract, score]) => [contract.question, score] as [string, number]
),
([, score]) => -score
)
console.log('score', questionPairs.slice(0, 100), questionPairs.slice(-100))
*/
return _.fromPairs(
scorePairs.map(([contract, score]) => [contract.id, score])
)
} }
// Caluculate Term Frequency-Inverse Document Frequency (TF-IDF): // Caluculate Term Frequency-Inverse Document Frequency (TF-IDF):

View File

@ -8,7 +8,7 @@ import { getValues } from '../utils'
import { User } from '../../../common/user' import { User } from '../../../common/user'
import { batchedWaitAll } from '../../../common/util/promise' import { batchedWaitAll } from '../../../common/util/promise'
import { Contract } from '../../../common/contract' import { Contract } from '../../../common/contract'
import { updateUserRecommendations } from '../update-recommendations' import { updateWordScores } from '../update-recommendations'
import { import {
getFeedContracts, getFeedContracts,
updateFeed as updateUserFeed, updateFeed as updateUserFeed,
@ -23,13 +23,13 @@ async function updateFeed() {
const feedContracts = await getFeedContracts() const feedContracts = await getFeedContracts()
const users = await getValues<User>( const users = await getValues<User>(
firestore.collection('users') firestore.collection('users')
//.where('username', '==', 'JamesGrugett') // .where('username', '==', 'JamesGrugett')
) )
await batchedWaitAll( await batchedWaitAll(
users.map((user) => async () => { users.map((user) => async () => {
console.log('Updating recs for', user.username) console.log('Updating recs for', user.username)
await updateUserRecommendations(user, contracts) await updateWordScores(user, contracts)
console.log('Updating feed for', user.username) console.log('Updating feed for', user.username)
await updateUserFeed(user, feedContracts) await updateUserFeed(user, feedContracts)
}) })

View File

@ -7,10 +7,7 @@ import { Contract } from '../../common/contract'
import { Bet } from '../../common/bet' import { Bet } from '../../common/bet'
import { User } from '../../common/user' import { User } from '../../common/user'
import { ClickEvent } from '../../common/tracking' import { ClickEvent } from '../../common/tracking'
import { import { getWordScores } from '../../common/recommended-contracts'
getContractScores,
getWordScores,
} from '../../common/recommended-contracts'
import { batchedWaitAll } from '../../common/util/promise' import { batchedWaitAll } from '../../common/util/promise'
const firestore = admin.firestore() const firestore = admin.firestore()
@ -25,14 +22,11 @@ export const updateRecommendations = functions.pubsub
const users = await getValues<User>(firestore.collection('users')) const users = await getValues<User>(firestore.collection('users'))
await batchedWaitAll( await batchedWaitAll(
users.map((user) => () => updateUserRecommendations(user, contracts)) users.map((user) => () => updateWordScores(user, contracts))
) )
}) })
export const updateUserRecommendations = async ( export const updateWordScores = async (user: User, contracts: Contract[]) => {
user: User,
contracts: Contract[]
) => {
const [bets, viewCounts, clicks] = await Promise.all([ const [bets, viewCounts, clicks] = await Promise.all([
getValues<Bet>( getValues<Bet>(
firestore.collectionGroup('bets').where('userId', '==', user.id) firestore.collectionGroup('bets').where('userId', '==', user.id)
@ -50,11 +44,9 @@ export const updateUserRecommendations = async (
]) ])
const wordScores = getWordScores(contracts, viewCounts ?? {}, clicks, bets) const wordScores = getWordScores(contracts, viewCounts ?? {}, clicks, bets)
const contractScores = getContractScores(contracts, wordScores)
const cachedCollection = firestore.collection( const cachedCollection = firestore.collection(
`private-users/${user.id}/cache` `private-users/${user.id}/cache`
) )
await cachedCollection.doc('wordScores').set(wordScores) await cachedCollection.doc('wordScores').set(wordScores)
await cachedCollection.doc('contractScores').set(contractScores)
} }

View File

@ -15,6 +15,7 @@ import { Bet } from '../../common/bet'
import { Comment } from '../../common/comment' import { Comment } from '../../common/comment'
import { User } from '../../common/user' import { User } from '../../common/user'
import { batchedWaitAll } from '../../common/util/promise' import { batchedWaitAll } from '../../common/util/promise'
import { getContractScore } from '../../common/recommended-contracts'
const firestore = admin.firestore() const firestore = admin.firestore()
@ -59,27 +60,17 @@ export const updateFeed = async (user: User, contracts: Contract[]) => {
const userCacheCollection = firestore.collection( const userCacheCollection = firestore.collection(
`private-users/${user.id}/cache` `private-users/${user.id}/cache`
) )
const [recommendationScores, lastViewedTime] = await Promise.all([ const [wordScores, lastViewedTime] = await Promise.all([
getValue<{ [contractId: string]: number }>( getValue<{ [word: string]: number }>(userCacheCollection.doc('wordScores')),
userCacheCollection.doc('contractScores')
),
getValue<{ [contractId: string]: number }>( getValue<{ [contractId: string]: number }>(
userCacheCollection.doc('lastViewTime') userCacheCollection.doc('lastViewTime')
), ),
]).then((dicts) => dicts.map((dict) => dict ?? {})) ]).then((dicts) => dicts.map((dict) => dict ?? {}))
const averageRecScore =
1 +
_.sumBy(
contracts.filter((c) => recommendationScores[c.id] !== undefined),
(c) => recommendationScores[c.id]
) /
(contracts.length + 1)
const scoredContracts = contracts.map((contract) => { const scoredContracts = contracts.map((contract) => {
const score = scoreContract( const score = scoreContract(
contract, contract,
recommendationScores[contract.id] ?? averageRecScore, wordScores,
lastViewedTime[contract.id] lastViewedTime[contract.id]
) )
return [contract, score] as [Contract, number] return [contract, score] as [Contract, number]
@ -105,12 +96,13 @@ export const updateFeed = async (user: User, contracts: Contract[]) => {
function scoreContract( function scoreContract(
contract: Contract, contract: Contract,
recommendationScore: number, wordScores: { [word: string]: number },
viewTime: number | undefined viewTime: number | undefined
) { ) {
const lastViewedScore = getLastViewedScore(viewTime) const recommendationScore = getContractScore(contract, wordScores)
const activityScore = getActivityScore(contract, viewTime) const activityScore = getActivityScore(contract, viewTime)
return recommendationScore * lastViewedScore * activityScore const lastViewedScore = getLastViewedScore(viewTime)
return recommendationScore * activityScore * lastViewedScore
} }
function getActivityScore(contract: Contract, viewTime: number | undefined) { function getActivityScore(contract: Contract, viewTime: number | undefined) {