I have two entities with an explicit many-to-many relationship, Events and Peas, their relation table is named PeaInEvent. A field in this relation table is attendance, obviously marking whether a Pea is attending an Event or not.
I'm trying to update a particular row in the PeaInEvent table by passing the PeaId, EventId, and the new attendance status.
This is my resolver here:
updateAttendance: async (parent, args, context) => {
return context.prisma.peaInEvent.update({
where: {
peaId: args.peaId,
eventId: args.eventId
},
data: {
attendance: args.attendance
},
})
}
The issue is that each row of the PeaInEvent table has a composite key (like usual) of the PeaId and the EventId. But the 'where' argument only takes in one unique argument, not two. Here I'm trying to pass in both the PeaId and EventId, but the error states that it's looking for the composite key, which I haven't provided and am not sure how to find.
Is there a way to write this resolver so that it combines the two ids or is there a way to gain access to the composite key in a graphql query.
The only solution I have at the moment is to create a separate key in the PeaInEvent rows so as to avoid the composite key altogether, but surely there's a better way.
Related
I want to order events by "beginningDate", but this is inside the table of components_date_hour witch is inside dateHour, and I can't find a way of referencing it in the line
static queryEvents() {
return gql`
query Events($start:Int, $limit:Int) {
events(where: {habilitaded: "true"}, sort: "createdAt:desc", start:$start, limit:$limit) {
...
I tried every variation possible, with sort:"dateHour["beginningDate"]:desc", or"[]", ".", etc...
To clarify a little bit, I have a "event" table, inside there is a "dateHour" witch have an id that reference to another table "components_date_hour", and there it is the "beginningDate"
Hasura: How to establish relationship to existing record (many to many) at insert time?
I have two tables: product and category that are linked to each other in a many to many relationship based on an id column in each table and an intermediary table product_category.
I can insert records directly into postgres for both tables and link them with product_category and this works great in Hasura for queries so I know I have set things up properly.
What I want to be able to do is insert a new product and knowing the id of a category (or categories) I want to establish the relationship at insert time. Preferably without a seperate call
https://hasura.io/docs/1.0/graphql/manual/mutations/insert.html#insert-an-object-along-with-its-related-objects-through-relationships
The documentation only covers inserting both the object and related objects at the same time but what if the other one already exists?
I have tried what I would expect to work (linking this product to category with id 1):
mutation MyMutation {
insert_product_one(
object: {
name: "Champion",
category: {data: {id: 1}}
}) {
id
}
}
But that throws:
"Not-NULL violation. null value in column \"product_id\" violates not-null constraint"
How can I insert this new product and link it to one or more categories? Preferably all in one statement but even an example of retrieving the generated id and an update mutation would be not ideal, but non-the-less a solution.
Update: As a sanity check I've recreated the product and category tables as a minimal, basic example and tried both my query and the upsert conflict method xadm suggested.
The data and relations I've added some screenshots of here:
https://imgur.com/a/GUomMbe
mutation MyMutation {
insert_testproduct_one(
object: {
name: "Champion",
category: {
data: {id: 1},
on_conflict: { constraint: primarykeything , update_columns: [id] }
}
}) {
id
}
}
The error is similar: "Not-NULL violation. null value in column \"testcategory_id\" violates not-null constraint"
Note: primarykeything is the primary key on the bridge table consisting of the two ids.
Since it's a many to many relationship, you have a join table in between them. From what I can see in the screenshot you posted the id for the category in your category relationship is called testcategory_id and not id.
mutation MyMutation {
insert_testproduct_one(
object: {
name: "Champion",
category: {
data: {testcategory_id: 1}
}
}) {
id
}
}
For it to work the id in the table testproduct_testcategory has to be auto-incremented
I'm using GraphQL to query a database that has two data types: User and Group.
Groups have a field users which is an array of User objects which are in that group. I have one field at root named groups which returns an array of all of my groups.
A typical query might look something like this:
{
groups {
id,
name,
users {
id,
name,
address,
email,
phone,
attitude,
job,
favoriteQuote,
favoriteColor,
birthday
}
}
}
The problem is that a lot of those users can belong to multiple groups and, seeing as User has a lot of fields, this can make responses quite large.
Is there any way to get one set of fields for the first instance of an object, and a different set for every other instance in the response?
I only need name, job, email etc etc once per user in the response, and just the id thereafter (I can do my own normalization afterwards).
alternatively
Is there any way to only get id fields for all users in groups and return a separate array of all unique User objects that have been referenced in the query (which is not all User objects)?
Is there any way to get one set of fields for the first instance of an object, and a different set for every other instance in the response?
No. The same set of fields will be returned for each item in a list unless the type of the individual item is different, since a separate selection set can be specified for each type returned at runtime.
Is there any way to only get id fields for all users in groups and return a separate array of all unique User objects that have been referenced in the query (which is not all User objects)?
You could design your schema to accommodate this. Something like
{
groups {
nodes {
id
name
users {
id
}
}
uniqueUsers {
id
# other fields
}
}
}
Your groups resolver would need to handle all the normalization and return the data in the appropriate shape. However, a simpler solution might be to just invert your relationship:
{
users {
id
name
address
email
phone
attitude
job
favoriteQuote
favoriteColor
birthday
groups {
id
name
}
}
}
Generally - usually
... normalization ... of course ... f.e. using apollo and it's normalized cache.
All records returned from API has to be the same shape.
You can get data and render some <MembersList/> component using query for ids and names only (full/paginated).
Later you can render details in some <UserProfile/> component with own query (hook useQuery inside) to fetch additional data from cache/api (controllable).
Your specific requirements - possible
1st option:
Usually response is of one common shape (as requested), but you can decide on resolver level what to return. This requires query structure changes that allows (API, backend) to null-ify some properties. F.e.
group {
id
name
users {
id
name
profile {
photo
email
address
With profile custom json type ... you can construct users resolver to return full data only for 1st record and null for all following users.
2nd option:
You can use 2 slightly different queries in one request. Use aliases (see docs), in short:
groupWithFullMember: group ( groupId:xxx, limitUsers:1 ) {
id
name
users {
id
name
address
email
...
}
}
groupMembers: group ( groupId:xxx ) {
id
name // not required
users {
id
name
}
}
Group resolver can return it's child users ... or users resolver can access limitUsers param to limit response/modify db query.
According to the connection based model for pagination using graphQL, I have the following simplified schema.
type User {
id: ID!
name: String!
}
type UserConnection {
totalCount: Int
pageInfo: PageInfo
edges: [UserEdge]
}
type UserEdge {
cursor: String
node: User
}
type PageInfo {
lastCursor: Int
hasNextPage: Boolean
}
type Query {
users(first: Int, after: String): UserConnection
}
Consider the following router on within SPA front-end:
/users - once the user hit this page, I'm fetching first 10 records right up from the top of the list and further I'm able to paginate by reusing a cursor that I've retrieved from the first response.
/user/52 - here I'd like to show up 10 records that should go right from the position of user52.
Problem What are the possible ways to retrieve a particular subset of records on the very first request? On this moment I don't have any cursor to construct something similar to
query GetTenUsersAfter52 {
users(first: 10, after: "????") { # struggling to pass anything as a cursor...
edges {
node {
name
}
}
}
}
What I've already tried(a possible solution) is that I know that on a back-end the cursor is encoded value of an _id of the record in the DB. So, being on /users/52 I can make an individual request for that particular user, grab the value of id, then on the front-end I can compute a cursor and pass it to the back-end in the query above.
But in this case personally, I found a couple of disadvantages:
I'm exposing the way of how my cursor is computed to the front-end, which is bad since if I needed to change that procedure I need to change it on front-end and back-end...
I don't want to make another query field for an individual user simply because I need its id to pass to the users query field.
I don't want to make 2 API calls for that as well...
This is a good example of how Relay-style pagination can be limiting. You'll hit a similar scenario with create mutations, where manually adding a created object into the cache ends up screwing up your pagination because you won't have a cursor for the created object.
As long as you're not actually using Relay client-side, one solution is to just abandon using cursors altogether. You can keep your before and after fields, but instead simply accept the id (or _id or whatever PK) value instead of a cursor. This is what I ended up doing on a recent project and it simplified things significantly.
I'm setting up a GraphQL Apollo Server, and want to define a resolver for an object type that has an element containing an array of data based on a many to many relationship.
I have a Measurement object that is associated to an Activity object with a many to many relationship:
Measurement.belongsToMany(Activity, {
as: 'activity',
through: 'activity_measurement_entries',
underscored: true
});
Activity.belongsToMany(Measurement, {
as: 'measurements',
through: 'activity_measurement_entries',
underscored: true
});
A resolver query works fine for the getActivities query when Measurement is included directly in the query like this:
getActivities: async (parent, args, { models }) => {
const activities = await models.Activity.findAll({
include: [{
model: models.Measurement,
as: 'measurements',
}],
});
return activities;
},
An activity_measurement table is generated automatically through Sequelize which contains activity_id and measurement_id.
I would like to define a resolver for the measurements field on Activity like below, but I'm not sure how to structure the where clause and what is passed in the parent parameter with the many to many relationship table in the middle (assume a similar getActivities query is used to get all Activities and the associated measurements but no input parameter to the query):
Activity: {
measurements: (parent, args, { models }) => models.Measurement.findAll({
where: {
????
},
}),
},
For a query that attempts to get all the Activities when the Activity type is defined like this:
getActivities: async (parent, args, { models }) =>
models.ActivityEntry.findAll()
If there is no where clause in the resolver for the measurements field of the Activity type then every measurement is returned under every Activity (instead of just the measurements associated through the activity_measurement table).
I am trying to figure out how to get only the measurements associated with the Activity to be returned when using the separate Activity type definition with a measurements element.
The parent value that's passed to the resolver reflects the value that was returned in the parent field's resolver. In this case, for the measurements field, the parent field is getActivities. You're returning an array of Activity model instances in getActivities, so the parent value passed to the measurements field resolver will also be an instance of your Activity model.
That means you can do something like:
Activity: {
measurements: (activity, args, { models }) => activity.getMeasurements(),
},