jejunum
jejunum•12mo ago

Appending to Array using Mutation

A super simple question but I couldn't find too much on this, I have a data schema for a node which contains a childArray[]. What is the mutation call for appending to an array?
node: defineTable({
// Array of childNodes
childNodes: v.array(v.string()),
})
node: defineTable({
// Array of childNodes
childNodes: v.array(v.string()),
})
For some more context, this is what I have at the moment for my array push function... (syntax is wrong!):
const node = await ctx.db.patch(args.parentNode, {
$push: {childNodes: args.childNode},
});
const node = await ctx.db.patch(args.parentNode, {
$push: {childNodes: args.childNode},
});
Effectively, I need a function that appends a storageId into childNodes :3 Thanks
11 Replies
ballingt
ballingt•12mo ago
You can read the document, modify the array, then db.replace the document. https://docs.convex.dev/database/writing-data#updating-existing-documents shows this method but doesn't actually demonstrate reading a document, changing it, then writing it — we can add that. Convex mutations are transactions; there's no potential for a race condition where another function modifies this document between the read and the write.
jejunum
jejunumOP•12mo ago
That makes sense, so just confirming that means that there are no methods that allow for appending the array directly?
ballingt
ballingt•12mo ago
Yep, there's nothing built in. If you wanted to write a helper function it might look like
async function addElementToArray(ctx, id, field, value) {
const doc = await ctx.db.get(id);
const arr = doc[field];
if (!Array.isArray(arr)) throw new Error("not an array!");
arr.push(value);
await ctx.db.patch(id, {[field]: arr});
}
async function addElementToArray(ctx, id, field, value) {
const doc = await ctx.db.get(id);
const arr = doc[field];
if (!Array.isArray(arr)) throw new Error("not an array!");
arr.push(value);
await ctx.db.patch(id, {[field]: arr});
}
but the TypeScript types are left as an exercise — I wouldn't use this function, I'd write it out normally.
jejunum
jejunumOP•12mo ago
That makes a lot of sense! Thanks so much 😘
FleetAdmiralJakob 🗕 🗗 🗙
Hi, I am using convex via convex-ents and this is how far I got:
export async function addElementToArray<
Table extends TableNames,
Field extends keyof (typeof entDefinitions)[Table]["document"],
>(
ctx: MutationCtx | QueryCtx,
id: typeof v.id(), // Pass Table name into that
value: (typeof entDefinitions)[Table]["document"][Field],
) {}
export async function addElementToArray<
Table extends TableNames,
Field extends keyof (typeof entDefinitions)[Table]["document"],
>(
ctx: MutationCtx | QueryCtx,
id: typeof v.id(), // Pass Table name into that
value: (typeof entDefinitions)[Table]["document"][Field],
) {}
But I can't figure out how to pass the TableName (Table) to the v.id() Can there be added a helper to convex, convex-helpers or convex-ents? Because anyone here will just copy and paste the code after that and I think a helper would be a nice abstraction for many, especially because after that it can be documented very well in the docs of the package that the function is added to. Oh hell. I think I found it after all the hours of being stupid:
export async function addElementToArray<
Table extends TableNames,
Field extends keyof (typeof entDefinitions)[Table]["document"],
>(
ctx: MutationCtx | QueryCtx,
id: Id<Table>,
value: (typeof entDefinitions)[Table]["document"][Field],
) {}
export async function addElementToArray<
Table extends TableNames,
Field extends keyof (typeof entDefinitions)[Table]["document"],
>(
ctx: MutationCtx | QueryCtx,
id: Id<Table>,
value: (typeof entDefinitions)[Table]["document"][Field],
) {}
Michal Srb
Michal Srb•8mo ago
Curious, is the situation making the "naive" read + update code hard to write? Can you share an example of your callsite?
FleetAdmiralJakob 🗕 🗗 🗙
wdym with callsite? should I give you an example of when adding a new value to an array is needed or should I give you the full code on how I solved the problem?
Michal Srb
Michal Srb•8mo ago
Yeah, the example of adding a new value to an array, that makes you want to have addElementToArray
FleetAdmiralJakob 🗕 🗗 🗙
let's say that I have an app similar to discord and I want to give each user roles that's stored as v.array(v.string()) and I want to add roles to the user or an array to store all the platforms that a user is on I think you get the idea In my case I refactored the code anyway because I first wanted to store read receipts in an array with clerk ids but I just used an relation to the users table to do that
Michal Srb
Michal Srb•8mo ago
Yeah, I get that there's a need for updating array fields and adding values, I was just curious whether reading the document first was too cumbersome like this:
const user = ....
await user.patch({roles: [...user.roles, newRole]})
const user = ....
await user.patch({roles: [...user.roles, newRole]})
ampp
ampp•8mo ago
isn't this another way to do it with lodash? https://discord.com/channels/1019350475847499849/1245949753024319578 I've avoided using these child objects as it just seems to inefficient to resend the whole thing, especially if you just need to append but i go some on the roadmap...

Did you find this page helpful?