fawwaz
fawwaz•2y ago

Hey all again 👋 I have an aggregate

Hey all again 👋 , I have an aggregate that get intilized by it's own id before being persisted to Convex. After many tries I kept hitting a validator error (the on the mutation). Eventually I just copied an the id of an existing record, deleted that record and tried using that Id, but I discovered that Convex ignored the _id and inserted the document with another _id. Is there is no way of assigning an Id to a record before insert?
7 Replies
fawwaz
fawwazOP•2y ago
this the code I have for the mutation, newImageGenerationTask will ideal have _id inside it already
export const saveImageGenerationTask = internalMutation({
args: {
args: ImageGenerationConvexModelDefinition,
},
handler: async (ctx, args) => {
const { _id, ...rest } = ImageGenerationTaskToPromptAndResponse(args.args);
const storedImageGenerationTask = _id ? await ctx.db.get(_id) : null;
const newImageGenerationTask = { ...storedImageGenerationTask, ...rest };
const newId = _id ? await ctx.db.replace(_id, newImageGenerationTask) : await ctx.db.insert("imageGenerationTasks", newImageGenerationTask);
return (_id ?? newId) as Id<"imageGenerationTasks">;
},
});
export const saveImageGenerationTask = internalMutation({
args: {
args: ImageGenerationConvexModelDefinition,
},
handler: async (ctx, args) => {
const { _id, ...rest } = ImageGenerationTaskToPromptAndResponse(args.args);
const storedImageGenerationTask = _id ? await ctx.db.get(_id) : null;
const newImageGenerationTask = { ...storedImageGenerationTask, ...rest };
const newId = _id ? await ctx.db.replace(_id, newImageGenerationTask) : await ctx.db.insert("imageGenerationTasks", newImageGenerationTask);
return (_id ?? newId) as Id<"imageGenerationTasks">;
},
});
sshader
sshader•2y ago
There's currently no way of assigning an ID to a Document before insert. IDs used in optimistic updates are essentially placeholders until insert can assign a real ID (we may build a way to allocate IDs from the client in the future, but at the moment it's not supported). Can you tell me more about your use case so we can brainstorm some alternatives? Where are you currently trying to assign the ID?
fawwaz
fawwazOP•2y ago
Hey @sshader So I'm exploring apply DDD with Convex. What I'm trying to do is isolate the domain from the database implementation. so usually I start by defining the shape of my Aggregates which in a simple application will have mirror many the same properties as the Convex Model, including the id After that I have list of commands that take any depndecies that might need to do their work. For example I have a scheduleImageGeneration that will create the ImageGenrationTask aggregate. Since the model definition including an Id a random Id will be assigned.
this is my current implementation for that command handler
export const createScheduleImageGeneration =
(ctx: ActionCtx) =>
async ({ prompt }: { prompt: string }) => {
console.log("SCHEDULING IMAGE GENERATION");
const docId: Id<"imageGenerationTasks"> = await ctx.runMutation(internal.imageGeneration.mutations.saveImageGenerationTask, {
args: {
prompt,
},
});
await ctx.scheduler.runAfter(0, internal.imageGeneration.actions.firePrediction, { prompt, docId });
return docId;
};
export const createScheduleImageGeneration =
(ctx: ActionCtx) =>
async ({ prompt }: { prompt: string }) => {
console.log("SCHEDULING IMAGE GENERATION");
const docId: Id<"imageGenerationTasks"> = await ctx.runMutation(internal.imageGeneration.mutations.saveImageGenerationTask, {
args: {
prompt,
},
});
await ctx.scheduler.runAfter(0, internal.imageGeneration.actions.firePrediction, { prompt, docId });
return docId;
};
of course I can change the modle shape for ImageGenerationTask to have the id optional and this is what I'm currently doing, but that mean checking for if id exist in flows where I know an Id will most currently be there. Not sure if I'm departing very far from how Convex should be used though 😄 , tell me what you think?
sshader
sshader•2y ago
Where does the ID come from when it is provided? Looks like createScheduleImageGeneration doesn't provide an _id but saveImageGenerationTask is written to expect an optional _id? Also a workaround is to define your own field (e.g. id) and an index to support fast look ups, and then you'd be able to control how that field gets populated (and essentially ignore the built in _id field). But I'm still curious if there's a way we could support this with the built in field
fawwaz
fawwazOP•2y ago
oh my applogies that was my current impemenation, here is the one I was trying to do before I discovred the issue with assigning Ids
const docId: Id<"imageGenerationTasks"> = await ctx.runMutation(internal.imageGeneration.mutations.saveImageGenerationTask, {
args: createImageGenerationTask({ prompt }),
});
const docId: Id<"imageGenerationTasks"> = await ctx.runMutation(internal.imageGeneration.mutations.saveImageGenerationTask, {
args: createImageGenerationTask({ prompt }),
});
so basically createImageGenerationTask will check for id if non exist will add one hmm the approach of assigning my id does seem interesting , I might hit similar issues of needing to check for Convex _id but will give it try 🙂
sshader
sshader•2y ago
I think I'm still missing why saveImageGenerationTask needs an _id at all vs. providing all the fields aside from _id and _creationTime and always creating a new document using db.insert?
fawwaz
fawwazOP•2y ago
so createImageGenerationTask is not concern where is it going to be used, it only guarantee that you will get a valid ImageGenerationTask aggregate. For the inset flow the id might not be needed but for other flows it would be needed (eg handle model run result).

Did you find this page helpful?