I have a node.js app that serves sqlite database data using sequelize via graphql.
The data types are following:
Student
id: ID!
name: String!
class: Class
Class
id: ID!
title: String!
floor: Int!
I want to get an array of classes with each class containing an array of corresponding students like
[
{
"id": 1,
"title" : "first",
"students" : [
{ "id": 1, "name": "John Doe" },
{ "id": 2, "name": "Mary Smith" }
]
]
Is there any way to do it despite of that kind of reverse linking?
If your database isn't large enough yet, you could always run a one-time script to link it the other way as well, with:
Class {
id: ID!
title: String!
floor: Int!
students: [Student]!
}
Otherwise, you would have to do a manual query for all students that are taking a certain class and join that data into your query for classes.
Related
I am new with Aws Amplify and GraphQL and I am trying to find the best way to handle the following:
I want to be able to edit my FAQ in the frontend. The frontend gets the data from the backend, i edit one field in the frontend and save it, it should be stored in the backend
I have created a simple amplify graphql schema:
type FAQ #model(timestamps: null) {
id: ID!
sectionTitle: String
questionAndAnswer: [QuestionAndAnswer!]!
}
type QuestionAndAnswer {
id: ID!
question: String
answer: String
}
Then I populated it so it contains the following data:
{
"data": {
"listFAQS": {
"items": [
{
"sectionTitle": "Title2",
"id": "22735682-2bab-4695-b93e-1e50642d4654",
"questionAndAnswer": [
{
"answer": "answer4",
"id": "4",
"question": "question4"
},
{
"answer": "answer5",
"id": "5",
"question": "question5"
},
{
"answer": "answer6",
"id": "6",
"question": "question6"
}
]
},
{
"sectionTitle": "Title1",
"id": "21be8234-69f2-47fe-b942-d3e655197c70",
"questionAndAnswer": [
{
"answer": "answer1",
"id": "1",
"question": "question1"
},
{
"answer": "answer2",
"id": "2",
"question": "question2"
},
{
"answer": "answer3",
"id": "3",
"question": "question3"
}
]
}
]
}
}
}
Is there an easy way I can only change for instance the question "question4" without having to send the whole FAQ "row"?
When I was experimenting with it it seemed that the whole array was reseted and only the new data was saved. Should both FAQ and QuestionAndAnswer be a #model each where I connect them with #connection and update them separately as needed?
The following seems to be an easy way, as you can update only "question4" if you need, by using its own unique ID with GraphQL mutation.
type FAQ #model(timestamps: null) {
id: ID!
sectionTitle: String
questionAndAnswer: [QuestionAndAnswer!]!
}
type QuestionAndAnswer #model {
id: ID!
question: Question
answer: Answer
}
type Question #model {
id: ID!
question: String
}
type Answer #model {
id: ID!
answer: String
}
I did not include #connections (they should be in QuestionAndAnswer model), but 'Yes' would be the answer to " Should both FAQ and QuestionAndAnswer be a #model each where I connect them with #connection and update them separately as needed?".
I am attempting to return a projection of a nested document and some properties from the root document also. I would like to do this within the query if at all possible.
For example, I have an index cities where and the initial dataset:
[
{
_id: "1",
city: "New York"
state:"NY"
users: [
{
firstName: "John",
lastname: "Peters",
birthYear: 1985
}, {
firstName: "Raul",
lastname: "Other",
birthYear: 1986
}, {
firstName: "Paul",
lastname: "Ray",
birthYear: 1997
}
]
},
{
_id: "2",
city: "Hackensack",
state: "NJ"
users: [
{
firstName: "Joe",
lastname: "Anders",
birthYear: 1988
}
]
},
{
_id: "3",
city: "Albany"
state:"NY"
users: [
{
firstName: "Zoy",
lastname: "Bat",
birthYear: 1984
}, {
firstName: "Ana",
lastname: "Lily",
birthYear: 1999
}
]
}
]
users is of type nested. (S/n: I think this is for the best currently because I would like to possibly filter on one of the properties, but I am willing to change this if need be.)
I would like to do a query for users in the state of NY ordered by birthYear, and what I would like Elasticsearch to return is something like:
[
{
city: "Albany"
firstName: "Zoy",
lastname: "Bat",
birthYear: 1984
}, {
city: "New York"
firstName: "John",
lastname: "Peters",
birthYear: 1985
}, {
city: "New York"
firstName: "Raul",
lastname: "Other",
birthYear: 1986
}, {
city: "New York"
firstName: "Paul",
lastname: "Ray",
birthYear: 1997
}, {
city: "Albany"
firstName: "Ana",
lastname: "Lily",
birthYear: 1999
}
]
This feels like something that should be possible during query time but I have not been able to find the appropriate functionality.
It is not possible to return what you expect using nested documents, as you cannot mix and match documents nested into different top-level documents and return them in the order you mention.
You should actually denormalize your data and promote users to primary entities. First, that would easily solve the query need you expressed and second I'm pretty confident this would also support your other needs.
So your documents should look like this instead:
{
id": 1,
firstName: "John",
lastname: "Peters",
birthYear: 1985,
city: "New York",
state:"NY"
}
{
id": 2,
firstName: "Raul",
lastname: "Other",
birthYear: 1986,
city: "New York",
state:"NY"
}
etc...
I'm using AWS appsync along with DynamoDB for my project, and I have the following schema:
type List {
id: String!
title: String!
items: [String!]! ## it's of String Set (SS) type on DynamoDB
}
type Item {
id: String!
name: String!
}
I want to get a specific list along with their items. the ids of these items are in the List object. e.g
e.g:
List
{
id: "list0001",
title: "My First list",
items: ["item_001", "item_002"]
}
Item
{
id: "item_001",
name: "Item 001"
}
I want to have the following result when querying list0001
{
id: "list0001",
title: "My First list",
items: [
{
id: "item_001",
name: "Item 001"
},
{
id: "item_002",
name: "Item 002"
}
]
}
I know that I can have the list id on Item type and then I use that id to fetch the items but I want to have it as described above by getting the items from the set of the string in List type. I want to know whether it's feasible. if so, what are the mapping templates for both queries.
N.B: I'm using serverless for my project with serverless-appsync-plugin plugin.
You could set this up with two tables, ListTable and ItemTable.
The ListTable would store the information about lists. An example entry would look like:
{
"id": "list_0000",
"title": "List0"
}
The ItemTable would be used to to relate Items to the List that they belong to. An example entry would look like:
{
"id": "item_0001",
"name": "item1",
"listId": "list_0000"
}
You would need to modify your schema as follows:
type List {
id: String!
title: String!
items: [Item!]! ## A List of Items
}
type Item {
id: String!
name: String!
}
type Query {
getList(listId: ID!): List
}
This setup would request setting up 2 resolvers, 1 on getList and 1 on the field items of the type List.
Your request mapping template for getList would look like:
{
"version": "2017-02-28",
"operation": "GetItem",
"key": {
"id": $util.dynamodb.toDynamoDBJson($ctx.args.listId),
}
}
The response mapping template would be:
$util.toJson($ctx.result)
Your request mapping template for items of the type List would look like:
{
"version" : "2018-05-29",
"operation" : "Query",
"query" : {
"expression": "listId = :listId",
"expressionValues" : {
":listId" : { "S": "$ctx.source.id" }
}
}
}
The response mapping template would be:
$util.toJson($ctx.result.items)
Running the query:
query {
getList(listId: "list_0000") {
id
title
items {
id
name
}
}
}
Would have a result like:
{
"data": {
"getList": {
"id": "list_0000",
"title": "List0",
"items": [
{
"id": "item_0001",
"name": "item1"
}
]
}
}
}
Using this schema definition:
schemas:
AllContacts:
type: array
items:
$ref: '#/definitions/ContactModel1'
example:
- id: 1
firstName: Sherlock
lastName: Holmes
- id: 2
firstName: John
lastName: Watson
I get this expected result:
[
{
"id": 1,
"firstName": "Sherlock",
"lastName": "Holmes"
},
{
"id": 2,
"firstName": "John",
"lastName": "Watson"
}
]
Now I'd like to reuse the Holmes example for both the single user (ContactModel1) and as part of an array of users (AllContacts). But if I use the referenced examples:
schemas:
AllContacts:
type: array
items:
$ref: '#/definitions/ContactModel1'
example:
Homes:
$ref: '#/components/examples/Homes'
Watson:
$ref: '#/components/examples/Watson'
examples:
Holmes:
value:
id: 1
first_name: Sherlock
last_name: Holmes
Watson:
value:
id: 2
first_name: John
last_name: Watson
I get this unexpected result in Swagger UI:
[
{
"value": {
"id": 1,
"first_name": "Sherlock",
"last_name": "Holmes",
},
"$$ref": "#/components/examples/Holmes"
},
{
"value": {
"id": 2,
"first_name": "John",
"last_name": "Watson",
},
"$$ref": "#/components/examples/Watson"
}
]
and a similar unexpected example for GET /user/1:
[
{
"value": {
"id": 1,
"first_name": "Sherlock",
"last_name": "Holmes",
},
"$$ref": "#/components/examples/Holmes"
}
]
What am I doing wrong?
I am using this doc as reference:
https://swagger.io/docs/specification/adding-examples/#reuse
This is NOT a valid definition:
components:
schemas:
AllContacts:
type: array
items:
$ref: '#/definitions/ContactModel1'
example:
Homes:
$ref: '#/components/examples/Homes'
Watson:
$ref: '#/components/examples/Watson'
1) The example syntax is wrong. OpenAPI 3.0 has two keywords for examples - example (singular) and examples (plural). They work differently:
example requires an inline example and does not support $ref.
examples is a map (collection) of named examples. It supports $ref - but you can only $ref whole examples, not individual parts of an example. This also means it's not possible to build an example from multiple $refs. Note that not all elements support plural examples.
Note for Swagger UI users: Swagger UI currently supports example (singular) but not examples (plural). Support for examples is tracked in this issue.
2) The Schema Object only supports singular example but not plural examples. In other words, schemas support inline examples only.
3) In OpenAPI 3.0, schema references use the format #/components/schemas/..., not #/definitions/...
I'd like to use the same EXAMPLE definition for Holmes in both cases, the array of users and the single user.
There's no way to reuse a part of an example in this case. You'll have to repeat the example value in both schemas:
components:
schemas:
ContactModel1:
type: object
properties:
...
example:
id: 1
first_name: Sherlock
last_name: Holmes
AllContacts:
type: array
items:
$ref: '#/components/schemas/ContactModel1'
example:
- id: 1
first_name: Sherlock
last_name: Holmes
- id: 2
first_name: John
last_name: Watson
Use allOf for arrays
components:
schemas:
AllContacts:
type: array
items:
$ref: '#/definitions/ContactModel1'
example:
allOf:
- $ref: '#/components/examples/Homes'
- $ref: '#/components/examples/Watson'
I am using graphQL to perform a search across multiple mongoDB collections and API's by combining queries. All queries return a result type of
{
_id: string;
name: string;
type: string;
}
Is there any way to flatten the data into a single array?
Combined query example:
query searchAll {
books(input: {text: "e"}) {
_id
name
type
}
magazines(input: {text: "e"}) {
_id
name
type
}
}
Response currently looks like:
{"data": {
"books": [
{
"_id": "5a8ac759c25b7235ffdc6888",
"name": "someBook",
"type": "book"
}
],
"magazines": [
{
"_id": "5a87005bc25b7235ffdc4bdf",
"name": "someMagazine-1",
"type": "magazine"
},
{
"_id": "5a870067c25b7235ffdc4be4",
"name": "someMagazine-2",
"type": "client"
}
]
}
}
Desired response:
{"data": {
"results": [
{
"_id": "5a8ac759c25b7235ffdc6888",
"name": "someBook",
"type": "book"
},
{
"_id": "5a87005bc25b7235ffdc4bdf",
"name": "someMagazine-1",
"type": "magazine"
},
{
"_id": "5a870067c25b7235ffdc4be4",
"name": "someMagazine-2",
"type": "client"
}
]
}
}
You want to look into using interfaces, here's an example of a (slightly richer) schema definition:
interface Searchable {
id: ID!
name: String!
}
type Book implements Searchable {
id: ID!
name: String!
author: Author!
publisher: Publisher!
isbn: String!
}
type Magazine implements Searchable {
id: ID!
name: String!
publisher: Publisher!
issn: String!
}
input SearchInput {
text: String!
}
type Query {
search(input: SearchInput!): [Searchable!]!
}
Here's how you'd query it:
query searchAll {
search(input: {text: "e"}) {
__typename
id
name
... on Book {
author
isbn
}
... on Magazine {
issn
}
}
}
The resolver for search would be responsible for calling all the different collections and aggregating the results into a single array. Going into more depth than this is implementation-specific, but there should be docs for using interfaces (and unions, which are similar) in whichever GraphQL implementation you're using.
The __typename field is what tells you the concrete type of the returned object, thereby letting front-end code do the appropriate display logic.