Skip to content

GraphQL: How to query data from array of pointers #5894

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
murshudov opened this issue Aug 8, 2019 · 9 comments
Closed

GraphQL: How to query data from array of pointers #5894

murshudov opened this issue Aug 8, 2019 · 9 comments
Labels
type:feature New feature or improvement of existing feature

Comments

@murshudov
Copy link

This is my query

query JobQuery {
  objects {
    getJob(objectId: "sqvJTrN9no") {
      title
      company {
        name
      }
      countries
    }
  }
}

and it returns

{
  "data": {
    "objects": {
      "getJob": {
        "title": "Sales Assistant",
        "company": {
          "name": "Gulfstream Distribution"
        },
        "countries": [
          {
            "__type": "Pointer",
            "className": "Country",
            "objectId": "lZ15JnGq7h"
          }
        ]
      }
    }
  }
}

But I want to get country details inside this query results. Is there a way to do that?

I thought maybe something like this exists:

query JobQuery {
  objects {
    getJob(objectId: "sqvJTrN9no") {
      title
      company {
        name
      }
      countries
    }
    
    findCountry(where: { objectId: { _in: ["lZ15JnGq7h"]} }) {
      results {
        name
      }
    }
  }
}

Result:

{
  "data": {
    "objects": {
      "getJob": {
        "title": "Sales Assistant",
        "company": {
          "name": "Gulfstream Distribution"
        },
        "countries": [
          {
            "__type": "Pointer",
            "className": "Country",
            "objectId": "lZ15JnGq7h"
          }
        ]
      },
      "findCountry": {
        "results": [
          {
            "name": "Azerbaijan"
          }
        ]
      }
    }
  }
}

I don't know how to include results from getJob query into findCountry query. Any help would be appreciated 😊

@davimacedo
Copy link
Member

It works pretty much like you expect if you have a field that is a Pointer or a Relation. Since it is an Array, and an Array can have any kind of item, the Parse GraphQL Server does not know beforehand what kind of object you have there and can't make its fields automatically available. We can think about some solutions for this. Maybe allow the developer to specify the type of the array? @Moumouls @omairvaiyani @douglasmuraoka

@omairvaiyani
Copy link
Contributor

One option is to introduce this into the Parse GraphQL Config:

{
 ...
 classConfigs: [
 {
  className: "Country",
  type: {
      pointerArrays: [ {  field: "cities",  className: "City"  }, {  field: "states",  className: "State"  }  ]
  }
 }
] 
 ...
}

Longer-term would be to introduce this knowledge directly into the database schema, although I've not assessed the drawbacks yet.

@davimacedo
Copy link
Member

@omairvaiyani I like this idea. I am only wondering if this information could be also useful for Parse Server overall. So maybe having this information in the parse server schema would be better?

@omairvaiyani
Copy link
Contributor

@davimacedo it certainly would be. I understand that the Parse Server _Schema class stores the field type as "array", regardless of whether the array stores regular POJOS, or Parse pointers. The source code also freely allows pointers to be changed from one class to another. If we did introduce a specific type, it would result in stricter behaviour, possibly classified as a breaking change.

A way around this would be to allow the current type "array" to continue behaving exactly as it does today, whilst allowing those who wish to upgrade to store the type as e.g. "array$_User", where the $ would signal a bifurcation in the SchemaController.

This migration could be sped up using a simple code snippet that runs through an active database (at runtime), and updates the _Schema class with extracted array pointer classes where available.

@davimacedo
Copy link
Member

We could also leave the Array data type as it is now (to avoid the breaking change or any migration) and just add a metadata specifying it is a pointer Array (like we did in the required fields). What do you think? Is this something that you would be willed to tackle?

@Moumouls
Copy link
Member

Moumouls commented Aug 12, 2019

We could also have a huge problem with multi pointers in an Array (ex: [User, Role, Product]).

I think the solution is to implement an InlineFragment strategy: GraphQL InlineFragment

Tasks:

  • Create an Union Type: ArrayResult, add all Classes to the UnionType and a ScalarObjectType: Implement Union Type
  • Add doc on the field about the InlineFragment strategy
  • Implement the InlineFragment on Array Resolvers

Example:

query JobQuery {
  objects {
    getJob(objectId: "sqvJTrN9no") {
      title
      company {
        name
      }
      countries {
        ... on Country {
          name
        }
      }
    }
}

Multi Pointer example:

query JobQuery {
  objects {
    getJob(objectId: "sqvJTrN9no") {
      title
      company {
        name
      }
      relatedTo {
        ... on Country {
          name
          objectId
        }
        ... on ProfessionalCode {
          readableName
          code
          objectId
        }
      }
    }
}

@davimacedo
Copy link
Member

@Moumouls thanks for the suggestion. I think it is really the best way to solve this issue! I will take a look in your PR and revert my feedback.

@Moumouls
Copy link
Member

Moumouls commented Aug 14, 2019

@murshudov on the master branch you can test the new powerful InlineFragment feature, example here:

Query

{
  objects {
    getCountry(objectId: "8dDDVCHN2q") {
      objectId
      name
      companies {
        ... on CompanyClass {
          objectId
          name
          employees {
            ... on EmployeeClass {
              objectId
              name
            }
          }
          teams {
            ... on TeamClass {
              objectId
              name
              employees {
                ... on EmployeeClass {
                  objectId
                  name
                  country {
                    objectId
                    name
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}

Response

{
  "data": {
    "objects": {
      "getCountry": {
        "objectId": "8dDDVCHN2q",
        "name": "imACountry",
        "companies": [
          {
            "objectId": "jk2pZjsDsv",
            "name": "imACompany",
            "employees": [
              {
                "objectId": "7KBQ8bOpZA",
                "name": "imAnEmployee"
              }
            ],
            "teams": [
              {
                "objectId": "dRiSUajdpq",
                "name": "imATeam",
                "employees": [
                  {
                    "objectId": "7KBQ8bOpZA",
                    "name": "imAnEmployee",
                    "country": {
                      "objectId": "8dDDVCHN2q",
                      "name": "imACountry"
                    }
                  }
                ]
              }
            ]
          }
        ]
      }
    }
  }
}

Don't be afraid of performance when diving into objects, this example was executed in 17ms (macbook pro i7 15" 2016), with only one call REST Parse Server.

@murshudov
Copy link
Author

@Moumouls and others, thank you very much for the great effort and awesome work 👍

@mtrezza mtrezza added type:feature New feature or improvement of existing feature and removed type:improvement labels Dec 6, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type:feature New feature or improvement of existing feature
Projects
None yet
Development

No branches or pull requests

5 participants