Query to get data ordered by the number of items in a relation - graphql

Let’s say I have this typical datamodel, the one used in many tutorials:
type User {
id: ID! #unique
name: String!
posts: [Post!]!
}
type Post {
id: ID! #unique
title: String!
content: String!
published: Boolean! #default(value: "false")
author: User!
}
Is there a query I can build to get a list of, let’s say, the 10 Users with more Posts??
Basically I need to query ordering by “count” of Posts… but I haven’t found a way to do it
Any help will be highly appreciated
Cheers

As #shivam-panday said in a comment, this is currently not implemented in Prisma (See issue: https://github.com/prisma/prisma/issues/95 )
This comment especially explains your problem:
It would be great to be able to order by "to-many" related fields as well (by the count of related items).
For example, to get a list of the top 10 most voted-for links (assuming votes is a related field of type [Vote!]!):
query {
allLinks(first: 10, orderBy: votes_DESC) {
id
url
description
_votesMeta {
count
}
}
}
Currently, to get that list you'd have to query for every Link and then sort/slice it on the client, which is potentially a ton of overfetching.
Comment in question: https://github.com/prisma/prisma/issues/95#issuecomment-320433296

I came across the same issue with Prisma and this is a major problem. Retrieving all the users with all their posts, and sorting by the number of their posts is not a practical solution.
The workaround I can think of is to track the number of posts (every time when adding/deleting)and store it in User.
type User {
id: ID! #unique
name: String!
postsCount: Int!
posts: [Post!]!
}
This way, Prisma will expose the sorting options for the postsCount.
I am not sure if Prisma 2 offers a proper solution for this issue... Does anybody know?

Related

Introducing a "Connecting" Type in GraphQL

In my graphql schema, I have the two following (abbreviated) types.
type author {
id: Int!
name: String!
articles: [article!]
}
type article {
id: Int!
title: String!
abstract: String!
createdAt: DateTime!
}
That is to say, one author has many articles.
A new requirement has come in, which is to also show whether the author is the first or last named author on the article. How best to introduce this information into the schema without it being a breaking change? The following applies.
Both author and article have other relationships not shown. It doesn't make logical sense to have the new information sit directly on the article.
The information on the authors position in the article is in the link table in the database. Conceptually there is the author <-> author_article <-> article tables with the many to many illustrating the fact one author can publish many articles, and articles can have many authors. Ideas I've had.
Simply change the schema to the following.
type author {
id: Int!
name: String!
articles: [article!]
articleInformation: [articleInformation!]
}
type articleInformation {
firstAuthorship boolean
lastAuthorship boolean
article article
}
type article {
id: Int!
title: String!
abstract: String!
createdAt: DateTime!
}
to allow the querier to get the articleInformation if they choose, but this feels somewhat clunky.
use something like an interface that a normal article, and an "articleWithInformation" can implement.
Can someone with more experience than me at working with graphql offer some insight?

How to query a third party package trait in Laravel lighthouse

I read Laravel Lighthouse documentation and searched around the web I did not find how to query a trait in a third party package in Laravel.
I'm using the qirolab/laravel-reactions package, it has the reactionSummary() trait.
I was asking how can I add this relation in the lighthouse query?
type Post {
id: ID!
title: String!
excerpt: String!
image_url: String!
slug: String!
source: Source! #belongsTo
reactionSummary: ???????
created_at: DateTime!
updated_at: DateTime!
}
I have a purpose with my question beside solving my issue, understanding how lighthouse work with packages or how to integrate third party packages with lighthouse?
You need to define the schema for the reaction model in graphql and in the post schema you need to define an array of the reaction type.
According to the model (https://github.com/qirolab/laravel-reactions/blob/master/src/Models/Reaction.php) the reaction graphql schema would look something like this:
type Reaction {
reactBy: User! #belongsTo
type: String
reactable: Reactable! #morphTo
}
Your post schema would change to
type Post {
id: ID!
title: String!
excerpt: String!
image_url: String!
slug: String!
source: Source! #belongsTo
reactionSummary: [Reaction]
created_at: DateTime!
updated_at: DateTime!
}
As I can see from the migration file https://github.com/qirolab/laravel-reactions/blob/master/migrations/2018_07_10_000000_create_reactions_table.php. The reaction has a polymorphic relation.
That means the returning type can vary depending on which kind of model is set as the reactable_type in the field. Therefore you need to define your own Union type.
A Union is an abstract type that simply enumerates other Object Types. They are similar to interfaces in that they can return different types, but they can not have fields defined.
Source: https://lighthouse-php.com/5/the-basics/types.html#union
See also polymorphic relations and the union section: https://lighthouse-php.com/5/eloquent/polymorphic-relationships.html#one-to-one
I hope that gives you the direction on how you can proceed.

Add many to many connection in amplify graphql

I have a time consuming problem and no idea what I can test more.
This is working, but I need the in Recruiter for positions an array. But then I have a many to many connection and nothing is working anymore. Is there a nice way to solve it?
Here is my code:
type Position #model #auth(rules: []) {
id: ID!
title: String!
candidates: [Candidate]! #connection(name: "PositionCandidates")
interestedRecruiters: [Recruiter]! #connection(name: "PositionRecruiter")
}
type Candidate #model #auth(rules: []) {
id: ID!
firstname: String!
lastname: String!
email: AWSEmail!
birthday: AWSDate!
position: Position! #connection(name: "PositionCandidates")
}
type Recruiter #model #auth(rules: []) {
id: ID!
firstname: String!
lastname: String!
email: AWSEmail!
birthday: AWSDate!
positions: Position! #connection(name: "PositionRecruiter")
}
thanks !
To work with many-to-many connections with amplify/app sync you have to create a 'Join' table (type). You can think of the join table as a table where you store the connections.
I didn't completely understand from your example what you wanted the result to be, so I'll try to explain with an alternate example:
Lets say you have a system where multiple users can be the creators of a document, and a user also can be one of the creators of multiple documents. To accomplish this you must create three dynamoDb tables (or three types is the schema).
type User #model {
id: ID!
name: String
documents: [UserDocument] #connection(name: "UserDocumentConnection")
}
type Document #model {
id: ID!
title: String
content: String
users: [UserDocument] #connection(name: "DocumentUserConnection")
}
type UserDocument #model {
id: ID!
user: User #connection(name: "UserDocumentConnection")
document: Document #connection(name: "DocumentUserConnection")
}
Then you have a table containing all your users, a table containing all your Documents, and a table containing all the connections between users and documents.
So let's say you have a users that is creating a new document. Then you first create the document, then when it is created and you have received the document back from appsync with the new id of the document, then you must create a new object in the UserDocument table, containing the id of the user and the id of the document. You can then also add more users on the document by adding more items to the UserDocument table.
I hope this will help you to the correct path forwards.

GraphQL data modelling - extended types (Prisma)

In my Prisma Data Model I started out with a basic User type like this:
type User {
name: String!
email: String! #unique
password: String!
}
Now a User can have two roles: either as a candidate or as a user associated with an employer. If a candidate, the user should also have a set of applications and a set of qualifications, if associated with an employer it should have an access level and a reference to the employer.
First off, is there any way to extend basic types in GraphQL data modelling? If so, how would I go about doing it?
If there is not, I can see three different methods used, and I'm curious what are the pros and cons of each approach:
Having two separate types CandidateUser and EmployerUser, each with the fields name, email, password. I see two problems with this approach: The #unique tag on email is not reliable, and I would have to write a custom verification to make sure the field is unique across both types; and having a single login-function that takes email and fetches the users corresponding data is no longer trivial: it needs to do a lookup in both tables.
Like this:
type CandidateUser {
name: String!
email: String! #unique
password: String!
applications: [Application!]!
qualifications: [Qualification!]!
}
type EmployerUser{
name: String!
email: String! #unique
password: String!
employer: Employer!
accessRight: AccessRight!
}
Again two separate types, but with a RootUser containing name, email and password, and with CandidateUser and EmployerUser each having a one-to-one reference to a RootUser. This would enforce the #unique tag on the email field, but lookup would still be nontrivial.
type RootUser{
name: String!
email: String! #unique
password: String!
}
type CandidateUser {
rootUser: RootUser!
applications: [Application!]!
qualifications: [Qualification!]!
}
type EmployerUser{
rootUser: RootUser!
employer: Employer!
accessRight: AccessRight!
}
Extending User to have the fields within EmployerUser and CandidateUser as optional parameters. This is a pretty simple approach, but I would need custom handling to enforce requiring fields (as in, I can not mark for instance employer as required as that field would not exist for a Candidate).
type User{
name: String!
email: String! #unique
password: String!
applications: [Application!]!
qualifications: [Qualification!]!
employer: Employer
accessRight: AccessRight
}
I really want to ask if there is a better way of solving this. I'm still pretty new to GraphQL and not the best data modeler to begin with, but I'd greatly appraciate any nudge in the right direction :)
And if I do not have any other choice but the three I listed, which one would make the most sense?
What you're trying to do is implementing an interface type:
An Interface is an abstract type that includes a certain set of fields that a type must include to implement the interface.
interface User {
name: String!
email: String! #unique
password: String!
}
This means that any type that implements User needs to have these exact fields, with these arguments and return types. So now your Candidate type can implement User:
type Candidate implements User {
name: String!
email: String! #unique
password: String!
applications: [Application!]!
qualifications: [Qualification!]!
}
Interfaces are useful when you want to return an object or set of objects, but those might be of several different types. Have a look at the interface abstract type documentation for more information.
Update:
Since this is a Prisma GraphQL question now, you should be aware that Prisma does not support Interfaces or Union Types as yet. Issue #83 and issue #165 discuss both respectively as feature requests.
However, there is this great article that discuss the workarounds for such approach:
GraphQL Interfaces (and Union Types) with Prisma and Yoga
Which boils down to 2 options:
Storing all data with optional type-specific fields under one type (the interface) in Prisma, and then splitting the data back between the primitive types in the app server.
Storing the data in each primitive type on Prisma, and stitching things for queries on the app server.

GraphQL Prisma - define "vote" type that links to two users

I've just started using Prisma. Before was mainly using firebase and mongodb to define my schemas.
I'm trying to define the following schema:
Vote {
id: ID!
from: User! # The user who voted
for: User! # The user that received a vote
rate: Float!
}
Basically, what I want to achieve is enable users to vote for other users (give them a score).
In, say, MongoDB I would do it by creating a separate collection like following:
{
id: DocumentID
from: String // id of the user who voted
for: String // id of the user that received a vote
rate: Number
}
In here I just specify those fields (from and for) as strings and after link them with the User collection by the application logic.
For sure, it's gonna be different in GraphQL Prisma. But I'm still a bit confused on how the relationships are built. And what really happens underneath.
How can I create such schema using Prisma GraphQL?
When there are more than one relational field to the same type, you need to use the #relation directive to make it unambiguous.
type Vote {
id: ID! #unique
votingUser: User! #relation(name: "VoteAuthor")
votedUser: User! #relation(name: "VoteReceiver")
rate: Float!
}
type User {
id: ID! #unique
receivedVotes: [Vote!]! #relation(name: "VoteReceiver")
givenVotes: [Vote!]! #relation(name: "VoteAuthor")
name: String!
}

Resources