How can I create a relationship between `json` column and a `int` (id) column in Hasura + Postgres? - graphql

I have 2 tables users and post
Table users has columns id and post, column contains an array of the form [1, 2, 3, 4, 5] - where 1, 2, 3, 4, 5 is id in table post
In the table posts the following columns id and text
Table users:
https://i.stack.imgur.com/ywdS7.png
Table posts:
https://i.stack.imgur.com/IBdpb.png
in hasura made an array relation
https://i.stack.imgur.com/311sd.png
Next I made the following request
{
users_test {
postz {
id
}
}
}
I would like to receive such data in response:
postz: [
   {
     text: 'qwe'
   },
   {
     text: 'sdf'
   }
]
But with such a request, I get a trace. error:
{
"errors": [
{
"extensions": {
"internal": {
"statement": "SELECT coalesce(json_agg(\"root\" ), '[]' ) AS \"root\" FROM (SELECT row_to_json((SELECT \"_5_e\" FROM (SELECT \"_4_root.ar.root.postz\".\"postz\" AS \"postz\" ) AS \"_5_e\" ) ) AS \"root\" FROM (SELECT * FROM \"public\".\"users_test\" WHERE ('true') ) AS \"_0_root.base\" LEFT OUTER JOIN LATERAL (SELECT coalesce(json_agg(\"postz\" ), '[]' ) AS \"postz\" FROM (SELECT row_to_json((SELECT \"_2_e\" FROM (SELECT \"_1_root.ar.root.postz.base\".\"id\" AS \"id\" ) AS \"_2_e\" ) ) AS \"postz\" FROM (SELECT * FROM \"public\".\"posts\" WHERE ((\"_0_root.base\".\"post\") = (\"id\")) ) AS \"_1_root.ar.root.postz.base\" ) AS \"_3_root.ar.root.postz\" ) AS \"_4_root.ar.root.postz\" ON ('true') ) AS \"_6_root\" ",
"prepared": true,
"error": {
"exec_status": "FatalError",
"hint": "No operator matches the given name and argument type(s). You might need to add explicit type casts.",
"message": "operator does not exist: json = integer",
"status_code": "42883",
"description": null
},
"arguments": [
"(Oid 114,Just (\"{\\\"x-hasura-role\\\":\\\"admin\\\"}\",Binary))"
]
},
"path": "$",
"code": "unexpected"
},
"message": "postgres query error"
}
]
}
What am I doing wrong and how can I fix it?

A few suggestions:
There are some typos in your query, as far as I can tell. Try:
{
users {
id
posts {
text
}
}
}
You don't need the post column on the users table. You just need a user_id column on the posts table, and a foreign key constraint from the posts table to the users table using the user_id and id columns of the tables respectively. Check out the docs here:
https://docs.hasura.io/1.0/graphql/manual/schema/relationships/create.html#step-3-create-an-array-relationship
https://docs.hasura.io/1.0/graphql/manual/schema/relationships/database-modelling/one-to-many.html
If you have to have the post array column for some reason, you can use computed fields to create a "relationship" between a json array and another table’s id.
https://docs.hasura.io/1.0/graphql/manual/schema/computed-fields.html#table-computed-fields
Your function would:
Take in the json array column
Extract the id's
Return select * from table where id in id's
Example:
https://jsonb-relationships-hasura.herokuapp.com/console/api-explorer
Computed field definition at: https://jsonb-relationships-hasura.herokuapp.com/console/data/schema/public/tables/authors/modify
Run these queries:
# Get list of articles for each author
query {
authors {
id
name
articles
}
}
# Get actual articles for each author
query {
authors {
id
name
owned_articles {
id
title
}
}
}

Related

How do I loop over an input array using a UDF in FaunaDB?

I have a question about mapping an array of ID's (inputdata) and returning all related documents to those ID's. I have a UDF set up to retrieve the documents for a single ID and was hoping some tweaking would make that work. I can't seem to figure out how to map over the inputdata and create a variable (data:) to store the new array of documents. Any help is appreciated. Here is the single entry UDF which works:
Query(
Lambda(
["inputdata"],
Let(
{
data: Map(
Paginate(
Match(
Index("certificate_by_dealer"),
Ref(Collection("Dealers"), Select("dealer", Var("inputdata")))
)
),
Lambda(["ref"], Get(Var("ref")))
)
},
Select(["data"], Var("data"))
)
)
)
Is there a simple...or any solution to make this work for an array of ID's as inputdata?
Call function is:
Call("user_dealers_all_certificates", {
ids: [301393590798516736, 301393590798516749]
}
Unfortunately I get no results. (Adding quotes solved the issue)
Here is implementing the suggested UDF:
Query(
Lambda(
["inputdata"],
Let(
{ dataIds: Select("ids", Var("inputdata")) },
Union(
Map(
Var("dataIds"),
Lambda(
["id"],
Select(
["data"],
Paginate(
Match(
Index("certificate_by_dealer"),
Ref(Collection("Dealers"), Var("id"))
)
)
)
)
)
)
)
)
)
Adding quotes created a proper response:
Call("user_dealers_all_certificates", {ids: ["302122229239382536", "301394099049595400"]})
[
Ref(Collection("Certificate"), "302122488174739977"),
Ref(Collection("Certificate"), "302120872550859273")
]
However the GraphQL query returns bad data:
query {
allUserDealersCertificate(data: {ids: ["302122229239382536", "301394099049595400"]}){
data {
_id
}
}
}
response:
{
"errors": [
{
"message": "Lambda expects an array with 1 elements. Array contains 4.",
"extensions": {
"code": "invalid argument"
}
}
]
}
GraphQL error without paginated: true in schema:
{
"data": {
"allUserDealersCertificate": [
null,
null
]
},
"errors": [
{
"message": "Cannot return null for non-nullable type (line 3, column 5):\n _id\n ^",
"path": [
"allUserDealersCertificate",
0,
"_id"
],
"locations": [
{
"line": 3,
"column": 5
}
]
},
{
"message": "Cannot return null for non-nullable type (line 3, column 5):\n _id\n ^",
"path": [
"allUserDealersCertificate",
1,
"_id"
],
"locations": [
{
"line": 3,
"column": 5
}
]
}
]
}
Based on the query you provided, I feel the need to point out that the Match function performs exact matches. It does not (and cannot) unroll a structured array for you. Neither can the Ref function.
You'd need to call Map on the inputdata, and get results for each id. Then you can Union those results together into a single list.
I don't know the exact shape of the data that you're dealing with, so here's a query that works with the pre-populated data available in the Dashboard:
Let(
{
// the pre-populated data has 3 stores, with ids 301, 302, and 303
// here, we want the products in stores 301 and 302
ids: [ 301, 302 ]
},
// this is where we combine all of the results
Union(
Map(
// here is where we loop over the list of ids
Var("ids"),
Lambda(
// for each id, run this function's expression
"id",
Select(
// each Paginate call returns a page of results with its own data
// field, so we have to select those out
"data",
Paginate(
Match(
Index("products_by_store"),
// here we compose a reference to a specific store, using the
// Lambda function's current id
Ref(Collection("stores"), Var("id"))
)
)
)
)
)
)
)
Npte that I've used Let to simulate passing an array to the body of a UDF. When you run this query, the result should be:
[
["avocados", "Conventional Hass, 4ct bag", 3.99],
["cilantro", "Organic, 1 bunch", 1.49],
["limes", "Conventional, 1 ct", 0.35],
["limes", "Organic, 16 oz bag", 3.49],
["cups", "Translucent 9 Oz, 100 ct", 6.98],
["pinata", "Giant Taco Pinata", 23.99],
["pinata", "Original Classic Donkey Pinata", 24.99]
]

Strapi Graphql deep sort

I would like to sort with strapi's graphql
I can do that by
{
users (sort: "id:asc") {
id
username
email
address {
id
city
state
street
}
}
}
I can sort by the first level of columns but how do I go deeper, for example address.city, because
". / :" don't work, graphql playground returns:
"errors": [
{
"message": "select \"users\".* from \"users\" order by \"address\".\"city\" desc limit $1 - missing FROM-clause entry for table \"address\"",

How to insert an object and related array objects in one mutation

I have two tables reels and reel_variations, reels can have many reel_variations and reel_variations belong to one reel. I have read the Hasura docs and haven't been able to figure out how to insert a reel and a couple of reel variations in a single mutation.
mutation insertReelsAndVariations($objects: [reels_insert_input!]! = {}) {
insert_reels(objects: $objects) {
affected_rows
returning {
description
id
name
variations {
ball_bearings
braid_capacity
created_at
deleted_at
gear_ratio
max_drag
line_capacity
id
model_number
recovery
reel_id
retrieve
}
}
}
}
Variables
{
"objects": {
"name": "nice reel",
"description": "wicked nice reel",
"variations": {
"data": {
"ball_bearings": "djjdfkjdkjfdjkfjkd",
"braid_capacity": "dkfjdkfjkdf",
"gear_ratio": "20:1",
"max_drag": "20lbs",
"line_capacity": "400yrds",
"model_number": "jfdkjfkjdkfjkdjfjdf",
"recovery": "30 per turn"
}
}
}
}
Errors
{
"errors": [
{
"extensions": {
"path": "$.selectionSet.insert_reels.args.objects[0].variations.data",
"code": "constraint-violation"
},
"message": "Not-NULL violation. null value in column \"reel_id\" violates not-null constraint"
}
]
}
That's because the reel_id column is not set as a foreign key col referencing the reels table. So u can simply:
Make the reel_id col a foreign key which points to the id column of reels table!

How to upsert nested tables values in Hasura GraphQL?

wanted to ask if it is possible to upsert nested objects? for example, if i have a 'Users' table and a 'Students' table, and I'm inserting a new User(with a taken id), i want to update all fields (using on_conflict and update_columns) including the fields in the 'Students' table.
Basically replace all user's fields except the primary key.
mutation($UsersData: [core_users_insert_input!]!) {
insert_core_users(
objects: $UsersData
on_conflict: {
constraint: core_users_id_unique
update_columns: [first_name, last_name, gender]
}
) {
affected_rows
}
}
The update_column array should include fields from the 'Students' table but i can't figure it out.
It is possible, relevant documentation is here: https://hasura.io/docs/1.0/graphql/manual/mutations/upsert.html#upsert-in-nested-mutations
It is possible to use on_conflict key on any level (top, or nested) where you want to resolve updating an existing record.
mutation upsert_author_article {
insert_author(
objects: [
{
name: "John",
articles: {
data: [
{
title: "Article 3",
content: "Article 3 content"
}
],
on_conflict: {
constraint: article_title_key,
update_columns: [content]
}
}
}
]
) {
affected_rows
}
}

how to get id of last inserted row in RDS Aurora in GraphQL mutation

I've a table with (id, price). id is autoincremented. I want to return the id and full data of last inserted row.
Resolver:
{
"version": "2018-05-29",
"statements": [
"insert into notes (price) VALUES (100)",
"select * from notes WHERE id = $id"
]
}
How can I find the id of last inserted row. Second query of course will not work here as id is not known.
Does SELECT LAST_INSERT_ID(); work well in serverless context? How to store that variable and run third query?
I was running into the same issue. So far, this seems to work for us:
{
"version": "2018-05-29",
"statements": [
"INSERT INTO customer_addresses (`id`, `customerid`, `firstname`, `lastname`) VALUES (NULL, :CUSTOMERID, :FIRSTNAME, :LASTNAME)",
"SELECT * FROM customer_addresses WHERE id = LAST_INSERT_ID()"
],
"variableMap": {
":CUSTOMERID": $context.args.customerid,
":FIRSTNAME": "$context.args.input.firstname",
":LASTNAME": "$context.args.input.lastname"
}
}

Resources