How do I loop over an input array using a UDF in FaunaDB? - user-defined-functions

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]
]

Related

Laravel 8 json colum where with array of object

I would like to perform a query in a table where the column is of json type and it contains array of objects, but only if a certain condition is met
This is my current code
$initial_results = DB::table('toys')->select('id','name')->where(['name' => 'sammy', 'email' => 'whateveremail']);
if($sk ==='yes') {
$results = $initial_results->>whereRaw('JSON_CONTAINS(`info`,\'{"sku":"B07V3SSLN11"}\')')
>whereRaw('JSON_CONTAINS(`info`,\'{"asin":"DTI-LALF3-EA18"}\')')
->get();
} else {
$results = $initial_results->get();
}
But I always get 0 result if the condition is met. In database, the info I want to query indeed exist. What is the proper way to query a json column which contains array of objects? See my example data
[
{
"sku": "DTI-LALF3-EA18",
"adId": 244077676726655,
"asin": "B07V3SSLN11",
"cost": 0,
},
{
"sku": "DTI-LALF3-EA18",
"adId": 242968940906362,
"asin": "B07V3SSLN11",
"cost": 10,
.........
................
I even tried
$initial_results = DB::table('toys')->select('id','name')->where(['name' => 'sammy', 'email' => 'whateveremail'])->->whereIn(DB::raw("JSON_EXTRACT(info, '$[*].asin')"),['B07V3SSLN11']);
Thanks in advance
You can query JSON columns by using the -> operator in your clause:
->where('info->asin', 'DTI-LALF3-EA18')
JSON Where Clauses Docs

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);

argument Where of type xxxxxx is required, but it was not provided

I have this strange issue . maybe i missed something
i have two tables , Vehicles and purchases
when i try to query the vehicles via
query {
vehicles{
id
}
}
It returns data normally 👍
{
"data": {
"vehicles": [
{
"id": 29
}
]
}
}
But with this query
query {
purchase{
id
}
}
I recieve this error
{
"error": {
"errors": [
{
"message": "Field \"purchase\" argument \"where\" of type \"purchaseWhereUniqueInput!\" is required, but it was not provided.",
"locations": [
{
"line": 2,
"column": 3
}
],
here is my code : -
export const PaymentQuery =extendType({
type :"Query",
definition(t) {
t.crud.purchase({filtering : true , pagination : true , ordering : true , })
}
})
export const VehicleQuery = extendType({
type : "Query",
definition(t) {
t.crud.vehicles({filtering : true , pagination : true , ordering : true , });
}
})
I realized that nexusjs use naming convention to determine if the query should return a list or not !
changing the table name from purchase to purchases solves the problem

How to handle graphql errors on Apollo server? [duplicate]

In an express-graphql app, I have a userLogin resolver like so:
const userLogin = async ({ id, password }), context, info) => {
if (!id) {
throw new Error('No id provided.')
}
if (!password) {
throw new Error('No password provided.')
}
// actual resolver logic here
// …
}
If the user doesn't provide an id AND a password, it will throw only one error.
{
"errors": [
{
"message": "No id provided.",
"locations": [
{
"line": 2,
"column": 3
}
],
"path": [
"userLogin"
]
}
],
"data": {
"userLogin": null
}
}
How is it possible to throw multiple errors in the errors response array?
There is no way to throw an array of errors in JavaScript or otherwise have a single resolver reject with more than one error. A GraphQL response includes an errors array and not just a single error object because the total response can include multiple errors when those errors originate from different fields. Consider this schema and resolvers:
type Query {
a: String
b: String
c: String
}
const resolvers = {
Query: {
a: () => { throw new Error('A rejected') },
b: () => { throw new Error('B rejected') },
c: () => 'Still works!',
},
}
If you query all three fields...
query {
a
b
c
}
Your data will look something like this:
{
"errors": [
{
"message": "A rejected",
"locations": [
{
"line": 2,
"column": 3
}
],
"path": [
"a"
]
},
{
"message": "B rejected",
"locations": [
{
"line": 3,
"column": 3
}
],
"path": [
"b"
]
}
],
"data": {
"a": null,
"b": null,
"c": "Still works!"
}
}
This is because GraphQL supports partial responses. However, keep in mind that this works because the fields are nullable. If they were non-null, those errors would bubble up to the closest nullable parent field.
Here are some alternative approaches:
You can utilize formatError to change how the errors returned by GraphQL are displayed to the client. That means you can include any sort of extra information with your errors, like an error code or multiple error messages. A simple example:
// The middleware
app.use('/graphql', graphqlExpress({
schema: schema,
formatError: (error) => ({
message: error.message,
path: error.path,
locations: error.locations,
errors: error.originalError.details
})
}))
// The error class
class CustomError extends Error {
constructor(detailsArray) {
this.message = String(details)
this.details = details
}
}
// The resolver
const userLogin = async ({ id, password }), context, info) => {
const errorDetails = []
if (!id) errorDetails.push('No id provided.')
if (!password) errorDetails.push('No password provided.')
if (errorDetails.length) throw new CustomError(errorDetails)
// actual resolver logic here
}
Your response then looks more like this:
{
"errors": [
{
"message": "[No id provided.,No password provided.]",
"locations": [
{
"line": 2,
"column": 3
}
],
"path": [
"userLogin"
]
"errors" [
"No id provided.",
"No password provided."
]
}
],
"data": {
"userLogin": null
}
}
That said, there's something a bit unsavory about returning user-facing error messages alongside GraphQL validation errors. Another approach that some APIs have taken is to include an errors field alongside the actual mutation response. For example:
type Mutation {
userLogin: UserLoginResponse
}
type UserLoginResponse {
response: User
errors: [String!]
}
You can also use unions to achieve a similar effect:
type Mutation {
userLogin: UserLoginResponse
}
type Errors {
errors: [String!]!
}
union UserLoginResponse = User | Errors

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

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
}
}
}

Resources