Hasura GraphQL UPSERT mutation for nested objects - graphql

So I'm trying to construct a mutation for inserting/updating a record for a "Person" along with it's address, email, telephones information into multiple tables, using variables.
mutation insertPerson ($address: [adr_insert_input!]!, $emails: [emails_insert_input!]!) {
insert_info(objects: [{
f_name: "User1",
l_name: "Test"
address: {
data: $address,
on_conflict: {
constraint: person_id_pk,
update_column: [add_text_line1, zip_code]
}
},
emails: {
data: $emails,
on_conflict: {
constraint: person_id_pk,
update_column: [email_text]
}
}
}], on_conflict: {
constraints: person_pk,
update_columns: [f_name, l_name]
}) {
affected_rows
}
}
and my variables are set-up as follows...
{
"address": [{
"add_text_line1": "123 Main Street",
"zip_code": 50501
}],
"emails": [{
"email_text": "FirstLastName#email.com"
}]
}
This works as expected for me (with multiple values in emails array too), but I need to move the f_name & l_name values (whole Person object) into a variable as well. How do I achieve that?
I tried the below mutation this way, but this resulted into two separate inserts & empty values being passed...
mutation insertPerson ($person: person_insert_input!, $address: [adr_insert_input!]!){
insertData(objects: [
$person,
{
address: { data: $address }
}
]) { affected_rows }
}
This resulted to a two separate insertions... First person with empty address, then empty person with address.
How do I achieve the first mutation's result, while using Person Info as part of variables NOT hard-code it into the query itself?
Thank you!

You will need to pass the insert_info objects as the variables
mutation insertPerson ($info_objects: [insert_info_insert_input!]!) {
insert_info(objects: $info_objects, on_conflict: {
constraints: person_pk,
update_columns: [f_name, l_name]
}) {
affected_rows
}
}
And your variables will be an array of the info_objects
{info_objects: [{
f_name: "User1",
l_name: "Test"
address: {
data: [{
"add_text_line1": "123 Main Street",
"zip_code": 50501
}]
},
on_conflict: {
constraint: person_id_pk,
update_column: [add_text_line1, zip_code]
}
},
emails: {
data: [{
email_text: "FirstLastName#email.com"
}],
on_conflict: {
constraint: person_id_pk,
update_column: [email_text]
}
}
}]}

Related

How to return complex object as scalar type in GraphQL?

Let's imagine we have GraphQL API that can return an object Entity with Id and Name properties and I requested Name only:
query {
entities {
name
}
}
And it returns
{
"data": {
"entities": [
{
"name": "Name1"
},
{
"name": "Name2"
}
]
}
}
But what if I want to have only the name of entities as a scalar type? In other words, I want to have something like:
{
"data": {
"entities": [
"Name1",
"Name2"
]
}
}
Is it possible to have such result without changes on the GraphQL API side? Aliases, Fragments, etc. GraphQL has a lot of built-in query capabilities, but none of the known me can return complex objects as scalar type.
what you're asking for is almost impossible if you don't want to change the type definition for Entities.
This: 👇🏽
Entity: id: int! name: String
entities(): [Entity]
returns an array of objects with keys name and id.
To achieve what you're asking you either change Entity to be just a string or have your client reduce that object to an array of just Entity names when they receive it.
They could do something like this:
const data = {
entities: [
{
name: 'Name1',
},
{
name: 'Name2',
},
],
};
const entityNames = data.entities.reduce(
(acc, curr) => [...acc, curr.name],
[]
);
console.log(entityNames);

Upsert Graphql Mutation with conflict constraint on nested object

I am trying to do an upsert in a single mutation. Here I have two tables Users table [id,isVerified,type] and Customers table [id,name,deviceToken] Here,Customers.id is a foreign key of Users.Id.
The following is the mutation-
MyMutation {
insert_Users(objects: [{isVerified: false, name: "+9100000000", type: "customer",
Customers: {data: {deviceToken: "TestToken001"}}}],
on_conflict: {
constraint: Users_name_key,
update_columns: [isVerified]
}) {
affected_rows
returning {
Customers{
deviceToken
}
}
}
} ```
//But when I run this, I get the exception
{
"errors": [
{
"extensions": {
"path": "$.selectionSet.insert_Users.args.objects[0].Customers.data",
"code": "constraint-violation"
},
"message": "Uniqueness violation. duplicate key value violates unique constraint \"Customers_pkey\""
}
]
}
This seems to be because I am not setting conflict constraint on the nested Customers Object. How do I add the conflict constraint for a nested object?
You need to add the constraint object inside the nested data as well.
Something like:
MyMutation {
insert_Users(objects: [{isVerified: false, name: "+9100000000", type: "customer",
Customers: {
data: {deviceToken: "TestToken001"},
on_conflict: {
constraint: Customers_pkey,
update_columns: [deviceToken]
}
}}],
on_conflict: {
constraint: Users_name_key,
update_columns: [isVerified]
}) {
affected_rows
returning {
Customers{
deviceToken
}
}
}
}

Partial Update Mutations(GraphQL)

How can I be able to update a node with only one field change and leave the rest of the fields alone?
My User Type
type User {
id: ID!
user_id: String!
username: String!
email: String!
role: Role!
isVerified: Boolean!
}
My Input Types
input UserUpdateInput {
user_id: String
username: String
email: String
password: String
role: Role
isVerified: Boolean
}
input UserWhereUniqueInput {
id: ID
user_id: String
email: String
}
My Mutation type
type Mutation {
updateUser(data: UserUpdateInput!, where: UserWhereUniqueInput): User
}
My Resolver
function updateUser(root, args, context, info){
return context.db.mutation.updateUser({
data: args.data,
where: {
id: args.where.id
}
}, info)
}
This is the request am sending on the GraphQL playground
mutation{
updateUser(
data: {
isVerified: true
}
where:{
user_id : "afc485b"
}
)
{
isVerified
}
}
This is the error am getting
{
"errors": [
{
"message": "Cannot read property 'mutation' of undefined",
"locations": [
{
"line": 2,
"column": 3
}
],
"path": [
"updateUser"
],
"extensions": {
"code": "INTERNAL_SERVER_ERROR",
"exception": {
"stacktrace": [
"TypeError: Cannot read property 'mutation' of undefined"
Someone help me. What am I missing?
After updating my server as suggested by Daniel Rearden on the answer section, am getting a new error
{
"message": "Cannot read property 'updateUser' of undefined",
"locations": [
{
"line": 2,
"column": 3
}
],
"path": [
"updateUser"
],
"extensions": {
"code": "INTERNAL_SERVER_ERROR",
"exception": {
"stacktrace": [
"TypeError: Cannot read property 'updateUser' of undefined"
The error is the result of not correctly adding the db property to you context. Assuming you're still using version 1, your code should look something like this:
const { prisma } = require('./generated/prisma-client')
const server = new ApolloServer({
...
context: {
db: prisma,
},
})
First thing I notice is you GQL query is not correct.
Yours:
mutation{
updateUser(
data: {
isVerified: true
}
where:{
user_id : "afc485b"
}
)
{
isVerified
}
}
after the word "mutation" you set a name to the call, i.e.
"UpdateUser" but can literally be anything. for each part of the
where clause you need to make the check value an object, i.e.
where: { myProperty: {eq: "some value"}}
So your query should be more like this:
mutation UpdateUser {
updateUser(
data: {isVerified: true}
where:{user_id : {eq: "afc485b"}}
)
{
isVerified
}
}
Hope that helps a little... I didn't fully read the rest but thought this would help with the initial error you were getting.

How can i get avg,sum of sub attribute?

I have problem with writing average query!
In my Rethink db, i have some documents in one table like this:
document1:
{
a:{
last:3
},
b:{
last:4
},
c:{
last:6
},
}
document2:
{
a:{
last:7
},
b:{
last:9
},
c:{
last:2
},
}
document3:
{
a:{
last:5
},
b:{
last:8
},
c:{
last:4
},
}
I want to get average of last attribute in every object like this:
{
sum_a_last:15,
sum_b_last:21,
sum_c_last:12,
avg_a_last:5,
avg_b_last:7,
avg_c_last:4
}
What is the query to return this result?
I believe what you're looking for is
r.db('dbName').table('tableName').avg((doc) => doc('a')('last'));
If you're trying to dynamically look for the last member for all objects in a doc there will obviously be more work.
https://rethinkdb.com/api/javascript/avg/

GraphQL - how to filter a hierarchy? ("customers who ordered gizmos last month")

Let's assume a type hierarchy of Customer -(hasMany)-> Orders -(hasMany)-> OrderLines
Something like this:
Customer {
Name
Orders [
{
OrderId
Date
OrderLines [
{
ItemCount
ItemName
}
]
}
]
}
I want to query for this whole tree, and filter on properties at any level in the tree.
For instance: Get all customers who ordered 'gizmos'.
This is what I tried: at each level of the hierarchy, I specify optional arguments that would filter based on the properties available at that level:
Customer (Name) {
Name
Orders (OrderId, Date) [
{
OrderId
Date
OrderLines (ItemCount, ItemName) [
{
ItemCount
ItemName
}
]
}
]
}
GraphQL needs me to define how to resolve each type in the hierarchy, so when resolving, I filter based on the arguments in the query.
But what if I only specify a filter at a deep level? e.g. ItemName : 'gizmo'
Assuming there's only one order line in the system containing a gizmo, I would expect to get a response like this:
[{
Name: "cust12",
Orders [{
OrderId: "ade32f",
OrderLines: [{
ItemCount: 50000, //customer really likes gizmos
ItemName: "gizmo"
}]
}]
}]
But what I actually get is all customers (no filter there), all their orders (no filter there) and all order items, mostly empty (the items inside are filtered).
[{
Name: "cust12",
Orders [
{
OrderId: "aaaaaa",
OrderLines: [ ]
},
{
OrderId: "ade32f",
OrderLines: [{
ItemCount: 50000,
ItemName: "gizmo"
}]
},
{
OrderId: "bbbbbb",
OrderLines: [ ]
},
{
OrderId: "cccccc",
OrderLines: [ ]
}
]
},
{
Name: "cust345",
Orders [
{
OrderId: "eeeeee",
OrderLines: [ ]
},
{
OrderId: "ffffff",
OrderLines: [ ]
}
]
}]
GraphQL calls the resolvers top-down:
- get all (filtered) clients
- for each of these get all (filtered) orders
- for each of those get all (filtered) order lines
Because of the top-down nature of calling the resolvers, I get a lot more data than I bargained for.
How should I approach this?
Relation filters
This is actually a more complex topic than it first seems. The problem is that your current filter condition expresses
get all customers, but only include items named 'gizmo'
but what you really want is
get all customers that are related to at least one item named 'gizmo'
get all customers that are related to at least one item named 'gizmo'
An elegant solution for this problem is the addition of relation filters to the schema. In your case, it could look like this:
query {
Customer(filter: {
orders_some: {
orderLines_some: {
item: {
itemName: "gizmo"
}
}
}
}) {
Name
Orders {
OrderId
Date
OrderLines {
ItemCount
ItemName
}
}
}
}
Using
orders_some: {
orderLines_some: {
item: {
itemName: "gizmo"
}
}
}
we only fetch customers that are indirectly related to an item named 'gizmo', exactly what we wanted.
Two more examples:
get all customers that are not related to any item named 'gizmo'
query {
Customer(filter: {
orders_none: {
orderLines_some: {
item: {
itemName: "gizmo"
}
}
}
}) {
Name
Orders {
OrderId
Date
OrderLines {
ItemCount
ItemName
}
}
}
}
get all customers where all their orders contain some order line with an item named 'gizmo'
query {
Customer(filter: {
orders_every: {
orderLines_some: {
item: {
itemName: "gizmo"
}
}
}
}) {
Name
Orders {
OrderId
Date
OrderLines {
ItemCount
ItemName
}
}
}
}
The every, some and none relation filters are an essential part of the Graphcool APIs - you can read more here.

Resources