Is this typically a bad practice? v.union(v.id("thisList"), v.id("thatList"))
I have two different types of lists that a user can select to take a singular action on (in my case, send emails).
I have a list of contacts that is a "segment", or a user can select the entire "list". A list is different from a segment, but the same action can be taken on both for all practical purposes.
I have my schema like this:
audience: v.union(v.id("mailchimpLists"), v.id("mailchimpSegments")),
Butttttt I'm immediately realizing that disambiguating this in a function seems hacky at best.
Is it better to do this, and then simply manage what's what further upstream?
This is fine as well, however, I find myself wanting to enforce that there must be one but not both.
I could do something like this:
But this still feels brittle.
I'm leaning toward the audienceType option, but would love to know if there's a better way, or if I'm missing something obvious with the v.union(v.id,v.id) idea.10 Replies
Thanks for posting in <#1088161997662724167>.
Reminder: If you have a Convex Pro account, use the Convex Dashboard to file support tickets.
- Provide context: What are you trying to achieve, what is the end-user interaction, what are you seeing? (full error message, command output, etc.)
- Use search.convex.dev to search Docs, Stack, and Discord all at once.
- Additionally, you can post your questions in the Convex Community's <#1228095053885476985> channel to receive a response from AI.
- Avoid tagging staff unless specifically instructed.
Thank you!
A discriminated union is the way to go:
type
allows you to easily discriminate at runtime which ID type you have, but without permitting nonsensical states like:
or
Ah this is perfect! Thank you @RJ !
"A discriminated union" is a great band name too
glad I stumbled upon this support issue - was able to apply this discriminated union approach myself for a mutation with 3 different permutations of inputs.
Beautiful!
I highly recommend using discriminated unions of this sort (with a
tag
or type
discriminant field) in normal TypeScript code, too. Your EventTicketingUpdate
data type is a great example of a union type that would be really confusing/difficult/tedious to discriminate otherwise!yeah, the coolest thing too is that this not only makes my front end forms much cleaner, but I'm also able to handle conditional logic in a single mutation by checking the type used in the payload. Super clean, really appreciate the tip
how does indexing work on discriminated union? Just the same way as any other column?
I battled with some nested discriminated unions that ended up being too difficult for me to work with, so I resorted to just splitting it up into two separate tables. I'm interested in hearing you guys' opinions on this.
It's for a notification system which can dispatch different notifications (in-app, push, e-mail etc). The challenge is that there are different types of notifications which vary on their:
recipient
, event
, and targetId
.
For now, recipient kinds are profile
and channel
, there are ~6 events in total, where some are unique to each recipient and others shared, and then 3-4 different types of targetId
(e.g. submission
, video
, channel
).
Not all tuples of recipient, event, targetId
are valid, and nesting discriminated unions quickly grew us over our heads, but I would love to hear if anyone thinks this data modelling sounds feasible to implement and work with, and what we maybe did wrong?Yeah, just like any other column/probably how you'd expect it to
This is a little hard for me to follow without more code examples, but if you open up a new support post I'd be happy to have a look and share my thoughts. Without seeing additional details I can still say two things that might be helpful:
1. The strategy that Tom was going for—storing a union with different types of references to different tables—in this post is a good one, and you can do this at the top-level too. So if
audience
were not a field but a table, you could do something like
2. In my experience, whenever an object field in a table feels cumbersome, breaking it out into its own table is pretty much always a good move. Though you can define both tables and fields with v.object()
, you can do more with tables than you can with object fields in tables. (This is for data modeling/correctness purposes—different rules, i.e. denormalization, may apply if you're running into performance issues.)Thanks @RJ , your second point is great and supports our eventual conclusion. It's great to have the option for heterogeneous and nested data in Convex, but also important to abandon it when its too detrimental to ergonomics or understanding!
Does anybody know how to define a table using
Table
from convex-helpers
when the schema is a discriminated union?