Size for FaunaDB GraphQL nested query - graphql

How do I set the size for a nested query?
For instance, the following will only return max. 50 users for each group.
const query = gql`
query GetGroups {
groups(_size: 100){
data{
_id
name
users(_size: 500){
data{
_id
name
}
}
}
}
}
`

It looks like you wrote your query correctly. You will get a GraphQL error if you provide the _size argument where it is not allowed.
If there are only 50 results showing up when providing a size greater than 50, then there most likely exactly 50 matches.
The GraphQL query you shared will be compiled to FQL that works roughly like the following, and return the result of paginating the nested relationship with the size that you provide.
Let(
v0: Paginate(Match(Index("groups")), { size: 100 }),
{
groups: {
data: Map(
Var("v0"),
Lambda(
"ref",
Let(
{
v1: Paginate(
Match(Index("group_users_by_group"), Var("ref")),
{ size: 500 }
)
},
{
users: Map(Var("v1"), /* ... select fields for users */),
/* ... select other fields for group */
}
)
)
)
}
}
)
If you are still concerned that there is an issue, please contact Fauna support at support.fauna.com or by emailing support#fauna.com, using the email you used to sign up. Then we can take a closer look at your account.

Related

How can I response to client based on what fields they are querying in graphql?

I am using AWS appsync for graphql server and have schema like:
type Order {
id: ID!
price: Int
refundAmount: Int
period: String!
}
query orders (userId: ID!) [Order]
It is to support query orders based on user id. It responses an array of orders for different time period. The response could be:
[{
id: xxx
price: 100
refundAmount: 10
period: '2021-01-01'
},{
id: xxx
price: 200
refundAmount: 0
period: '2021-01-03'
},
...
]
If the price and refundAmount in the period is 0, I won't response empty element in the array. In the above example, there is price and refundAmount on 2021-01-02, so there is no such element in the array.
My problem is how can I response the data based on what frontend queries? If customer only query refundAmount field in the response, I don't want to response 2021-01-03 period. How do I know what fields frontend wants to show in the response?
e.g.
If clients send this query:
query {
orders (userId: "someUserId") {
refundAmount
}
}
I will response below data but I don't want the second one to be there since the value is 0.
[{
id: xxx
refundAmount: 10
period: '2021-01-01'
},{
id: xxx
refundAmount: 0
period: '2021-01-03'
}
]
My problem is how can I response the data based on what frontend
queries?
GraphQL will do that out of the box for you provided you have the resolvers for the fields in the query. Look at appropriate resolver based on your underlying data source.
How do I know what fields frontend wants to show in the response?
This is what the frontend decides, it can send a different query based on the fields it is interested. A few examples below.
If the frontend is interested in only one field i.e. refundAmount, then it would send a query something like this.
query {
orders (userId: "someUserId") {
refundAmount
}
}
If it is interested in more than 1 field say price and refundAmount then the query would be something like this
query {
orders (userId: "someUserId") {
price,
refundAmount
}
}
Update: Filter response:
Now based on the updated question, you need to enhance your resolver to do this additional filtering.
The resolver can do this filtering always (Kind of hard coded like refundAmount > 0 )
Support a filter criteria in the query model query orders (userId: ID!, OrderFilterInput) [Order] and the define the criteria based on which you want to filter. Then support those filter criteria in the resolvers to query the underlying data source. Also take the filter criteria from the client.
Look at the ModelPostFilterInput generated model on this example.
Edit 2: Adds changed Schema for a filter
Let's say you change your Schema to support filtering and there is no additional VTL request/response mappers and you directly talk to a Lambda.
So this is how the Schema would look like (of course you would have your mutations and subscriptions and are omitted here.)
input IntFilterInput { # This is all the kind of filtering you want to support for Int data types
ne: Int
eq: Int
le: Int
lt: Int
ge: Int
gt: Int
}
type Order {
id: ID!
price: Int
refundAmount: Int
period: String!
}
input OrderFilterInput { # This only supports filter by refundAmount. You can add more filters if you need them.
refundAmount: IntFilterInput
}
type Query {
orders(userId: ID!, filter: OrderFilterInput): [Order] # Here you add an optional filter input
}
schema {
query: Query
}
Let's say you attached the Lambda resolver at the Query orders.
In this case, the Lambda would need to return an array/list of Orders.
If you are further sending this query to some table/api, you need to understand the filter, and create an appropriate query or api call for the downstream system.
I showing a simple Lambda with hard coded response. If we bring in the Filter, this is what changes.
const getFilterFunction = (operator, key, value) => {
switch (operator) {
case "ne":
return x => x[key] != value
case "eq":
return x => x[key] == value
case "le":
return x => x[key] <= value
case "lt":
return x => x[key] < value
case "ge":
return x => x[key] >= value
case "gt":
return x => x[key] > value
default:
throw Error("Unsupported filter operation");
}
}
exports.handler = async(event) => {
let response = [{
"id": "xxx1",
"refundAmount": 10,
"period": '2021-01-01'
}, {
"id": "xxx2",
"refundAmount": 0,
"period": '2021-01-03'
}]
const filter = event.arguments.filter;
if (filter) { // If possible send the filter to your downstream system rather handling in the Lambda
if (filter.refundAmount) {
const refundAmountFilters = Object.keys(filter.refundAmount)
.map(operator => getFilterFunction(operator + "", "refundAmount", filter.refundAmount[operator]));
refundAmountFilters.forEach(filterFunction => { response = response.filter(filterFunction) });
}
}
return response; // You don't have to return individual fields the query asks for. It is taken care by AppSync. Just return a list of orders.
};
With the above in place, you can send various queries like
query MyQuery {
orders(userId: "1") { #without any filters
id
refundAmount
}
}
query MyQuery {
orders(userId: "1", filter: {refundAmount: {ne: 0}}) { # The filter you are interested
id
refundAmount
}
}
query MyQuery {
orders(userId: "1", filter: {refundAmount: {ne: 0, gt: 5}}) { # Mix and Match filters
id
refundAmount
}
}
You don't have to support all the operators for filtering and you can focus only on ne or != and further simplify things. Look at this blog for a more simple version where the filter operation is assumed.
Finally the other possibility to filter without modifying the Schema is to change your Lambda only to ensure it returns a filtered set of results either doing the filtering itself or sending an appropriate query/request to the underlying system to do the filtering.

prismic - How to qgl select based on length or NOT NULL

Since prismic has no concept of required fields, we might end up with null instead of data.
I am looking for a way to pull prismic data in gatsby app, filtering out unwanted entities.
the SQL idea
SELECT *
FROM allPrismicAnnouncement
WHERE quote IS NOT NULL;
actual gql i use
export const query = graphql`
query AnnouncementCardQuery {
allPrismicAnnouncement(sort: {order: DESC, fields: data___date}) {
edges {
node {
uid
id
data {
quote {
text
}
subtitle1 {
text
}
}
}
}
}
}
`
how to use filter, include or similar approach to filter out filtering out articles with no quote.
Option 2 - ignore all quotes with length < 100 chars
This question has answers on prismic community forum
Yes, you can use the ne operator; to filter the response with Prismic fields.
I'm using allPrismicBlogpost and have a similar query and problem. I think something like this would work for you (changed sort to first_publication_date).
export const query = graphql`
query AnnouncementCardQuery {
allPrismicAnnouncement(filter: {data: {quote: {text: {ne: null}}}}, sort: { order: DESC, fields: first_publication_date}) {
edges {
node {
uid
id
data {
quote {
text
}
subtitle1 {
text
}
}
}
}
}
}
`

Passing Variables into GraphQL Query in Gatsby

I want to limit the number of posts fetched on my index page. Currently, the number of pages is hard-coded into the GraphQL query.
query {
allMarkdownRemark(limit: 5, sort: { fields: [frontmatter___date], order: DESC }) {
totalCount
edges {
node {
...
}
}
}
}
I want to replace "5" with the value of a variable. String interpolation will not work with the graphql function, so I have to use another method.
Is there a way to achieve this and pass variables into a GraphQL query in GatsbyJS?
You can only pass variables to GraphQL query via context since string interpolation doesn't work in that way. In page query (rather than static queries) you can pass a variable using the context object as an argument of createPage API. So, you'll need to add this page creation to your gatsby-node.js and use something like:
const limit = 10;
page.forEach(({ node }, index) => {
createPage({
path: node.fields.slug,
component: path.resolve(`./src/pages/index.js`), // your index path
// values in the context object are passed in as variables to page queries
context: {
limit: limit,
},
})
})
Now, you have in your context object a limit value with all the required logic behind (now it's a simple number but you can add there some calculations). In your index.js:
query yourQuery($limit: String) {
allMarkdownRemark(limit: $limit, sort: { fields: [frontmatter___date], order: DESC }) {
totalCount
edges {
node {
...
}
}
}
}

Store error: the application attempted to write an object with no provided typename but the store already contains an object

After mutation when I am updating the cache, changes are reflected in UI but getting the below error
Invariant Violation: Store error: the application attempted to write an object with no provided typename but the store already contains an object with typename of ItemCodeConnection for the object of id $ROOT_QUERY.itemCodes({"filter":{"number":10000001}}). The selectionSet that was trying to be written is:
{"kind":"Field","name":{"kind":"Name","value":"itemCodes"},"arguments":[{"kind":"Argument","name":{"kind":"Name","value":"filter"},"value":{"kind":"Variable","name":{"kind":"Name","value":"filter"}}}],"directives":[],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"Field","name":{"kind":"Name","value":"itemCodes"},"arguments":[],"directives":[],"selectionSet":{"kind":"SelectionSet","selections":[{"kind":"FragmentSpread","name":{"kind":"Name","value":"itemCodeTile"},"directives":[]},{"kind":"Field","name":{"kind":"Name","value":"__typename"}}]}},{"kind":"Field","name":{"kind":"Name","value":"__typename"}}]}}
GraphQL query:
const CREATE_ITEM_CODE_SPEC = gql`
mutation createItemCodeSpec($input: createItemCodeSpecInput) {
createItemCodeSpecification(input: $input){
__typename
id
itemCode {
number
}
product
spec_class
grade
}
}
`
const GET_ITEM_CODE = gql`
query itemCode($filter: filterInput){
itemCodes(filter: $filter){
itemCodes {
number
type
description
group
item_code_spec {
id
itemCode {
number
}
product
spec_class
grade
}
created_on
created_by
changed_on
changed_by
}
}
}
`
Below is the mutation:
const [mutation, { data, loading, error}] = useMutation(
CREATE_ITEM_CODE_SPEC,
{
update(cache, { data: { createItemCodeSpecification } }){
const currentData = cache.readQuery({
query: GET_ITEM_CODE,
variables: { filter : {number:itemCode} }
})
cache.writeQuery({
query: GET_ITEM_CODE,
variables: { filter : {number:itemCode} },
data: {
...currentData,
itemCodes: {
itemCodes: currentData.itemCodes.itemCodes.map((itemCode, index) => {
return {
...itemCode,
item_code_spec: index === 0? [
...itemCode.item_code_spec,
createItemCodeSpecification
] : itemCode.item_code_spec
}
})
}
}
})
}
}
);
You simply need to add "id" for each subsection of your query. Adding "id" for "itemCodes" in your GET_ITEM_CODE query might solve your problem.
You have fields missing in your response mutation.
Basically, you should make your mutation results have all of the data necessary to update the queries previously fetched.
That’s also why is a best practice to use fragments to share fields among all queries and mutations that are related.
To make it work both query and mutation should have exactly the same fields.
Have a look here to see more in depth how cache updates work:
https://medium.com/free-code-camp/how-to-update-the-apollo-clients-cache-after-a-mutation-79a0df79b840

GraphQL disable filtering if filter variable is empty

I have a Gatsby GraphQL query for a list of posts ordered by date and filtered by category.
{
posts: allContentfulPost(
sort: {fields: [date], order: DESC},
filter: {category: {slug: {eq: $slug}}}
) {
edges {
node {
title {
title
}
date
}
}
}
}
Right now when $slug is the empty string "", I get
{
"data": {
"posts": null
}
}
Is there a way to get all posts instead?
You can use the regex filter to your advantage. If you pass an empty expression, then all posts will be returned because everything will match.
query Posts($slugRegex: String = "//"){
posts: allContentfulPost(
sort: {fields: [date], order: DESC},
filter: {category: {slug: {eq: $slugRegex}}}
) {
# Rest of the query.
}
}
By default, all posts will be returned (the $slugRegex is an empty regex if nothing was passed). When the $slugRegex becomes a meaningful expression, then only matching posts will show up.
As for passing the value, I'm assuming you're using gatsby-node.js to create pages. In that case, it's as simple as that:
// gatsby-node.js
exports.createPages = async ({ actions }) => {
const { createPage } = actions
// Create a page with only "some-slug" posts.
createPage({
// ...
context: {
slugRegex: "/some-slug/"
}
})
// Create a page with all posts.
createPage({
// ...
context: {
// Nothing here. Or at least no `slugRegex`.
}
})
}
It's not possible with this query, even #skip/#include directives won't help because you can't apply them on input fields.
I would suggest to either adjust the server side logic so that null in the 'eq' field will ignore this filter or either to edit the query being sent (less favorable imo).
It seems that the graphql schema that you work against lacks the filtering support you need..
If anyone requires a solution for other systems than Gatsby this can be accomplished using #skip and #include.
fragment EventSearchResult on EventsConnection {
edges {
cursor
node {
id
name
}
}
totalCount
}
query Events($organizationId: UUID!, $isSearch: Boolean!, $search: String!) {
events(condition: { organizationId: $organizationId }, first: 100)
#skip(if: $isSearch) {
...EventSearchResult
}
eventsSearch: events(
condition: { organizationId: $organizationId }
filter: { name: { likeInsensitive: $search } }
first: 100
) #include(if: $isSearch) {
...EventSearchResult
}
}
Then in your client code you would provide search and isSearch to the query and get your events like:
const events = data.eventsSearch || data.events

Resources