makrdev
makrdev
Explore posts from servers
CDCloudflare Developers
Created by makrdev on 2/13/2025 in #workers-help
More than 100 custom domains
Thanks!
3 replies
CCConvex Community
Created by Corbi-Wan Kenobi on 1/26/2025 in #support-community
Using pagination in convex vs something like TanStack Tables?
What comes to mind is calculating total count with aggregate component and calculating number of pages based on total count / page limit
22 replies
CCConvex Community
Created by makrdev on 12/24/2024 in #general
makrdev's Thread
There was an error in one of the cascade triggers, and it prevented all of them deleting other resources. Is that what you mean by 'transactional'?
8 replies
CCConvex Community
Created by makrdev on 12/24/2024 in #general
makrdev's Thread
@lee Hey Lee, what do you think about the solution I shared above?
8 replies
CCConvex Community
Created by makrdev on 12/24/2024 in #general
makrdev's Thread
Solution:
// Cascade delete all files when a project is deleted
triggers.register("projects", async (ctx, change) => {
if (change.operation === "delete") {
// Get files with pagination
const { continueCursor, page } = await ctx.db
.query("files")
.withIndex("by_project_id", (q) => q.eq("projectId", change.id))
.paginate({ numItems: 1000, cursor: null });

// Delete files
await asyncMap(page, (file) => ctx.db.delete(file._id));

// Continue deleting files if there are more
if (continueCursor) {
await ctx.scheduler.runAfter(0, internal.helpers.batch.batchDeleteFiles, {
projectId: change.id,
cursor: continueCursor,
numItems: 1000,
});
}
}
});

export const batchDeleteFiles = internalMutation({
args: {
cursor: v.string(),
projectId: v.id("projects"),
numItems: v.number(),
},
handler: async (ctx, { projectId, cursor, numItems }) => {
// Get the files
const { page, continueCursor } = await ctx.db
.query("files")
.withIndex("by_project_id", (q) => q.eq("projectId", projectId))
.paginate({
numItems,
cursor,
});

// Delete files
await asyncMap(page, (file) => ctx.db.delete(file._id));

if (continueCursor) {
await ctx.scheduler.runAfter(0, internal.helpers.batch.batchDeleteFiles, {
projectId,
cursor: continueCursor,
numItems,
});
}
},
});
// Cascade delete all files when a project is deleted
triggers.register("projects", async (ctx, change) => {
if (change.operation === "delete") {
// Get files with pagination
const { continueCursor, page } = await ctx.db
.query("files")
.withIndex("by_project_id", (q) => q.eq("projectId", change.id))
.paginate({ numItems: 1000, cursor: null });

// Delete files
await asyncMap(page, (file) => ctx.db.delete(file._id));

// Continue deleting files if there are more
if (continueCursor) {
await ctx.scheduler.runAfter(0, internal.helpers.batch.batchDeleteFiles, {
projectId: change.id,
cursor: continueCursor,
numItems: 1000,
});
}
}
});

export const batchDeleteFiles = internalMutation({
args: {
cursor: v.string(),
projectId: v.id("projects"),
numItems: v.number(),
},
handler: async (ctx, { projectId, cursor, numItems }) => {
// Get the files
const { page, continueCursor } = await ctx.db
.query("files")
.withIndex("by_project_id", (q) => q.eq("projectId", projectId))
.paginate({
numItems,
cursor,
});

// Delete files
await asyncMap(page, (file) => ctx.db.delete(file._id));

if (continueCursor) {
await ctx.scheduler.runAfter(0, internal.helpers.batch.batchDeleteFiles, {
projectId,
cursor: continueCursor,
numItems,
});
}
},
});
8 replies