Good morning. I have a group of tables that use a ...
# orm-help
e
Good morning. I have a group of tables that use a Rails-style polymorphic association. It looks something like this, simplified:
Copy code
model Post {
  id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid
  tags Tag[]
}

model Comment {
  id String @id @default(dbgenerated("gen_random_uuid()")) @db.Uuid
  tags Tag[]
}

model Tag {
  resourceType String // One of "Post" or "Comment"
  resourceId @db.Uuid

  value String
}
For example, we could find all the tags associated with post a5c4b960-d5da-4e7b-abb5-0860bd85903d by querying:
Copy code
SELECT * FROM Tag WHERE resourceType = 'Post' AND resourceId = 'a5c4b960-d5da-4e7b-abb5-0860bd85903d';
Ideally, I would like to able to write:
Copy code
client.source_tables.findUnique({
  where: { id: "a5c4b960-d5da-4e7b-abb5-0860bd85903d" },
  include: { tags: true }
});
If I could do this, it would simplify a lot of code. The actual use case is more complicated than this, but that's the general idea. (The fact that
resourceId
is globally unique means that we have the option of ignoring
resourceType
, if that helps.) I've read https://www.prisma.io/docs/concepts/components/prisma-schema/relations and Googled "prisma polymorphic relations", but I can't find a "best practice" for handling this situation with the current version of Prisma. Thank you for any insight anyone can provide!
It looks like the most detailed discussion of this is here: https://github.com/prisma/prisma/issues/2505 In my case, I have two simplifying assumptions: 1. I don't need a fully polymorphic
resources (Post | Comment)[]
on
Tag
. I only want
include: { tags: true }
to work when loading a
Post
or
Comment
. 2. My
id
fields are globally unique across tables thanks to UUIDs. This suggests that there might be some clunky workaround?
I have found a local, slightly clunky workaround for this. Basically, everything is easier if you don't want a
resources (Post | Comment)[]
on
Tag
, but can settle for separate
posts
and
comments
relations.
r
@Eric Kidd 👋 I didn’t quite get your question. Do you want to achieve this SQL using Prisma’s API?
Copy code
SELECT * FROM Tag WHERE resourceType = 'Post' AND resourceId = 'a5c4b960-d5da-4e7b-abb5-0860bd85903d';
e
The goal here is to do:
Copy code
const posts = client.Post.findMany({
  where: { ... },
  include: "tags",
};
...where
Tag
is:
Copy code
model Tag {
  resourceType String // One of "Post" or "Comment"
  resourceId @db.Uuid // Reference to table defined by resourceType.

  value String
}
It turns out that since UUIDs are globally unique, I can just ignore
resourceType
and build multiple ordinary relations on
Tag
using named relations. So I wind up with
tag.posts
,
tag.comment
,
post.tags
,
comment.tags
, and so on.
Rails implements
Tag
-style polymorphism using
foo_id
and
foo_type
fields, where
foo_type
contains the table name being referred to, and
foo_id
contains the ID within that table. It's a common pattern (thanks to Rails and ActiveRecord), and it's not immediately obvious how to support it using Prisma. But I have it working nicely for our use case. No worries, and thank you for the help!
👍 1