ellis.valentiner
ellis.valentiner
CCConvex Community
Created by ellis.valentiner on 3/6/2025 in #support-community
Offset-based pagination returns NULL
(or most popular to least popular vs. least popular to most popular)
25 replies
CCConvex Community
Created by ellis.valentiner on 3/6/2025 in #support-community
Offset-based pagination returns NULL
I want to be able to paginate through items in alphabetical order and reverse alphabetical order
25 replies
CCConvex Community
Created by ellis.valentiner on 3/6/2025 in #support-community
Offset-based pagination returns NULL
Is it possible to specify reverse sort order?
25 replies
CCConvex Community
Created by ellis.valentiner on 3/6/2025 in #support-community
Offset-based pagination returns NULL
Aggregates repaired and it looks like its working now, thanks!
25 replies
CCConvex Community
Created by ellis.valentiner on 3/6/2025 in #support-community
Offset-based pagination returns NULL
I think so. I'll give that a try.
25 replies
CCConvex Community
Created by ellis.valentiner on 3/6/2025 in #support-community
Offset-based pagination returns NULL
ah I see. Does this mean I need to repair my aggregates again too?
25 replies
CCConvex Community
Created by ellis.valentiner on 3/6/2025 in #support-community
Offset-based pagination returns NULL
But looking at the item with the returned id I can see it is definitely not being sorted by name as I'd have expected
25 replies
CCConvex Community
Created by ellis.valentiner on 3/6/2025 in #support-community
Offset-based pagination returns NULL
I just tried examining the result of await pageOfAlphabeticalItems.at(ctx, 0, {namespace: namespace}); rather than destructuring it and it looks like I do get a valid id and sumValue: 0
25 replies
CCConvex Community
Created by ellis.valentiner on 3/6/2025 in #support-community
Offset-based pagination returns NULL
Nope!
25 replies
CCConvex Community
Created by ellis.valentiner on 3/6/2025 in #support-community
Offset-based pagination returns NULL
Hi @Lee ! The key is null, which causes item to also be null. Here is my TableAggregate for random access – this does work:
export const randomize = new TableAggregate<{
Key: null;
DataModel: DataModel;
TableName: "items";
}>(components.items, {
sortKey: () => null
});
export const randomize = new TableAggregate<{
Key: null;
DataModel: DataModel;
TableName: "items";
}>(components.items, {
sortKey: () => null
});
Here is my TableAggregate for offset based pagination for alphabetical items:
export const pageOfAlphabeticalItems = new TableAggregate<{
Namespace: string;
Key: string;
DataModel: DataModel;
TableName: "items";
}>(components.items, {
namespace: (doc) => doc.category,
sortKey: (doc) => doc.name,
});
export const pageOfAlphabeticalItems = new TableAggregate<{
Namespace: string;
Key: string;
DataModel: DataModel;
TableName: "items";
}>(components.items, {
namespace: (doc) => doc.category,
sortKey: (doc) => doc.name,
});
I've tried repairing the aggregates but the count is still incorrect, which is suspicious to me.
25 replies
CCConvex Community
Created by ellis.valentiner on 3/6/2025 in #support-community
Offset-based pagination returns NULL
My query for random access works:
export const getRandomItem = query({
args: {},
handler: async (ctx, args) => {
console.log("Calling get random item with args", args);
const randomItem = await randomize.random(ctx);
console.log("Got random item", randomItem);
if (!randomItem) {
console.log("No random item found");
throw new ConvexError("No random item found");
}
const item = await ctx.db.get(randomItem.id);
console.log("Got random item", item);
return item!;
},
});
export const getRandomItem = query({
args: {},
handler: async (ctx, args) => {
console.log("Calling get random item with args", args);
const randomItem = await randomize.random(ctx);
console.log("Got random item", randomItem);
if (!randomItem) {
console.log("No random item found");
throw new ConvexError("No random item found");
}
const item = await ctx.db.get(randomItem.id);
console.log("Got random item", item);
return item!;
},
});
However trying to use offset based pagination does not because the result of pageOfAlphabeticalItems.at is null:
export const getAlphabeticalItem = query({
args: {
category: v.union(v.literal("A"), v.literal("B")),
},
handler: async (ctx, {category}): Promise<Name | null> => {
// Authentication check
const userId = await authenticateUser(ctx);
if (!userId) return null;
const namespace = category;
console.log("getAlphabeticalItem - namespace", namespace);
const count = await pageOfAlphabeticalItems.count(ctx, {namespace: namespace, bounds: {}});
console.log("getAlphabeticalItem - count", count);
const {key} = await pageOfAlphabeticalItems.at(ctx, 0, {namespace: namespace});
console.log("getAlphabeticalItem - key", key);
const item = await ctx.db.query("items")
.withIndex("by_category_name", (q) => q.eq("category", namespace).gte("name", key))
.first();
console.log("getAlphabeticalItem - item", item);
if (item) return item as Item;
return null;
}}
);
export const getAlphabeticalItem = query({
args: {
category: v.union(v.literal("A"), v.literal("B")),
},
handler: async (ctx, {category}): Promise<Name | null> => {
// Authentication check
const userId = await authenticateUser(ctx);
if (!userId) return null;
const namespace = category;
console.log("getAlphabeticalItem - namespace", namespace);
const count = await pageOfAlphabeticalItems.count(ctx, {namespace: namespace, bounds: {}});
console.log("getAlphabeticalItem - count", count);
const {key} = await pageOfAlphabeticalItems.at(ctx, 0, {namespace: namespace});
console.log("getAlphabeticalItem - key", key);
const item = await ctx.db.query("items")
.withIndex("by_category_name", (q) => q.eq("category", namespace).gte("name", key))
.first();
console.log("getAlphabeticalItem - item", item);
if (item) return item as Item;
return null;
}}
);
25 replies
CCConvex Community
Created by ellis.valentiner on 3/6/2025 in #support-community
Offset-based pagination returns NULL
convex/migrations.ts
import {components, internal} from "./_generated/api";
import {pageOfItems, pageOfPopularItems, pageOfAlphabeticalItems, randomize} from "./shuffle";
import {Migrations} from "@convex-dev/migrations";
import {DataModel} from "./_generated/dataModel";
import {internalMutation} from "./_generated/server";

export const migrations = new Migrations<DataModel>(components.migrations);
export const run = migrations.runner();

export const backfillAggregatesMigration = migrations.define({
table: "items",
batchSize: 10,
parallelize: true,
migrateOne: async (ctx, doc) => {
await randomize.insertIfDoesNotExist(ctx, doc);
await pageOfItems.insertIfDoesNotExist(ctx, doc);
await pageOfPopularItems.insertIfDoesNotExist(ctx, doc);
await pageOfAlphabeticalItems.insertIfDoesNotExist(ctx, doc);
console.log("backfilled", doc.name);
},
});

export const clearAggregates = internalMutation({
args: {},
handler: async (ctx) => {
await randomize.clear(ctx);
await pageOfItems.clear(ctx, {namespace: 'A'});
await pageOfItems.clear(ctx, {namespace: 'B'});
await pageOfPopularItems.clear(ctx, {namespace: 'A'});
await pageOfPopularItems.clear(ctx, {namespace: 'B'});
await pageOfAlphabeticalItems.clear(ctx, {namespace: 'A'});
await pageOfAlphabeticalItems.clear(ctx, {namespace: 'B'});
},
});

export const runAggregateBackfill =
migrations.runner(
internal.migrations.backfillAggregatesMigration
);
import {components, internal} from "./_generated/api";
import {pageOfItems, pageOfPopularItems, pageOfAlphabeticalItems, randomize} from "./shuffle";
import {Migrations} from "@convex-dev/migrations";
import {DataModel} from "./_generated/dataModel";
import {internalMutation} from "./_generated/server";

export const migrations = new Migrations<DataModel>(components.migrations);
export const run = migrations.runner();

export const backfillAggregatesMigration = migrations.define({
table: "items",
batchSize: 10,
parallelize: true,
migrateOne: async (ctx, doc) => {
await randomize.insertIfDoesNotExist(ctx, doc);
await pageOfItems.insertIfDoesNotExist(ctx, doc);
await pageOfPopularItems.insertIfDoesNotExist(ctx, doc);
await pageOfAlphabeticalItems.insertIfDoesNotExist(ctx, doc);
console.log("backfilled", doc.name);
},
});

export const clearAggregates = internalMutation({
args: {},
handler: async (ctx) => {
await randomize.clear(ctx);
await pageOfItems.clear(ctx, {namespace: 'A'});
await pageOfItems.clear(ctx, {namespace: 'B'});
await pageOfPopularItems.clear(ctx, {namespace: 'A'});
await pageOfPopularItems.clear(ctx, {namespace: 'B'});
await pageOfAlphabeticalItems.clear(ctx, {namespace: 'A'});
await pageOfAlphabeticalItems.clear(ctx, {namespace: 'B'});
},
});

export const runAggregateBackfill =
migrations.runner(
internal.migrations.backfillAggregatesMigration
);
25 replies
CCConvex Community
Created by ellis.valentiner on 3/6/2025 in #support-community
Offset-based pagination returns NULL
convex/convex.config.ts
import { defineApp } from "convex/server";
import aggregate from "@convex-dev/aggregate/convex.config";
import migrations from "@convex-dev/migrations/convex.config";

const app = defineApp();
app.use(aggregate);
app.use(aggregate, { name: "items" });
app.use(aggregate, { name: "pageOfItems" });
app.use(aggregate, { name: "pageOfPopularItems" });
app.use(aggregate, { name: "pageOfAlphabeticalItems" });

app.use(migrations);

export default app;
import { defineApp } from "convex/server";
import aggregate from "@convex-dev/aggregate/convex.config";
import migrations from "@convex-dev/migrations/convex.config";

const app = defineApp();
app.use(aggregate);
app.use(aggregate, { name: "items" });
app.use(aggregate, { name: "pageOfItems" });
app.use(aggregate, { name: "pageOfPopularItems" });
app.use(aggregate, { name: "pageOfAlphabeticalItems" });

app.use(migrations);

export default app;
25 replies