#Batch Mutation of Rows
1 messages · Page 1 of 1 (latest)
Hi @plucky owl. I'm kapa.ai, your friendly support bot. I will try to answer your question by searching through the knowledge base. :wave:
To perform a batch mutation (i.e., update, insert, or delete multiple rows) in Convex, the standard approach is to write a mutation function that processes multiple documents in a loop. Convex executes all database changes within a mutation as a single transaction, making this both efficient and atomic for moderate batch sizes.
Here’s a basic example for bulk inserting documents:
export const bulkInsertProducts = mutation({
args: {
products: v.array(
v.object({
product_name: v.string(),
category: v.string(),
price: v.number(),
in_stock: v.boolean(),
}),
),
},
handler: async (ctx, args) => {
const { products } = args;
for (const product of products) {
await ctx.db.insert("products", {
product_name: product.product_name,
category: product.category,
price: product.price,
in_stock: product.in_stock,
});
}
},
});
This pattern also applies to updates and deletes—just loop over the items and perform the desired operation inside the mutation handler. All changes are queued and committed together when the mutation ends, ensuring transactional integrity Bulk inserts or updates.
For large tables:
If you need to mutate thousands of rows (e.g., more than a few thousand), you should break the operation into batches to avoid transaction and memory limits. This is typically done by paginating over your data and processing each batch in a separate mutation. You can use recursive scheduling or loop from an action to process all batches:
export const myMigrationBatch = internalMutation(
async ({ db }, { cursor, numItems }) => {
const data = await db.query("mytable").paginate({ cursor, numItems });
const { page, isDone, continueCursor } = data;
for (const doc of page) {
// modify doc
}
return { cursor: continueCursor, isDone };
},
);
And to process all batches:
export const runMigration = internalAction(
async ({ runMutation }, { name, cursor, batchSize }) => {
let isDone = false;
while (!isDone) {
const args = { cursor, numItems: batchSize };
({ isDone, cursor } = await runMutation(name, args));
}
},
);
This approach ensures you stay within Convex’s transaction limits and can handle very large datasets safely Defining your own migrations, Looping batches from an action.
Summary:
- For small to moderate batches: loop inside a mutation.
- For large datasets: paginate and process in batches, using recursive scheduling or an action to loop through all data.