Converting a Rails has_many :through relationship?

The “HMT” association is one of the very nice things about Rails and Active Record – and something many people moving to Prisma yearn for to be similar or simpler.

It’s been brought here here in this Community:

and in Prisma as well:

In short, I believe people are solving it – just like you noted above – via the “many-to-many” with Prisma’s implicit or explicit approach:

So a User and a Role and either an implicitly created _UserToRole or an explicitly UserRole join table or as they would name it RolesOnUsers.

The problem seen with the implicit table – and something I wish Prisma would improve on is that:

  • there are no timestamps on _UserToRole so you don’t know when a user was assigner a Role
  • You cannot store any other metadata or info on that join table

To do that would have to use the explicit method.

Ok - so back to your question :wink:

I expected to be able to query something like:

query FIND_USER_BY_ID($id: Int!) {
  user: user(id: $id) {
    firstName
    lastName
    roles {
      name
      id
    }
  }
}

In a project I am working on now, I have two models Tweet and Article where a there is a many to many relationship because a Tweet could have mentioned an article and so could many other tweets (where the article is defined by its url):

:point_right: This diagram shows the join table without the _ – this is the implicit _ArticleToTweet

image

When I am viewing the Tweet and I want to see the Article(s), my Prisma query is:

export const tweetById = ({ id }) => {
  return db.tweet.findOne({
    where: { id: id },
    include: {
      entry: true,
      articles: {
        include: {
          tags: { where: { confidence: { gte: 0.4 }, mentions: { gt: 0 } } },
        },
      },
      tweetContext: true,
      tweetCategories: true,
      tags: { where: { confidence: { gte: 0.4 }, mentions: { gt: 0 } } },
      tweetPriorities: true,
    },
  })
}

:rotating_light: Very Important! I also need:

export const Tweet = {
  articles: (_obj, { root }) =>
    db.tweet.findOne({ where: { id: root.id } }).articles(),
}

export const Article = {
  tweets: (_obj, { root }) =>
    db.article.findOne({ where: { id: root.id } }).tweets()
}

:spiral_notepad:And you might think this should be findMany and I did at first, too, but really this is saying “for each Article” there are many Tweets and vice versa when resolving in GraphQL (or that’s how I’ve understood it to work).

And in my cell to show the Tweet and the Article included to match the Prisma is:

export const QUERY = gql`
  query TweetQuery($id: String!) {
    tweet: tweetById(id: $id) {
      id
      createdAt
      updatedAt
      publishedAt
      author
      title
      content
      sentiment
      url

      articles {
        id
        title
        description
        url

        tags {
          label
          mentions
          confidence
          salience
          sentiment
        }
      }

      categories: tweetCategories {
        label
      }

      priorities: tweetPriorities {
        label
        terms: tweetPriorityTerms {
          label
        }
      }

      tags {
        label
        mentions
        confidence
        salience
        sentiment
        entityTypes
      }
    }
  }
`

And you can see that I can get both the Article on Tweet and Tweet on the Article:

Is it that same as a has_many through – maybe, maybe not.

So, far this is working, but it does take awhile to get comfortable.

That said, can see Prisma trying to work in some familiar Active Record -like features in their Nested Writes where they have a connectOrCreate which is similar to the find_or_create_by (or first_or_create_by).

As with many things, I hope Prisma will evolve to help make these things a bit easier. :crossed_fingers:

Thanks for reading.

4 Likes