Skip to content
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

DataStore.query to return connected objects #5111

Closed
jonperryxlm opened this issue Mar 13, 2020 · 19 comments
Closed

DataStore.query to return connected objects #5111

jonperryxlm opened this issue Mar 13, 2020 · 19 comments
Labels
DataStore Related to DataStore category to-be-reproduced Used in order for Amplify to reproduce said issue

Comments

@jonperryxlm
Copy link

Which Category is your question related to?
DataStore

Is there a way to return connections when querying the DataStore?

Let's say I have the following schema:

type Post @model {
    id: ID!
    post: String!
    comments: [Comment] @connection(name: keyName: "byPost", fields: ["id"]
}

type Comment 
    @model
    @key(name: "byPost", fields: ["postId", "postedDate"])
{
    id: ID!
    postId: ID!
    comment: String!
    postedDate: AWSDateTime
}

If I query the Graphql API, I can ask for the following (and this is what would be autogenerated by the CLI)...

{
    id
    post
    comments
        nextToken
        items
            id
            postId
            comment
            postedDate
}

But if I query the DataStore with: DataStore.query(Post, "my-id") I would only get the Post object back, not the related comments. This isn't a big deal for individual posts, as I can just query for the related comments, but consider querying for a list of posts... If I want to display comment information in that list (say a count of comments, last commented date etc.) then I have to do 2 queries and match up all the data.

Am I missing something or is there no way for the DataStore to do this? It has all the required information to join the objects.

If there isn't a way to do this, I'm not really sure what the advantage is of specifying connections when using the DataStore at all.

@yuth
Copy link
Contributor

yuth commented Mar 13, 2020

@jonperryxlm what platform are using to run this query? Is it in JS or iOS?

@jonperryxlm
Copy link
Author

@yuth Javascript, specifically a Vue application. All amplify libraries at latest.

@nikgavalas
Copy link

I'm having the same issue. The snippet of schema part I'm having issues with looks like this:

type ChordNode @model @auth(rules: [{ allow: owner }]) {
	id: ID!
	part: Part @connection(name: "PartChordNode")

	numberOfBeatsPerChord: Int!
	order: Float!
	selectedStateIndex: Int!

	chordNodeStates: [ChordNodeState!] @connection(name: "ChordNodeChordNodeState")
}

type ChordNodeState @model @auth(rules: [{ allow: owner }]) {
	id: ID!
	chordNode: ChordNode @connection(name: "ChordNodeChordNodeState")

	order: Float!

	chords: [Chord!] @connection(name: "ChordNodeStateChord")
}

What I want to do is do a:

const node = DataStore.query(ChordNode, 'my-chordnode-id'); 
node.chordNodeStates?.map(n => {
  // Some code to loop through the ChordNodeStates
});

It looks like what I need to do is

const nodeStates = DataStore.query(ChordNodeState).filter(n => n.chordNode.id === 'my-chordnode-id');
nodeStates.map(n => {
  // Some code to loop through the ChordNodeStates
});

My question with the latter method is: What happens if I have millions of ChordNodeStates for this user. Is that all going to be synced in the DataStore anyway so it's just a cpu thing?

It seems like the power of GraphQL would be to do the nested queries. I could completely be missing something though. Any help is appreciated!

@kaustavghosh06 kaustavghosh06 transferred this issue from aws-amplify/amplify-cli Mar 16, 2020
@apoorvmote
Copy link

apoorvmote commented Mar 17, 2020

I was also having same issues the solution is that its avaialble at one side of one to many relationship
let me demostrate with simpler example of @nikgavalas

    const results = (await Datastore.query(ChordNodeState)).filter(c => c.chordNode.id === 'my-chordnode-id')
  
    console.log(results)

EDIT:
If you use @connection(keyName: then it doesn't return connected objects. However as I said above if you use @connection(name: then you can find connected objects at one side of the relationship.

@sammartinez sammartinez added DataStore Related to DataStore category to-be-reproduced Used in order for Amplify to reproduce said issue labels Mar 17, 2020
@nikgavalas
Copy link

I was also having same issues the solution is that its avaialble at one side of one to many relationship
let me demostrate with simpler example of @nikgavalas

    const results = (await Datastore.query(ChordNodeState)).filter(c => c.chordNode.id === 'my-chordnode-id')
  
    console.log(results)

EDIT:
If you use @connection(keyName: then it doesn't return connected objects. However as I said above if you use @connection(name: then you can find connected objects at one side of the relationship.

So I’m using @connection(name: if you look at my schema and I’m still not getting the one to many connected back child objects.

Also, I can use the code you suggested. I listed that as a solution. If you read the last part of my post you can see my question :).

@apoorvmote
Copy link

@nikgavalas
Actually I changed my mind. Well yeah I didn't read last part of your comment. You already have the solution. And your question is different. But I feel @connection(name: is dead to me now. I am going with @connection(keyName: I was using keyName but something crashed so I quickly went back to name. Now I have changed my mind.

My question with the latter method is: What happens if I have millions of ChordNodeStates for this user. Is that all going to be synced in the DataStore anyway so it's just a cpu thing?

It seems like the power of GraphQL would be to do the nested queries. I could completely be missing something though. Any help is appreciated!

GraphQL is also dead to me now. Long live Datastore. If you have millions of ChrodNodeStates then Datastore will download all million to your device that you can query however you want when offline.
You no longer have to do nested queries. Datastore will basically replicate data from dynamodb table and only sync changes.

If you want the power of nested GraphQL then abandon Datastore and use API query just fetch the data you needed to show.

@nikgavalas
Copy link

Oh interesting, so the DataStore doesn’t use GraphQL underneath the hood?

@apoorvmote
Copy link

apoorvmote commented Mar 21, 2020

Yes it does use GrapthQL. I think you can probably modify it to get nested queries with DataStore.

Just go down to your generated graphql directory. Look for syncChordNode or listChordNode and modify it to fetch all ChordNodeState objects. You as client decides what data to request. That includes nested ChordNodeState array.

Also remember you are overriding autogenerated code so you will need to change every time you make update to schema and autogenerate new code.

@mdebo
Copy link

mdebo commented Mar 21, 2020

I meet the same problem - Im using a one to many relationship
type School {
childGroups: [ChildGroup]
@connection(keyName: "byCrecheByMinimumAge", fields: ["id"])
}
And when I query using DataStore.query(School, 'my-school-id') childGroups is undefined.
If I query directly ChildGroup I can retrieve them... what's going wrong?

@gyandeeps
Copy link

No to sound monotonous here but having the same issue. I am also using the example right from the docs. Feels little weird that datastore has been active since December and you would think the examples from the docs would work. Unless i am totally missing something here.

@undefobj
Copy link
Contributor

@gyandeeps @mdebo can you post your GraphQL schemas and client code so that we can take a look?

@jonperryxlm @nikgavalas when querying relations such a "comments on a post" you will need to obtain a reference to the parent and then perform a filter on the results at this time, such as in the following example:

https://aws-amplify.github.io/docs/js/datastore#querying-relations

This will eagerly return the values for the connected objects which is why you apply the filter. We have an optimization coming in the future which will allow you to do lazy evaluation which should address the question of "millions of child records" but for most applications that don't have this scale yet the optimization isn't necessary. We will introduce it in a way though that you can take advantage of that optimization in the future.

@gyandeeps
Copy link

gyandeeps commented Mar 23, 2020

Schema

type Picture @model @auth(rules: [{ allow: owner }]) {
    id: ID!
    url: String!
    tags: [Tag!]! @connection(keyName: "byPicture", fields: ["id"])
}

type Tag
    @model
    @auth(rules: [{ allow: owner }])
    @key(name: "byPicture", fields: ["pictureId", "name"]) {
    id: ID!
    pictureId: ID!
    name: String!
}

Queries

Here is the getPicture query generated, which i am not sure if it is correct or not.

First run

export const getPicture = /* GraphQL */ `
    query GetPicture($id: ID!) {
        getPicture(id: $id) {
            id
            url
            tags {
                items {
                    id
                    pictureId
                    name
                    _version
                    _deleted
                    _lastChangedAt
                    owner
                }
                nextToken
                startedAt
            }
            _version
            _deleted
            _lastChangedAt
            owner
        }
    }
`;

Second run

then i delete the api apmlify remove api and regenerated it and this time its different.

export const getPicture = /* GraphQL */ `
  query GetPicture($id: ID!) {
    getPicture(id: $id) {
      id
      url
      tags {
        nextToken
        startedAt
      }
      _version
      _deleted
      _lastChangedAt
      owner
    }
  }

Observations

  1. Whenever i save Picture object, all the _ fields are undefined. It does get saved as i can verify in local.
  2. It never makes it to the dynamo db.
  3. When i add tags they get created correctly but its never returned with the picture query. I have to query them separately and then filter which matches the pictureId.
  4. Adding another picture after all this will have the version field and other populated.
  5. But still the tags dont get pulled in picture query. this happened both times based on 2 different query structure.

@undefobj
Copy link
Contributor

@gyandeeps What does your DataStore app code look like? Could you create a gist of it for us to see? The above GraphQL queries don't need to bee built by you, the DataStore sync engine will do these conversions automatically. For example how are you saving the Picture object with DataStore.save()? Did you initialize your project with amplify-app or manually with amplify update api to enable sync on your backend?

@gyandeeps
Copy link

gyandeeps commented Mar 23, 2020

To save a picture:

DataStore.save(
    new Picture({
        url: "<fill with some data>",
        tags: []
    })
)

To add tags I do this:

DataStore.query(Picture, (p) => p.url("eq", "<some data>"))
    .then((picData) => {
        const pic = picData[0];
        return pic;
    })
    .then((pic) =>
        DataStore.save(
            new Tag({
                name: "<some string>",
                pictureId: pic.id
            })
        )
    )

Manually with amplify add api . Like i said i can see data saved up in dynamo db. First call to picture save have issue for some magical reason.

@manueliglesias
Copy link
Contributor

I think this might be a duplicate of #5054

See #5054 (comment)

Closing it for now, let me know if it needs to be re-opened

@gyandeeps
Copy link

Since i am new to all this so with limited understanding, i dont think all the problems discussed in here are present in #5054 . Their might be some overlap i guess.

@rpostulart
Copy link

I have the same problem:

Schema:
`
type Quiz @model @auth(rules: [{ allow: public, provider: iam }]) {
id: ID!
title: String!
subscribers: [Subscribers] @connection(name: "QuizSubscribers")
questions: [Questions] @connection(name: "QuizQuestions")
seconds: Int!
currentQuestion: String
questionOrder: String
started: Boolean
view: Int
}

type Questions @model @auth(rules: [{ allow: public, provider: iam }]) {
id: ID!
quiz: Quiz @connection(name: "QuizQuestions")
image: String
youtube: String
question: String!
answerOne: String
answerOneCorrect: Boolean
answerTwo: String
answerTwoCorrect: Boolean
answerThree: String
answerThreeCorrect: Boolean
answerFour: String
answerFourCorrect: Boolean
order: Int
}
`

This query is not working
const questions = await DataStore.query( Questions, c => c.quiz.id === quizid )

@atlascoder
Copy link

atlascoder commented May 10, 2020

the question of "millions of child records" but for most applications that don't have this scale yet the optimization isn't necessary

Sorry, @undefobj, but I'd like to understand your point better..

Let's suppose we have a chatting app with rooms. So that, a user can post messages to different rooms. Of course, many approaches are possible here, but, anyway, a kind of relation like User->Message will exist. So, if I've understood you right, then every query for messages means loading of all messages in memory and filtering then - is it right?

If true, I think that it's can't be called as an optimization - this is more a design feature, that should provide fast messages filtering.

I am not working on such an app, and it's true that in my case I have only several items in the datastore. But I am confused with this from start, because the current approach with filtering looks... at least strange.

@github-actions
Copy link

This issue has been automatically locked since there hasn't been any recent activity after it was closed. Please open a new issue for related bugs.

Looking for a help forum? We recommend joining the Amplify Community Discord server *-help channels or Discussions for those types of questions.

@github-actions github-actions bot locked as resolved and limited conversation to collaborators Jun 11, 2021
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
DataStore Related to DataStore category to-be-reproduced Used in order for Amplify to reproduce said issue
Projects
None yet
Development

No branches or pull requests