Using GraphQL structures to build complex database queries - graphql

I would like to specify inner constraints in a GraphQL query that would limit the results of the outermost query as part of a query / predicate builder I'm working on. I'm not sure if this is considered to be within GraphQL's capabilities but it makes sense to me as something that people would want to do.
For example, I might want to show a list of blog posts that were recently commented on like this:
{
posts{
title
date
comments(since: $earliestDate){
body
date
author {
name
}
}
}
}
The normal behaviour of this would be to bring back all blog posts and only comments that met the criteria.
{
"posts": [
{
"title": "Post 1",
"date": "2017-07-31"
"comments": [
]
},
{
"title": "Post 2",
"date": "2017-06-10",
"comments": [
{
"body": "Comment text",
"date": "2017-08-09",
"author": {
"name": "Michael"
}
}
]
}
]
}
But I want my query to prevent the retrieval of "Post 1" because it has no comments in the last month, but I'm not sure that's something GraphQL will make easy to do.
Is there functionality within GraphQL to support returning this result?
{
"posts": [
{
"title": "Post 2",
"date": "2017-06-10",
"comments": [
{
"body": "Comment text",
"date": "2017-08-09",
"author": {
"name": "Michael"
}
}
]
}
]
}

TL;DR - In general, you want the behavior of a field to only be defined by:
The arguments passed to that field
The identity of the object that is being queried
The global context of the query (eg, the identity of the user executing the query)
I think that the behavior you're after is not really ideal for GraphQL. At least as I've seen, GraphQL tends to treat fields as relatively independent from each other (even though they're nested).
For example, you would not expect arguments passed to a child field to change the behavior of its parent. And similarly, I think you would not expect the presence or absence of a child field to change the behavior of its parent.
The best way to accomplish the behavior you're after would be to add an argument to the posts field, indicating that only posts with comments should be returned:
{
posts(withCommentsOnly: true) {
title
date
comments(since: $earliestDate) {
body
date
author {
name
}
}
}
}
To understand the reasons why, just think about these kinds of scenarios:
{
authors {
id
posts {
comments { body }
}
}
}
If this query has the behavior you describe, for each author, you would only get the posts which also have comments. But then imagine that you re-query one of those authors later:
{
node(id: "author_id") {
... on Author {
posts { title }
}
}
}
Now would you get a different set of posts? That kind of behavior would make it very difficult for people to query your GraphQL schema using standard GraphQL clients, because client-side caching mechanisms would probably not be able to correctly update their caches.

Related

grpc/protobuffer ask for specific fields

GraphQL lets you ask for specific fields, the response contains only the fields that you had asked for. For example:
a graphql query like:
{
hero {
name
}
}
will return:
{
"data": {
"hero": {
"name": "R2-D2"
}
}
}
where as a graphQl query like:
{
hero {
name
friends {
name
}
}
}
would return:
{
"data": {
"hero": {
"name": "R2-D2",
"friends": [
{
"name": "Luke"
},
{
"name": "Han Solo"
},
{
"name": "Leia"
}
]
}
}
}
Is there a similar mechanism/library/pattern that can be used in gRPC to achieve the same?
FieldMask is similar in protobuf. It is a list of fields to retain, so the first example would be paths: "hero.name" and the second would be paths: ["hero.name", "hero.friends.name"].
It is probably most frequently used to specify which fields should be changed in an update. But it can equally be used to specify the fields that should be returned.
The server can either process the FieldMask directly (e.g., only using the listed fields in a SELECT SQL query), or it can retrieve all the information and filter the result using FieldMaskUtil.merge() to copy just the requested fields into a new proto message to return to the client.

GraphQL: Explore API without a wildcard (*)?

I am new to GraphQL and I wonder how I can explore an API without a possible wildcard (*) (https://github.com/graphql/graphql-spec/issues/127).
I am currently setting up a headless Craft CMS with GraphQL and I don't really know how my data is nested.
Event with the REST API I have no chance of just getting all the data, because I have to setup all the endpoints and therefore I have to know all field names as well.
So how could I easily explore my CraftCMS data structure?
Thanks for any hints on this.
Cheers
merc
------ Edit -------
If I use #simonpedro s suggestion:
{
__schema {
types {
name
kind
fields {
name
}
}
}
}
I can see a lot of types (?)/fields (?)...
For example I see:
{
"name": "FlexibleContentTeaser",
"kind": "OBJECT",
"fields": [
{
"name": "id"
},
{
"name": "enabled"
},
{
"name": "teaserTitle"
},
{
"name": "text"
},
{
"name": "teaserLink"
},
{
"name": "teaserLinkConnection"
}
]
But now I would like to know how a teaserLink ist structured.
I somehow found out that the teaserLink (it is a field with the type Entries, where I can link to another page) has the properties url & title.
But how would I set up query to explore the properties available within teaserLink?
I tried all sorts of queries, but I am always confrontend with messages like this:
I would be really glad if somebody could give me another pointer how I can find out which properties I can actually query...
Thank you
As far as I'm concerned currently there is no graphql implementation with that capability. However, if what you want to do is to explore the "data structure", i.e, the schema, you should use schema instrospection, which was thought for that (explore the graphql schema). For example, a simple graphql instrospection query would be like this:
{
__schema {
types {
name
kind
fields {
name
}
}
}
}
References:
- https://graphql.org/learn/introspection/
UPDATE for edit:
What you want to do I think is the following:
Make a query like this
{
__schema {
types {
name
kind
fields {
name
type {
fields {
name
}
}
}
}
}
}
And then find the wished type field to grab more information (the fields) from it. Something like this (I don't know if this works, just an idea):
const typeFlexibleContentTeaser = data.__schema.types.find(t => t === "FlexibleContentTeaser")
const teaserLinkField = typeFlexibleContentTeaser.fields.find(f => f.name === "teaserLink")
const teaserLinkField = teaserLinkField.type.fields;
i.e, you have to transverse recursively through the type field.

is there a way to group queries in graphQL?

I'm trying to group graphQL queries to have a more organized response.
I want to make a query for allEmployees and get back something in the following format
GraphQL Query
{
Employees:allEmployees{
id
firstName
lastName
}
}
Response
{
"data": {
"Employees": [
"new":[
{
"id": "1",
"firstName": "James",
"lastName": "Test"
},
{
"id": "3",
"firstName": "Charles",
"lastName": "Tes"
}
],
"updated":[
{
"id": "4",
"lastName": "Test"
},
],
"deleted":[
{
"id": "1",
},
],
}
}
}
I've looked into a few options to get named sub-request( like new, updated and deleted) via aliases on fragments but that doesn't seem to be a thing. I've looked at unions, but that doesn't seem to be what I'm looking for.
Ideally I would love to query graphql like...
{
Employees:{
new: allEmployees(status:"new"){
id
firstName
lastName
}
updated: allEmployees(status:"updated"){
id
firstName
lastName
}
deleted: allEmployees(status:"deleted"){
id
}
}
but I don't think it is possible to pass a nested query like this.
Is there anyway to do something like this? I'm using graphql with ruby via the graphql-ruby gem.
please let me know if anyone needs more information?
Thanks
Edit
To clarify. We have multiple entities that will follow the new, updated, deleted pattern. Looking to try and get a response where the results are nested inside a parent name/alias (Employees, Users)
{
"data": {
"Employees": [
"new":[...],
"updated":[...],
"deleted":[...],
],
"Users": [
"new":[...],
"updated":[...],
"deleted":[...],
],
...
}
That is why we would want to nest
GraphQL definitely supports nested queries and multiple top-level queries, and graphql-ruby supports these just fine.
If your GraphQL schema looks like:
type Employee {
id: ID!
firstName: String
lastName: String
}
enum Status { NEW, UPDATED, DELETED }
type Query {
allEmployees(status: Status): [Employee!]!
}
then you could write a query
fragment EmployeeData on Employee { id firstName lastName }
query Everyone {
new: allEmployees(status: NEW) { ... EmployeeData }
updated: allEmployees(status: UPDATED) { ... EmployeeData }
deleted: allEmployees(status: DELETED) { ... EmployeeData }
}
That wouldn't have quite the specific form you're looking for – there aren't good ways to add or remove arbitrary levels in your query, like adding an "Employees" label or removing layers from React-style connection records – but it can retrieve the data you're looking for.

GraphQL Move a child property into parent

I'm looking into GraphQL and got to a question I'm not even sure how to look for in the docs. Probably this isn't even supposed to work, but I'll ask anyway.
I have this query:
query {
organization(login: "facebook") {
repositories(first: 5) {
items: edges {
repo: node {
name
owner {
login
}
}
}
}
}
}
Now, in the response I would like a way to place the login next to name, instead of nested in owner login.
So in the response instead of:
{
...
"name": "react-native",
"owner": {
"login": "facebook"
}
}
I would like to have:
{
...
"name": "react-native",
"ownerName": "facebook"
}
Thank you.
From the specification this is not possible. The response has to be shaped in the way the object types are shaped. There is a project called GraphQL Lodash that gives you a new directive to modify the results. It can be helpful here but it is rather experimental and IMO very undogmatic.

How to get name/confidence individually from classify_text?

Most of the other methods in the language api, such as analyze_syntax, analyze_sentiment etc, have the ability to return the constituent elements like
sentiment.score
sentiment.magnitude
token.part_of_speech.tag
etc etc etc....
but I have not found a way to return name and confidence in isolation from classify_text. It doesn't look like it's possible but that seems weird. Am missing something? Thanks
The language.documents.classifyText method returns a ClassificationCategory object which contains name and confidence. If you only want one of the fields you can filter by categories/name or categories/confidence. As an example I executed:
POST https://language.googleapis.com/v1/documents:classifyText?fields=categories%2Fname&key={YOUR_API_KEY}
{
"document": {
"content": "this is a test for a StackOverflow question. I get an error because I need more words in the document and I don't know what else to say",
"type": "PLAIN_TEXT"
}
}
Which returns:
{
"categories": [
{
"name": "/Science/Computer Science"
},
{
"name": "/Computers & Electronics/Programming"
},
{
"name": "/Jobs & Education"
}
]
}
Direct link to API explorer for interactive testing of my example (change content, filters, etc.)

Resources