gmG
Convex Community7mo ago
10 replies
gm

pagination for aggregate

import { TableAggregate } from '@convex-dev/aggregate'
import { components, internal } from '../_generated/api'
import type { DataModel } from '../_generated/dataModel'
import type { QueryCtx } from '../_generated/server'
import { internalMutation } from '../_generated/server'
import { migrations } from '../migrations'

// Fame aggregate - tracks total fame score per user
const fameAggregate = new TableAggregate<{
  Key: number // to_user_id
  DataModel: DataModel
  TableName: 'fame'
}>(components.fameAggregate, {
  sortKey: (doc) => doc.to_user_id,
  sumValue: (doc) => (doc.is_positive ? 1 : -1)
})

export const backfillFame = migrations.define({
  table: 'fame',
  migrateOne: async (ctx, doc) => {
    // Update fameAggregate for each fame document
    await fameAggregate.insertIfDoesNotExist(ctx, doc)
    console.log(
      'backfilled fame for user',
      doc.to_user_id,
      doc.is_positive ? '+1' : '-1'
    )
  }
})

export const clearFameAggregate = internalMutation({
  args: {},
  handler: async (ctx) => {
    await fameAggregate.clear(ctx)
  }
})

// Run this to backfill fame aggregates for existing data
export const runBackfillFame = migrations.runner(
  internal.model.fame.backfillFame
)

export async function getFameTotal(
  ctx: QueryCtx,
  userId: number
): Promise<number> {
  // Use aggregate for O(log n) fame total calculation
  return await fameAggregate.sum(ctx, {
    bounds: {
      lower: { key: userId, inclusive: true },
      upper: { key: userId, inclusive: true }
    }
  })
}
Was this page helpful?