Filter Apollo connection with nested variables - graphql

I'd like to specify a nested argument as the filter for my #connection directive. Is it possible to do something like this:
input CommentsInput {
postId: String!
cursors: CommentsCursorInput
}
query getComments($input: CommentInput) {
getComments(input: $input)
#connection(key: "comments", filter: ["input.postId"]) {
comments {
id
commentBody
createdDate
liked
likeCount
}
}
}
postId is passed in input. This is a contrived example and I know I can structure this differently, but my main questions is can I use nested arguments as a filter, or can I only use top-level arguments in that filter array? I didn't see any discussion of this in the docs.

Related

Sorting results in AWS Amplify GraphQL without filtering

Provided a very simple model in graphql.schema, how would I perform a simple sort query?
type Todo #model
id: ID!
text: String!
}
Which generates the following in queries.js.
export const listTodos = /* GraphQL */ `
query ListTodos(
$filter: ModelTodoFilterInput
$limit: Int
$nextToken: String
) {
listTodos(filter: $filter, limit: $limit, nextToken: $nextToken) {
items {
id
text
}
nextToken
}
}
`;
I have found multiple sources pointing me in the direction of the #key directive. This similar question addresses that approach (GraphQL with AWS Amplify - how to enable sorting on query).
While that may seem promising and successfully generates new queries I can use, all the approaches I have tried require that I filter the data before sorting it. All I want to do is sort my todo results on a given column name, with a given sort direction (ASC/DESC).
This is how I would perform a simple (unsorted) query:
const todos = await API.graphql(graphqlOperation(listTodos));
I would be looking to do something along the lines of:
const todos = await API.graphql(graphqlOperation(listTodos, {sortField: "text", sortDirection: "ASC"} )).
Decorate your model with the #searchable directive, like so:
type Todo #model #searchable
{
id: ID!
text: String!
}
After that, you can query your data with sorting capabilities like below:
import { searchToDos } from '../graphql/queries';
import { API, graphqlOperation } from 'aws-amplify';
const toDoData = await API.graphql(graphqlOperation(searchToDos, {
sort: {
direction: 'asc',
field: 'text'
}
}));
console.log(toDoData.data.searchToDos.items);
For more information, see
https://github.com/aws-amplify/amplify-cli/issues/1851#issuecomment-545245633
https://docs.amplify.aws/cli/graphql-transformer/directives#searchable
Declaring #searchable incurs pointless extra server cost if all you need is straight forward sorting. It spins up an EBS and an OpenSearch that will be about $20 a month minumum.
Instead you need to use the #index directive.
As per the documentation here: https://docs.amplify.aws/guides/api-graphql/query-with-sorting/q/platform/js/
In your model, add the #index directive to one of the fields with a few parameters:
type Todo #model {
id: ID!
title: String!
type: String! #index(name: "todosByDate", queryField: "todosByDate", sortKeyFields: ["createdAt"])
createdAt: String!
}
By declaring the queryField and the sortKeyField you will now have a new query available to once you push your amplify config:
query todosByDate {
todosByDate(
type: "Todo"
sortDirection: ASC
) {
items {
id
title
createdAt
}
}
}
The field you declare this directive on can not be empty (notice the ! after the field name)
This is a much better way of doing it as opposed to #searchable, which is massively overkill.
I've accepted MTran's answer because it feels to me it is the nearest thing to an actual solution, but I've also decided to actually opt for a workaround. This way, I avoid adding a dependency to ElasticSearch.
I ended up adding a field to my schema and every single entry has the same value for that field. That way, I can filter by that value and still have the entire table of values, that I can then sort against.

how to match queries with apollo's refetchQuery

My fundimental question is do the variables for queries need to be exact for refetchQueries to work. Or can you give it a subset of variables and it will match similar queries.
Consider the following ....
<Query<NotesQuery, NotesQueryVariables>
query={notesQuery}
variables={{
input: {
notebookId: notebookContext.id,
first: 20
}
}}
>
</Query>
and the following mutation:
client
.mutate<NoteCreateOrUpdateMutation, NoteCreateOrUpdateMutationVariables>({
mutation: noteCreateOrUpdateMutation,
variables: {
input: {
noteId: note ? note.id : undefined,
subjectIds: noteSubjects,
notebookId: notebookContext.id,
authorId: userContext.id,
content: noteContent,
context: noteCaption,
}
},
refetchQueries: [
{
query: notesQuery,
variables: { input: { notebookId: notebookContext.id } } as NotesQueryVariables
}
]
})
when I do that mutation it is NOT refetching the note query with the pagination
If I add the first: 20 parameter -- it works.
I would like it to clear all noteQueries that match with the given parameters. Is that possible?
I believe you'll be wanting to add #connection directives to your gql definitions of notesQuery and measurementsQuery. You didn't post those, so unfortunately I can't show you exactly what that would look like for your use case.
Anyway, the #connection directive will allow Apollo to match on notebookId for example, while ignoring the value of first.
Unfortunately, you've bundled all your input into the object input, and I don't know how you would select just notebookId with the filter. Assuming that your gql definition looks something like this for notesQuery:
const notesQuery = gql`
query notes($input: InputType!) {
notes(input: $input) #connection(key: "notes", filter: ["input['notebookId']"]) {
id
...
}
}
`;
^^^ Unfortunately, that won't work because of the way that apollo-utilities/lib/storeUtils.js -> getStoreKeyName() function works. It'll just ignore the above attempt to get better resolution than an arg name, i.e. can't go beyond input. Any string in the filter array that doesn't match an arg name is silently ignored.
Looks like you'll have to modify your schema.
More info at: https://www.apollographql.com/docs/react/features/pagination.html#connection-directive

Validation error of type UndefinedFragment: Undefined fragment

I've a graphql-jave v8.0 app running on Spring boot v1.5.10, and I'm trying to utilize 'fragment' feature of GraphQL to fetch limited number of fields with the following schema type definition:
type School {
id: ID
name: String
address: String
age: String
jobTitle: String
...
}
fragment UserFields on School {
age
jobTitle
}
type Query {
user (id: String!): School!
}
schema {
query: Query
}
When I execute this query:
{
user (id: "123")
{
... UserFields
}
}
The expected result should be:
{
"user": {
"age": "12",
"jobTitle": "student"
}
}
However, It results in the following error
"message": "Validation error of type UndefinedFragment: Undefined fragment
UserFields # 'user'",
Off course I can do this with explicitly passing the field name in the query but for the sake of example, I'm interested in utilizing fragment feature of GraphQL.
Any idea what I'm doing wrong please?
Fragments aren't defined in the schema, they're something for letting you create abstractions whilst building complex queries -- the main purpose is to allow you to avoid repetition when querying the same type in multiple parts of your query.
As Andrew said, and as the official docs exemplify, fragments are not defined in the schema but (ad hoc) in the query:
{
user (id: "123") {
... UserFields
}
}
fragment UserFields on School {
age
jobTitle
}
Unconditional fragments (like the one here) are used to avoid repetition. Imagine having multiple places where you want to select age and jobTitle in the same operation.
Conditional fragments, on the other hand, are used to make a conditional selection depending on the concrete interface implementation or union subtype.

GraphQL non nested relation

I'm trying to have a representation of nodes on GraphQL more akin to what jsonapi would be like http://jsonapi.org/
What I mean is if we take one of the examples on GraphQL
{
hero {
name
# Queries can have comments!
friends {
name
}
}
}
Have a representation that would be more along these lines
{
hero {
name
# Queries can have comments!
friends {
id
}
},
friends {
id, name
}
}
Is that at all possible in GraphQL
Thanks
It is possible, and there's nothing wrong with having a friends field. In GraphQL terms you can have the following part of the schema:
type User {
id: ID
name: String
firends: [User]
}
type RootQuery {
hero: User
friends(forUserId: ID!): [User]
}
And then you can query this as you like – you can ask for friends separately:
{
friends(forUserId: "12") {
id, name
}
}
But the whole idea of GraphQL is that you don't have to do multiple queries to get the information you need. If you just need a list of users – that's a reasonable query, that most people have (with arguments for pagination and so on). With that said, there's no reason to fetch a list of IDs and to send another fetch query for the data right after that.

GraphQL arguments to connection sub results

I'm need help passing arguments to collections/connections/arrays in GraphQL syntax.
I'm just learning it, playing with the SWAPI at http://graphql.org/swapi-graphql/
I can pass an id argument to a single type, like this:
query getANewHope {
film(id: "ZmlsbXM6MQ==") {
id
title
}
}
But I don't know how to query the results of a collection/connection
query starships {
allStarships(id: "c3RhcnNoaXBzOjI=") { # this doesn't work
edges {
node(id: "c3RhcnNoaXBzOjI=") { # ...nor this.
id
}
}
}
}
I want to query collections because, I'd like to connect the two ideas like "All Starfighter type ships in A New Hope"?
query filmStarships {
film(id: "ZmlsbXM6MQ==") {
title
starshipConnection { #How to limit this? I can't use (starshipClass: "Starfighter") here...
edges {
node { # ...nor here..
starshipClass # ...nor here.
}
}
}
}
}
query starships2 {
starship (id: "c3RhcnNoaXBzOjI=") { # This doesn't work either
id # even without an arugment abovce, it says "Unknown argument \"id\" on field \"node\" of type \"StarshipsEdge\"."
}
}
Arguments like you're asking for have to be implemented in the schema. The SWAPI does does not expose an argument to filter by starshipClass. Click Docs on the top right of the GraphiQL window to explore the provided schema.
If you are implementing your own schema it would be very easy to add a filter on starshipClass in the resolvers.

Resources